表达式树与 IL.Emit 的运行时代码专业化
我最近了解到可以在运行时生成 C# 代码,我想使用此功能。我有一些代码可以执行一些非常基本的几何计算,例如计算线平面相交,我认为通过为某些方法生成专门的代码可以获得一些性能优势,因为许多计算是针对同一平面或同一直线执行的一遍又一遍。通过专门计算交叉点的代码,我认为我应该能够获得一些性能优势。
问题是我不知道从哪里开始。通过阅读一些博客文章和浏览 MSDN 文档,我发现了两种在运行时生成代码的可能策略:表达式树和 IL.Emit。使用表达式树似乎更容易,因为无需了解有关 OpCode 和各种其他 MSIL 相关复杂性的任何内容,但我不确定表达式树是否与手动生成的 MSIL 一样快。那么对于我应该采用哪种方法有什么建议吗?
I recently learned that it is possible to generate C# code at runtime and I would like to put this feature to use. I have code that does some very basic geometric calculations like computing line-plane intersections and I think I could gain some performance benefits by generating specialized code for some of the methods because many of the calculations are performed for the same plane or the same line over and over again. By specializing the code that computes the intersections I think I should be able to gain some performance benefits.
The problem is that I'm not sure where to begin. From reading a few blog posts and browsing MSDN documentation I've come across two possible strategies for generating code at runtime: Expression trees and IL.Emit. Using expression trees seems much easier because there is no need to learn anything about OpCodes and various other MSIL related intricacies but I'm not sure if expression trees are as fast as manually generated MSIL. So are there any suggestions on which method I should go with?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
两者的性能通常是相同的,因为使用与您自己使用的相同的底层系统函数在内部遍历表达式树并将其作为 IL 发出。理论上可以使用低级函数发出更高效的 IL,但我怀疑是否会带来任何实际重要的性能增益。这取决于任务,但与表达式树发出的 IL 相比,我还没有对发出的 IL 进行任何实际优化。
我强烈建议使用名为 ILSpy 的工具来反向编译 CLR 程序集。这样您就可以查看实际遍历表达式树并实际发出 IL 的代码。
最后,需要注意的是。我在语言解析器中使用了表达式树,其中函数调用绑定到运行时从文件编译的语法规则。编译是这里的关键。对于我遇到的许多问题,当您想要实现的目标在编译时已知时,那么您将不会通过运行时代码生成获得太多性能。某些 CLR JIT 优化可能也不适用于动态代码。这只是我的实践意见,您的领域会有所不同,但如果性能至关重要,我宁愿查看本机代码、高度优化的库。如果不使用 LAPACK/MKL,我所做的一些工作将会非常缓慢。但这只是一个没有被要求的建议,所以要持保留态度。
The performance of both is generally same, as expression trees internally are traversed and emitted as IL using the same underlying system functions that you would be using yourself. It is theoretically possible to emit a more efficient IL using low-level functions, but I doubt that there would be any practically important performance gain. That would depend on the task, but I have not come of any practical optimisation of emitted IL, compared to one emitted by expression trees.
I highly suggest getting the tool called ILSpy that reverse-compiles CLR assemblies. With that you can look at the code actually traversing the expression trees and actually emitting IL.
Finally, a caveat. I have used expression trees in a language parser, where function calls are bound to grammar rules that are compiled from a file at runtime. Compiled is a key here. For many problems I came across, when what you want to achieve is known at compile time, then you would not gain much performance by runtime code generation. Some CLR JIT optimizations might be also unavailable to dynamic code. This is only an opinion from my practice, and your domain would be different, but if performance is critical, I would rather look at native code, highly optimized libraries. Some of the work I have done would be snail slow if not using LAPACK/MKL. But that is only a piece of the advice not asked for, so take it with a grain of salt.
如果我处于您的情况,我会尝试从高级别到低级别的替代方案,增加“所需的时间和精力”并减少可重用性顺序,一旦性能暂时足够好,我就会停止,即:
首先,我会检查 Math.NET、LAPACK 或一些类似的数字库是否已经具有类似的功能,或者我可以根据我的需要调整/扩展代码;
第二,我会尝试表达式树;
第三,我会检查Roslyn 项目(即使它是预发行版本);
第四,我会考虑用不安全的 C 代码编写通用例程;
[第五,我会考虑辞职并在不同的职业中开始新的职业:)],
并且只有当这些都不起作用时,我才会如此绝望地尝试在运行时发出 IL。
但也许我对低层次的方法有偏见;您的专业知识、经验和观点可能会有所不同。
If I were in your situation, I would try alternatives from high level to low level, in increasing "needed time & effort" and decreasing reusability order, and I would stop as soon as the performance is good enough for the time being, i.e.:
first, I'd check to see if Math.NET, LAPACK or some similar numeric library already has similar functionality, or I can adapt/extend the code to my needs;
second, I'd try Expression Trees;
third, I'd check Roslyn Project (even though it is in prerelease version);
fourth, I'd think about writing common routines with unsafe C code;
[fifth, I'd think about quitting and starting a new career in a different profession :) ],
and only if none of these work out, would I be so hopeless to try emitting IL at run time.
But perhaps I'm biased against low level approaches; your expertise, experience and point of view might be different.