C# unsafe 性能提升

2023-09-18 17:10:46 浏览数 (1)

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复制
    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=?appId=Dev16IDEF1&l=EN-US&k=k(unsafe_CSharpKeyword);k(DevLang-csharp)&rd=true

0 人点赞