优化
上一次实验 代码写的较为随意,本次穷尽所学,优化了一把,
不过果然还是没 比过 Dapper aot, 虽然没使用 Interceptor, 但理论上其优化不该有这么大差距
知识差距不少呀,都看不懂 Dapper aot 利用了什么姿势领先, 有大神们能教教吗?
优化点
减少类型判断
提前 做类型判断,并在生成时利用 switch case 减少判断
之前
var needConvert = typeof(string) != reader.GetFieldType(i); s.Add((d,r) => d.Name = DBExtensions.ReadToString(r,j,needConvert));
之后
switch (name) { case "age": s.Add(type == typeof(int) ? 1 : 2); break; switch (ss[j]) { case 1: d.Age = EntitiesGenerator.ReadToInt32Nullable(reader,j); break; case 2: d.Age = EntitiesGenerator.ReadToInt32NullableConvert(reader,j); break;
避免生成委托
去除委托生成使用
之前
var s = new List<Action<BenchmarkTest.Dog, IDataReader>>(reader.FieldCount); for (int i = 0; i < reader.FieldCount; i++) { var j = i; switch (reader.GetName(j).ToLower()) { case "age": { // int? var needConvert = typeof(int) != reader.GetFieldType(i); s.Add((d,r) => d.Age = DBExtensions.ReadToInt32Nullable(r,j,needConvert)); } break; case "name": { // string var needConvert = typeof(string) != reader.GetFieldType(i); s.Add((d,r) => d.Name = DBExtensions.ReadToString(r,j,needConvert)); } break; case "weight": { // float? var needConvert = typeof(float) != reader.GetFieldType(i); s.Add((d,r) => d.Weight = DBExtensions.ReadToFloatNullable(r,j,needConvert)); } break; default: break; } } while (reader.Read()) { var d = new BenchmarkTest.Dog(); foreach (var item in s) { item?.Invoke(d,reader); } yield return d; }
之后
var s = new List<int>(reader.FieldCount); for (int i = 0; i < reader.FieldCount; i++) { var name = reader.GetName(i).ToLower(); var type = reader.GetFieldType(i); switch (name) { case "age": s.Add(type == typeof(int) ? 1 : 2); break; case "name": s.Add(type == typeof(string) ? 3 : 4); break; case "weight": s.Add(type == typeof(float) ? 5 : 6); break; default: break; } } ss = s.ToArray(); var d = new BenchmarkTest.Dog(); for (int j = 0; j < ss.Length; j++) { switch (ss[j]) { case 1: d.Age = EntitiesGenerator.ReadToInt32Nullable(reader,j); break; case 2: d.Age = EntitiesGenerator.ReadToInt32NullableConvert(reader,j); break; case 3: d.Name = EntitiesGenerator.ReadToString(reader,j); break; case 4: d.Name = EntitiesGenerator.ReadToStringConvert(reader,j); break; case 5: d.Weight = EntitiesGenerator.ReadToFloatNullable(reader,j); break; case 6: d.Weight = EntitiesGenerator.ReadToFloatNullableConvert(reader,j); break; default: break; } }
添加 reader 字段判断缓存
添加缓存,减少重复生成
var h = reader.GetColumnHash(); if (!tokenCache.TryGetValue(h, out var ss)) { var s = new List<int>(reader.FieldCount); for (int i = 0; i < reader.FieldCount; i++)
结果
BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.4651/22H2/2022Update) Intel Core i7-10700 CPU 2.90GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK 9.0.100-preview.5.24307.3 [Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2 DefaultJob : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2
| Method | Categories | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|---|---|
| SourceGeneratorMappingFirst | 1 | 434.7 ns | 8.67 ns | 7.69 ns | 0.84 | 0.02 | 0.0401 | 0.0396 | - | 336 B | 1.20 |
| SetClassFirst | 1 | 516.8 ns | 9.86 ns | 10.55 ns | 1.00 | 0.00 | 0.0334 | 0.0324 | 0.0019 | 280 B | 1.00 |
| DapperMappingFirst AOT | 1 | 1,333.4 ns | 2.49 ns | 2.33 ns | 2.58 | 0.06 | 0.0324 | - | - | 280 B | 1.00 |
| DapperMappingFirst | 1 | 1,421.4 ns | 3.08 ns | 2.88 ns | 2.84 | 0.12 | 0.0496 | - | - | 416 B | 1.49 |
| SetClass | 1000 | 8,139.8 ns | 130.22 ns | 115.43 ns | 1.00 | 0.00 | 6.7902 | 1.6937 | - | 56840 B | 1.00 |
| DapperMapping AOT | 1000 | 16,373.8 ns | 275.34 ns | 244.08 ns | 2.01 | 0.05 | 6.7749 | 0.9460 | - | 56840 B | 1.00 |
| SourceGeneratorMapping | 1000 | 20,911.5 ns | 77.69 ns | 60.65 ns | 2.57 | 0.04 | 6.7749 | 1.6785 | - | 56896 B | 1.00 |
| DapperMapping | 1000 | 48,707.3 ns | 430.05 ns | 381.23 ns | 5.67 | 0.29 | 12.5122 | 2.0752 | - | 105120 B | 1.85 |