我试图通过专门为该任务生成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(数组)时,它执行的速度比我使用访问者模式时的双分派慢;
发布于 2012-09-30 10:21:20
如果我理解正确,那么您将试图通过发出代码本身并直接调用该方法来消除调用虚拟方法的开销。例如,您要调用一个虚拟函数,而不是调用数千个虚拟函数。
但是,您希望不同的对象具有相同的接口。只有通过虚拟调用才能实现这一点。要么实现接口,要么使用委托,要么发出代码。是的,即使您发出代码,也需要某种接口来调用该方法,该方法可能是调用委托或将其转换为func/action预定义委托。
如果您想要有一些有效的方法来发出代码,我建议使用"LambdaExpression.CompileToMethod“。这个方法是使用一个方法生成器,而我假设您已经有了一个方法生成器。你可以在网上看到很多例子。但是,这也会导致一个虚拟电话。
因此,如果您希望在许多对象中具有相同的接口,那么除非您将对象放入关于对象类型的不同回收箱中,否则就不能进行非虚拟调用。这是反对多元主义的。
发布于 2012-10-02 22:29:41
您是否曾试图通过将循环封装在try-catch块中来欺骗JIT使用更快的内存?这还具有消除退出条件的优点,因此可以节省一些IL。
try
{
for (int i= 0; ; i++)
{
var visitor = new Visitor(array);
for(int i = 0;; ++i)
array[i].Accept(visitor);
}
}
catch (IndexOutOfRangeException)
{ }它看起来很糟糕,但它利用了JIT内存分配的怪癖,这可能有助于解决您的IL性能问题。
有关此问题的更多信息,请参见Optimisation of for loop。
发布于 2012-10-04 19:44:22
如果您真的感兴趣的超级优化您的代码,您需要学习IL!
看看下面链接的IL OP代码..。
http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes(v=vs.95).aspx
还可以使用ILDasm查看从方法中生成的代码.
虽然我怀疑您无法对IL进行很大的优化,但是用C++编写它和调用非托管代码会好得多.
只是给你一个想法..。
祝你好运马修
https://stackoverflow.com/questions/12590054
复制相似问题