实际使用`stackalloc`关键字

时间:2009-04-24 09:51:31

标签: c# keyword stackalloc

在使用C#进行编程时,有没有人真正使用stackalloc?我知道它是做什么的,但它出现在我的代码中的唯一一次是偶然的,因为当我开始输入static时,Intellisense会建议它。

虽然它与stackalloc的使用方案无关,但我实际上在我的应用中做了相当多的遗留互操作,因此我偶尔会使用unsafe代码。但是,我通常会找到完全避免unsafe的方法。

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

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

5 个答案:

答案 0 :(得分:120)

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

性能方面,如果使用stackalloc,由于数据的位置,会大大增加CPU上缓存命中的几率。

答案 1 :(得分:35)

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

答案 2 :(得分:23)

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

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

答案 3 :(得分:7)

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

import speech_recognition as sr
import time

r = sr.Recognizer()

# Words that sphinx should listen closely for. 0-1 is the sensitivity
# of the wake word.
keywords = [("google", 1), ("hey google", 1), ]

source = sr.Microphone()


def callback(recognizer, audio):  # this is called from the background thread

    try:
        speech_as_text = recognizer.recognize_sphinx(audio, keyword_entries=keywords)
        print(speech_as_text)

        # Look for your "Ok Google" keyword in speech_as_text
        if "google" in speech_as_text or "hey google":
            recognize_main()

    except sr.UnknownValueError:
        print("Oops! Didn't catch that")


def recognize_main():
    print("Recognizing Main...")
    audio_data = r.listen(source)
    # interpret the user's words however you normally interpret them


def start_recognizer():
    r.listen_in_background(source, callback)
    time.sleep(1000000)


start_recognizer()

您可以简单地编写:

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

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

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

来源: C# - All About Span: Exploring a New .NET Mainstay

答案 4 :(得分:0)

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

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