当前位置: 首页 > news >正文

C# 泛型编译特性对性能的影响

C#作为一种强类型语言,具有丰富的泛型支持,允许开发者编写可以应对不同数据类型的通用代码。然而,在泛型编译时,针对结构和类作为泛型参数时,会对性能产生不同的影响。

泛型编译行为

在C#中,泛型编译行为取决于泛型参数的类型。具体而言,当泛型参数是结构(Struct)时,编译器会针对每个具体的结构类型生成特定的实现。而当泛型参数是类(Class)时,编译器则可能生成更通用的实现。

结构 vs 类

结构(Struct)

结构是值类型,它们存储在栈上,具有较小的内存开销。当泛型参数是结构时,编译器会针对每个具体的结构类型生成专门的实现,这可能导致更高的性能。因为每个结构类型都有自己的实现,避免了装箱和拆箱的开销,同时优化了内存分配和访问。

类(Class)

类是引用类型,存储在堆上,需要通过引用进行访问。当泛型参数是类时,编译器可能生成更通用的实现。这可能导致较低的性能,因为通用实现需要进行动态调度和引用类型的操作,增加了一些开销。

测试性能差异

针对不同的泛型参数进行性能测试是一种有效的方法,以观察结构和类对泛型编译特性的影响。在测试中,可能会发现对结构类型的泛型参数,其性能可能更高,而对类类型的泛型参数,其性能可能略低。

using System.Diagnostics;namespace ConsoleApp1 {internal interface IValueGetter {int GetValue(int index);}internal class MyTestClass<T> where T : IValueGetter {private readonly T _valueGetter;public MyTestClass(T valueGetter) {_valueGetter = valueGetter;}public void Run() {long r = 0L;for (int i = 0; i < int.MaxValue; i++) {r += _valueGetter.GetValue(i);}}}internal struct StructValueGetter : IValueGetter {public readonly int GetValue(int index) {return index + 3;}}internal struct StructValueGetter2(int someField) : IValueGetter {public readonly int GetValue(int index) {return index + 5;}}internal class ClassValueGetter1 : IValueGetter {public int GetValue(int index) {return index + 5;}}internal class ClassValueGetter2 : IValueGetter {public int GetValue(int index) {return index + 7;}}internal static class Demo2 {public static  void Run() {var t1 = new MyTestClass<StructValueGetter>(new StructValueGetter());RunDemo("StructValueGetter ", t1.Run);var t2 = new MyTestClass<ClassValueGetter1>(new ClassValueGetter1());RunDemo("ClassValueGetter1 ", t2.Run);var t3 = new MyTestClass<ClassValueGetter2>(new ClassValueGetter2());RunDemo("ClassValueGetter2 ", t3.Run);var t4 = new MyTestClass<IValueGetter>(new ClassValueGetter1());RunDemo("IValueGetter-1    ", t4.Run);var t5 = new MyTestClass<ClassValueGetter1>(new ClassValueGetter1());RunDemo("ClassValueGetter1 ", t5.Run);var t6 = new MyTestClass<StructValueGetter2>(new StructValueGetter2());RunDemo("StructValueGetter2", t6.Run);var t7 = new MyTestClass<IValueGetter>(new ClassValueGetter2());RunDemo("IValueGetter-2    ", t7.Run);var t8 = new MyTestClass<IValueGetter>(new StructValueGetter());RunDemo("IValueGetter-3    ", t8.Run);var t9 = Activator.CreateInstance(typeof(MyTestClass<>).MakeGenericType(typeof(StructValueGetter)), new StructValueGetter());Action action9 = (Action)Delegate.CreateDelegate(typeof(Action), t9, t9.GetType().GetMethod("Run"));RunDemo("Dynamic-Struct    ", action9);}static void RunDemo(string caption, Action action) {var stopWatch = Stopwatch.StartNew();action();stopWatch.Stop();Console.WriteLine($"{caption} time = {stopWatch.Elapsed}");}}
}Demo2.Run();

在.net 8.0 Release 编译执行的参考结果如下:

StructValueGetter  time = 00:00:00.6920186
ClassValueGetter1  time = 00:00:01.1887137
ClassValueGetter2  time = 00:00:05.2889692
IValueGetter-1     time = 00:00:01.1652195
ClassValueGetter1  time = 00:00:01.1625259
StructValueGetter2 time = 00:00:00.6488674
IValueGetter-2     time = 00:00:05.2114724
IValueGetter-3     time = 00:00:07.1394676
Dynamic-Struct     time = 00:00:00.6491220

结论

泛型编译特性对性能有所影响,我们发现:

  • 泛型参数是 Struct 比 class 的性能要好,大约有两倍的差异;

  • 泛型参数如果存在多个 Struct 可能时,性能没有影响,但如果泛型参数存在多个 class 可能时,性能急剧下降5倍之多;

  • 泛型参数如果是接口形式,无论实际填充的结构还是类,其最终的执行性能一定是很慢的;

  • 使用反射(例如:MakeGenericType)构建出的泛型实例,其实际运行性能并不受影响,非常适合高度定制的运行时类型构建,这一点非常重要,例如你可以在运行时检测实际情况,构建出不同的比较器对象,虽然构建的工厂方法返回的是接口,但你可以使用反射的方式动态传入字典的比较器参数(实际上c#的 Dictionary<TKey, TValue> 这点设计是失败的,他的comparer不是一个泛型参数,而是接口);

综上所述,了解C#泛型编译特性对性能的影响是编写高性能代码的重要一部分,合理使用对于关键性代码性能至关重要。

文章转载自:编写人生

原文链接:https://www.cnblogs.com/tansm/p/CSharp-Generic-Performance.html

相关文章:

C# 泛型编译特性对性能的影响

C#作为一种强类型语言&#xff0c;具有丰富的泛型支持&#xff0c;允许开发者编写可以应对不同数据类型的通用代码。然而&#xff0c;在泛型编译时&#xff0c;针对结构和类作为泛型参数时&#xff0c;会对性能产生不同的影响。 泛型编译行为 在C#中&#xff0c;泛型编译行为取…...

11-30 JavaWeb

修改与删除操作 防止空指针异常 localhost:8080 -> 分页查询 修改流程&#xff1a;(先查后改(两个servlet)) 修改&#xff1a; 传用户id(用户id怎么得到 -> 循环一次得到一个user 对象 user对象里用user.getId()得到用户id) UpdateUserQueryServlet.java &#xff08;…...

LCR 047. 二叉树剪枝 和 leetCode 1110. 删点成林 + 递归 + 图解

给定一个二叉树 根节点 root &#xff0c;树的每个节点的值要么是 0&#xff0c;要么是 1。请剪除该二叉树中所有节点的值为 0 的子树。节点 node 的子树为 node 本身&#xff0c;以及所有 node 的后代。 示例 1: 输入: [1,null,0,0,1] 输出: [1,null,0,null,1] 解释: 只有红…...

Flutter笔记:路由观察者

Flutter系列 路由观察者 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/134572181 目 录 1. 概述2. 路由…...

【驱动】串口驱动分析(三)-serial driver

简介 前两节我们介绍串口驱动的框架和tty core部分。这节我们介绍和硬件紧密相关的串口驱动部分。 UART驱动部分依赖于硬件平台&#xff0c;而TTY驱动和具体的平台无关。虽然UART部分依赖于平台&#xff0c;但是不管是哪个硬件平台&#xff0c;驱动的思路都是一致的&#xff…...

(C++20) constinit常量初始化

文章目录 由来constinit 常量初始化常量初始化 ! 初始化常量初始化声明静态存储对象非初始化声明thread_local END 由来 在C多文件编译中会出现一个常见的问题&#xff0c;叫做静态初始化顺序问题。Static Initialization Order Fiasco。 比如现在有两个文件&#xff0c;其中…...

python实现获取aws route53域名信息

最近由于工作原因接触到aws的服务&#xff0c;我需要实时获取所有的域名信息&#xff0c;用于对其进行扫描&#xff0c;因此写了一个自动化爬取脚本 给需要的人分享。 1.基础准备 代码环境&#xff1a;python3 第三方库&#xff1a;boto3 &#xff08;安装方法pip install…...

Linux_Linux终端常用快捷键

Linux命令行核心常用快捷键是一些在终端中使用的快捷键组合&#xff0c;用于提高命令行操作的效率。下面是这些快捷键的原理详细解释、使用场景解释 Ctrl A &#xff1a;将光标移动到命令行的开头。这个快捷键的原理是发送一个控制序列到终端&#xff0c;告诉终端将光标移动到…...

Neo4j 数据库管理 数据备份与恢复(头歌)

文章目录 第1关&#xff1a;数据备份与恢复任务描述相关知识数据备份数据导入 编程要求测试说明答案测试前准备Cypher 代码数据备份与导入 第1关&#xff1a;数据备份与恢复 任务描述 本关任务&#xff1a;熟练掌握数据备份与恢复。 相关知识 为了完成本关任务&#xff0c;…...

TCP传输的三次握手四次挥手策略

TCP传输的三次握手四次挥手策略如下&#xff1a; 第一次握手&#xff1a;客户端发送一个带有SYN标志的数据包给服务器&#xff0c;并记为SYN_Client。第二次握手&#xff1a;服务器收到SYN_Client后&#xff0c;向客户端发送一个带有SYN和ACK标志的数据包&#xff0c;记为SYN_…...

在gitlab上使用server_hooks

文章目录 1. 前置条件2. Git Hook2.1 Git Hook 分为两部分&#xff1a;本地和远程2.1.1 本地 Git Hook&#xff0c;由提交和合并等操作触发&#xff1a;2.1.2 远程 Git Hook&#xff0c;运行在网络操作上&#xff0c;例如接收推送的提交&#xff1a; 3. 操作步骤3.1 对所有的仓…...

【云原生系列】Kubernetes知识点

目录 概念 基础架构 单master节点 多master节点 组件 Master节点核心组件 其他组件 请求发送流程 插件 核心资源 调度资源 Pod 创建pod组件间调用流程 pod生命周期&#xff1a; 初始化容器 镜像拉取策略 重启策略 钩子函数 探针 探针的实现方式 DownwardAP…...

Hugging-Face报错锦囊(不断更新)

requests.exceptions.SSLError: (MaxRetryError(“HTTPSConnectionPool(host‘huggingface.co’, port443): Max retries exceeded with url: /api/models/bert-base-chinese (Caused by SSLError(SSLCertVerificationError(1, ‘[SSL: CERTIFICATE_VERIFY_FAILED] certificate…...

Redis核心数据结构

目录 五种基础数据结构 string hash list set zset 用zset实现微博热搜 scan遍历 高频问题 五种基础数据结构 string 单个赋值set 批量赋值/取值 msetmget 设置不存在字符串setnx, 如果不存在, 则设置成功返回1, 如果存在返回0, 可以当做分布式锁 删除值 设置过期时…...

Redis 如何批量删除指定前缀的Key

批量删除指定前缀的Key有两中方法&#xff0c;一种是借助 redis-cli&#xff0c;另一种是通过 SCAN 命令来遍历所有匹配前缀的 key&#xff0c;并使用 DEL 命令逐个删除它们。 redis-cli 使用 Redis 自带的 redis-cli 命令行工具&#xff0c;你可以通过以下方式批量删除指定前…...

如何熟练使用vim工具?

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…...

ClassNotFoundException: org.apache.hive.spark.client.Job

hive使用的是3.13版本&#xff0c;spark是3.3.3支持hadoop3.x hive将engine从mr改成spark&#xff0c;通过beeline执行insert、delete时一直报错&#xff0c;sparkTask rpc关闭&#xff0c; 查看yarn是出现ClassNotFoundException: org.apache.hive.spark.client.Job。 开始…...

《合成孔径雷达成像算法与实现》_使用CS算法对RADARSAT-1数据进行成像

CSA 简介&#xff1a;Chirp Scaling 算法 (简称 CS 算法&#xff0c;即 CSA) 避免了 RCMC 中的插值操作。该算法基于 Scaling 原理&#xff0c;通过对 chirp 信号进行频率调制&#xff0c;实现了对信号的尺度变换或平移。基于这种原理&#xff0c;可以通过相位相乘代替时域插值…...

GCN01——Ubuntu中设置vivado编辑器为vscode

确定vscode位置 在命令行中输入 which code得到文件地址 进入文件夹后可看到&#xff0c;这是个链接文件&#xff0c;不过无所谓&#xff0c;就用这个地址就行 设置Text Editor 打开setting选择右侧text editor 这里说明了如何进行设置 将自己的地址加进去就行 /usr/share…...

Android 11.0 软硬键盘同时使用的兼容(软键盘与内置物理键盘共存)

1.概述 在11.0的系统rom产品定制化开发总,在有些设备上,如果外接了USB扫描枪之类的设备,当插入USB扫描枪以后,然后点击输入调用输入法的时候,没有反应,但是拔掉USB扫描枪以后,输入法又能正常使用,这说明和输入法起冲突了,询问了好多同时,说可能把会把USB扫描枪识别为…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...