首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C# unsafe 性能提升

C# unsafe 性能提升

作者头像
JusterZhu
发布于 2023-09-18 09:10:46
发布于 2023-09-18 09:10:46
55610
代码可运行
举报
文章被收录于专栏:JusterZhuJusterZhu
运行总次数:0
代码可运行

1.概要

在C#中,unsafe关键字被用来定义一种特殊的代码上下文,在该上下文中可以使用指针类型和直接操作内存地址。这通常在执行某些低级操作,或者需要与未托管代码(例如C或C++编写的代码)交互时非常有用。

主要作用如下:

  1. 直接操作内存:使用unsafe关键字,你可以声明一个 "unsafe context",它能让你直接通过指针来操作内存。这与C和C++等语言中的行为类似。
  2. 创建和使用指针类型:在unsafe context中,可以声明和操作指针类型。例如,可以创建指向整数、浮点数或自定义类型的指针。
  3. 提高性能:对于某些低级别的系统编程任务,直接操作内存可能会比使用一些更抽象的.NET框架方法更有效率。
  4. 调用本地函数:如果你需要调用使用C或C++编写的本地DLL,那么可能需要使用到 unsafe代码。许多Windows API函数都需要指针参数,因此必须在unsafe context中调用它们。
  5. 固定变量:在unsafe context中,可以使用 fixed 语句将对象固定在内存中,防止垃圾回收器移动它们。

尽管unsafe关键字可以提供更多的灵活性和控制力,但它也增加了出错的风险。在直接操作内存时,很容易引入潜在的安全性问题和难以跟踪的错误。非必要应避免使用unsafe

unsafe关键字结合使用的其他关键字和运算符主要包括以下几个:

  1. 指针操作符:这些操作符用于处理指针变量。
    • * (解引用操作符):返回指针指向的变量值。
    • ->(成员选择操作符):访问指针指向的结构体或类的成员。
    • &(取址操作符):获取变量的地址。
  2. fixed 关键字:在unsafe代码块中,可以使用fixed语句来固定一个变量,防止垃圾收集器移动它。这对于需要直接操作内存的代码段非常重要。
  3. stackalloc 关键字stackalloc关键字用于在栈上分配一块内存区域。这种内存区域在所属的方法执行完毕后会被自动释放。
  4. sizeof 运算符:在unsafe代码块中,sizeof运算符可以用来获取未托管类型的大小(以字节为单位)。

2.详细内容

但是在这里并不打算演示所有的关键字或运算符的用法,主要分享的是大家可能会看重的性能提升。在大家遇到性能瓶颈的时候发现自己代码已经是当前情况下优解,实在想不出办法的办法一种引导。

勾选unsafe选项:

在C#中默认禁用unsafe代码,如果不勾选则编译不通过会提示。

测试代码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    class Program
    {
        const int size = 1000000000;
        static void Main()
        {
            int[] arr = new int[size];
            for (int i = 0; i < size; ++i)
                arr[i] = i;

            Stopwatch sw = new Stopwatch();

            // 不使用 unsafe 的版本
            sw.Start();
            for (int i = 0; i < size; ++i)
                ++arr[i];
            sw.Stop();
            Console.WriteLine("Without unsafe: {0}ms", sw.ElapsedMilliseconds);

            // 使用 unsafe 的版本
            sw.Reset();
            sw.Start();
            unsafe
            {
                fixed (int* pArr = arr)
                {
                    int* pEnd = pArr + size;
                    for (int* p = pArr; p < pEnd; ++p)
                        ++(*p);
                }
            }
            sw.Stop();
            Console.WriteLine("With unsafe: {0}ms", sw.ElapsedMilliseconds);
        }
    }
代码耗时对比:

unsafe 能够提升性能的原因主要与其底层直接访问内存的能力有关。在某些特定的场景下,这种直接访问和操作内存的方式可以比 .NET Framework 提供的更高级别的抽象方式更快、更有效率。

  1. 避免了额外的检查和装箱操作:托管代码常常会进行一些额外的操作来确保类型安全和内存安全,例如边界检查、空引用检查和装箱操作等。然而,在 unsafe 块中,这些额外的检查和操作通常都被省略了,从而节省了CPU周期。
  2. 优化数据复制:当处理大量数据或者需要频繁地复制数据时,unsafe 代码通常能提供更好的性能。由于直接操作指针,你可以避免不必要的数据复制。
  3. 优化数组操作unsafe 允许直接访问数组元素,而无需通过索引器。这样可以省略一些额外的边界检查和计算,从而提升性能。
  4. 与底层API交互:当需要与底层 API(如Windows API)交互时,unsafe 代码可以提供更直接的访问方式,从而提升性能。

ref

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/unsafe?devlangs=csharp&f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(unsafe_CSharpKeyword)%3Bk(DevLang-csharp)%26rd%3Dtrue

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-09-11 21:49,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JusterZhu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
这个代码耗时对比是每次运行都差这么多吗?为啥我本人试的差距没有那么大呢
这个代码耗时对比是每次运行都差这么多吗?为啥我本人试的差距没有那么大呢
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
c#基础系列2---深入理解 String
“大菜”:源于自己刚踏入猿途混沌时起,自我感觉不是一般的菜,因而得名“大菜”,于自身共勉。 扩展阅读:c#基础系列1---值类型和引用类型 基本概念 string(严格来说应该是System.Str
架构师修行之路
2019/07/23
6540
c#基础系列2---深入理解 String
c#基础系列1---值类型和引用类型
不知不觉已经踏入坑已10余年之多,对于c#多多少少有一点自己的认识,写出来渴求同类抨击,对自己也算是个十年之痒的一个总结。
架构师修行之路
2019/07/23
8830
c#基础系列1---值类型和引用类型
【愚公系列】2023年02月 .NET/C#知识点-数组复制总结
第二步:在你的项目文件的属性组中添加一行 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>:
愚公搬代码
2023/03/16
4560
【愚公系列】2023年02月 .NET/C#知识点-数组复制总结
【愚公系列】2023年02月 .NET/C#知识点-List转成字符串的总结
List转成字符串的总结的应用场景非常广泛,可以用于字符串拼接、数据处理和存储、显示等方面。它还可以用于格式化输出,以及将列表数据导出到文本文件等。
愚公搬代码
2023/03/16
1.9K0
【愚公系列】2023年02月 .NET/C#知识点-List转成字符串的总结
也谈枚举ToString()性能的改进
昨天看到 “性能相差7千倍的ToString方法”这篇文章,对于作者这种良好的性能意识和探索精神很佩服,以前还真没注意到这点。 不过,用switch的做法,个人觉得虽然性能上去了,但是可维护性就下来了,以后该枚举要增加或删除一项,这段switch代码都要改一下,其实该问题的关键就是反射带来的性能损耗,在调用枚举的ToString()方法时,无非就是要得到一个字符串而已,我个人更倾向于用key-value这种经典的键值对来优化。 下面是示例代码: public static class TestClass
菩提树下的杨过
2018/01/23
9680
.NET中的值类型与引用类型
这是一个常见面试题,值类型(Value Type)和引用类型(Reference Type)有什么区别?他们性能方面有什么区别?
AI.NET 极客圈
2019/08/09
2.2K0
C# ——计算线性关系kb值、R平方,类似于excel的趋势线线性关系功能
这些功能Excel上都有,原理一模一样,现在需要C#的实现代码; 各函数的线性拟合,相关系数、截距为0(即强制过原点)等等
vv彭
2021/03/16
1.2K0
C# ——计算线性关系kb值、R平方,类似于excel的趋势线线性关系功能
C#7.3 新增功能
C# 7.3 版本有两个主要主题。 第一个主题提供使安全代码的性能与不安全代码的性能一样好的功能。 第二个主题提供对现有功能的增量改进。 此外,在此版本中添加了新的编译器选项。
张传宁IT讲堂
2019/09/17
1.9K0
Unity性能调优手册10C#优化:GC,对象池,for/foreach,string,LINQ
翻译自https://github.com/CyberAgentGameEntertainment/UnityPerformanceTuningBible/
立羽
2023/11/27
1.9K0
Unity性能调优手册10C#优化:GC,对象池,for/foreach,string,LINQ
用事实说话,成熟的ORM性能不是瓶颈,灵活性不是问题:EF5.0、PDF.NET5.0、Dapper原理分析与测试手记
[本文篇幅较长,可以通过目录查看您感兴趣的内容,或者下载格式良好的PDF版本文件查看]  目录 一、ORM的"三国志"    2 1,PDF.NET诞生历程    2 2,Linq2Sql&EF:    3 3,微型ORM崛起    4 二、一决高下    4 2.1,ORM没有DataSet快?    4 2.1.1,ORM生成SQL的质量问题    4 2.1.2,DataReader没有DataSet快?    5 2,ORM的三个火枪手    6 2.1,委托+缓存    6 2.2,表达式树 
用户1177503
2018/02/27
4.3K0
用事实说话,成熟的ORM性能不是瓶颈,灵活性不是问题:EF5.0、PDF.NET5.0、Dapper原理分析与测试手记
C# 中比较实用的关键字,基础高频面试题!
在C#编程中关键字是构建逻辑和实现功能的基石,它承载着编程语言的语法规则和编程智慧。熟练掌握这些基础高频关键字对提升编程能力和面试表现至关重要,它们是日常开发和解决复杂问题的关键。
追逐时光者
2025/03/22
1870
C# 中比较实用的关键字,基础高频面试题!
c#测试字符串是否为GUID的几种方法
以前为了赶项目遇到这种需求时,也没过多考虑性能因素,随便写了一个(现在看起来很原始的)方法来实现: static bool IsGuidByError(string strSrc) { if (String.IsNullOrEmpty(strSrc)) { return false; } bool _result = false; try { Guid _t = n
菩提树下的杨过
2018/01/19
2.1K0
.NET Core中妙用unsafe减少gc提升字符串处理性能
昨天在群里讨论怎么样效率的把一个字符串进行反转,一般的情况我们都知道,只要对String对象进行操作, 那么就会生成新的String对象,比如"1"+"2" 这样的操作会生成新的String对象。
yoyofx
2018/09/05
1.8K0
MSSQL数据批量插入优化详细
那么我们针对以上两点做优化,1、创建一次sqlcommon对象,只与数据库建立一次连接。优化改造代码如下:
Vincent-yuan
2020/04/10
1.4K0
MSSQL数据批量插入优化详细
使用 C# 集合表达式重构代码
本文是系列文章的第二篇,该系列文章涵盖了探索 C# 12功能的各种重构场景。在这篇文章中,我们将了解如何使用集合表达式重构代码,我们将学习集合初始化器、各种表达式用法、支持的集合目标类型和 spread 语法。该系列的进展情况如下:
郑子铭
2024/07/02
2830
使用 C# 集合表达式重构代码
C#使用yield关键字提升迭代性能与效率
yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,减少了内存占用,并允许在迭代时执行复杂逻辑。
追逐时光者
2025/01/23
740
C#使用yield关键字提升迭代性能与效率
【C#学习笔记之一】C#中的关键字
C#中的关键字 关键字是对编译器具有特殊意义的预定义保留标识符。它们不能在程序中用作标识符,除非它们有一个 @ 前缀。例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。 下面是列出的所有的关键字在 C# 程序的任何部分都是保留标识符: abstract  as base bool break byte case catch char checked class const continue d
Angel_Kitty
2018/04/09
2.6K0
像.NET大牛一样提升C#应用性能
引言 在当今快速发展的世界中,缓慢的应用程序会导致用户沮丧并错失商业机会。无论你正在开发Web API、桌面应用程序还是企业系统,优化性能对于可扩展性、响应速度和效率至关重要。
郑子铭
2025/04/10
1960
像.NET大牛一样提升C#应用性能
Replace方法与正则表达式的性能比较
今天做项目时遇到一个小需求:要将字符串中的回车符号替换成其它符号(比如"<br/>")。 考虑到不同的情况下,有些系统中是用\r\n作回车符,有些仅用\n就代表回车符了。以前都是用String类的Replace方法连接替换多次来处理的,今天突然想改为正则表达式一次性搞定,但又怕性能上消耗太大,于是写了下面的测试代码: using System; using System.Diagnostics; namespace ConsoleApplication1 { class Program {
菩提树下的杨过
2018/01/22
1.8K0
C# FileStream简单介绍和使用
使用try{} catch(Exception ex){} 进行一次捕获;
全栈程序员站长
2022/09/15
9780
相关推荐
c#基础系列2---深入理解 String
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验