2

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

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];
        }    
    }
}

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

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

  • 使用罗斯林。当前的绊脚石是如何将语法中的表达式绑定到来自宿主程序的成员变量(即“状态”字典中的值)。这可能吗?
  • 使用 LINQ 表达式 (Expression.Compile)。
  • 使用 CodeDom。最近才在我的谷歌搜索中意识到这一点,以及是什么引发了这个问题。我对在.Net 中的第三个编译框架中跌跌撞撞并不太兴奋。
  • 在我知道之前我的原始计划这些工具存在之前,我最初的计划是调用我自己 JIT 编译的本机代码 (x86)。我对此有一些经验,但是这里有很多未知数我还没有解决。如果上述选项的性能不够,这也是我的备用选项。我更喜欢上述 3 种解决方案中的一种,因为我相信它们会简单得多,假设我可以让其中一种工作!

有没有人有类似的经验可以分享?

4

1 回答 1

7

我不确定我是否理解您的示例,也不确定代码生成是提高其性能的最佳方式。

但是,如果您想了解代码生成选项,请首先考虑您的要求。性能是你想要的,但还有代码生成的性能,以及生成代码的性能。这些绝对不是一回事。然后是代码的可写性和可读性。不同的选项在这一项上有非常不同的分数。

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

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

至于 Roslyn,您可以选择生成语法树,或者生成 C#(或 VB)并将其解析为要编译的语法树。由于我们没有可用的生产代码(在撰写本文时),因此猜测性能可能是什么还为时过早。显然,解析语法树会有一些开销,如果您生成单个方法,Roslyn 并行生成多个方法的能力不会有太大帮助。不过,使用 Roslyn 具有允许非常易读的程序的潜力。

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

于 2013-10-14T16:32:58.927 回答