使用不安全的C#代码与使用安全的C#代码相比有什么优势?

8
unsafe static void SquarePtrParam (int* p) 
   {
      *p *= *p;
   }

VS

static void SquarePtrParam (ref int p) 
   {
      p *= p;
   }
5个回答

10

安全代码可以在任何可以运行C#代码的情况下运行(Silverlight、共享托管ASP.NET、XNA、SQL Server等),而不安全的代码需要提升信任级别。这意味着您可以在更多地方运行代码,并受到更少的限制。

此外,它是安全的,这意味着您不必担心做错事情并崩溃您的进程。


4
在这种情况下使用不安全的代码没有任何优势,因此我告诉他使用安全的代码的优点。 - Gabe
1
担心做错事情并使进程崩溃是有点误导性的陈述 - 做一个无限循环它也会崩溃。安全并不能完全防止错误。 - Sir

9
您的示例不是一个好的示例,JIT编译器已经生成了像那样的代码。底层引用也是指针。这需要快速操作,托管代码永远无法竞争。
垃圾收集堆与指针非常不兼容,您必须固定对象以使创建指向它们的指针成为可能。如果没有固定,垃圾收集器可能会移动对象,导致您的代码随机失败,破坏堆完整性。固定具有非零成本,在操作和效率损失方面,当对象被固定时进行垃圾收集时,您将遭受损失。
在访问非托管内存时,指针非常有效。典型的例子是需要访问位图像素的图像处理。这也是一种快速访问固定数组的方法,所有安全互锁都被删除,当您不迭代它们时,数组索引检查不是免费的。

我认为只要没有垃圾回收,固定的成本就是零,不是吗? - user541686
@Mehrdad - 创建GCHandle不是免费的。 - Hans Passant
固定对象肯定会有一些代价,否则它就是一个无操作。问题在于这个代价是什么。BCL 在 StringBitConverter 等类中广泛使用了 fixed 指针,因此我想代价不会太高。 - Gabe
现在我想起来了,fixed 部分可能只是元数据,由 GC 使用。JITter 可以存储信息,即代码中某个寄存器中的值指向一个无法重新定位的对象。因此,使用 fixed 可能会增加额外的元数据开销和 GC 的额外工作量,但在执行时不会消耗 CPU 周期。 - Gabe
fixed 关键字实际上并不会固定一个对象。它只是创建了另一个指针,这个指针对垃圾回收器可见。非常高效。 - Hans Passant

7

使用不安全的代码只有一个原因:原始性能。

使用不安全的代码,您可以像使用C++指针一样使用它,而运行时几乎没有太多检查。没有检查意味着您需要自己负责,但开销较小。

我只看到过它用于加速图像/位图处理。但您也可以将其用于内联字符串操作(是的,使字符串可变!!!无论如何都是个坏主意,除非您想构建StringBuilder)。其他用途包括矩阵计算或其他重型数学运算。还可能涉及与操作系统的接口和一些黑客技术。


@Timeless:请查看第二段 - user541686
@Mehrdad 谢谢。"与底层操作系统进行接口,访问内存映射设备或实现时间关键算法"等。 - Timeless

2
一个完美的例子在J.Richter的书《CLR via C#》,第三版,第16章中有所描述: 以下C#代码演示了访问二维数组的三种技术(安全、锯齿状和不安全):
using System;
using System.Diagnostics;

public static class Program {
    private const Int32 c_numElements = 10000;

    public static void Main() {
        const Int32 testCount = 10;
        Stopwatch sw;

        // Declare a two-dimensional array
        Int32[,] a2Dim = new Int32[c_numElements, c_numElements];
        // Declare a two-dimensional array as a jagged array (a vector of vectors)
        Int32[][] aJagged = new Int32[c_numElements][];
        for (Int32 x = 0; x < c_numElements; x++)
            aJagged[x] = new Int32[c_numElements];

        // 1: Access all elements of the array using the usual, safe technique
        sw = Stopwatch.StartNew();
        for (Int32 test = 0; test < testCount; test++)
            Safe2DimArrayAccess(a2Dim);
        Console.WriteLine("{0}: Safe2DimArrayAccess", sw.Elapsed);

        // 2: Access all elements of the array using the jagged array technique
        sw = Stopwatch.StartNew();
        for (Int32 test = 0; test < testCount; test++)
            SafeJaggedArrayAccess(aJagged);
        Console.WriteLine("{0}: SafeJaggedArrayAccess", sw.Elapsed);

        // 3: Access all elements of the array using the unsafe technique
        sw = Stopwatch.StartNew();
        for (Int32 test = 0; test < testCount; test++)
            Unsafe2DimArrayAccess(a2Dim);
        Console.WriteLine("{0}: Unsafe2DimArrayAccess", sw.Elapsed);
        Console.ReadLine();
    }

    private static Int32 Safe2DimArrayAccess(Int32[,] a) {
        Int32 sum = 0;
        for (Int32 x = 0; x < c_numElements; x++) {
            for (Int32 y = 0; y < c_numElements; y++) {
                sum += a[x, y];
            }
        }

        return sum;
    }

    private static Int32 SafeJaggedArrayAccess(Int32[][] a) {
        Int32 sum = 0;
        for (Int32 x = 0; x < c_numElements; x++) {
            for (Int32 y = 0; y < c_numElements; y++) {
                sum += a[x][y];
            }
        }

        return sum;
    }

    private static unsafe Int32 Unsafe2DimArrayAccess(Int32[,] a) {
        Int32 sum = 0;
        fixed (Int32* pi = a) {
            for (Int32 x = 0; x < c_numElements; x++) {
                Int32 baseOfDim = x * c_numElements;
                for (Int32 y = 0; y < c_numElements; y++) {
                    sum += pi[baseOfDim + y];
                }
            }
        }

        return sum;
    }
}

Unsafe2DimArrayAccess方法标记为不安全,需要使用C#的fixed语句。要编译此代码,您需要在调用C#编译器时指定/unsafe开关或在Microsoft Visual Studio的项目属性窗格的“生成”选项卡上选中“允许不安全代码”复选框。 当我在我的机器上运行此程序时,我得到以下输出:

00:00:02.0017692: Safe2DimArrayAccess
00:00:01.5197844: SafeJaggedArrayAccess
00:00:01.7343436: Unsafe2DimArrayAccess

从上面可以看出,安全的二维数组访问技术是最慢的。安全的交错数组访问技术比安全的二维数组访问技术快一点。但是需要注意的是,创建交错数组比创建多维数组更耗时,因为创建交错数组需要在堆上为每个维度分配一个对象,这会导致垃圾收集器周期性地启动。所以存在一个权衡:如果您需要创建大量“多维数组”,并且打算不经常访问元素,则创建多维数组更快。如果您只需要创建“多维数组”一次,并且经常访问其元素,则交错数组将提供更好的性能。当然,在大多数应用程序中,后一种情况更为常见。


1

我认为在你给出的例子中使用不安全代码并没有优势。我只有在需要与非托管代码交互时才会真正使用不安全代码,例如调用非组件 DLL 接口。


1
通常情况下,您不需要使用不安全的代码进行Interop(COM或P / Invoke)。 您几乎总是可以使用refout而不是指针。 - P Daddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接