前置き
maya (gui) 起動中何らかの処理を他の maya インスタンスにぶち投げたいということがある。そのような場合どのようにしたらよいだろう? maya にはバッチモードで動作するための仕組みがいくある。ここでは mayapy を maya(gui) から起動すると陥る2つの落とし穴について述べる。
・・・guiからmayapyを使うと、たとえばこのようなことができる
- 現在のシーンを保存して裏でそのシーンに対して何かする
素朴な起動方法
multiprocessing を利用するには、たとえば以下のようにする、
import multiprocessing
po = multiprocessing.Pool(process_count)
po.map(initialize_process, range(process_count))
res = po.map_async(my_super_heavy_process, arg_list)
# gather results
results = res.get()
po.close()
po.join()
ところが、maya(gui) から実行すると gui
を起動してしまう。そうじゃない 計算資源だけを調達したいので mayapy
を起動したい。それにはドキュメントにあるように set_executable
を使用し起動インタプリンタを設定してやればよい。
multiprocessing:
https://docs.python.org/2/library/multiprocessing.html#multiprocessing.set_executable
import multiprocessing
exe = sys.executable
mayapy = os.path.join(os.path.dirname(exe), "mayapy.exe")
multiprocessing.set_executable(mayapy)
po = multiprocessing.Pool(process_count)
以下略
これでよさそうだが、今度はプロセスをスポーンするさいに不思議なエラーが発生する。
ImportError: 'No module named maya'
関連
https://pikovolt.com/?p=3497
・・・リンク先だけでは不十分なので以下に補足する。
ImportError: 'No module named maya' の回避策
先に回避策を書く、
import multiprocessing
import multiprocessing.process as process
exe = sys.executable
mayapy = os.path.join(os.path.dirname(exe), "mayapy.exe")
multiprocessing.set_executable(mayapy)
process.ORIGINAL_DIR = os.path.join(
os.path.dirname(exe),
"../Python/Lib/site-packages"
)
po = multiprocessing.Pool(process_count)
以下略
ここでいったん multiprocessing
が何をやっているのか、何のために使っているのか振り返ってみよう。やりたいことはメインスレッドとは独立してなにかの処理を子プロセスで成したいわけだ。ではこの子プロセスとは何者であるか。それがこのエラーを解決する手がかりとなる。
multiprocessing
では子プロセスを作成する際、親プロセス環境を(なるべく)複製し受け渡す。具体的には、sys.pathであったり、sys.argv だったりを、無垢の新プロセスに注入している。この時に同時に、multiprocess.process を実行しているスクリプト スクリプトエディタかもしれないし何らかのファイルかもしれないのだが の インポート が行われる1。端折ると multiprocessing はPythonスクリプトファイルからの起動されることを前提としており、何らかのバイナリファイルから使用されることは考慮していないわけだ。これが先の ImportError: 'No module named maya'
の正体だ。 maya.exe から呼び出したから import basename("maya.exe")[0]
(疑似コード)みたいなことを試行する そして失敗する 。
以上の事態を(比較的副作用なさそうに)回避するためのコードが↓だったわけだ
process.ORIGINAL_DIR = os.path.join(
os.path.dirname(exe),
"../Python/Lib/site-packages"
)
ここでは モジュール maya
が multiprocessing から見えるようにしてやっている。"maya" を通すのが筋だろうということでこうしているが、ほかにも回避法はあるのでお好みで運用すればよい。
PicklingError: Can't pickle...
さて ImportError が回避できた!と喜ぶのもつかの間
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed`
に遭遇することになるだろう。しないかもしれない これは何か?エラーメッセージを見ればわかるが Pickle できないと怒られている。何が Pickle できないのか。それは子プロセスに渡す情報:つまり 関数および引数のことだ。この2つが pickleによるシリアライズが通らないと子プロセスに伝えられない。ゆえにエラーが発生する。解決策はいくつか考えられるが、multiprocessing で並列実行させる処理本体は、なるべく平易な構造、モジュールに配し、引数も単純な構造にしてやるとよい。