解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
3.62 KiB
X
# Reflect 性能测试报告 ## 测试环境 ``` BenchmarkDotNet v0.15.8,Windows 10 (10.0.19045.6456/22H2) Intel Core i9-10900K CPU 3.70GHz,20 逻辑核 / 10 物理核 .NET SDK 10.0.104 / Runtime .NET 10.0.4,X64 RyuJIT x86-64-v3 IterationCount=20,Release 模式 ``` ## 测试结果 对比 `Reflect` 扩展方法(用户侧 API)与等价原始 .NET 反射调用的耗时和内存分配。 | 场景 | 原始反射 | Reflect 扩展方法 | 提升倍数 | 内存分配 | |------|---------|-----------------|---------|---------| | 属性 Get | 5.96 ns | **2.45 ns** | **2.4x** | 零分配(两者相同) | | 属性 Set | 11.42 ns | **6.16 ns** | **1.9x** | 零分配(两者相同) | | 创建实例 | 7.12 ns(Activator) | **5.81 ns** | **1.2x** | 两者均 40 B | | 方法调用(无参) | 28.62 ns | **26.71 ns** | **1.1x** | 两者均 48 B | | 方法调用(有参) | 19.44 ns | **7.52 ns** | **2.6x** | 两者均 24 B | | 获取属性列表 | 23.97 ns,48 B | **4.68 ns** | **5.1x** | **零分配** | > 原始数据:`属性Get-原始反射 5.9596 ns`、`属性Get-扩展方法 2.4522 ns`、`属性Set-原始反射 11.4165 ns`、`属性Set-扩展方法 6.1593 ns`、`CreateInstance-Activator 7.1203 ns`、`CreateInstance-扩展方法 5.8080 ns`、`Invoke无参-原始反射 28.6235 ns`、`Invoke无参-扩展方法 26.7132 ns`、`Invoke带参-原始反射 19.4416 ns`、`Invoke带参-扩展方法 7.5211 ns`、`GetProperties-原始反射 23.9717 ns / 48B`、`GetProperties-扩展方法缓存 4.6842 ns / 0B` ## 性能分析 ### 为什么 Reflect 扩展方法快于原始反射? 原始 .NET 反射(`PropertyInfo.GetValue`、`MethodInfo.Invoke` 等)每次调用都要经过参数校验、安全检查、装箱/拆箱和动态分派,本质是通用路径,没有任何针对具体类型的优化。 `Reflect` 扩展方法采用三层加速结构: 1. **Lambda 编译缓存**:首次调用时为目标成员编译强类型委托(`Expression.Lambda.Compile()`),后续调用直接执行机器码,省去反射的通用路径开销。 2. **静态 COW 字典**:编译后的委托存入 Copy-on-Write 字典,读路径无锁,并发安全。`TryGetValue` 在已缓存时约 3 ns 即可取到委托。 3. **线程本地单态内联缓存**:每个线程维护最近一次调用的成员引用 + 委托,连续访问同一成员时只需一次指针比较(~0.3 ns),完全跳过字典查找。这是属性 Get 从 5.96 ns 降至 2.45 ns 的主要原因。 ### 各场景解读 **属性 Get / Set(2.4x / 1.9x)**:原始反射每次走完整校验路径;扩展方法热路径为"指针比较 → 委托调用",已接近直接属性访问的理论下限。Set 额外含一次类型判断(`value.GetType() == propertyType`),因此略慢于 Get。 **方法调用有参(2.6x)**:原始 `MethodInfo.Invoke` 需要装箱参数并分配 `Object[]`;扩展方法的强类型委托直接传递参数,省去装箱和数组开销,因此提升最为显著。 **方法调用无参(1.1x)**:提升较小,因为被测方法体本身含字符串插值(`$"{Name}({Age})"`),约 23 ns 的方法本体开销远大于调用开销,反射层面的差异被稀释。 **创建实例(1.2x)**:`Activator.CreateInstance` 内部已有优化,提升空间有限;扩展方法通过缓存无参构造委托,主要节省类型查找和安全检查的固定成本。 **获取属性列表(5.1x,零分配)**:原始 `Type.GetProperties()` 每次都分配新数组(48 B)并遍历反射元数据;扩展方法将结果缓存为 `IList<PropertyInfo>`,热路径只读缓存,零分配,是提升最大的场景。