Stackalloc vs. Fixed sized buffer in C#. What is the difference
Asked Answered
M

1

6

As far as I'm concerned the following code will create an array on the stack:

unsafe struct Foo 
{ 
   public fixed int bar[10]; 
}
var foo = new Foo();

And stackalloc statement will do the same thing:

public void unsafe foo(int length)
{
    Span<int> bar = stackalloc int[length];
}

So I'm wondering what is the difference between those approaches. And also what is the purpose of fixed size buffers at all? Everyone talks about performance boost, but I can not understand why do I need them, when I already can create an array on the stack with stackalloc. MSDN says that fixed sized buffers are used to "interoperate" with other platforms. So what is this "interoperation" looks like?

Marsiella answered 12/11, 2021 at 12:32 Comment(3)
"And also what is the purpose of fixed size buffers at all?" -- interop scenarios, where the C/C++ function being called expects a struct with an array stored inline inside itNosedive
Fixed-size arrays are, as the name suggests, fixed-size. Their use is interop. stackalloc lets the size of the allocated array be dynamicNosedive
(To be clear: "Interoperation" means calling into native C/C++ code. These languages let you create structures which contain arrays embedded in them, which is something that C# doesn't normally do. In order to support calling C/C++ functions which make use of this functionality, C# has (niche) support for fixed-size arrays inside structs)Nosedive
S
10

The fixed statement only says that an array is inlined (fixed inside) the struct. Meaning the data stored in the array is directly stored in your struct. In your example the Foo struct would have the size needed to store 10 integer values. As structs are value types they are allocated on the stack. However they can also be copied to the heap when for example storing them in a reference type.

class Test1
{
    private Foo Foo = new();
}

unsafe struct Foo
{
    public fixed int bar[10];
}

The code above will compile and the private Foo instance will live on the managed heap.

Without the fixed statement (just a "normal" int[]) the data of the array would not be stored in the struct itself but on the heap. The struct would only own a reference to that array.

When using stackalloc the data is allocated on the stack and cannot be automatically moved to the heap by the CLR. This means that stackalloc'ed data remains on the stack and the compiler will enforce this by not allowing code like this:

unsafe class Test1
{
    // CS8345: Field or auto-implemented property cannot be of type 'Span<int>' unless it is an instance member of a ref struct.

    private Span<int> mySpan;

    public Test1()
    {
        // CS8353: A result of a stackalloc expression of type 'Span<int>' cannot be used in this context because it may be exposed outside of the containing method
        mySpan = stackalloc int[10];
    }
}

Therefore you use stackalloc when you absolutely want to make sure that your allocated data can not escape to the heap resulting in increased pressure on the grabage collector (it's a performance thing). fixed on the other hand is mainly used for interop scenarios with native C/C++ libraries which may use inlined buffers for some reason or another. So when calling methods from the native world which take structs with inlined buffers as a parameter you must be able to recreate this in .NET or else you wouldn't be able to easily work with native code (thus the fixed statement exists). Another reason to use fixed is for inlining the data in your struct which can allow for better caching when the CPU is accessing it as it can just read all the data in Foo in one go without having to dereference a reference and jump around the memory to access some array stored elsewhere.

Shouse answered 12/11, 2021 at 13:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.