1

我试图通过专门为该任务生成 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); 时访问者模式的双重调度慢。

4

4 回答 4

1

我很好奇为什么标题是动态方法。当您生成 IL 时。您的意思是动态 IL 生成,然后是静态执行。或者您是否也在生成使用与 c# 动态关键字等效的 IL 的 IL?

动态(运行时)IL

我假设代码只被 jitted 一次。你已经检查过了。

在提供的示例中使用数组而不是泛型增加了神秘感。问题在于生成的 IL 而不是生成 IL 的代码。但是,如果您在生成的 IL 中使用了 ARRAY,您将使用 box unbox。昂贵的堆栈/堆和返回操作。

您是否 IL 生成使用 BOX 和 UNBOX 的代码。IL 操作?我会从那里开始。

集合初始化是下一个要看的地方。

只是其他一些快速的想法:标记 LARGE 代码段以希望节省方法调用开销可能会对 JIT 时间产生负面影响。因为编译器必须处理整个方法/成员。如果你有小方法,它会在需要时编译。但你说这不是 JIT 问题。

这些 LARGE 方法可能有大量的堆栈操作?

经常调用的方法中是否有任何大型 Value 对象?例如大于 64 字节的 STRUCT 对象?每次分配和销毁堆栈。

RedGate 性能分析器告诉您什么? http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/?utm_source=google&utm_medium=cpc&utm_content=unmet_need&utm_campaign=antsperformanceprofiler&gclid=CIXamdiA6bICFQYcpQodDA0Akw

顺便说一句,我是 IL 的新手。只是抛出一些想法。

祝你好运。

于 2012-10-05T05:27:19.923 回答
1

如果我理解正确,您正试图通过发出代码本身并直接调用该方法来消除调用虚拟方法的开销。比如你想调用一个虚函数而不是调用数千个虚函数。

但是,您希望不同的对象具有相同的界面。您只能通过虚拟调用来实现这一点。要么实现接口,要么使用委托,要么发出代码。是的,即使您发出代码,您也需要某种接口来调用该方法,这可能是调用委托或将其转换为 func/action 预定义委托。

如果你想有一些有效的方法来发出代码,我建议使用“LambdaExpression.CompileToMethod”。该方法正在使用方法构建器,而我假设您已经拥有了一个。你可以在互联网上看到很多例子。但是,这也会导致虚拟呼叫。

因此,如果您想在许多对象中具有相同的接口,则除非您将对象放入与其类型有关的不同容器中,否则您不能进行非虚拟调用。这是反对多态的。

于 2012-09-30T10:21:20.030 回答
1

如果你真的对超级优化你的代码感兴趣,你需要学习 IL!

查看以下链接中的 IL OP 代码...

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes(v=vs.95).aspx

还可以使用 ILDasm 查看您从方法中生成的代码...

虽然我怀疑你将无法优化 IL 并且会更好地用 C++ 编写它并调用非托管代码......

只是对你的一个想法...

祝你好运马修

于 2012-10-04T19:44:22.420 回答
1

您是否试图通过将循环封闭在 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 性能问题。

有关这方面的更多信息,请参阅for 循环的优化。

于 2012-10-02T22:29:41.170 回答