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

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

class Simulation { Dictionary 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 Input, IDictionary Output) { for (int i = 0; i < N; ++i) { foreach (KeyValuePair j in Input) nodes[j.Key] = j.Value[i]; ProcessOneSample(); foreach (KeyValuePair j in Output) j.Value[i] = nodes[j.Key]; } } } 

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

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

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

有没有人对这样的东西有任何经验可以分享?

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

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

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

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

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

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