以下の投稿で、個人でも可能なCOVID-19に対する研究への貢献として、Folding@homeへコンピューティングパワーを提供する方法を紹介しました。
wave.hatenablog.com
世界中から同プロジェクトへの貢献が殺到しているようで、自分のマシンのCPUやGPUに作業(WorkUnit)が割り振られずに、無駄に遊ばせてしまう状態が発生するようになっています。公式サポートフォーラムにもそれらしいトピックが3/14に投稿されており、以下の言及があります。
We've been overwhelmed with the support we're getting from new Donors.
Several servers ran out of WU's overnight.
The issue is being addressed as fast as the project's owner can get to it.
Unfortunately the server(s) may have to be taken off-line while the new WUs are being generated.
Folding Forum • View topic - Temporary server outages
ざっくり意訳すると、「新しい貢献者(ドナー)からの支援に圧倒されており、いくつかのサーバーは夜間にWU(WorkUnit)が不足してしまっている。この事象にはプロジェクトオーナーができるだけ早く対処するけど、残念ながら新しいWUを生成する間は(複数の)サーバーをオフラインにしなければいけないかも。」といった感じの内容です*1。
せっかく強力な演算能力を持ったマシンも、WorkUnitが得られなければアイドル状態で無駄に電力消費する箱でしかありません。故に、可及的速やかに新しいWorkUnitを得て演算を開始させたいところです。
もちろん、自動でWorkUnitの再ダウンロード試行は行われますが、これが曲者でリトライ回数が増えるごとに倍々に近い時間(正確なロジックは不明)、リトライ間隔が延ばされます。その結果、リトライまで数時間待ちとなっていることもあります。
調べてみたところ、直ちにWorkUnitの再ダウンロードを試行させる直接的な方法は無いようで、Folding@homeのプロセスを終了して再起動させるとか、マシン自体を再起動させることでWorkUnitのダウンロードを試行させるような方法しか見つかりませんでした。
いろいろ試行錯誤してみたところ、以下の手段が効果的だったので紹介します。
実行中のWorkUnitの処理が完了する前に、次のWorkUnitのダウンロードを開始する
単にFolding@homeをインストールしたままの状態だと、実行中のWorkUnitの処理がほぼ(99%)完了するまで次のWorkUnitのダウンロードは開始されません。
ダウンロードを試行して1発でWorkUnitが得られるなら無駄なタイムロスは少ないですが、複数回のリトライを想定すると事前にダウンロードしておくことが望ましいです。
実行中のWorkUnitの処理の進行具合が指定値に達したら次のWorkUnitのダウンロードを開始させる機能は公式に実装されており、Extra client optionsのnext-unit-percentageというオプションを使います。
Configuration guide – Folding@home
next-unit-percentageという名称からも察せられますが、これは実行中のWorkUnitの処理が何%まで完了したら次のWorkUnitのダウンロードを開始するかを指定します。ダウンロードのリトライが多発する現状では小さな値を指定したいところですが、最小値は90に制限されていますので、90を指定するのが良いでしょう。
FAHControl(Advanced Control)のConfigureボタンからExpertタブを選択するとExtra client optionsの追加ができます。
お使いのマシンのスペックによって異なりますが実行中のWorkUnitの残り10%の処理に要する処理時間中に、(リトライごとに増える待ち時間を含めて)次のWorkUnitが首尾よくダウンロードできれば効率的にシステムリソースを献上できます。が、リトライ時間が膨れ上がって残り10%の処理も完了してしまうと、GPUまたはCPUは暇な状態となってしまいます。
(2020/4/20追記)
以下の方法はFolding@home 7.6.9でコマンドサーバーの挙動が変わったため、依然と同様には使えなくなりました(ウェルカムメッセージが変わっていたり、request-wsのレスポンスが出力されなくなっていたりするため、期待通りの動作をしません)。pause/unpauseでリトライ間隔を短縮するのは引き続き有効なようです。末尾に7.6.9への暫定対応スクリプトを追記しました。
ダウンロードのリトライ間隔を短縮する(それなりにPC系の知識ある人向け)
FAHControlでDownloadステータスのWorkQueueのNextAttemptに表示されている時間が、次回ダウンロード試行までの待ち時間ですが、これが数十秒~数分ならともかく、数時間に膨れ上がっていると、座して待つのは合理的とは思えません。かと言って、即時にダウンロードを試行する機能はありませんので、何とかしたいと思います。
隠しコンソール?
ところで、FAHControlの左側のClients欄に127.0.0.1:36330と表示されていることに気づいている方もいるかも知れませんが、デフォルトでインストールするとPort: 36330が開いてることにお気づきでしょうか。これ、アクセスできるんじゃね?と思い、telnetでlocalhostのポート36330に接続してみたところ繋がりました。
$ telnet localhost 36330 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Welcome to the Folding@home Client command server. >
このプロンプトでftp等と同じノリでhelpと叩いてみたらコマンド一覧出てきました。
> help auth Authenticate. error Error message. exit Exit the command processor heartbeat Prints an increasing hearbeat count. log-updates start | restart | stop Enable/diable log updates. quit Exit the command processor screensaver Unpause all slots which are paused waiting for a screensaver and pause them again on disconnect. updates add <id> <rate> <expression> | del <id> | list | clear | reset Enable/disable updates. Folding@home Client: always_on [slot] Set all or one slot(s) always on. bond <ip>:<port> <input> [output] [ip:port] Bond a packet file to a outgoing debug socket connection. configured Return a PyON message indicating if the client has set a user, team or passkey. do-cycle Run one client cycle. download-core <type> <url> Download a core. finish [slot] Finish all or one slot(s). get-info <category> <key> Print application information info Print application information in PyON format inject <ip>:<port> <input> [output] [ip:port] Inject a packet file to a listening debug socket. Will wait until packet is processed. mask-unit-state Disable specified unit states. num-slots Get number of slots in PyON format. on_idle [slot] Set all or one slot(s) on idle. option <name> [value] Get or set a configuration option options List or set options with their values. If no name arguments are given then all options with non-default values will be listed. If the '-d' argument is given then even defaulted options will be listed. If the '-a' option is given then unset options will also be listed. Otherwise, if option names are provided only those options will be listed. The special name '*' lists all options which have not yet been listed and is affected by the '-d' and '-a' options. If a name argument is followed directly by an equal sign then the rest of the arugment will be used to set the option's value. If instead a name argument is followed immediately by a '!' then the option will be reset to its default value. Options which are set or reset will also be listed. Options are listed as a PyON format dictionary.[-d | -a] | [<name>[! | =<value>]]... pause [slot] Pause all or one slot(s). ppd Get current total estimated Points Per Day. queue-info Get work unit queue information in PyON format. request-id Request an ID from the assignment server. request-ws Request work server assignment from the assignment server. save [file] Save the configuration either to the specified file or to the file the configuration was last loaded from. shutdown Shutdown the application simulation-info <slot id> Get current simulation information. slot-add <type> [<name>=<value>]... Add a new slot. Configuration options for the new slot can be provided. slot-delete <slot> Delete a slot. If it is running a unit it will be stopped. slot-info Get slot information in PyON format. slot-modify <id> <type> [<name><! | =<value>>]... Modify an existing slot. Configuration options can be either set or reset using the same syntax used by the 'options' command. slot-options <slot> [-d | -a] | [name]... The first argument is the slot ID. See 'options' help for a description of the remaining arguments. trajectory <slot id> Get current protein trajectory. unpause [slot] Unpause all or one slot(s). uptime Print application uptime wait-for-units Wait for all running units to finish. Standard Commands: add <number> <number> Add two values clear Clear the screen date [format] Print the date and time. Optionally, with 'format'. See: man strftime div <number> <number> Divide two values eq <string> <string> True if arguments are equal eval [expr]... Evaluate all arguments if <cond> <expr1> [expr2] If 'cond' evaluates to a non-empty string then evalute 'expr1' otherwise, if provided, evaluate 'expr2' less <string> <string> True the first argument is lexigraphically less than the second mul <number> <number> Multiply two values neq <string> <string> True if arguments are not equal not <expr> Invert the truth value of the argument sleep <seconds> Sleep for a number of seconds sub <number> <number> Subtract two values
隠し機能を見つけたかと思いきや、実は3rd party FAHClient APIとして公開されているようです。
3rd party FAHClient API · FoldingAtHome/fah-control Wiki · GitHub
なお、Firewallでポート36330への通信をブロックしていなければLAN内のマシンからアクセスされるリスクがありますが、OSコマンドを実行できそうなコマンドは無いので、隠れた脆弱性でもなければシステムを危険にさらすリスクは少なさそうです(WorkUnitの停止/開始や使用スレッド数を変更されたりして、体感上のシステムパフォーマンスを変えたりされる可能性は否定できませんが、LAN内に侵入されている時点で…)。
ちなみに、Folding@homeの設定でパスワードを指定していれば、パスワード認証しない限りポート36330に接続してもhelpを含めた各種コマンド実行を受け付けませんので、ハードニングの観点からはパスワード設定もしておいた方がいいかもしれません*2。
リトライ間隔短縮のために使えそうなコマンド
いろいろ試してみたところ、上記FAHClient APIで用意されているコマンドのうち、request-ws, pause [slot], unpause [slot], の3つが使えそうです。
そもそも、WorkUnitのダウンロードに失敗しているときのログを確認すると、概ね以下の3種類ありそうです。
WARNING:WU00:FS00:WorkServer connection failed on port 8080 trying 80 ERROR:WU00:FS00:Exception: Failed to connect to HOSTADDRESS:80: 接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。
サーバーの80番ポートが応答しないから8080番で再試行(あるいは逆に8080⇒80)してみたけど失敗というパターンです。ネットワークの問題も考えられますが、恐らくはサーバーが落ちているのでしょう。冒頭に引用したフォーラムの内容から察するに、新規WorkUnitを生成する作業などを行っているのだと想像されます。
WARNING:WU00:FS00:Failed to get assignment from 'HOSTADDRESS:8080': No WUs available for this configuration
この構成に向けたWorkUnitは無いと言っているパターンです。CPU用とGPU用ではWorkUnitが違う*3ため、CPU用のWorkUnitを要求した時に、サーバーにはGPU用のWorkUnitしかなかった場合や、逆にGPU用を要求した時にCPU用のWorkUnitしかないとか言った場合に発生するのだと思われます。なお、GPU用WorkUnitも多分CUDA用とOpenGL用の2種類が存在してそうな気がします*4ので、非NVIDIAプラットフォームのGPU搭載機でCUDA用WorkUnitしか持ってないサーバーに要求した場合も同様の挙動となると想像されます。
ERROR:WU00:FS00:Exception: Server did not assign work unit
サーバーがWorkUnitを割り振ってくれないと言っているパターンです。configurationについての指摘が無いので、単にそのサーバーが割り当てるWorkUnitが尽きたのだと想像されます。
これらのログが継続している場合でも、相手側サーバーのIPアドレスは逐次異なっています。要するに、理由はどうであれWorkUnitを得られなければ、(リトライ間隔を増して待機したうえで)他のサーバーに仕事をもらいに行くという挙動となっています。これを複数回繰り返して、WorkUnitを割り当ててくれるサーバを引き当てれば無事ダウンロードが始まるという流れです。
そこで、request-wsコマンドが使えそうな気がします。Request work server assignment from the assignment server.と書いてありますから。
> request-ws PyON 1 error "Could not get an assignment"
叩いてみると、上記のように割り当てられなかったと出力されるパターンと、何も出力されずにプロンプトに戻るパターンがあります。何も出力されなかった場合は、WorkUnitを割り当てることができる状態にあるサーバーに割り当てられたという意味だと想像しています(実際は不明)。
ですが、WorkUnit再ダウンロードまでの時間が膨れ上がっているとダウンロードは開始されません。
ここでpause / unpauseコマンドが活きました!pause / unpauseコマンドはその引数に指定したスロットの処理を停止/再開することができます。で、説明には何も書かれていないのですが、何故かunpauseした際にNextAttempt時間が激減します。unpause後に即時~約1分30秒以内にダウンロードが試行されるような挙動を示します。
なお、前述のrequest-wsで"Could not get an assignment"が表示されなかった後でも、unpause後のダウンロード試行で失敗することは多々あります。
故に、ダウンロードが開始されるまで、pause ⇒ request-ws ⇒ unpauseを繰り返せば良さそうだと解ります。手で叩くのは面倒ですし、そもそもこの状態にいつ陥っているのか解りませんので、状態検知も含めて自動化しましょう。
以下のスクリプトはWSLでUbuntu18.04を導入済みのWindows10Pro環境で動作を確認しています。
- 事前準備
telnetの自動応答をするためにexpectを使います。未導入の環境ではexpectパッケージを導入してください。
$ sudo apt install expect
以下のシェルスクリプト(fah_autorequest.sh)と、expectスクリプト(fah_reqws.exp)はwsl環境内の~/bin/に配置する前提です。両スクリプトともに、実行権限を付与してパーミッションは744等にしてください。
$ cat fah_autorequest.sh #!/bin/bash TMPFILE=/tmp/fah_response.tmp # Get status ( sleep 1 && echo queue-info && sleep 1 ) | telnet localhost 36330 > ${TMPFILE} # Parse the response # 使用するマシンのスロット数によってループ範囲を調整(GPU非搭載機は00だけにする) for ID in 00 01; do grep "\"slot\": \"${ID}\"" ${TMPFILE} | grep "\"state\": \"DOWNLOAD\"" | sed -e 's/,/\r\n/g' | grep next | grep -v "0.00 secs" > /dev/null if [[ $? == 0 ]]; then grep "\"slot\": \"${ID}\"" ${TMPFILE} | grep "\"state\": \"RUNNING\"" > /dev/null if [[ $? == 1 ]]; then echo "Slot:${ID} needs work unit!" ~/bin/fah_reqws.exp ${ID} fi fi done rm ${TMPFILE}
- 自動応答用expectスクリプト(pauseしてCould not get an assignmentが出なくなるまでrequest-wsをSLEEPTIME毎に叩き、unpauseして終了)(Folding@home 7.5.1用、7.6.9では期待した動作をしません)
$ cat fah_reqws.exp #!/usr/bin/expect set TARGETHOST localhost set TARGETPORT 36330 set TARGETSLOT [lrange $argv 0 0] set SLEEPTIME 10 set timeout 5 spawn env LANG=C /usr/bin/telnet ${TARGETHOST} ${TARGETPORT} expect { "Welcome to the Folding@home Client command server." { send "pause ${TARGETSLOT}\n" sleep 1 send "request-ws\n" exp_continue } "Could not get an assignment" { sleep ${SLEEPTIME} send "request-ws\n" exp_continue } timeout { send "unpause ${TARGETSLOT}\n" sleep 1 send-user "finished request work-server for slot ${TARGETSLOT}\n" exit } }
unpause後にWorkUnitが確実にダウンロードできるとは限りませんので、fah_autorequest.shをサイクリックに叩く必要があります。ここではWindows標準のタスクスケジューラを使うことにします*5。
タスクスケジューラからWSL内のシェルスクリプトは直接は叩けないので、ラッパーとなるバッチファイル(fah_autorequest.bat)を書きます。配置パスは任意の場所で構いません。
>type fah_autorequest.bat wsl ~/bin/fah_autorequest.sh
このバッチファイルをタスクスケジューラから一定間隔(最短5分毎)で実行すれば、WorkUnitのダウンロードのNextAttemptに何時間も待たされるようなことは無くなります*6。
これで、SARS-CoV-2を殲滅するための研究に、より多くの時間のコンピューティングリソースを貢献できます!
以下追記
- 自動応答用expectスクリプト(pause/unpauseのみ)(Folding@home 7.6.9用暫定対応版)
7.6.9でコマンドサーバーの挙動が変わったことに対応したスクリプトです。ウェルカムメッセージが変わっている事、request-wsのレスポンスが無くなったことに対応し、単にpause/unpauseのみ実行します。一見無意味ですが、前述の通りpause/unpauseでリトライ間隔が劇的に短縮される挙動は変わっていないようですので、リトライ時間が雪だるま式に膨れ上がるのは抑止できます。
※このスクリプトは場当たり的に修正しただけで、長時間動かして問題ないことを確認したわけではありません。予期しない問題が発生する可能性があります。
#!/usr/bin/expect set TARGETHOST localhost set TARGETPORT 36330 set TARGETSLOT [lrange $argv 0 0] set SLEEPTIME 3 set timeout 5 spawn env LANG=C /usr/bin/telnet ${TARGETHOST} ${TARGETPORT} expect { "Welcome to the FAHClient command server." { send "pause ${TARGETSLOT}\n" sleep 1 send "unpause ${TARGETSLOT}\n" sleep 1 send-user "finished request work-server for slot ${TARGETSLOT}\n" exit } }
以上。
*1:文中の夜間(overnight)は現地時間の意味だと思われます。
*2:FAHControlのConfigureボタンからConnectionタブを選択すると一番下にPassword設定があります。パスワード設定した場合、ポート36330に接続後にパスワードを尋ねてはこないので、authコマンドでパスワード入力します。
*3:FahCore:0xA7がAMD64(X86?)用、FahCore:0x22がOpenGL用を意味しているのではと想像しています。
*4:Folding@homeを実行できるNVIDIAプラットフォームが手元に無いので未確認。
*5:既にWSLでcrondを常時生かしている環境であれば、タスクスケジューラを使わずにcronからfah_autorequest.shを数分おきに叩けばいいです。
*6:あまりにも短時間間隔で実行すると、上記スクリプトは排他制御を行っていないため、多重起動されてpause/unpause周りでおかしな挙動をするかもしれません。そもそも、短時間過ぎるのはFolding@homeのサーバー側にも多少なりとも負荷をかける可能性もありますから自重しましょう。