实际使用`stackalloc`关键字

Gro*_*roo 121 c# keyword stackalloc

有没有人stackalloc在C#编程时实际使用过?我知道它的作用是什么,但它出现在我的代码中的唯一一次是偶然的,因为Intellisense在我开始输入时建议它static,例如.

虽然它与使用场景无关,但stackalloc我实际上在我的应用程序中做了大量的遗留互操作,因此我偶尔会使用unsafe代码.但是,我通常会找到unsafe完全避免的方法.

而且由于.Net中单个线程的堆栈大小约为1Mb(如果我错了,请纠正我),我使用时更加保留stackalloc.

是否有一些实际案例可以说:"这对我来说是不正确的数据和处理不安全和使用stackalloc"?

Pop*_*lin 132

使用的唯一原因stackalloc是性能(用于计算或互操作).通过使用stackalloc而不是堆分配的数组,您可以创建更少的GC压力(GC需要运行更少),您不需要固定数组,分配比堆数组更快,它会自动释放方法exit(堆积分配的数组仅在GC运行时释放).此外,通过使用stackalloc而不是本机分配器(如malloc或.Net等效项),您还可以在范围退出时获得速度和自动释放.

性能方面,如果您使用它stackalloc会大大增加由于数据的位置而导致CPU上的缓存命中的可能性.

  • 数据的位置,好点!当你想要分配几个结构或数组时,这就是托管内存很少会实现的.谢谢! (24认同)
  • 对于托管对象,堆分配通常比非托管对象更快,因为没有可用于遍历的空闲列表; CLR只是递增堆指针.至于位置,由于堆压缩,顺序分配更有可能最终为长时间运行的托管进程共同定位. (22认同)
  • “分配比堆数组更快”为什么呢?只是地区性吗?不管怎样,这只是一个指针碰撞,不是吗? (2认同)
  • @MaxBarraclough因为您在整个应用程序生存期内将GC成本添加到了堆分配中。总分配成本=分配+释放,在这种情况下为指针碰撞+ GC堆,vs指针碰撞+指针减量堆栈 (2认同)

Jim*_*old 35

我已经使用stackalloc为[近]实时DSP工作分配缓冲区.这是一个非常具体的案例,其中性能需要尽可能一致.请注意,一致性和总吞吐量之间存在差异 - 在这种情况下,我并不关心堆分配太慢,只是在程序中该点的垃圾收集的非确定性.我不会在99%的情况下使用它.


Bri*_*sen 24

stackalloc仅适用于不安全的代码.对于托管代码,您无法决定在何处分配数据.默认情况下,值类型在堆栈上分配(除非它们是引用类型的一部分,在这种情况下,它们在堆上分配).引用类型在堆上分配.

普通vanilla .NET应用程序的默认堆栈大小为1 MB,但您可以在PE标头中更改此值.如果您明确地启动线程,您还可以通过构造函数重载设置不同的大小.对于ASP.NET应用程序,默认堆栈大小仅为256K,如果您在两个环境之间切换,请记住这一点.

  • C# 7.2 的 `Span<T>` 和 `ReadOnlySpan<T>` 不再是这种情况。@Anth 在下面的答案中指出了这一点。 (7认同)

ant*_*nth 11

范围的Stackalloc初始化。在早期版本的C#中,stackalloc的结果只能存储在指针局部变量中。从C#7.2开始,现在可以将stackalloc用作表达式的一部分,并且可以将其作为目标范围,并且可以在不使用unsafe关键字的情况下完成此操作。因此,与其写作

Span<byte> bytes;
unsafe
{
  byte* tmp = stackalloc byte[length];
  bytes = new Span<byte>(tmp, length);
}
Run Code Online (Sandbox Code Playgroud)

您可以简单地编写:

Span<byte> bytes = stackalloc byte[length];
Run Code Online (Sandbox Code Playgroud)

这在需要一些暂存空间来执行操作但又希望避免为较小的内存分配堆内存的情况下非常有用

Span<byte> bytes = length <= 128 ? stackalloc byte[length] : new byte[length];
... // Code that operates on the Span<byte>
Run Code Online (Sandbox Code Playgroud)

来源: C#-有关跨度的所有信息:探索新的.NET主流

  • @Frederic:显然,.NET Framework 4.8 是[.NET Framework 的最后一个主要版本](https://devblogs.microsoft.com/dotnet/net-core-is-the-future-of-net/)。.NET Core 的下一个版本将称为 [.NET 5](https://devblogs.microsoft.com/dotnet/introducing-net-5/)(不再有“核心”,并且跳过版本 4 以避免与框架),因此 .NET 的未来是 .NET Core,现在很明显 .NET Framework 应用程序不会获得此更新。我还相信这个想法是在未来删除 .NET Standard(因为从此时起将只有“.NET”)。 (4认同)
  • 谢谢你的提示。似乎每个新版本的C#都离C ++有点近,这实际上是一件好事,恕我直言。 (2认同)
  • 可以看出 [此处](https://github.com/dotnet/standard/blob/master/docs/versions.md) 和 [此处](https://devblogs.microsoft.com/dotnet/annoucing-net -standard-2-1/),唉,`Span` 在 .NET 框架 4.7.2 中不可用,甚至在 4.8 中也不可用......因此,该新语言功能目前的用途仍然有限。 (2认同)

Ton*_*Nam 11

回答晚了,但我相信仍然有帮助。

我来到这个问题时,仍然对性能差异感到好奇,因此我创建了以下基准测试(使用 BenchmarkDotNet NuGet Package):

[MemoryDiagnoser]
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
[RankColumn]
public class Benchmark1
{
    //private MemoryStream ms = new MemoryStream();

    static void FakeRead(byte[] buffer, int start, int length)
    {
        for (int i = start; i < length; i++)
            buffer[i] = (byte) (i % 250);
    }

    static void FakeRead(Span<byte> buffer)
    {
        for (int i = 0; i < buffer.Length; i++)
            buffer[i] = (byte) (i % 250);
    }

    [Benchmark]
    public void AllocatingOnHeap()
    {
        var buffer = new byte[1024];
        FakeRead(buffer, 0, buffer.Length);
    }

    [Benchmark]
    public void ConvertingToSpan()
    {
        var buffer = new Span<byte>(new byte[1024]);
        FakeRead(buffer);
    }

    [Benchmark]
    public void UsingStackAlloc()
    {
        Span<byte> buffer = stackalloc byte[1024];
        FakeRead(buffer);
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是结果

|           Method |     Mean |    Error |   StdDev | Rank |  Gen 0 | Allocated |
|----------------- |---------:|---------:|---------:|-----:|-------:|----------:|
|  UsingStackAlloc | 704.9 ns | 13.81 ns | 12.91 ns |    1 |      - |         - |
| ConvertingToSpan | 755.8 ns |  5.77 ns |  5.40 ns |    2 | 0.0124 |   1,048 B |
| AllocatingOnHeap | 839.3 ns |  4.52 ns |  4.23 ns |    3 | 0.0124 |   1,048 B |
Run Code Online (Sandbox Code Playgroud)

该基准测试表明 usingstackalloc是最快的解决方案,而且它不使用任何分配!如果您对如何使用 NuGet Package BenchmarkDotNet 感到好奇,请观看此视频


fjc*_*997 8

这个问题有一些很好的答案,但我只想指出

Stackalloc也可用于调用本机API

许多本机函数需要调用者分配缓冲区来获取返回结果。例如,CfGetPlaceholderInfo函数cfapi.h具有以下签名。

HRESULT CfGetPlaceholderInfo(
HANDLE                    FileHandle,
CF_PLACEHOLDER_INFO_CLASS InfoClass,
PVOID                     InfoBuffer,
DWORD                     InfoBufferLength,
PDWORD                    ReturnedLength);
Run Code Online (Sandbox Code Playgroud)

为了通过互操作在C#中调用它,

[DllImport("Cfapi.dll")]
public static unsafe extern HResult CfGetPlaceholderInfo(IntPtr fileHandle, uint infoClass, void* infoBuffer, uint infoBufferLength, out uint returnedLength);
Run Code Online (Sandbox Code Playgroud)

您可以使用 stackalloc。

byte* buffer = stackalloc byte[1024];
CfGetPlaceholderInfo(fileHandle, 0, buffer, 1024, out var returnedLength);
Run Code Online (Sandbox Code Playgroud)