我试图通过专门为该任务生成 IL 来提高项目中某些代码的性能。
该任务目前是通过对数组元素执行 for 循环并通过接口运行各种方法来完成的。我想用专门执行此任务的 IL 替换它,而无需任何虚拟/接口调用(通过直接执行所需的操作)。
出于某种原因,此 DynamicMethod的运行时性能比执行每个元素的接口调用的原始代码的运行时性能要慢得多。我能看到的唯一原因是我的 DynamicMethod 非常大(数组的每个元素有几条指令)。
我认为这可能是由于 JIT 而导致的第一个缓慢的调用,但事实并非如此。所有的调用都比较慢。有没有人遇到过这样的事情?
编辑
这里的人求代码。。原来的代码挺大的,这里是缩小版的(用反向模式AD计算函数梯度的自动微分代码)。我的数组中的所有元素都继承了以下类
abstract class Element
{
public double Value
public double Adjoint
public abstract void Accept(IVisitor visitor)
}
我有两个派生自元素的类。为简单起见,我将仅定义以下两个
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); 时访问者模式的双重调度慢。