Posted at

windows maya (GUI環境)から multiprocessing でmayapy を使用する

More than 1 year has passed since last update.


前置き

maya (gui) 起動中何らかの処理を他の maya インスタンスにぶち投げたいということがある。そのような場合どのようにしたらよいだろう? maya にはバッチモードで動作するための仕組みがいくある。ここでは mayapy を maya(gui) から起動すると陥る2つの落とし穴について述べる。

・・・guiからmayapyを使うと、たとえばこのようなことができる


  • 現在のシーンを保存して裏でそのシーンに対して何かする


素朴な起動方法

multiprocessing を利用するには、たとえば以下のようにする、


mpの使用例

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


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' の回避策

先に回避策を書く、


multiprocessing.process.ORIGINAL_DIRを設定する


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 で並列実行させる処理本体は、なるべく平易な構造、モジュールに配し、引数も単純な構造にしてやるとよい。





  1. 行われないケースもある。ソース参照 https://github.com/python/cpython/blob/2.7/Lib/multiprocessing/forking.py#L504