publicclassNode { public required Operation Op { get; init; } public required int Value { get; init; } public Node? Next = null;
public Foo BuildLambda() { Foo proc = Op switch { Operation.Add => x => x + Value, Operation.Sub => x => x - Value, Operation.Mul => x => x * Value, Operation.Div => x => x / Value, _ => x => x, }; return x => proc((Next?.BuildLambda() ?? (x => x))(x)); } }
var node = Generate(); var sw = new Stopwatch(); sw.Start(); var f = node.BuildLambda(); sw.Stop(); Console.WriteLine($"Lambda Build used {sw.ElapsedMilliseconds} ms"); sw.Restart(); for (int i = 0; i < N; i++) { f(i); } sw.Stop(); Console.WriteLine($"Lambda Run used {sw.ElapsedMilliseconds} ms"); }
publicstatic Node Generate() { var root = new Node { Op = Operation.Add, Value = 114514 }; var node = root; for (int i = 0; i < 10; i++) { var op = (Operation)Random.Shared.Next(4); var val = Random.Shared.Next(1919810); node!.Next = new Node { Op = op, Value = val }; node = root.Next; } return root; }
简单生成了一个套了大概10层的节点,然后构建成 lambda 去跑数据,测试了几个 N 得到了如下数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Test 1000 Lambda Build used 0 ms Lambda Run used 0 ms
Test 10000 Lambda Build used 0 ms Lambda Run used 21 ms
Test 100000 Lambda Build used 0 ms Lambda Run used 36 ms
Test 1000000 Lambda Build used 0 ms Lambda Run used 102 ms
[Benchmark] publicvoidTestExpression() { var param = Expression.Parameter(typeof(int), "x"); var expr = node.BuildExpression(param); var f2 = expr.Compile(); for (int i = 0; i < N; i++) { f2(i); } }
[Benchmark] publicvoidTestLambda() { var f = node.BuildLambda(); for (int i = 0; i < N; i++) { f(i); } } }
publicclassProgram { publicstaticvoidMain(string[] args) { var summary = BenchmarkRunner.Run<Benchmark>(); Console.WriteLine(summary); } // other code }
基本上这个表达式树需要大概 15-30 ms 的时间编译,而运行的性能非常高,这个优势会在数据数量级越高,树结构越复杂的时候由很显著的表现