for ... collect と for ... do のパフォーマンスの比較
質問:
すべてのオブジェクトを選択するためのスクリプトを手早く作成したところ、undo を使用せず、disableSceneRedraw および suspendEditing
を使用しても処理速度が遅いことが分かりました。
for o in objects do selectmore o
少し考えて、次のようにオブジェクトの配列を選択することにしました。
a=#()
for o in objects do append a o
selectmore a
すると、処理速度が劇的に向上しました(3000% 以上)。
しかし、MAXScript のヘルプを見ても、「処理速度を上げる方法」のセクションにさえもオブジェクトの配列を選択する方法に関する説明がまったくありません。
オブジェクトの選択について知っておくべきことはありますか?
非常に効果的であるように見えるこの方法についてまったく記述がないのはなぜでしょうか?
回答:
真相は、このようなコーディング方法を使用することが珍しいということです。
必要な情報はすべてリファレンスに記載されていますが、「このようにしてはいけません」という記述はありません。これは、誰かが書くかもしれないすべての式を予想することはできないからです。
コレクション全体について 1 回の処理を行う代わりに 1 つずつ処理していくと処理速度が遅くなると予測され、実際に遅くなります。
最初の例は ObjectSet をループしてそれぞれのノードの isSelected プロパティを逐一 true に設定しているため、処理速度が非常に遅くなっています。
2 つ目の例ではマッピングによる方法で selectMore() を呼び出してはいるものの、呼び出しごとに直前の配列をメモリ内で複製することによって配列を大きくしていく
append() を使用しているので、配列の構築が若干非効率です。オブジェクトの数がごくわずかであればそれほどでもありませんが、オブジェクトの数が多くなれば処理速度が低下しはじめます。
式の一例
|
どうしても for ループを使用しなくてはならない場合は、do の代わりに collect を使用します。collect は配列を構築し、明示的に append()
を使用したり、配列を格納するための中間変数を使用したりすることなく結果を返します。選択のスナップショットを保持したい場合には、変数に割り当てることもできます。
これはたとえば以下のようになります:
|
select (for o in objects collect o)
|
この例では、where 条件文を適用して、たとえばすべての可視のオブジェクトを選択することができます。
|
select (for o in objects where not o.isHiddenInVpt collect o)
|
または、すべてのシーン オブジェクトを選択したいのであれば、単に次のようにすることもできます。
|
|
すべてのシーン オブジェクトを何らかの方法で選択したいのであれば selectmore() を使用する理由はありませんし、配列に変換しなくてもシーン オブジェクトの現在のコレクションを操作することもできます。select()
では直前の選択が除去され、selectMore() ではもちろん既存の選択に追加されていくという点に注意してください。
ベンチマーク結果の例
|
時間は select() メソッドの前後で同一のリファレンス コンピュータ上で計測され、ビューポートの再描画にかかる時間は含まれず、ノードの選択済みフラグを設定する時間のみです。
|
式 1 - すべてを選択
|
|
100 個のオブジェクト (ボックス インスタンス) を使用した場合、上の式の処理に 31ms かかりました。
|
1,000 個のオブジェクトを使用した場合、78 ミリ秒 でした。
|
10,000 個のオブジェクトを使用した場合、328 ミリ秒 でした。
|
100,000 個のオブジェクトを使用した場合、15,468 ミリ秒 でした。
|
式 2 - do、append、select を実行
|
a=#()
for o in objects do append a o
select a
|
100 個、1000 個、10,000 個のオブジェクトを使用した場合、上の式のパフォーマンスは最初の式とほぼ同じで、反復が比較的少ない場合には配列管理のメモリ オーバーヘッドは計測されない程度であることが分かりました。
コードは長く、効率は良くありません。
|
100,000 個のオブジェクトを使用した場合、この処理には 17,547 ミリ秒、つまり最初の式よりも 2 秒余計にかかっています。
|
式 3 - テスト、collect、選択を実行
|
select (for o in objects where not o.isHiddenInVpt collect o)
|
上の式は、1000 オブジェクトで約 430ms かかります。 1000 個のオブジェクトに対して条件のテストを行っても大きな変化はありませんが、オブジェクトのプロパティによって特定のオブジェクトを選択するという柔軟性が増しています。
|
式 4 - do と selectMore を実行
|
for o in objects do selectMore o
|
上の式は、1000 個のオブジェクトがすべて既に選択されている場合に 171 ミリ秒 で実行されます(実際には何の処理も行われないため)。ただし、1000 個のオブジェクトが 1 つも選択されていない場合には 674172 ミリ秒 (11 分以上!!!) かかります。!
この方法は使用しないでください。
|
前のヒント
インデックスを使用する for ループとコレクションを使用する for ループのパフォーマンス比較
次のヒント
パーティクル フロー[スクリプト オペレータ](script operator)の for ループを最適化して処理速度を上げる