首页 > 其他分享 >一个基于 SourceGenerator 生成 从 dbReader转换为 class 数据的性能测试实验

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

时间:2024-07-30 17:28:51浏览次数:11  
标签:using dog dbReader connection reader var SourceGenerator ns class

好奇

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' warning
using 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


Method Categories Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
SetClassFirst 1 实体 18.38 ns 0.378 ns 0.316 ns 1.00 0.00 0.0181 - 152 B 1.00
SourceGeneratorMappingFirst 1 实体 183.31 ns 3.525 ns 3.462 ns 9.98 0.14 0.0899 - 752 B 4.95
DapperMappingFirst 1 实体 1,336.69 ns 5.777 ns 5.121 ns 72.77 1.30 0.0343 - 288 B 1.89
SetClass 1000 实体 7,700.08 ns 87.311 ns 68.167 ns 1.00 0.00 6.7749 1.1139 56712 B 1.00
SourceGeneratorMapping 1000 实体 23,428.85 ns 262.698 ns 232.875 ns 3.04 0.03 6.8359 1.1292 57312 B 1.01
DapperMapping 1000 实体 48,880.92 ns 682.693 ns 533.002 ns 6.35 0.06 13.4888 2.1362 113048 B 1.99

标签:using,dog,dbReader,connection,reader,var,SourceGenerator,ns,class
From: https://www.cnblogs.com/fs7744/p/18332993

相关文章

  • Python - Creating alternative initializers using class Methods
    Classmethodsallowustodefinealternativeinitializers(alsoknownasfactorymethods)inaclass.Thesemethodshelpuscreateinstanceobjectsfromdifferenttypesofinputdata.Letusunderstandthiswiththehelpofanexample.Again,wetakethe......
  • Taro 框架中使用iconfont 阿里巴巴矢量图标 class版
    想必大家都知道这个图标库的图标还是很多的‘大家应该都也用过 最进开发Taro+nut-ui的H5移动端的时候我发现一些图标我在组件库中找不到我看到了nut-ui中还有另一种使用方法这个叫做自定一图标、我来用实际操作代码来介绍一下这个怎么使用 我总结文章......
  • 带 ClassVar 的通用类型别名
    我目前的类型注释看起来类似于以下内容,我想使用键入别名以免重复太多:类具有类变量,可以是:某些指定的类型,或一个函数(带参数)返回该相同的类型。classFoo(object):state:ClassVar[Union[str,Callable[[SomeObject],str]]]#st......
  • ChatGPT:Raw use of parameterized class ‘R‘ 是什么错误?
    ChatGPT:Rawuseofparameterizedclass‘R’是什么错误?“对参数化类‘R’的原始使用”在Java中,使用泛型类时应指定其类型参数,以确保类型安全和代码的可读性。如果没有指定类型参数而直接使用泛型类,就会出现Rawuseofparameterizedclass警告。例如,假设你有......
  • InputStream inputStream = classLoader.getResourceAsStream("aaa.properties") ; 
    问:InputStreaminputStream=classLoader.getResourceAsStream("aaa.properties"); 获取到的 inputStream 是null答:当您尝试使用ClassLoader的getResourceAsStream方法来获取一个资源文件(如"aaa.properties")的InputStream,但得到的结果是null时,这通常意味着资源文......
  • dataclass_transform() 得到了意外的关键字参数“field_specifiers”
    我正在尝试使用这个G2P包但我收到dataclass_transform()gotanunexpectedkeywordargument'field_specifiers'这个错误。我正在尝试运行他们在我的jupyter笔记本中运行后提供的示例代码。下面附有我收到错误的示例代码。有人可以帮我解决这个问题吗?......
  • Python SQLAlchemy 2.0 使用 dataclass_transform 非必需字段类型
    我刚刚在一个新项目上安装了SQLAlchemy2.0,我正在尝试使我的模型尽可能类型安全。通过使用@typing_extensions.dataclass_transform,我已经能够实现我想要实现的大部分目标类型检查,但是当前所有字段都被标记为不需要。例如:@typing_extensions.dataclass_tran......
  • 如何反转“dataclass.astuple”?
    我正在尝试从元组构造dataclasses的层次结构,如下所示:fromdataclassesimportastuple,dataclass@dataclassclassChild:name:str@dataclassclassParent:child:Child#thisiswhatIwantp=Parent(Child("Tim"))print(p)#thisiswhat......
  • vue 在当前页面按下enter键时 判断页面中是否存在enterclass这个class名 存在的话执行
    在Vue中,如果你想在当前页面监听Enter键的按下,并检查页面上是否存在具有特定类名(如enterclass)的元素,然后执行某个事件,你可以通过几种方法来实现。以下是一个示例,展示了如何结合使用Vue的事件监听、DOM操作和条件判断来完成这个任务。方法一:使用mounted生命周期钩子和全局事......
  • Hadoop(5-1) HBase 提示SLF4J: Class path contains multiple SLF4J bindings.
    Hadoop(5-1)HBase提示SLF4J:ClasspathcontainsmultipleSLF4Jbindings.问题描述在使用hbaseversion命令时,出现下列的警告信息 。SLF4J:ClasspathcontainsmultipleSLF4Jbindings.SLF4J:Foundbindingin[jar:file:/usr/local/hadoop-3.3.6/share/hadoop/com......