c# - DynamicMethod 性能低下

标签 c# .net cil reflection.emit

我试图通过专门为该任务生成 IL 来提高项目中某段代码的性能。

此任务目前是通过对数组元素执行 for 循环并通过接口(interface)运行各种方法来完成的。我想用 IL 代替它,它专门执行此任务而无需任何虚拟/接口(interface)调用(通过直接执行所需的操作)。

出于某种原因,此 DynamicMethod 的运行时性能比按元素执行接口(interface)调用的原始代码的运行时性能很多慢。我能看到的唯一原因是我的 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) 时访问者模式的双重分派(dispatch)要慢;

最佳答案

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

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

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

因此,如果你想在许多对象中拥有相同的接口(interface),你就不能进行非虚拟调用,除非你将对象放入不同类型的容器中。这是反对多态性的。

关于c# - DynamicMethod 性能低下,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12590054/

相关文章:

c# - 具有相同类类型成员的类

c# - Windows/.NET 的 System.Drawing.Save(Stream, ImageFormat) 中的错误。生成损坏的 PNG

c# - .NET 单元测试项目组织

c# - 如何从 PEVerify 诊断 "Type load failed"

c# - CIL - 装箱/拆箱与可空

C# 变量作用域不一致

c# - 如何并排加载c#对象

c# - 任务并行库执行长时间运行的操作

c# - OpCodes.Ret 使用的地址存储在哪里?可以改变吗?

c# - 代码隐藏中带有转换器的 DataTemplate