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

  • 12513
  • 0
  • 2025-11-18

最近在看一些 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 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[] 花費時間:{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 花費時間:{sw2.ElapsedMilliseconds} ms");
            sw2.Stop();
          
        }
   

結果:

new byte[] 花費時間:3675 ms
stackalloc 花費時間:955 ms

new byte[] 花費時間:3599 ms
stackalloc 花費時間:951 ms

new byte[] 花費時間:3951 ms
stackalloc 花費時間:875 ms

new byte[] 花費時間:3662 ms
stackalloc 花費時間:905 ms

說說一下他的限制

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

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

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

如果你這樣寫 compile 就會 error

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

result:


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

--

本文原文首發於我的個人部落格:重遇 stackalloc 關於效能跟限制的二三事

---

The bug existed in all possible states.
Until I ran the code.