[C#] 重遇 stackalloc 關於效能跟限制的二三事

2025-06-11

最近在看一些 open source 專案的原始碼時,看到某個地方用到了 stackalloc

這關鍵字讓我一點一熟悉看一下文件這不是以前要開 unsafe 的東西

結果一查,才發現這玩意兒原來早在以前就有,只是因為後來有了 Span<T>

現在可以安全地在受控環境裡使用堆疊記憶體,完全不需要 unsafe 了。

簡單講,stackalloc 是用來在 stack 上配置一段暫時的記憶體區域。

一般我們用 new byte[256],這會在 heap 裡分配,交給 GC 管理。

但 stackalloc 是直接開一塊暫存空間,不用 GC,也不用釋放,方法結束後自動消失。

因為 為他很快,但是也有一些限制我們先看他多快

public static int GetBytesByByte(string text) { byte[] buffer = new byte[256]; return Encoding.UTF8.GetBytes(text, 0, text.Length, buffer, 0); } public static int GetBytesByStackalloc(string text) { Span<byte> buffer = stackalloc byte[256]; return Encoding.UTF8.GetBytes(text, buffer); } static void Main(string[] args) { Console.WriteLine("Hello, World!"); string text = Guid.NewGuid().ToString() + Guid.NewGuid().ToString(); int iterations = 10_000_000; // 執行次數 // --- 用 new byte[] --- var sw1 = Stopwatch.StartNew(); for (int round = 1; round <= 10; round++) { Parallel.For(0, iterations, i => { GetBytesByByte(text); }); } Console.WriteLine($"new byte[] 花費時間&#65306;{sw1.ElapsedMilliseconds} ms"); sw1.Stop(); // --- 用 stackalloc --- var sw2 = Stopwatch.StartNew(); for (int round = 1; round <= 10; round++) { Parallel.For(0, iterations, i => { GetBytesByStackalloc(text); }); } Console.WriteLine($"stackalloc 花費時間&#65306;{sw2.ElapsedMilliseconds} ms"); sw2.Stop(); }


結果:

new byte[] 花費時間&#65306;3675 ms stackalloc 花費時間&#65306;955 ms new byte[] 花費時間&#65306;3599 ms stackalloc 花費時間&#65306;951 ms new byte[] 花費時間&#65306;3951 ms stackalloc 花費時間&#65306;875 ms new byte[] 花費時間&#65306;3662 ms stackalloc 花費時間&#65306;905 ms


說說一下他的限制

限制1: 每個執行緒的堆疊空間大概只有 1MB,所以在設計 function 的時候要注意

限制2: 記憶體只在當前方法有效,方法一結束就釋放,不能回傳這個 Span、也不能存在外部物件中。

限制3: 無法在 async / iterator 裡使用

如果你這樣寫 compile 就會 error

async Task FooAsync() { Span<byte> buffer = stackalloc byte[256]; //compile error await Task.Delay(1); }


result:


大概筆記到這邊,畢竟是要追求極致效能才會使用到的東西,實際上常常寫起來都會忘記可以使用



當麻許的碎念筆記 2014 | Donma Hsu Design.