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%制御されているのが確認できました。
この方法の課題は、
- Administrator privilegesを指定するため、
キャッシュが消えたころにパスワードを促すダイアログが表示されて
面倒に感じる
ですね。
Administrator privilegesのキャッシュ期間はどこで定められているのでしょうか・・
ローテクな手段ですが、AppNapもどき完成です^^
新しいバージョンのスクリプトがあります。
AppNapライクにWebProcessをコントロール(cputhrottle & applescript)v2.0
AppNapライクにSafari,Firefoxをコントロール(cputhrottle & applescript)v3.0