Opsの改善

スケーラブルなメモリアロケーターの使用を検討する

メモリアロケーターは、ユーザーアプリケーションからメモリのリクエストを受け取ります(経由してmalloc())そして、アプリケーションが使用できるメモリ位置のアドレスで応答します。アプリケーションがメモリの使用を終了すると、このメモリをアロケータに返すことができます(経由してfree())。ほとんどのアプリケーションでメモリが中心的な役割を果たすことを考えると、メモリアロケータのパフォーマンスは非常に重要です。

オペレーティングシステムとC標準ライブラリの実装に同梱されているほとんどの汎用アロケーターは、高度にマルチスレッド化されたアプリケーション内で使用すると、多くの落とし穴に悩まされる可能性があります。

  • 必要以上のメモリブロックのオペレーティングシステムへの要求。オペレーティングシステムへの各リクエストは、より高い特権レベルに切り替えてカーネルモードでコードを実行するためにリクエストが行われたCPUを必要とするシステムコールになります。アロケーターがオペレーティングシステムに対して、必要以上にメモリを割り当てる要求を行うと、アプリケーションのパフォーマンスが低下する可能性があります。
  • 相互排除のための並行処理プリミティブの使用。多くの汎用アロケーターは、マルチスレッドアプリケーションでの使用を目的として設計されておらず、クリティカルセクションを保護するために、ミューテックスまたはその他の同時実行プリミティブを使用して正確性を強化しています。高度にマルチスレッド化されたアプリケーションで使用される場合、ミューテックスの使用は(直接的または間接的に)アプリケーションのパフォーマンスに悪影響を及ぼす可能性があります。

ほとんどのGNU / Linuxオペレーティングシステムで使用される汎用アロケーターは、ptmalloc2またはそのバリアントです。ptmalloc2は低いメモリオーバーヘッドを示しますが、複数のスレッドから要求が行われるとスケーリングに苦労します。したがって、プロファイリングがシーンの呼び出しにかなりの時間を費やしていることを示している場合malloc()/free()汎用アロケーターをスケーラブルな代替物に置き換えることを検討してください。

内部的にGeolib3-MTは、クリティカルパス内のメモリに対するいくつかの要求を満たすための賢明なjemallocを含む、メモリ割り当てを処理するための多くのテクニックを使用します。

可能な場合、カスタムOpをスレッドセーフとしてマークする

Geolib3-MTは、場所を並行して調理することにより、複数のコアにまたがってスケーリングするように設計されています。Opは、以下を呼び出すことにより、スレッドセーフではないことを宣言できます。 FnGeolibCookInterface::setThreading()、その場合、Opで場所を調理している間にグローバル実行ロック(GEL)を取得する必要があります。これにより、他のスレッドセーフなOpが調理場所から妨げられるため、シーンのトラバース中に非効率的なポケットが発生する可能性があります。シーンのプロファイリングでは、まずすべてのスレッドセーフでないOpを特定して変換します。

スレッドセーフでないOpsは、 Render Log:OpツリーでスレッドセーフでないOpが検出されると、警告が発行されます。

[WARN plugins.Geolib3-MT.OpTree]:Op(<opName>:<opType>)はThreadModeGlobalUnsafeとマークされています-これによりパフォーマンスが低下する可能性があります。

可能な場合はカスタムOpsを折りたたみ可能としてマークします

実世界のシーンには、次のOp Chain構造の多くのインスタンスが含まれています。

これは、さまざまな理由で発生する可能性があります。 AttributeSetノード、それらは頻繁に使用されますhotfix特定の有効化属性がレンダリング時に存在することを確認するシーン。または、Opsのシーケンスは、特定のノードグラフの作成中に実行される一連の論理ステップを表すことができます。ただし、類似のOpのチェーンは、Geolib3-MTの潜在的なオーバーヘッドと最適化の機会の両方を表していますOpsの改善

シーンでは、Opツリーのトポロジを最適化するGeolib3-MTの機能を利用できます。カスタムOpsは、呼び出すことで折りたたむことができることを示す場合がありますFnGeolibCookInterface::setOpsCollapsible()。この関数はFnAttribute::StringAttributeパラメータとして、その値は折りたたまれたOpsの引数が渡される属性の名前を示します。

たとえば、4つのチェーンを考えますAttributeSet上記の操作。Geolib3-MTは、各OpのOp引数を収集して、 GroupAttribute呼ばれたbatch、その子にはチェーン内の各opのOp引数が含まれます。

  • バッチ
    • Op1
    • Op2
    • Op3
    • Op4

注意:  Op引数は、トップダウンの順序で折りたたまれたOpに渡されます。

カスタムユーザーOpsは、次を呼び出すことでOp Chainの折りたたみ機能に参加できます。 setOpsCollapsible()上記のように。上記の例ではAttributeSet呼び出しsetOpsCollapsible(“batch”)そして、その間にバッチモードで実行されているかどうかをテストしますcook()コール。

Opチェーン崩壊システムに参加している場合、Opは、Opsの崩壊したチェーンを処理した結果が、それぞれを順番に処理することと同一でなければならないことを会社に保証します。これの意味は、主に、上流の場所または属性のクエリに関連しています。それらの場所または属性は、崩壊したチェーンによって生成または変更された可能性があります。具体的な例として、最初のOpが属性を設定する2 Opチェーンを考えますhello=world and the second Op in the chain prints Hi There! if hello is equal to world. If the second Op uses Interface::getAttr() to query the value of hello in the collapsed chain the result is empty, as hello has been set on the location’s output but did not exist on the input. これを改善するには、opをリファクタリングして、 getOutputAttr()代わりに。

Cache frequently accessed attributes

While access of an FnAttributeのデータは安価であり、 FnAttributeオブジェクトは参照カウントを変更する必要があります。これは通常は問題ではありませんが、一時的または短命の多くを作成するOpsで蓄積される可能性がありますFnAttributeオブジェクト、特に多数のスレッドが一度にOpを実行している場合。の場合FnAttributeインスタンスが頻繁に使用される場合は、このオーバーヘッドを回避するためにキャッシュされるかどうかを検討してください。

具体的な例として、次のOpScriptスニペットを検討してください。

ローカル関数generateChildren(count)

i = 1の場合、カウント

ローカル名= Interface.GetAttr(“ data.name”)。getValue().. tostring(i)

Interface.CreateChild(name)

終わり

終わり

この関数は、入力属性から派生した名前で、count個の子を作成しますdata.name。カウントが大きい場合、ループ内でこの属性にアクセスすると、2つの理由で不要な作業が発生します。

  • Luaは、オブジェクトの割り当てと割り当て解除を行う必要がありますdata.name反復ごとに属性を設定し、ガベージコレクターの作業を増やします。
  • この割り当てと破棄により、属性のアトミック参照カウントが増加および減少し、スレッド間にストールが発生する可能性があります。

2番目のボトルネックは、同じ属性セットにアクセスするOpScriptsが多くのスレッドで実行される場合に適用されます。参照カウントに頻繁にアクセスすることを避けるために、このスニペットは次のように書き換えられます。

ローカル関数generateChildren(count)

ローカルステム= Interface.GetAttr(“ data.name”)。getValue()

i = 1の場合、カウント

ローカル名= stem .. tostring(i)

Interface.CreateChild(name)

終わり

終わり

いまdata.nameループ外で1回のみアクセスされるため、ループの実行中に参照カウントは変更されません。