13

so in c++ it's very easy. you want whatever class/struct to be allocated on the heap, use new. if you want it on the stack, don't use new.

in C# we always use the new keyword, and depending on whether it's a struct or a class it's allocated either on the stack or on the heap (structs go to the stack, classes to the heap) - and in some applications there can be a HUGE performance difference when changing the design such that only those objects go to the heap that really belong there.

What I wonder is - is there a direct way to control where an object is allocated independant of whether it's declared as struct or class? i know that value types (structs) can be boxed to go to the heap (but boxing/unboxing comes at a performance cost). is there a way to allocate classes on the stack?

Also, is there any mechanism to allocate raw memory and use something like placement new in C++? I know that this breaks with the idea of being managed - but it can make a big performance difference if you can use your custom memory management.

I love C# for it's convenience, for it's garbage collector and other things - but sometimes, when working on the bottleneck of an application, it meight be desirable to have more control over what is actually happening.

Any tips/hints welcome :)

edit: performance example:

struct Foo1
{
    public int i;
    public float f;
    public double d;
}

struct Foo2
{
   public Foo1[] bar;

   public void Init(){
        bar = new Foo1[100];
        for (int i = 0; i < 100; i++)
            bar[i] = new Foo1();
    }
}

class Program
{
    static void Main(string[] args)
    {
        DateTime time = DateTime.Now;
        Foo2[] arr = new Foo2[1000000];
        for (int i = 0; i < 1000000; i++)
        {
            arr[i] = new Foo2();
            arr[i].Init();
        }

        Console.WriteLine((DateTime.Now - time).TotalMilliseconds);
    }
}

This takes 1.8 seconds on my machine to execute (note that there is actually only allocation going on - no parameter passing)

if Foo1 is changed from struct to class, execution takes 8.9 seconds! that's five times slower

trincot
  • 317,000
  • 35
  • 244
  • 286
Mat
  • 11,263
  • 10
  • 45
  • 48
  • 3
    What makes you believe there is a *huge* performance difference between allocating objects on the stack vs. the heap? – LBushkin Jan 08 '10 at 22:37
  • I noticed when i implemented a physics solver for a 3d game - i could optimize performance drastically by only carefully changing things connected to where objects are allocated and how objects are passed in functions – Mat Jan 08 '10 at 22:39
  • You do know that the stack and the heap are essentially the same block of memory (caching aside), allocation is a matter of which pointer you reference as the base. Sorry, to be clear the classic stack/heap are essentially the same block of memory – GrayWizardx Jan 08 '10 at 22:40
  • you want to say allocation on the heap is as cheap as allocation on the stack? that's not true – Mat Jan 08 '10 at 22:42
  • For C# allocation semantics for speed are less of a concern: http://stackoverflow.com/questions/477101/heap-versus-stack-allocation-implications-net – GrayWizardx Jan 08 '10 at 22:50
  • his conclusion is that developers shouldn't care. however - during my development of an XNA game for the XBox i noticed that it can make a real difference if you care in fact. – Mat Jan 08 '10 at 22:55
  • I added an example showing performance difference – Mat Jan 08 '10 at 23:55
  • +1 @Mat: Even though I think that your fear is misplaced, I learned a lot from the answers on this thread. – Jim G. Jan 09 '10 at 00:26
  • Mat, yesterday I overlooked that you are using XNA. You may want to read this question: http://stackoverflow.com/questions/1582754/does-using-a-delegate-create-garbage – H H Jan 09 '10 at 09:37
  • well the problem is not related to the garbage collector being implemented differently on the Xbox, since those performance differences apply also to XNA/regular C# code on Windows – Mat Jan 09 '10 at 12:53

5 Answers5

10

While in the general case it's true that objects are always allocated on the heap, C# does let you drop down to the pointer level for heavy interop or for very high performance critical code.

In unsafe blocks, you can use stackalloc to allocate objects on the stack and use them as pointers.

To quote their example:

// cs_keyword_stackalloc.cs
// compile with: /unsafe
using System; 

class Test
{
   public static unsafe void Main() 
   {
      int* fib = stackalloc int[100];
      int* p = fib;
      *p++ = *p++ = 1;
      for (int i=2; i<100; ++i, ++p)
         *p = p[-1] + p[-2];
      for (int i=0; i<10; ++i)
         Console.WriteLine (fib[i]);
   }
}

Note however that you don't need to declare an entire method unsafe, you can just use an unsafe {...} block for it.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • 1
    Note that this requires some special permissions, so if you run your code in less than a full trust environment, you may run into trouble. An example of this is Silverlight. – Blindy Jan 08 '10 at 23:00
  • Did you mean "in the general case it's true that objects are always allocated on the *heap*"? – G-Wiz Jan 08 '10 at 23:02
  • 1
    As a side note, the reason why there is no way to do it "the other way around" - a kind of a "placement `new`" for C# classes - is because classes have to be tracked by GC, as well as everything they reference, and that isn't possible to implement (efficiently, anyway) with just any random memory block. So with classes, you're stuck with the heap. Also, the trick described in this answer won't work for structs which have fields of reference types (again, because GC must be able trace them). Fields of pointer types are fine, though, so it's just as expressive as plain ANSI C. – Pavel Minaev Jan 09 '10 at 00:08
  • 1
    why do they have to be traced by the GC? if i allocate something on the stack, i'm fine with it being deallocated once it goes out of scope - or if it's used in an array, and therefore the objects are allocated continguously in memory, then i'm fine with the objects being deleted as soon as the array is deleted – Mat Jan 09 '10 at 00:14
  • @Blindy: how would one - using this unsafe syntax - define an array variable of CLASS Foo: - Foo[] myArr; - such that it would be equivalent to following C++ syntax: Foo* myArr = new Foo[100]? (as oposed to Foo** myArr = new Foo*[100], which is what C# does by default) – Mat Jan 09 '10 at 13:08
  • The best you can do is `Foo *myArr=stackalloc Foo[100];` – Blindy Jan 09 '10 at 20:36
  • The reason why it has to be GC-traced is because .NET guarantees memory safety, which, among other things, means "no dangling references". In C++, if you create an object on the stack, get a pointer to it, store it past the lifetime of the object, and then use it - well, it's U.B., but the runtime won't stop you. .NET is specifically designed to avoid such things. Hence it _must_ trace references to ensure that object is unreferenced prior to deallocating it (as an optimization, if it can statically prove that object isn't referenced - escape analysis - it can skip the GC and use the stack). – Pavel Minaev Jan 11 '10 at 07:26
  • stakalloc is best left as a JIT optimization. – H H Jan 20 '10 at 23:35
5

Your explanation of where value types vs. reference types go (stack v. heap) is not completely correct.

Structs can also get allocated on the heap if they are members of a reference type for example. Or if you have boxed them while passing them via an object reference.

You should read http://www.yoda.arachsys.com/csharp/memory.html to get a better understanding of where different types actually are allocated.

On a separate note, in .Net, you really shouldn't care about where a types is allocated - as Eric Lippert writes: the stack is an implementation detail. You are better off understanding the semantics of how types are passed (by value, be reference, etc).

Furthermore, you seem to be making an implication that allocating an object on the heap is more expensive than on the stack. In reality, I would argue that the performance cost of copying value types outweighs the benefit of any savings in a slightly quicker allocation on the stack. The biggest difference between stack and heap, is that on most CPU architectures the stack is more likely to be retained in CPU cache - and thereby avoid cache misses.

This is not the most important issue to be concerned with. You should decide whether the type should have pass-by-value semantics or not. If it doesn't - then perhaps it should be a reference type.

LBushkin
  • 129,300
  • 32
  • 216
  • 265
  • let's say you create a large array of some objecttype. The Array itself will go to the heap anyway. however - after my understanding - if the objecttype is a struct, the whole thing will be allocated in continguous memory (one cluster of data - or probably a few splitup depending on implementation) on the heap. however - if it's a class, each element of the array itself will be allocated on the heap as well - taking more time for allocation and making garbage collection slower – Mat Jan 08 '10 at 22:49
  • @Mat: the fact that array is allocated on the heap is an implementation detail. The fact that it is an array of references (i.e. pointers, for us C++ guys) is not, however. – Pavel Minaev Jan 09 '10 at 00:05
  • @Pavel - exactly that's what i mean (i don't care whether the array itself is on the heap, since a single allocation on the heap doesn't hurt as much as one allocation for each element) - so i think it's desirable to decide the referencetype/valuetype behaviour upon usage, and not upon declaration of the class/struct – Mat Jan 09 '10 at 00:17
  • 1
    The memory article disappeared in 2019, it can now be found at: https://web.archive.org/web/20190124144928/http://www.yoda.arachsys.com/csharp/memory.html – Loring Jul 01 '21 at 20:16
2

Don't be fooled by the new keyword, it's optional for structs.

In C# there is a managed world where you enjoy the Garbage collector and Type-Safety and don't (have to) worry about many memory details. The Stack/Heap difference is irrelevant, it's about copy-semantics.

For those rare cases where you do want control, there is the unsafe (unmanaged) part of C# with real pointers and everything.

But the cost of things are different in C# than they are in C++ so don't hunt ghosts, unmanaged, short lived objects are very cheap. And the compiler can allocate small arrays on the stack as an optimization, you won't be able to tell and neither should you care.

H H
  • 263,252
  • 30
  • 330
  • 514
  • but there's a notable performance difference in allocation time for structs and classes - so it'd be nice to decide where to allocate at allocation time, and not while writing the actual class – Mat Jan 09 '10 at 00:01
  • 1
    What do you mean by "`new`... is optional for structs"? In what context? – Pavel Minaev Jan 09 '10 at 00:05
  • I think he means that for structs, the default constructor is automaticall called, also without keyword new. however, for classes the variable will be a Null pointer until a constructor is explicitely called (using new) – Mat Jan 09 '10 at 00:19
  • Mat: "notable performance difference ..." is not (equally) true for the managed world. Check your assumptions. – H H Jan 09 '10 at 00:27
  • Pavel: it is not necessary to construct a struct with new. But without, C#'s assignment tracking comes into play. – H H Jan 09 '10 at 00:29
  • Mat, re "default constructor is automatically called": No it isn't. – H H Jan 09 '10 at 00:31
  • @Henk: don't really know what you mean by that? if one thing takes 5 times longer than another thing, i'd say it's a notable performance difference, independantly of whether it's managed code or not. – Mat Jan 09 '10 at 00:32
  • @Henk. No? but a struct gets automatically initialized to zero, and that's performed by the (parameterless) default constructor – Mat Jan 09 '10 at 00:34
  • Mat, only field-of-struct are initialized, a struct variable is not. And structs do have an advantage when used in arrays, but your benchmark is comparing apples and oranges. – H H Jan 09 '10 at 09:34
  • guess you're right about the initialization - but my benchmark is comparing structs to classes - not apples and oranges. Let's say you'd write a struct containing a few hundred bytes of fields. if you only think about copy semantics, you most probably will declare it as class. But if you know that you're going to use it in a context like in my example, using a struct would be much more performant. The problem is, that you can't always know about this when you write your class/struct. That's why it's desirable to decide this behaviour when you use it. No? – Mat Jan 09 '10 at 12:51
1

Dont worry about it - you head is still in the c / c++ world where it can matter a lot where things go. There are a bunch of really smart people in CLR team who spend all day worrying about making this magically fast.

There are some gotchas in c# and memory usage usually assocaited with creating lots of tiny object by accidents (doing string = string + other string in a loop is a classic)

There is a memprofiler that will show you whats happening if you really think you have a perf issue caused by memory management

I have written lots of performance intense code (graphics rendering clients, network servers) in c# and never had to worry about any of this

pm100
  • 48,078
  • 23
  • 82
  • 145
  • but especially in graphics rendering, this meight make a big difference. for example a particle system will be a lot slower if each particle (even when stored in an array and reused upon death) is allocated on it's own on the heap as opposed of being allocated in one bunch of memory – Mat Jan 09 '10 at 00:04
  • make a big array of particle structs (as other posters pointed out , you can put structs all over the place, not just the stack). You will end up with one big contigous block of memory – pm100 Jan 09 '10 at 00:19
  • exactly - but the point is, that this decision has to be taken when the code for the particle is written. if you want to use some class from a library, you don't have the choice of allocating it in one continguous block – Mat Jan 09 '10 at 00:22
1

This is the wrong way to view structs and classes in C#. In C# the difference between a struct and a class is not where it is allocated, but the copy semantics. A struct has value semantics and a class has reference semantics. C++ programmers tend to read more into this, as they are used to objects on the stack having value semantics and objects on the heap having reference semantics.

How this memory is allocated is an implementation detail of the runtime. The runtime can use stacks, heaps, or any other hybrid allocation scheme it likes. While it's true that usually structs will be allocated on something like a stack, and classes will be allocated on some sort of a heap, it is not required. For example, a class allocated in a function and not passed outside the scope of the function could quite easily be allocated on the stack instead.

Niall
  • 4,983
  • 1
  • 20
  • 12
  • i do have quite good control over the copy semantics independantly of whether it's a struct or class. i can use deepCopy/shallowCopy to have copying for reference types, i can use boxing/unboxing to get reference semantics for valuetype, i can use ref keyword to accept value types by reference as a function parameter. But deciding about where to allocate a variable can be important for performance too – Mat Jan 09 '10 at 00:52
  • I'm not saying that choosing between a class and struct is the one and only way to control copy semantics. I'm saying that class/struct is only about copy semantics, not about allocation policy. You might change the performance against a particular runtime by persuading it to allocate differently, but that is implementation dependent. Which is fine if you're optimizing for performance. The cases where this will actually make a difference are probably rarer than you think though. – Niall Jan 09 '10 at 05:27
  • but that was my question - my problem is that i've got the tools for persuading copy semantics, but i don't have the tools for persuading allocation behaviour. And that class/struct is not about allocation policy is not true - for example read this msdn article: http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx "When you call the New operator on a class, it will be allocated on the heap. However, when you instantiate a struct, it gets created on the stack. This will yield performance gains." – Mat Jan 09 '10 at 12:44
  • Whether or not this is valid advice, it in no way attempts to answer his question. – Chad Schouggins May 28 '14 at 17:24