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

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

2018.12.15 macyarounanoka(管理者)のメールアドレスを失って、disqusにログインできなくなっていました。 そのためmacyarounanoka-2で返信するように変わっています。 エキサイトのフリーメールアドレス使っていたことに気がつけませんでした。 ちょっとやってしまいました感がありますOrz。 記事へのコメントはいままで通りdisqusでお願いします。 個別のお問い合わせはお問い合わせからお願いします。
2013.8.19 DISQUS(外部コメントサービス)の利用を開始しました。
Facebook, google, Twitter等のアカウントで投稿可能です。


↓↓Office365 Soloは年間契約がお得です↓↓
Microsoft Public Affiliate Program (JP)

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

このページの先頭へ