“ stackalloc”关键字的实际使用

有人在用 C # 编程时真正使用过 stackalloc吗?我知道它是做什么的,但是它只是偶然出现在我的代码中,因为当我开始键入 static时,智能感知会提示它,例如。

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

中的单个线程的堆栈大小。Net 是 ~ 1Mb (如果我错了请纠正我) ,我对使用 stackalloc更加保留。

是否有一些实际的情况下,人们可以说: “这正是正确的数据量和处理为我去不安全和使用 stackalloc”?

38972 次浏览

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

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

使用 stackalloc的唯一原因是性能(用于计算或互操作)。通过使用 stackalloc而不是堆分配的数组,你创建了更少的 GC 压力(GC 需要运行更少) ,你不需要固定数组,分配比堆数组更快,它在方法退出时自动释放(堆分配的数组只有在 GC 运行时才被释放)。也可以使用 stackalloc代替本机分配器(如 malloc 或。在范围退出时,您还可以获得速度和自动释放。

就性能而言,如果使用 stackalloc,由于数据的局部性,您大大增加了缓存命中 CPU 的几率。

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

跨区域的 Stackalloc 初始化。在以前的 C # 版本中,stackalloc 的结果只能存储在指针局部变量中。从 C # 7.2开始,stackalloc 现在可以用作表达式的一部分,并且可以针对 span,而且不需要使用不安全的关键字。因此,代替写作

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>

来源: http://em > < a href = “ https://msdn.microsoft.com/en-us/magazine/mt814808.aspx”rel = “ noReferrer”> C #-All About Span: Explorer a New. NET Mainstay

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

Stackalloc 还可以用来调用本机 API

许多本机函数要求调用方分配一个缓冲区以获得返回结果。例如,cfapi.h中的 CfGetPlaceholderInfo函数具有以下签名。

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);

回答晚了,但我相信还是有帮助的。

我来到这个问题,我仍然很好奇看到的性能差异,所以我创建了以下基准(使用 BenchmarkDotNet NuGet 包) :

[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);
}
}

这就是结果

|           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 |

这个基准测试表明,使用 stackalloc是最快的解决方案,而且它不使用分配! 如果你对如何使用 NuGet 包 BenchmarkDotNet 感到好奇,请观看 这个视频