简体   繁体   English

c#结构/类堆栈/堆控制?

[英]c# structs/classes stack/heap control?

so in c++ it's very easy.所以在 C++ 中很容易。 you want whatever class/struct to be allocated on the heap, use new.您希望在堆上分配任何类/结构,请使用 new。 if you want it on the stack, don't use new.如果你想要它在堆栈上,不要使用 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.在 C# 中,我们总是使用 new 关键字,并且根据它是结构体还是类,它是在堆栈上还是在堆上分配(结构体进入堆栈,类进入堆) - 在某些应用程序中可能有一个更改设计时的巨大性能差异,以便只有那些对象进入真正属于那里的堆。

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++?另外,是否有任何机制来分配原始内存并在 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.我喜欢 C#,因为它很方便,因为它是垃圾收集器和其他东西——但有时,当处理应用程序的瓶颈时,可能需要对实际发生的事情有更多的控制。

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)这在我的机器上执行需要 1.8 秒(请注意,实际上只有分配正在进行 - 没有参数传递)

if Foo1 is changed from struct to class, execution takes 8.9 seconds!如果将 Foo1 从 struct 更改为 class,则执行需要 8.9 秒! that's five times slower那慢了五倍

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.虽然在一般情况下,对象总是在堆上分配是正确的,但 C# 确实允许您下降到指针级别以进行繁重的互操作或非常高性能的关键代码。

In unsafe blocks, you can use stackalloc to allocate objects on the stack and use them as pointers.不安全的块中,您可以使用stackalloc在堆栈上分配对象并将它们用作指针。

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.但是请注意,您不需要将整个方法声明为不安全的,您只需使用一个unsafe {...}块即可。

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.您应该阅读http://www.yoda.arachsys.com/csharp/memory.html以更好地了解不同类型的实际分配位置。

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 .另外,在 .Net 中,您真的不应该关心类型的分配位置 - 正如 Eric Lippert 所写: 堆栈是一个实现细节 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.堆栈和堆之间的最大区别在于,在大多数 CPU 架构中,堆栈更有可能保留在 CPU 缓存中——从而避免缓存未命中。

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.如果不是 - 那么它可能应该是一个引用类型。

Don't be fooled by the new keyword, it's optional for structs.不要被new关键字所迷惑,它对于结构来说是可选的。

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.在 C# 中有一个托管世界,您可以在其中享受垃圾收集器和类型安全,而不必(不必)担心许多内存细节。 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.对于那些您确实想要控制的罕见情况,C# 的不安全(非托管)部分包含真实指针和所有内容。

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.但是 C# 中的东西的成本与 C++ 中的不同,所以不要寻找鬼魂,非托管的、短寿命的对象非常便宜。 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.编译器可以在堆栈上分配小数组作为优化,您将无法分辨,也不应该关心。

Dont worry about it - you head is still in the c / c++ world where it can matter a lot where things go.别担心 - 你的头脑仍然处于 c/c++ 世界,在那里事情发生的地方很重要。 There are a bunch of really smart people in CLR team who spend all day worrying about making this magically fast. CLR 团队中有一群非常聪明的人,他们整天都在担心如何神奇地加快速度。

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) c# 中有一些问题,内存使用通常与意外创建大量小对象相关(在循环中执行 string = string + other string 是经典的)

There is a memprofiler that will show you whats happening if you really think you have a perf issue caused by memory management如果你真的认为你有内存管理引起的性能问题,有一个 memprofiler 会告诉你发生了什么

I have written lots of performance intense code (graphics rendering clients, network servers) in c# and never had to worry about any of this我已经用 C# 编写了大量性能密集型代码(图形渲染客户端、网络服务器),而且从来不必担心这些

This is the wrong way to view structs and classes in C#.这是在 C# 中查看结构和类的错误方法。 In C# the difference between a struct and a class is not where it is allocated, but the copy semantics.在 C# 中,struct 和 class 之间的区别不在于它的分配位置,而在于复制语义。 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. C++ 程序员倾向于更多地阅读这一点,因为他们习惯于堆栈上的对象具有值语义和堆上的对象具有引用语义。

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.例如,在函数中分配但未在函数范围之外传递的类可以很容易地分配在堆栈上。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM