アフィリエイト広告を利用しています

故障予防・熱対策の最新記事

AppNapライクにWebProcessをコントロール(cputhrottle & applescript)

Firefoxをセーフモードで使用するようにしてから、
safariのWebProcessがCPU使用率の上位に位置することが多くなりました。

裏に隠れている場合にCPUの使用率を低下させてくれるという
次期OS X Mavericksには、App Napという魅力的な省電力機能があります。

processの使用上限を設定できる
cputhrottleというコマンドを見つけました。

これを使って、App Napライクなことができるのでは?と鈍く閃いたwので試してみます。

やりたいこと


AppNapライクを使えるならば、Firefoxをセーフモード→通常モードで使用する。

AppNap-browser applescriptの機能


CPUの制御対象はsafari(WebProcess), firefoxに限定する
  • safariがアクティブの場合は、WebProcessのCPU の上限を指定しない。
  • safariにフォーカスがない場合は、WebProcessのCPU上限を5%に制限する。
  • firefoxがアクティブの場合は、FirefoxのCPUの上限を指定しない。
  • firefoxにフォーカスがない場合は、FirefoxのCPU上限を5%に制限する。

準備


cputhrottleのインストール


cputhrottleをダウンロード後、コンパイル済みのバイナリがgz形式で保存されています。
> gzip -dc cputhrottle.gz
で解凍できます。
(私の環境では、拡張子がgzですが、実行形式だったようです。
 cp -p cputhrottle.gz cputhrottleでうまくいきました)

実行するために実行権限を与える必要があります。
> chmod +x cputhrottle

また、実行には、管理者権限が必要なため、sudoで実行する必要があります。
ひとまず、実行形式を/tmp/cputhrottleに置きました。

WebProcessを相手に挙動を確認したところcputhrottleで指定後、
CPU 制限中にcputhrottleプログラムは、常時生きている必要があるようです。
(Ctrl+Cで終了させて、制御終了)

AppleScript


ウィンドウの状態を監視し、cputhrottleを呼び出す
AppNap-browser.app(applescript)を作成します。

ウィンドウのアクティブ状態が変化した!というイベントを検出する方法が
わかりませんでした。
そのため、on idleで常に監視する苦肉の策で対応します。

AppNap-browser.app

global CPU_SLOW_PERCENTAGE
global myProcessDataList

set CPU_SLOW_PERCENTAGE to "5"
set myProcessDataList to {}

script ProcessData
property pid : -1
property procName : ""

on new(thePid, theProcName)
copy me to theData
set_pid(thePid) of theData
set_procName(theProcName) of theData
theData
end new
on set_pid(value)
set pid to value
end set_pid
on set_procName(value)
set procName to value
end set_procName
end script

on FindProcessDataList(theList, theProcName, retList)
repeat with v in theList
if procName of v is equal to theProcName then
set end of retList to v
end if
end repeat
return retList
end FindProcessDataList

on DeleteProcessDataList(theList, theProcName)
set retList to {}
repeat with v in theList
if procName of v is not equal to theProcName then
set end of retList to v
end if
end repeat
return retList
end DeleteProcessDataList

on StartBackgroundCpuControl(theProcName, theControledList)
syslog("StartBackgroundCpuControl " & theProcName & "") of me
tell application "System Events"
set pList to (unix id of every process whose name contains theProcName)
if pList is not equal to {} then
repeat with unixID in pList
syslog("cputhrottle " & (unixID as string) & " " & CPU_SLOW_PERCENTAGE) of me
do shell script "/tmp/cputhrottle " & (unixID as string) & " " & CPU_SLOW_PERCENTAGE & " >/dev/null 2>&1 & echo $!" with administrator privileges
set pid to result
set end of theControledList to new(pid, theProcName) of ProcessData
end repeat
end if
end tell
end StartBackgroundCpuControl

on StopBackgroundCpuControl(theControledList)
syslog("StopBackgroundCpuControl targetCount=" & (length of theControledList as string) & "") of me
tell application "System Events"
repeat with v in theControledList
do shell script "kill -INT " & (pid of v as string) with administrator privileges
end repeat
end tell
end StopBackgroundCpuControl

on idle
tell application "System Events"
set activeProcs to every process whose frontmost is true
set activeProc to item 1 of activeProcs
tell activeProc
set activeProcName to name
end tell
end tell
if activeProcName is equal to "Safari" then
set targetProcess to {}
set targetProcess to FindProcessDataList(myProcessDataList, "WebProcess", targetProcess)
if length of targetProcess is not equal to 0 then
StopBackgroundCpuControl(targetProcess)
set myProcessDataList to DeleteProcessDataList(myProcessDataList, "WebProcess")
end if
else if activeProcName is equal to "Firefox" then
set targetProcess to {}
set targetProcess to FindProcessDataList(myProcessDataList, "Firefox", targetProcess)
if length of targetProcess is not equal to 0 then
StopBackgroundCpuControl(targetProcess)
set myProcessDataList to DeleteProcessDataList(myProcessDataList, "Firefox")
end if
else
set targetProcess to {}
set targetProcess to FindProcessDataList(myProcessDataList, "WebProcess", targetProcess)
if length of targetProcess is equal to 0 then
-- 制御中は、再実行しない
StartBackgroundCpuControl("WebProcess", myProcessDataList)
end if
set targetProcess to {}
set targetProcess to FindProcessDataList(myProcessDataList, "Firefox", targetProcess)
if length of targetProcess is equal to 0 then
-- 制御中は、再実行しない
StartBackgroundCpuControl("Firefox", myProcessDataList)
end if
end if
return 1
end idle

on syslog(logmsg)
do shell script "logger -s " & logmsg
end syslog


ファイルフォーマット:アプリケーション、オプションとして「ハンドラの実行時に終了しない」を選んで、書き出します。

こちらに新しいバージョンのスクリプトがあります。(追記)
AppNapライクにWebProcessをコントロール(cputhrottle & applescript)v2.0



AppNap-browser.appは、ウィンドウを持ちません。そのため終了するためには
ターミナルからkillコマンドを打ち込む必要があります。
> ps -el | grep AppNap-browser
でPIDを調べて、kill -9 調べたPID で終了させます。
CPU 制御中に終了させた場合、cputhrottleプロセスが生きている可能性があります。
同様に、kill -INTで終了させてください。


実行してみました


system.logに制御を書き出すようにしているので、
ログを見ながら挙動を確認します。

safariがアクティブウィンドウの場合、WebProcess 100%の指示を出しています。
safariではないアプリがアクティブウィンドウの場合、WebProcess 5%の指示を出していることが確認できます。

safari, firefox、その他ウィンドウにアクティブウィンドウを切り替えながらCPUの使用率を
確認(miniUsageを使用)してみると、
若干遅れて、5%制御されているのが確認できました。

この方法の課題は、
  1. Administrator privilegesを指定するため、
    キャッシュが消えたころにパスワードを促すダイアログが表示されて
    面倒に感じる
ですね。
Administrator privilegesのキャッシュ期間はどこで定められているのでしょうか・・

ローテクな手段ですが、AppNapもどき完成です^^

新しいバージョンのスクリプトがあります。
AppNapライクにWebProcessをコントロール(cputhrottle & applescript)v2.0
AppNapライクにSafari,Firefoxをコントロール(cputhrottle & applescript)v3.0

 

 

AppNapライクにWebProcessをコントロール(cputhrottle & applescript)
サブコンテンツ

コメント

comments powered by Disqus

コメントシステムを利用したくない方はお問い合わせからお願いします。
2013.8.19 DISQUS(外部コメントサービス)の利用を開始しました。
Facebook, google, Twitter等のアカウントで投稿可能です。

Microsoft 365 プランの比較 icon

(Microsoft 365 Personal 2,500 円 キャッシュバック キャンペーン 実施中 2022/3/30まで)

  • 祝!初マック(mac book retina 13インチ)!retina美しいです^^マックに関係するTips、情報、はまったことの解決策等
  • Mac野郎なのか
  • プロフィール

このページの先頭へ