SourceGenerator 生成db to class代码优化结果记录

40 阅读2分钟

优化

上一次实验 代码写的较为随意,本次穷尽所学,优化了一把,

不过果然还是没 比过 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


MethodCategoriesMeanErrorStdDevRatioRatioSDGen0Gen1Gen2AllocatedAlloc Ratio
SourceGeneratorMappingFirst1434.7 ns8.67 ns7.69 ns0.840.020.04010.0396-336 B1.20
SetClassFirst1516.8 ns9.86 ns10.55 ns1.000.000.03340.03240.0019280 B1.00
DapperMappingFirst AOT11,333.4 ns2.49 ns2.33 ns2.580.060.0324--280 B1.00
DapperMappingFirst11,421.4 ns3.08 ns2.88 ns2.840.120.0496--416 B1.49
SetClass10008,139.8 ns130.22 ns115.43 ns1.000.006.79021.6937-56840 B1.00
DapperMapping AOT100016,373.8 ns275.34 ns244.08 ns2.010.056.77490.9460-56840 B1.00
SourceGeneratorMapping100020,911.5 ns77.69 ns60.65 ns2.570.046.77491.6785-56896 B1.00
DapperMapping100048,707.3 ns430.05 ns381.23 ns5.670.2912.51222.0752-105120 B1.85