そのタスク専用の IL を生成することにより、プロジェクト内のコードの一部のパフォーマンスを改善しようとしました。
このタスクは現在、配列の要素に対して for ループを実行し、インターフェイスを介してさまざまなメソッドを実行することによって行われます。仮想/インターフェイスの呼び出しなしで (必要な操作を直接実行することにより)、このタスクを具体的に実行する IL に置き換えたいと考えました。
何らかの理由で、この DynamicMethodの実行時パフォーマンスは、要素ごとにインターフェイス呼び出しを行う元のコードの実行時パフォーマンスよりも大幅に遅くなります。私が見ることができる唯一の理由は、私の DynamicMethod が非常に大きい (配列の要素ごとにいくつかの命令) ことです。
JIT のせいで最初の呼び出しが遅いのではないかと思っていましたが、そうではありません。すべての通話が遅くなります。誰もそのようなことに遭遇しましたか?
編集
ここの人々はコードを要求します..元のコードはかなり大きいですが、縮小版を次に示します (これは、リバース モード AD を使用して関数勾配を計算するための自動微分コードです)。配列内のすべての要素は、次のクラスを継承します
abstract class Element
{
public double Value
public double Adjoint
public abstract void Accept(IVisitor visitor)
}
要素から派生する 2 つのクラスがあります。簡単にするために、次の 2 つだけを定義します。
class Sum : Element
{
public int IndexOfLeft; // the index in the array of the first operand
public int IndexOfRight; // the index in the array of the second operand
public abstract void Accept(IVisitor visitor) { visitor.Visit(this); }
}
class Product : Element
{
public int IndexOfLeft; // the index in the array of the first operand
public int IndexOfRight; // the index in the array of second first operand
public abstract void Accept(IVisitor visitor) { visitor.Visit(this); }
}
ビジターの実装は次のとおりです。
class Visitor : IVisitor
{
private Element[] array;
public Visitor(Element[] array) { this.array = array; }
public void Visit(Product product)
{
var left = array[product.IndexOfLeft].Value;
var right = array[product.IndexOfRight].Value;
// here we update product.Value and product.Adjoint according to some mathematical formulas involving left & right
}
public void Visit(Sum sum)
{
var left = array[sum.IndexOfLeft].Value;
var right = array[sum.IndexOfRight].Value;
// here we update sum.Value and product.Adjoint according to some mathematical formulas involving left & right
}
}
私の元のコードは次のようになります。
void Compute(Element[] array)
{
var visitor = new Visitor(array);
for(int i = 0; i < array.Length; ++i)
array[i].Accept(visitor);
}
私の新しいコードは、このようなことをしようとします
void GenerateIL(Element[] array, ILGenerator ilGenerator)
{
for(int i = 0; i < array.Length; ++i)
{
// for each element we emit calls that push "array[i]" and "array"
// to the stack, treating "i" as constant,
// and emit a call to a method similar to Visit in the above visitor that
// performs a computation similar to Visitor.Visit.
}
}
次に、生成されたコードを呼び出します。これは、Compute(array); を呼び出すときにビジター パターンを使用したダブル ディスパッチよりも遅く実行されます。