以下の結果についてはWindows 10、Ubuntu 14.04、ev3dev(jessie)で計測していますが、UbuntuについてはParallels仮想環境で実験しているため、実環境のUbuntuとは結果が異なる場合があります。
実験に使用したソースコードは以下から入手できます。
以下の手順で計測
実行周期は1000000Hzに設定してあるため、ほとんど待機しないはずです。
Luaはゲームに関連する処理に利用される頻度が多いため、実装例として10000体のキャラクター(矩形)のあたり判定をする処理で実験します。 Luaはテーブル、C++はmap、Pythonはディクショナリで実装します。
言語 | 結果[s] |
---|---|
Lua | 4.3324 |
LuaJIT | 1.2459 |
C++ | 0.6142 |
Python | 6.4802 |
LuaJITがPythonを圧倒してC++に次ぐ速度を記録しています。 LuaもPythonよりは速い。OpenRTM Lua版に未実装の機能がある分も影響しているかもしれません。
言語 | 結果[s] |
---|---|
Lua | 5.4076 |
LuaJIT | 1.4919 |
C++ | 0.2871 |
Python | 9.4799 |
Windowsでの結果と比較すると、C++が高速化して他が劣化するという謎の結果になりました。 コンパイラの違いでこういうことが発生するのかは謎ですが、ちょっとPythonは遅すぎです。
言語 | 結果[s] |
---|---|
Lua | 5.1760 |
LuaJIT | 3.2487 |
C++ | 1.3929 |
Python | 20.4041 |
Pythonが論外すぎる。LuaがUbuntuの場合よりも高速化しているのが最大の謎。
言語 | 結果[s] |
---|---|
Lua | 0.5343 |
LuaJIT | 0.5372 |
C++ | 0.6214 |
Python | 0.1713 |
Python以外碌な結果になっていない。C++に至っては処理ありの場合よりも遅くなっている。 C++に関してはcoilのsleep関数の精度が悪いのが原因です。 Luaに関しても実行周期が遅い場合は問題なかったため、同様の理由だと思います。 この挙動はバグに近いため、OpenRTM-aistについては今後の修正に期待します。
言語 | 結果[s] |
---|---|
Lua | 0.03327 |
LuaJIT | 0.01445 |
C++ | 0.04378 |
Python | 0.06448 |
色々と謎の残る結果です。 LuaとLuaJITがやたらと速くはなっていますが、未実装部分がどの程度影響を及ぼしているのかは気になります。
言語 | 結果[MB] |
---|---|
Lua | 13.6 |
LuaJIT | 8.6 |
C++ | 1.5 |
Python | 14.7 |
LuaとPythonでそれほどの差はありません。C++は他を圧倒しています。 単純にlua.exeを実行した場合は0.3MB、luajit.exeは0.4MB、python.exeは3.5MBのメモリ使用量だったので、もっと差がつくかと思ったのですが、あまり影響はないようです。 Pythonで使用しているomniORBはCモジュールで実装しているので、その分はメモリ使用量は低くなってもおかしくないはずですが、それでもメモリ使用量でPythonがLuaを上回っているということになります。 ちなみにLua、LuaJITのRTCはメモリ使用量の変動がかなり激しいです。 この実験では実行周期を10Hzと低めに設定してあるため大して変動していませんが、実行周期が高い場合は大きく変動するため注意が必要です。
言語 | 結果[MB] |
---|---|
Lua | 12.8 |
LuaJIT | 8.3 |
C++ | 1.7 |
Python | 16.5 |
概ねWindowsと似た結果。 これらの結果だけ見ればPythonを使うメリットはあまり無いように見えるが、Luaにはマルチスレッド機能がないため不便なことが多い。
LuaやPythonでも普通のPCならば問題はありませんが、メモリが64MBのEV3で起動すると相当な負担になるので使い方を考える必要があります。
念のために詳細な結果も載せておきます。
言語 | VmPeak[kB] | VmSize[kB] | VmLck[kB] | VmPin[kB] | VmHWM[kB] | VmRSS[kB] | VmData[kB] | VmStk[kB] | VmExe[kB] | VmLib[kB] | VmPTE[kB] | VmSwap[kB] | Threads | State |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Lua | 49164 | 47344 | 0 | 0 | 30180 | 28276 | 27068 | 132 | 168 | 3480 | 112 | 0 | 1 | running |
LuaJIT | 34592 | 34136 | 0 | 0 | 17652 | 17180 | 15996 | 132 | 432 | 3176 | 124 | 0 | 1 | running |
C++ | 567016 | 567012 | 0 | 0 | 9984 | 9984 | 524920 | 132 | 88 | 12112 | 164 | 0 | 7 | sleeping |
Python | 675864 | 675344 | 0 | 0 | 24900 | 24900 | 613948 | 132 | 2796 | 8580 | 228 | 0 | 7 | sleeping |
言語 | VmPeak[kB] | VmSize[kB] | VmLck[kB] | VmPin[kB] | VmHWM[kB] | VmRSS[kB] | VmData[kB] | VmStk[kB] | VmExe[kB] | VmLib[kB] | VmPTE[kB] | VmSwap[kB] | Threads | State |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Lua | 34892 | 34892 | 0 | 0 | 15828 | 15828 | 14616 | 132 | 168 | 3480 | 88 | 0 | 1 | sleeping |
LuaJIT | 27296 | 27172 | 0 | 0 | 10388 | 10200 | 9032 | 132 | 432 | 3176 | 120 | 0 | 1 | sleeping |
C++ | 558816 | 493284 | 0 | 0 | 9556 | 9556 | 451192 | 132 | 88 | 12112 | 148 | 0 | 6 | sleeping |
Python | 440828 | 380164 | 0 | 0 | 22716 | 22716 | 318768 | 132 | 2796 | 8580 | 200 | 0 | 6 | sleeping |
VmPeak(最大仮想メモリサイズ)がC++とPythonの場合はLuaよりも桁違いに大きな値となっていますが、これは原因がよく分からないので詳しい人は教えてください。共有ライブラリをロードすると大きくなったりするのでしょうかね?
VmHWM(最大物理メモリサイズ)を見ると、Luaが大きな値になっています。 ただし、それは実行周期が速い場合であり、実行周期が遅い場合はPythonよりもLuaの方が小さくなっています。 基本的には、LuaのRTCで実行周期を上回るような処理をさせないほうがいいという事にはなります。
VmExe(実行ファイルのサイズ)をみると、Pythonが圧倒的に大きくなっており、次いでLuaJITが大きくなっています。 LuaJITをLuaと比較した場合に、地味にデメリットになりそうではあります。
VmLib(ロードされたライブラリのサイズ)を見ると、C++>Python>Luaとなっている。 C++、PythonはomniORBの共有ライブラリのロードが必要なため、その分大きくはなっているのだが、C++はさらにOpenRTM-aistの共有ライブラリのロードが必要なためさらに大きくなります。
スレッド数を見ると、Lua、LuaJITは当然1となっています。 C++、Pythonは7になっていますが、これは5~8で変動します。 ちなみにomniORBを起動するだけでスレッドが3つ起動するため、OpenRTM-aist関連のスレッドはメインスレッドを除くと2~5つという事になります。 とりあえず、OpenRTM-aistで起動するスレッドの数を数えてみました。
名前 | 説明 |
---|---|
PeriodicExecutionContext | 周期実行コンテキストのスレッド。RTCを駆動する。 |
Timer | タイマースレッド。RTCが存在しないときのマネージャ自動終了等のイベントを実行する。設定で無効にできる。 |
get_component_profile | RT System EditorがRTCのプロファイルを取得するオペレーションを呼び出すとスレッドが起動する |
get_component_state | RT System EditorがRTCの状態を取得するオペレーションを呼び出すとスレッドが起動する |
実験では8つのスレッドが起動する場合があったので、もう一つスレッドが起動する場合があるはずですが、何のスレッドなのか特定できませんでした。 普通に使う分にはスレッドが多数起動してもあまり気にならないと思いますが、組込みシステム等で動かす場合はタイマースレッドを無効化する、RT System Editorからは極力操作しない等の工夫が必要です。
今回の実験で使用したRTCにはデータポートが存在しませんが、データポートがある場合はスレッド数が増える可能性があります。 コネクタ接続時にサブスクリプション型をnew、periodicに設定した場合は、コネクタの数だけスレッド数が増えるため、状況によっては注意が必要です。
ちなみにLuaでget_component_profile
、get_component_state
等のオペレーションを呼び出した場合、コルーチンで順番に処理を進めるため、実行コンテキストの処理が遅れる可能性があります。
最後にStateを見てみるとC++、Pythonはsleeping、Luaはrunningになっています。 これらのデータはRTCが非アクティブ状態の時に計測していますが、C++、PythonがRTCが1つもアクティブ状態ではないときにスレッドの処理を一時停止するのに対して、Luaにはそのような機能はないため動き続けるということになっています。とは言っても、なかなか実装が難しいため手が出せていません。
以下の手順で計測
Luaはluacによるコンパイルで起動が高速化するとどこかで見たのですが、見ての通りあまり変わっていません。 まとめてluacを実行する場合は、以下のスクリプトをopenrtm-lua-x.y.z-x64-lua5.1直下にコピーして実行してください。
言語 | 結果[s] |
---|---|
Lua | 0.7851 |
Lua(luac使用) | 0.6749 |
LuaJIT | 0.6276 |
C++ | 0.1315 |
Python | 0.3993 |
お、遅い。LuaとLuaJITがどうしてこんなに遅いのか。 シングルスレッドなのが影響しているのか、Pythonは一度実行するとバイトコードを生成するので速いのか。 ちなみにpycファイルを消した状態で実験すると、Pythonの場合は2秒程かかります。 そう考えるとLuaは凄まじく速いと言えなくもないです。
ちなみに実験ではスレーブマネージャに設定してマスターマネージャは外部で起動した状態で開始していますが、マスターマネージャが起動していない場合は+2秒ぐらいかかります。
言語 | 結果[s] |
---|---|
Lua | 0.510135 |
Lua(luac使用) | 0.505378 |
LuaJIT | 0.310405 |
C++ | 0.015031 |
Python | 0.082029 |
全体の傾向としてWindowsと違いはありません。
言語 | 結果[s] |
---|---|
Lua | 60.1691 |
LuaJIT | 31.1603 |
C++ | 1.1395 |
Python | 25.3410 |
C++と他との差が開きすぎです。 Luaが起動に1分もかかるのは、流石に許容できるレベルではなさそうです。 スクリプト言語を使う場合は、通常はプロセスを1つ起動してモジュールを複数ロードする使い方が正しいかもしれません。
実験結果を見ても分かる通り、Lua実行時に13MB、LuaJIT実行時に9MB程とメモリ消費量は少なくありません。
以下はLua、LuaJITでLuaの実行のみ
、OiLの実行まで
、IDLファイルの読み込みまで
、RTCの実行
の条件でメモリ使用量を比較した結果です。
条件 | 結果[MB] |
---|---|
Luaの実行のみ | 0.7 |
OiLの実行まで | 4.4 |
IDLファイルの読み込みまで | 8.5 |
RTCの実行 | 13.6 |
条件 | 結果[MB] |
---|---|
LuaJITの実行のみ | 0.7 |
OiLの実行まで | 3.1 |
IDLファイルの読み込みまで | 7.5 |
RTCの実行 | 8.6 |
OiLの実行で3~4MBのメモリを使用するため、流石にC++実装のRTCのメモリ使用量を下回るのは無理そうです。
IDLファイルの読み込みで4MB以上激増しているため、この部分は削減する余地がありそうです。 RTSystemEditorからRTCのプロファイルを取得する、ポートを接続する、データをpush、pullする機能以外は全て削ってもいいかもしれません。 マネージャや実行コンテキストも削りましょう。
OpenRTM Luaにより増加する分についても、必要最低限の機能以外を削れば大分減りそうです。
大まかな見積もりですが、LuaJITの場合で4~5MB程度のメモリで実行できそうです。