【问题标题】:Expression trees vs IL.Emit for runtime code specialization用于运行时代码专业化的表达式树与 IL.Emit
【发布时间】:2012-01-15 01:00:17
【问题描述】:

我最近了解到可以在运行时生成 C# 代码,我想使用此功能。我有一些代码可以进行一些非常基本的几何计算,例如计算线平面交点,我认为通过为某些方法生成专门的代码可以获得一些性能优势,因为许多计算是针对同一平面或同一直线执行的一遍又一遍。通过专门计算交叉点的代码,我认为我应该能够获得一些性能优势。

问题是我不知道从哪里开始。通过阅读一些博客文章和浏览 MSDN 文档,我发现了两种在运行时生成代码的可能策略:表达式树和 IL.Emit。使用表达式树似乎要容易得多,因为不需要学习任何关于 OpCodes 和其他各种与 MSIL 相关的复杂知识,但我不确定表达式树是否与手动生成的 MSIL 一样快。那么对于我应该采用哪种方法有什么建议吗?

【问题讨论】:

    标签: c#-4.0 code-generation metaprogramming specialization partial-specialization


    【解决方案1】:

    两者的性能通常相同,因为表达式树在内部被遍历并作为 IL 发出,使用的底层系统函数与您自己使用的相同。理论上可以使用低级函数发出更高效的 IL,但我怀疑是否会有任何实际重要的性能增益。这取决于任务,但与表达式树发出的 IL 相比,我没有对发出的 IL 进行任何实际优化。

    我强烈建议使用名为 ILSpy 的工具来反向编译 CLR 程序集。有了它,您可以查看实际遍历表达式树并实际发出 IL 的代码。

    最后,请注意。我在语言解析器中使用了表达式树,其中函数调用绑定到在运行时从文件编译的语法规则。编译是这里的关键。对于我遇到的许多问题,当你想要实现的目标在编译时就知道了,那么通过运行时代码生成就不会获得太多的性能。某些 CLR JIT 优化也可能对动态代码不可用。这只是我实践中的一个意见,你的领域会有所不同,但如果性能很关键,我宁愿看原生代码,高度优化的库。如果不使用 LAPACK/MKL,我所做的一些工作将会非常缓慢。但这只是一个没有被要求的建议,所以请持保留态度。

    【讨论】:

    • 我要补充一点,编写自己的 IL 充满了错过 C# 编译器可能发现的优化的机会。您可能想要发出实际的 C#,并使用 C# 编译器生成您的结果程序集:msdn.microsoft.com/en-us/library/…
    • @ChrisShain,不幸的是,C# 编译器没有做任何有趣的可能优化。出于某种原因,甚至 C++/CLI 也做得更多。这就是为什么我更喜欢自己生成代码,绕过 C# 有限的优化功能。有些东西在 C# 中很容易实现但缺少 - 适当的常量传播、常量参数专门化(带内联)、区域分析和分配消除、循环展开、变量合并、循环不变提升等。
    • C# 编译器不做这些优化的原因是这个责任在 JIT 编译器中。这里的原因是,通过将循环展开等优化转移到 JIT 编译器,它们适用于所有 .NET 语言。当然,JIT 编译器确实会进行循环展开、内联等等。
    【解决方案2】:

    如果我处于你的情况,我会尝试从高级别到低级别的替代方案,增加“需要的时间和精力”并降低可重用性顺序,一旦性能暂时足够好我就会停止,即:

    • 首先,我会检查一下 Math.NET、LAPACK 或一些类似的数字库是否已经具有类似的功能,或者我可以根据自己的需要调整/扩展代码;

    • 其次,我会尝试表达式树;

    • 第三,我会检查Roslyn Project(即使它是预发布版本);

    • 第四,我会考虑用不安全的 C 代码编写通用例程;

    • [第五,我会考虑辞职并开始另一种职业的新职业:)],

    • 只有当这些都不起作用时,我才会如此绝望地尝试在运行时发出 IL。

    但也许我对低级方法有偏见;您的专业知识、经验和观点可能会有所不同。

    【讨论】:

    • 发射 il 是### 中的一大痛点,但你会学到很多关于 clr 的内部工作原理的知识!!!
    猜你喜欢
    • 2012-03-16
    • 2017-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    • 1970-01-01
    相关资源
    最近更新 更多