文档:动态函数调用相关

This commit is contained in:
若汝棋茗
2025-05-18 22:04:27 +08:00
parent 0130f0be8b
commit d225cef860

View File

@@ -5,6 +5,7 @@ title: 动态方法调用(DynamicMethod)
import CardLink from "@site/src/components/CardLink.js";
### 定义
命名空间TouchSocket.Core <br/>
@@ -21,7 +22,7 @@ import CardLink from "@site/src/components/CardLink.js";
- 表达式树DynamicBuilderType.Expression
- 传统反射DynamicBuilderType.Reflect
- 源生成DynamicBuilderType.SourceGenerator
- **性能卓越**:相比原生反射调用,IL模式提升100倍性能
- **性能卓越**:相比原生反射调用,性能提升10倍性能
- **AOT友好**源生成模式实现零反射完美支持iOS/Android等AOT环境
- **智能异步支持**自动识别Task/ValueTask返回值类型
- **全参数支持**支持ref/out参数、泛型参数、参数默认值
@@ -103,7 +104,7 @@ public class MyClass
// 异步调用
var method = new Method(typeof(MyClass), nameof(MyClass.GetDataAsync));
var result = await method.InvokeObjectAsync(instance);
var result = await method.InvokeAsync(instance);
Console.WriteLine($"Result: {result}"); // 输出 42
```
@@ -134,20 +135,310 @@ Console.WriteLine($"Result: {parameters[2]}"); // 输出 "data_1"
### 5.1 性能对比测试
```csharp showLineNumbers
// 执行1000万次调用性能测试
| 构建器类型 | 耗时(ms) |
|------------------|----------|
| IL生成 | 120 |
| 表达式树 | 450 |
| 源生成 | 150 |
| 传统反射 | 5200 |
:::tip 10000次调用性能分析
**核心结论**
1. **同步方法Add性能**
- **直接调用** 最快(~2.1µs无内存分配。
- **源生成器SourceGeneratorRun** 在 .NET 8+ 接近直接调用性能(~3.1µs较 IL/表达式树快 6-7 倍,较反射快 28-30 倍。
- **反射MethodInfo** 性能最差(.NET 6: 400µs → .NET 8: 88µs.NET 8+ 反射性能显著优化。
2. **异步方法AddAsync性能**
- **直接调用** 仍最优(.NET 8: 38µs.NET 9: 39µs
- **源生成器** 表现接近 IL/表达式树(.NET 8: 91µs vs 103µs较反射快 2-3 倍(.NET 8: 91µs vs 184µs
- **.NET Framework 4.8.1** 异步性能最差(反射达 1,305µs内存分配显著高于其他版本。
3. **内存分配**
- 同步方法:源生成器与 IL/表达式树分配 80B反射额外多 1B.NET 6
- 异步方法:所有动态方法分配 ~720KB.NET Core或 ~802KBFramework反射在 Framework 分配超 1MB。
**附:关键数据对比(.NET 8**
| 方法 | 同步耗时 | 异步耗时 | 内存分配 |
|----------------------|---------|---------|----------|
| DirectRun | 2.1µs | 38µs | 56B |
| SourceGeneratorRun | 3.1µs | 91µs | 80B |
| MethodInfoRun | 88µs | 184µs | 80B |
:::
```csharp {7,12,18,23,29,34,40,45} showLineNumbers
| Method | Job | Runtime | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|----------------------------- |--------------------- |--------------------- |-------------:|----------:|----------:|---------:|-------:|----------:|
| DirectRun_Add | .NET 6.0 | .NET 6.0 | 2.132 us | 0.0031 us | 0.0027 us | - | - | 56 B |
| MethodILRun_Add | .NET 6.0 | .NET 6.0 | 23.887 us | 0.0795 us | 0.0744 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET 6.0 | .NET 6.0 | 20.108 us | 0.1542 us | 0.1443 us | - | - | 80 B |
| MethodInfoRun_Add | .NET 6.0 | .NET 6.0 | 399.999 us | 0.3781 us | 0.3537 us | - | - | 81 B |
| SourceGeneratorRun_Add | .NET 6.0 | .NET 6.0 | 17.648 us | 0.0945 us | 0.0884 us | - | - | 80 B |
| DirectRun_AddAsync | .NET 6.0 | .NET 6.0 | 46.567 us | 0.4623 us | 0.4098 us | 45.8984 | - | 720080 B |
| MethodILRun_AddAsync | .NET 6.0 | .NET 6.0 | 141.010 us | 0.8145 us | 0.7619 us | 45.8984 | - | 720080 B |
| MethodExpressionRun_AddAsync | .NET 6.0 | .NET 6.0 | 142.758 us | 0.8197 us | 0.7266 us | 45.8984 | - | 720080 B |
| MethodInfoRun_AddAsync | .NET 6.0 | .NET 6.0 | 563.645 us | 1.8681 us | 1.6560 us | 45.8984 | - | 720081 B |
| SourceGeneratorRun_AddAsync | .NET 6.0 | .NET 6.0 | 141.399 us | 0.8781 us | 0.8214 us | 45.8984 | - | 720080 B |
| StaticMethodRun | .NET 6.0 | .NET 6.0 | 2.130 us | 0.0018 us | 0.0017 us | - | - | 56 B |
| DirectRun_Add | .NET 8.0 | .NET 8.0 | 2.140 us | 0.0116 us | 0.0109 us | - | - | 56 B |
| MethodILRun_Add | .NET 8.0 | .NET 8.0 | 19.332 us | 0.0491 us | 0.0459 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET 8.0 | .NET 8.0 | 12.889 us | 0.1189 us | 0.1054 us | - | - | 80 B |
| MethodInfoRun_Add | .NET 8.0 | .NET 8.0 | 88.879 us | 0.1784 us | 0.1581 us | - | - | 80 B |
| SourceGeneratorRun_Add | .NET 8.0 | .NET 8.0 | 3.160 us | 0.0073 us | 0.0068 us | 0.0038 | - | 80 B |
| DirectRun_AddAsync | .NET 8.0 | .NET 8.0 | 38.401 us | 0.1199 us | 0.1063 us | 45.8984 | - | 720080 B |
| MethodILRun_AddAsync | .NET 8.0 | .NET 8.0 | 103.627 us | 1.2387 us | 1.1587 us | 45.8984 | - | 720080 B |
| MethodExpressionRun_AddAsync | .NET 8.0 | .NET 8.0 | 101.368 us | 0.7190 us | 0.6725 us | 45.8984 | - | 720080 B |
| MethodInfoRun_AddAsync | .NET 8.0 | .NET 8.0 | 183.882 us | 0.7238 us | 0.6044 us | 45.8984 | - | 720080 B |
| SourceGeneratorRun_AddAsync | .NET 8.0 | .NET 8.0 | 91.024 us | 0.6482 us | 0.6063 us | 45.8984 | - | 720080 B |
| StaticMethodRun | .NET 8.0 | .NET 8.0 | 2.150 us | 0.0073 us | 0.0068 us | - | - | 56 B |
| DirectRun_Add | .NET 9.0 | .NET 9.0 | 2.119 us | 0.0024 us | 0.0022 us | - | - | 56 B |
| MethodILRun_Add | .NET 9.0 | .NET 9.0 | 19.348 us | 0.0133 us | 0.0124 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET 9.0 | .NET 9.0 | 11.753 us | 0.0625 us | 0.0585 us | - | - | 80 B |
| MethodInfoRun_Add | .NET 9.0 | .NET 9.0 | 91.756 us | 0.2344 us | 0.2193 us | - | - | 80 B |
| SourceGeneratorRun_Add | .NET 9.0 | .NET 9.0 | 3.145 us | 0.0056 us | 0.0046 us | 0.0038 | - | 80 B |
| DirectRun_AddAsync | .NET 9.0 | .NET 9.0 | 39.219 us | 0.1669 us | 0.1561 us | 45.8984 | - | 720080 B |
| MethodILRun_AddAsync | .NET 9.0 | .NET 9.0 | 107.920 us | 0.4728 us | 0.4191 us | 45.8984 | - | 720080 B |
| MethodExpressionRun_AddAsync | .NET 9.0 | .NET 9.0 | 106.539 us | 0.3894 us | 0.3642 us | 45.8984 | - | 720080 B |
| MethodInfoRun_AddAsync | .NET 9.0 | .NET 9.0 | 189.294 us | 0.7231 us | 0.6764 us | 45.8984 | - | 720080 B |
| SourceGeneratorRun_AddAsync | .NET 9.0 | .NET 9.0 | 99.195 us | 0.3677 us | 0.3440 us | 45.8984 | - | 720080 B |
| StaticMethodRun | .NET 9.0 | .NET 9.0 | 2.125 us | 0.0036 us | 0.0034 us | - | - | 56 B |
| DirectRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 2.134 us | 0.0047 us | 0.0042 us | 0.0076 | - | 56 B |
| MethodILRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 30.162 us | 0.0629 us | 0.0588 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 56.063 us | 0.0559 us | 0.0467 us | - | - | 80 B |
| MethodInfoRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 770.931 us | 5.9446 us | 5.5606 us | 50.7813 | - | 321027 B |
| SourceGeneratorRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 17.521 us | 0.0350 us | 0.0310 us | - | - | 80 B |
| DirectRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 41.866 us | 0.1300 us | 0.1216 us | 127.5024 | 0.0610 | 802442 B |
| MethodILRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 523.507 us | 2.1568 us | 1.9120 us | 126.9531 | - | 802445 B |
| MethodExpressionRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 554.333 us | 2.3657 us | 2.2129 us | 126.9531 | - | 802445 B |
| MethodInfoRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 1,304.826 us | 1.4392 us | 1.2758 us | 177.7344 | - | 1123389 B |
| SourceGeneratorRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 508.533 us | 0.8991 us | 0.8410 us | 126.9531 | - | 802445 B |
| StaticMethodRun | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 3.267 us | 0.0070 us | 0.0062 us | 0.0114 | - | 80 B |
```
:::success 性能优势
- IL模式相比反射提升**43倍**性能
- 源生成模式在AOT环境下保持高性能
:::
<details>
<summary>基准测试代码</summary>
<div>
```csharp showLineNumbers
[SimpleJob(RuntimeMoniker.Net481)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net90)]
[MemoryDiagnoser]
public class BenchmarkInvokeMethodClass
{
public BenchmarkInvokeMethodClass()
{
var methodInfo_Add = typeof(MyMethodClass1).GetMethod(nameof(MyMethodClass1.Add));
var methodInfo_AddAsync = typeof(MyMethodClass1).GetMethod(nameof(MyMethodClass1.AddAsync));
this.m_method_Add_IL = new Method(methodInfo_Add, DynamicBuilderType.IL);
this.m_method_Add_Expression = new Method(methodInfo_Add, DynamicBuilderType.Expression);
this.m_method_Add_SourceGenerator = new Method(methodInfo_Add, DynamicBuilderType.SourceGenerator);
this.m_method_Add_Reflect = new Method(methodInfo_Add, DynamicBuilderType.Reflect);
this.m_method_AddAsync_IL = new Method(methodInfo_AddAsync, DynamicBuilderType.IL);
this.m_method_AddAsync_Expression = new Method(methodInfo_AddAsync, DynamicBuilderType.Expression);
this.m_method_AddAsync_SourceGenerator = new Method(methodInfo_AddAsync, DynamicBuilderType.SourceGenerator);
this.m_method_AddAsync_Reflect = new Method(methodInfo_AddAsync, DynamicBuilderType.Reflect);
var AddStatic = typeof(MyMethodClass1).GetProperty("AddStaticAction");
}
private readonly Method m_method_Add_IL;
private readonly Method m_method_Add_Expression;
private readonly Method m_method_Add_SourceGenerator;
private readonly Method m_method_Add_Reflect;
private readonly Method m_method_AddAsync_IL;
private readonly Method m_method_AddAsync_Expression;
private readonly Method m_method_AddAsync_SourceGenerator;
private readonly Method m_method_AddAsync_Reflect;
public int Count=10000;
[Benchmark]
public void DirectRun_Add()
{
var myClass1 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
myClass1.Add(a);
}
}
[Benchmark]
public void MethodILRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_IL.Invoke(myClass2, objects);
}
}
[Benchmark]
public void MethodExpressionRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_Expression.Invoke(myClass2, objects);
}
}
[Benchmark]
public void MethodInfoRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_Reflect.Invoke(myClass2, objects);
}
}
[Benchmark]
public void SourceGeneratorRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_SourceGenerator.Invoke(myClass2, objects);
}
}
[Benchmark]
public async Task DirectRun_AddAsync()
{
var myClass1 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await myClass1.AddAsync(a);
}
}
[Benchmark]
public async Task MethodILRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_IL.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public async Task MethodExpressionRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_Expression.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public async Task MethodInfoRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_Reflect.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public async Task SourceGeneratorRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_SourceGenerator.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public void StaticMethodRun()
{
var myClass1 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
MyMethodClass1.AddStatic(myClass1, objects);
}
}
}
public class MyMethodClass1
{
public static object AddStatic(object myClass, object[] ps)
{
var a = ps[0];
var result = ((MyMethodClass1)myClass).Add(a);
return result;
}
//这个是被调用函数。
[DynamicMethod]
public object Add(object obj)
{
return obj;
}
[DynamicMethod]
public Task<object> AddAsync(object obj)
{
return Task.FromResult(obj);
}
}
```
</div>
</details>
### 5.2 缓存策略