SourceGenerator 生成db to class程式碼最佳化結果記錄

victor.x.qu發表於2024-08-02

最佳化

上一次實驗 程式碼寫的較為隨意,本次窮盡所學,最佳化了一把,

不過果然還是沒 比過 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

相關文章