写点什么

一个基于 SourceGenerator 生成 从 dbReader 转换为 class 数据的性能测试实验

作者:八苦-瞿昙
  • 2024-10-18
    中国台湾
  • 本文字数:2152 字

    阅读完需:约 7 分钟

好奇

SourceGenerator 出现开始,好几年了,虽然一直好奇用 SourceGenerator 生成代码 与 emit 等动态生成的代码会有多少差距,


但是一直特别懒,不想搞


其实 dapper aot 项目做了类似事情,不过功能特别积极,还引用了实验特性,所以还是想更为简单客观对比


本次乘着自己暂时性不懒了,做了一个基于 SourceGenerator 生成 从 dbReader 转换为 class 数据的测试

no generate code when

  • Generic Type (如果不用 emit 动态生成,还真无法处理未知类型 T)

  • Anonymous Type (SourceGenerator 生成时机要早于匿名类生成,所以还没机会生成)

generate code

具体怎么做的就这里不写了,感兴趣参考 https://github.com/fs7744/SlowestEM


生成的代码带有一定 db 结果动态类型处理,以此更接近实际使用



// <auto-generated/>#pragma warning disable 8019 //disable 'unnecessary using directive' warningusing System;using System.Data;using System.Runtime.CompilerServices;using System.Collections.Generic;
namespace SlowestEM.Generator{ public static partial class Dog_Accessors { public static IEnumerable<BenchmarkTest.Dog> Read(IDataReader reader) { 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; } } }}
复制代码

测试结果

mock db, 避免 db 层实现性能和没有正确处理数据类型装箱拆箱问题


[Benchmark(Baseline = true), BenchmarkCategory("1")]public void SetClassFirst(){    Dog dog;    try    {        connection.Open();        var cmd = connection.CreateCommand();        cmd.CommandText = "select ";        using (var reader = cmd.ExecuteReader(CommandBehavior.Default))        {            if (reader.Read())            {                dog = new Dog();                dog.Name = reader.GetString(0);                dog.Age = reader.GetInt32(1);                dog.Weight = reader.GetFloat(2);            }        }    }    finally    {        connection.Close();    }}
[Benchmark, BenchmarkCategory("1")]public void DapperMappingFirst(){ var dogs = connection.QueryFirst<Dog>("select ");}
[Benchmark, BenchmarkCategory("1")]public void SourceGeneratorMappingFirst(){ Dog dog; try { connection.Open(); var cmd = connection.CreateCommand(); cmd.CommandText = "select "; using (var reader = cmd.ExecuteReader(CommandBehavior.Default)) { dog = reader.ReadTo<Dog>().FirstOrDefault(); } } finally { connection.Close(); }}
复制代码



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
复制代码



发布于: 刚刚阅读数: 5
用户头像

八苦-瞿昙

关注

一个假和尚,不懂人情世故。 2018-11-23 加入

会点点技术,能写些代码,只爱静静。 g hub: https://github.com/fs7744 黑历史:https://www.cnblogs.com/fs7744

评论

发布
暂无评论
一个基于 SourceGenerator 生成 从 dbReader转换为 class 数据的性能测试实验_C#_八苦-瞿昙_InfoQ写作社区