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#作为一种强类型语言,具有丰富的泛型支持,允许开发者编写可以应对不同数据类型的通用代码。然而,在泛型编译时,针对结构和类作为泛型参数时,会对性能产生不同的影响。 泛型编译行为 在C#中,泛型编译行为取…...
11-30 JavaWeb
修改与删除操作 防止空指针异常 localhost:8080 -> 分页查询 修改流程:(先查后改(两个servlet)) 修改: 传用户id(用户id怎么得到 -> 循环一次得到一个user 对象 user对象里用user.getId()得到用户id) UpdateUserQueryServlet.java (…...
LCR 047. 二叉树剪枝 和 leetCode 1110. 删点成林 + 递归 + 图解
给定一个二叉树 根节点 root ,树的每个节点的值要么是 0,要么是 1。请剪除该二叉树中所有节点的值为 0 的子树。节点 node 的子树为 node 本身,以及所有 node 的后代。 示例 1: 输入: [1,null,0,0,1] 输出: [1,null,0,null,1] 解释: 只有红…...
Flutter笔记:路由观察者
Flutter系列 路由观察者 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/134572181 目 录 1. 概述2. 路由…...
【驱动】串口驱动分析(三)-serial driver
简介 前两节我们介绍串口驱动的框架和tty core部分。这节我们介绍和硬件紧密相关的串口驱动部分。 UART驱动部分依赖于硬件平台,而TTY驱动和具体的平台无关。虽然UART部分依赖于平台,但是不管是哪个硬件平台,驱动的思路都是一致的ÿ…...
(C++20) constinit常量初始化
文章目录 由来constinit 常量初始化常量初始化 ! 初始化常量初始化声明静态存储对象非初始化声明thread_local END 由来 在C多文件编译中会出现一个常见的问题,叫做静态初始化顺序问题。Static Initialization Order Fiasco。 比如现在有两个文件,其中…...
python实现获取aws route53域名信息
最近由于工作原因接触到aws的服务,我需要实时获取所有的域名信息,用于对其进行扫描,因此写了一个自动化爬取脚本 给需要的人分享。 1.基础准备 代码环境:python3 第三方库:boto3 (安装方法pip install…...
Linux_Linux终端常用快捷键
Linux命令行核心常用快捷键是一些在终端中使用的快捷键组合,用于提高命令行操作的效率。下面是这些快捷键的原理详细解释、使用场景解释 Ctrl A :将光标移动到命令行的开头。这个快捷键的原理是发送一个控制序列到终端,告诉终端将光标移动到…...
Neo4j 数据库管理 数据备份与恢复(头歌)
文章目录 第1关:数据备份与恢复任务描述相关知识数据备份数据导入 编程要求测试说明答案测试前准备Cypher 代码数据备份与导入 第1关:数据备份与恢复 任务描述 本关任务:熟练掌握数据备份与恢复。 相关知识 为了完成本关任务,…...
TCP传输的三次握手四次挥手策略
TCP传输的三次握手四次挥手策略如下: 第一次握手:客户端发送一个带有SYN标志的数据包给服务器,并记为SYN_Client。第二次握手:服务器收到SYN_Client后,向客户端发送一个带有SYN和ACK标志的数据包,记为SYN_…...
在gitlab上使用server_hooks
文章目录 1. 前置条件2. Git Hook2.1 Git Hook 分为两部分:本地和远程2.1.1 本地 Git Hook,由提交和合并等操作触发:2.1.2 远程 Git Hook,运行在网络操作上,例如接收推送的提交: 3. 操作步骤3.1 对所有的仓…...
【云原生系列】Kubernetes知识点
目录 概念 基础架构 单master节点 多master节点 组件 Master节点核心组件 其他组件 请求发送流程 插件 核心资源 调度资源 Pod 创建pod组件间调用流程 pod生命周期: 初始化容器 镜像拉取策略 重启策略 钩子函数 探针 探针的实现方式 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有两中方法,一种是借助 redis-cli,另一种是通过 SCAN 命令来遍历所有匹配前缀的 key,并使用 DEL 命令逐个删除它们。 redis-cli 使用 Redis 自带的 redis-cli 命令行工具,你可以通过以下方式批量删除指定前…...
如何熟练使用vim工具?
🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏1: 🍔🍟🌯C语言初阶 🐻推荐专栏2: 🍔🍟🌯C语言进阶 🔑个人信条: 🌵知行合一 …...
ClassNotFoundException: org.apache.hive.spark.client.Job
hive使用的是3.13版本,spark是3.3.3支持hadoop3.x hive将engine从mr改成spark,通过beeline执行insert、delete时一直报错,sparkTask rpc关闭, 查看yarn是出现ClassNotFoundException: org.apache.hive.spark.client.Job。 开始…...
《合成孔径雷达成像算法与实现》_使用CS算法对RADARSAT-1数据进行成像
CSA 简介:Chirp Scaling 算法 (简称 CS 算法,即 CSA) 避免了 RCMC 中的插值操作。该算法基于 Scaling 原理,通过对 chirp 信号进行频率调制,实现了对信号的尺度变换或平移。基于这种原理,可以通过相位相乘代替时域插值…...
GCN01——Ubuntu中设置vivado编辑器为vscode
确定vscode位置 在命令行中输入 which code得到文件地址 进入文件夹后可看到,这是个链接文件,不过无所谓,就用这个地址就行 设置Text Editor 打开setting选择右侧text editor 这里说明了如何进行设置 将自己的地址加进去就行 /usr/share…...
Android 11.0 软硬键盘同时使用的兼容(软键盘与内置物理键盘共存)
1.概述 在11.0的系统rom产品定制化开发总,在有些设备上,如果外接了USB扫描枪之类的设备,当插入USB扫描枪以后,然后点击输入调用输入法的时候,没有反应,但是拔掉USB扫描枪以后,输入法又能正常使用,这说明和输入法起冲突了,询问了好多同时,说可能把会把USB扫描枪识别为…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...
2.2.2 ASPICE的需求分析
ASPICE的需求分析是汽车软件开发过程中至关重要的一环,它涉及到对需求进行详细分析、验证和确认,以确保软件产品能够满足客户和用户的需求。在ASPICE中,需求分析的关键步骤包括: 需求细化:将从需求收集阶段获得的高层需…...
