7

LuaJIT FFI ドキュメントでは、C から Lua コードへの呼び出しは比較的遅く、可能な場合は避けることを推奨しています。

パフォーマンスが重要な作業にコールバックを使用しないでください。たとえば、ユーザー定義関数を使用して積分する数値積分ルーチンを検討してください。ユーザー定義の Lua 関数を C コードから何百万回も呼び出すのはよくありません。コールバックのオーバーヘッドは、パフォーマンスにとって絶対に有害です。

新しい設計では、プッシュ スタイルの API (結果ごとにコールバックを繰り返し呼び出す C 関数) は避けてください。代わりに、プル スタイルの API を使用します (C 関数を繰り返し呼び出して、新しい結果を取得します)。FFI を介した Lua から C への呼び出しは、その逆よりもはるかに高速です。適切に設計されたほとんどのライブラリは、すでにプル スタイルの API (読み取り/書き込み、取得/入力) を使用しています。

ただし、C からのコールバックがどれほど遅いかはわかりません。コールバックを使用するコードをスピードアップしたい場合、プル スタイル API を使用するように書き直した場合、どのくらいのスピードアップが期待できますか? 各スタイルの API を使用して同等の機能の実装を比較するベンチマークを持っている人はいますか?

4

4 に答える 4

10

私のコンピューターでは、LuaJIT から C への関数呼び出しには 5 クロック サイクルのオーバーヘッドがあります (特に、プレーンな C で関数ポインターを介して関数を呼び出すのと同じくらい高速です)。一方、C から Lua に戻る呼び出しには 135 サイクルのオーバーヘッドがあります。27倍遅い。そうは言っても、C から Lua への 100 万回の呼び出しを必要とするプログラムは、プログラムのランタイムに最大 100 ミリ秒のオーバーヘッドしか追加しません。大部分がキャッシュ内のデータで動作するタイトなループで FFI コールバックを回避することは価値があるかもしれませんが、コールバックが呼び出された場合のオーバーヘッド (たとえば、I/O 操作ごとに 1 回) は、 I/O 自体のオーバーヘッド。

$ luajit-2.0.0-beta10 callback-bench.lua   
C into C          3.344 nsec/call
Lua into C        3.345 nsec/call
C into Lua       75.386 nsec/call
Lua into Lua      0.557 nsec/call
C empty loop      0.557 nsec/call
Lua empty loop    0.557 nsec/call

$ sysctl -n machdep.cpu.brand_string         
Intel(R) Core(TM) i5-3427U CPU @ 1.80GHz

ベンチマーク コード: https://gist.github.com/3726661

于 2012-09-15T06:59:56.093 に答える
7

この問題 (および一般的な LJ) は私にとって大きな苦痛の源であったため、将来誰かを助けることができることを期待して、いくつかの追加情報をリングに投げ込みたいと思います.

「コールバック」は常に遅いとは限らない

LuaJIT FFI のドキュメントでは、「コールバックが遅い」と記載されている場合、LuaJIT によって作成され、FFI を介して C 関数に渡されたコールバックのケースについて非常に具体的に言及しています。これは、他のコールバック メカニズムとはまったく異なります。特に、API を使用してコールバックを呼び出す標準の lua_CFunction を呼び出す場合と比べて、パフォーマンス特性がまったく異なります。

そうは言っても、本当の問題は次のとおりです。Lua C API を使用して、pcall などを含むロジックを実装するのではなく、すべてを Lua に保持するのはいつですか? パフォーマンスに関してはいつものことですが、特にトレース JIT の場合は、答えを知るために (-jp) をプロファイリングする必要があります。限目。

私は、似ているように見えても、パフォーマンス スペクトルの両端に位置する状況を見てきました。つまり、Lua 専用として構造化した方がパフォーマンスが向上するコード (おもちゃのコードではなく、高パフォーマンスのゲーム エンジンを作成するコンテキストでの製品コード) と、構造的に類似しているように見えるコードに遭遇しました。 luaL_ref を使用してコールバックとコールバック引数へのハンドルを維持する lua_CFunction を呼び出して言語境界を導入すると、パフォーマンスが向上します。

測定せずに LuaJIT を最適化するのはばかげたことです

静的言語のパフォーマンス分析の専門家であっても、JIT のトレースを理解するのはすでに困難です。彼らは、あなたがパフォーマンスについて知っていると思っていたすべてを取り上げ、それを粉々に砕きます。関数をコンパイルするのではなく、記録された IR をコンパイルするという概念が、LuaJIT のパフォーマンスについて推論する能力をまだ消滅させていない場合、FFI を介して C を呼び出すことは、JIT が成功した場合、多かれ少なかれ自由であるという事実ですが、潜在的に注文 -解釈されると、同等の lua_CFunction 呼び出しよりもはるかに高価です...まあ、これは確かに状況を限界に押し上げます。

具体的には、あなたが先週書いた C の同等のシステムよりもはるかにパフォーマンスが優れていたシステムが、今週は機能しなくなる可能性があります。なぜなら、そのシステムのトレースに近い場所に NYI を導入したからです。システムが後退し、パフォーマンスが低下しています。さらに悪いことに、あなたは NYI とそうでないものをよく知っているかもしれませんが、JIT の最大記録 IR 命令、最大仮想レジスタ、呼び出し深度、アンロール係数を超えるコードをトレース近接に追加しました。サイドトレースリミット...など

また、「空の」ベンチマークは非常に一般的な洞察を与えることもありますが、LJ では (前述の理由から) コードをコンテキストでプロファイリングすることがさらに重要であることに注意してください。トレースはその性質上非ローカルであるため、LuaJIT の代表的なパフォーマンス ベンチマークを作成することは非常に困難です。大規模なアプリケーションで LJ を使用すると、これらの非ローカルな相互作用が非常に影響力を持つようになります。

TL;DR

LuaJIT の動作を真に理解している人は、この地球上に 1 人しかいません彼の名前はマイク・ポールです。

あなたが Mike Pall でない場合は、LJ の動作とパフォーマンスについて何も想定しないでください。-jv (冗長; NYI とフォールバックに注意)、-jp (プロファイラー! カスタム アノテーション用にjit.zoneと組み合わせます。-jp=vf を使用して、フォールバックのためにインタープリターで費やされている時間の割合を確認します) 、そして、何が起こっているのかを本当に知る必要がある場合は、-jdump(トレース IR & ASM)。測る、測る、測る。LJ のパフォーマンス特性に関する一般化は、その人自身によるものか、特定の使用例で測定したものでない限り、大まかに考えてください (その場合、結局のところ、それは一般化ではありません)。覚えておいてください、正しい解決策はすべて Lua であるかもしれませんし、すべて C であるかもしれません。Lua -> FFI を介した C であるかもしれません。Lua -> lua_CFunction -> Lua であるかもしれません。

何度も何度もだまされて LuaJIT を理解していると思い込み、翌週に間違っていることが証明された人からの情報ですが、この情報が誰かの役に立てば幸いです :) LuaJITについての知識に基づいた推測。私のエンジンは実行ごとに jv と jp のログを出力します。これらは最適化に関して私にとって「神の言葉」です。

于 2017-11-29T19:52:31.927 に答える
5

これらの結果が示すように、パフォーマンスには大きな違いがあります。

LuaJIT 2.0.0-beta10 (Windows x64)
JIT: ON CMOV SSE2 SSE3 SSE4.1 fold cse dce fwd dse narrow loop abc sink fuse
n          Push Time        Pull Time        Push Mem         Pull Mem
256        0.000333         0                68               64
4096       0.002999         0.001333         188              124
65536      0.037999         0.017333         2108             1084
1048576    0.588333         0.255            32828            16444
16777216   9.535666         4.282999         524348           262204

このベンチマークのコードはここにあります。

于 2012-09-08T11:05:19.373 に答える