168

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

虽然与 的使用场景无关,但stackalloc我实际上在我的应用程序中做了相当多的遗留互操作,所以我时不时可以求助于使用unsafe代码。但尽管如此,我通常会找到unsafe完全避免的方法。

而且由于 .Net 中单个线程的堆栈大小约为 1Mb(如果我错了,请纠正我),因此我更不愿意使用stackalloc.

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

4

5 回答 5

186

使用的唯一原因stackalloc是性能(用于计算或互操作)。通过使用stackalloc而不是堆分配的数组,您可以减少 GC 压力(GC 需要运行更少),您不需要将数组固定下来,它比堆数组分配更快,并且在方法上自动释放退出(堆分配的数组仅在 GC 运行时释放)。此外,通过使用stackalloc而不是本地分配器(如 malloc 或 .Net 等价物),您还可以在范围退出时获得速度和自动释放。

性能方面,如果您使用stackalloc,由于数据的局部性,您会大大增加 CPU 上缓存命中的机会。

于 2009-04-24T10:08:32.237 回答
45

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

于 2009-04-24T10:26:27.780 回答
29

跨度的 Stackalloc 初始化。在以前的 C# 版本中,stackalloc 的结果只能存储到指针局部变量中。从 C# 7.2 开始,stackalloc 现在可以用作表达式的一部分并且可以针对跨度,这可以在不使用 unsafe 关键字的情况下完成。因此,而不是写

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

你可以简单地写:

Span<byte> bytes = stackalloc byte[length];

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

Span<byte> bytes = length <= 128 ? stackalloc byte[length] : new byte[length];
... // Code that operates on the Span<byte>

来源: C#——关于 Span 的一切:探索新的 .NET 支柱

于 2018-12-09T11:18:58.527 回答
28

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

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

于 2009-04-24T10:03:50.937 回答
5

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

Stackalloc 也可用于调用原生 API

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

HRESULT CfGetPlaceholderInfo(
HANDLE                    FileHandle,
CF_PLACEHOLDER_INFO_CLASS InfoClass,
PVOID                     InfoBuffer,
DWORD                     InfoBufferLength,
PDWORD                    ReturnedLength);

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

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

您可以使用stackalloc。

byte* buffer = stackalloc byte[1024];
CfGetPlaceholderInfo(fileHandle, 0, buffer, 1024, out var returnedLength);
于 2019-10-14T15:24:37.557 回答