我有一个规则引擎,它支持两种操作模式:
- 编译成C#程序并链接到引擎
- 解析成基于堆栈的反向抛光指令并解释
规则是带有函数调用的简单算术表达式(max、min、sin、cos 等)
我会假设编译版本(即#1)比解释版本(即#2)快很多——事实上,这是在中使用编译模式的主要原因第一名。然而,我的速度测试显示并非如此。
编译版本
Action<double>[] Rules = new[] { calc1, calc2, calc3 ... };
double[] v = new double[...]; // Variables
void calc1(double arg) { v[3]=v[12]+v[15]/v[20] }; // "x3=x12+x15/x20"
void calc2(double arg) { ... };
:
// Start timer now
Rules.AsParallel().ForAll(r => r(...));
// End timer
解释版
Expression[] Rules = ...
// Each rule is already parsed into an Expression object, which is a set of
// reverse-polish stack-based instructions.
// For example, "x3=x12+x15/x20" will be parsed to:
// [ Push(12), Push(15), Push(20), Divide(), Add() ]
// Start timer now
Rules.AsParallel().ForAll(r => r.Evaluate(...));
// End timer
在这里,“表达式”是第三方库的一部分,该库将一个简单的字符串解析为一组简单的基于反向抛光堆栈的指令,然后可以对其进行解释。它不是 LINQ 中的表达式树对象 -- 只是为了澄清。
注意:不要担心并发性,因为在实际代码中,我按“层”对规则进行排序并按顺序计算层,每一层仅取决于前一层计算的值。两种模式具有完全相同的层结构。
结果
令人震惊的是,解释版本的运行速度比编译版本快 MUCH,平均快 4 倍!换句话说,编译版本需要 0.3 秒来运行大约 1,200 条规则,而解释版本平均需要 0.08-0.1 秒。
我的电脑是马马虎虎的双核 Core2。
我正在使用 .NET 4.0、Visual Studio 10。
调试或发布版本的性能相似。
我的问题
什么会导致编译模式显着变慢?
注意:我已经发布了一个可能的答案
最佳答案
.NET是一个JIT编译环境,所以JIT编译的代码越多,速度就越慢。可能有 1,200 个方法在运行时在执行时进行了 JIT 编译,而在解释模式下,只有解释器进行了一次 JIT 编译。我可能会在编译模式的循环中看到额外的 JIT 时间。
实验:
- 每个模式运行 5 次(只是为了完成任何 JIT 编译和填充缓存)
- 运行 50 次,取平均值
结果:
- 编译模式:每次运行 1.6 毫秒
- 解释模式:每次运行 5.3 毫秒
观察:
似乎在编译模式的FIRST 运行期间花费了大量时间。
编译模式的SECOND运行速度已经与解释模式相似。
解释模式不会随着运行次数的增加而显着加速。
因此表明我的观点,规则代码在第一次运行时是 JIT 编译的。
关于c# - 编译表达式比解释版本运行得慢得多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5963649/