了解C#中运行时代码生成的各种选项(Roslyn,CodeDom,Linq Expressions,...?)

dsh*_*let 2 c# linq jit codedom roslyn

我正在开发一个应用程序,我想动态生成用于数值计算的代码(用于性能).将此计算作为数据驱动操作太慢.要描述我的要求,请考虑以下类:

class Simulation
{
    Dictionary<string, double> nodes;

    double t, dt;

    private void ProcessOneSample()
    {
        t += dt;
        // Expensive operation that computes the state of nodes at the current t.
    }

    public void Process(int N, IDictionary<string, double[]> Input, IDictionary<string, double[]> Output)
    {
        for (int i = 0; i < N; ++i)
        {
            foreach (KeyValuePair<string, double[]> j in Input)
                nodes[j.Key] = j.Value[i];
            ProcessOneSample();
            foreach (KeyValuePair<string, double[]> j in Output)
                j.Value[i] = nodes[j.Key];
        }    
    }
}
Run Code Online (Sandbox Code Playgroud)

我想要做的是JIT编译一个在Process中实现外部循环的函数.定义此函数的代码将由当前用于实现ProcessOneSample的数据生成.为了澄清我的期望,我希望所有的字典查找都在编译过程中执行一次(即JIT编译将直接绑定到字典中的相关对象),这样当编译的代码实际上是执行后,好像所有查找都已经过硬编码.

我想弄清楚的是解决这个问题的最佳工具.我问这个问题的原因是因为有很多选择:

  • 使用Roslyn.当前的障碍是如何将语法中的表达式绑定到宿主程序中的成员变量(即'state'字典中的值).这可能吗?
  • 使用LINQ表达式(Expression.Compile).
  • 使用CodeDom.刚刚在我的谷歌搜索中意识到了这一点,以及是什么促成了这个问题.通过.Net中的第三个编译框架,我不会太蠢蠢欲动.
  • 我之前我就知道原来的计划任何这些工具的存在是为了打电话,我JIT编译自己的本机代码(86).我对此有一些经验,但这里有很多未知因素我尚未解决.如果上述选项的性能不足,这也是我的备份选项.我更喜欢上述3个解决方案中的一个,因为我相信它们会更简单,更简单,假设我可以让其中一个解决方案工作!

有没有人有这样的经验可以分享?

Kri*_*ten 6

我不确定我理解你的例子,也不知道代码生成是提高它性能的最好方法.

但是,如果您想了解代码生成选项,请首先考虑您的要求.性能是您想要的,但代码生成的性能和生成的代码的性能.这些定义不一样.然后是代码的可写性和可读性.不同的选项在这个上有很不同的分数.

你的第一个选择是Reflection.Emit,尤其是DynamicMethod.Reflection.Emit是一个非常低级的API,非常高效(即代码生成具有良好的性能).此外,因为您可以完全控制生成的代码,所以您有可能生成最有效的代码(或者显然生成非常糟糕的代码).此外,您不仅限于C#等语言允许的操作,CLR的全部功能都在您的指尖.Reflection.Emit的最大问题是需要编写大量代码,并且需要深入了解IL.编写代码并不容易,之后也不会阅读或维护代码.

Linq.Expressions,更具体地说是Compile方法提供了一个不错的选择.您可以将此视为基本上是使用Reflection.Emit围绕DynamicMethod生成的类型安全包装器.生成代码有一些开销,这可能不是一个大问题.至于表达自由,你几乎可以用普通的C#方法做所有事情.您无法完全控制生成的代码,但质量通常非常好.这种方法的最大优点是使用这种技术编写和读取程序要容易得多.

对于Roslyn,您可以选择生成语法树,或生成C#(或VB)并将其解析为要编译的语法树.由于我们没有可用的生产代码(在撰写本文时),因此可以尽早猜测性能可能是什么.显然,解析语法树会产生一些开销,如果你生成一个单一的方法,Roslyn在paralle中生成多个方法的能力将无济于事.使用Roslyn有可能允许非常易读的程序.

对于CodeDom,我建议反对它.这是一个非常古老的API,(在当前实现中)启动CSC.exe进程来编译代码.我也相信它不支持完整的C#语言.