c # структурирует / классы управляют стеком / кучей? - PullRequest
13 голосов
/ 09 января 2010

так что в с ++ это очень просто. Вы хотите, чтобы любой класс / структура был размещен в куче, используйте new. если вы хотите использовать его в стеке, не используйте new.

в C # мы всегда используем ключевое слово new, и в зависимости от того, является ли это структура или класс, он размещается либо в стеке, либо в куче (структуры идут в стек, классы в кучу) - и в некоторых приложениях может быть ОГРОМНАЯ разница в производительности при изменении дизайна, так что только те объекты попадают в кучу, которая действительно там находится.

Что мне интересно, так это - есть ли прямой способ контролировать, где объект размещается независимо от того, объявлен ли он как структура или класс? я знаю, что типы значений (структуры) могут быть упакованы, чтобы попасть в кучу (но упаковка / распаковка происходит за счет производительности). Есть ли способ размещения классов в стеке?

Кроме того, есть ли механизм для выделения необработанной памяти и использования чего-то вроде размещения новых в C ++? Я знаю, что это противоречит идее управления, но это может иметь большое значение для производительности, если вы можете использовать свое собственное управление памятью.

Мне нравится C # за его удобство, за сборщик мусора и другие вещи - но иногда, работая над узким местом приложения, желательно иметь больший контроль над тем, что происходит на самом деле.

Любые советы / подсказки приветствуются:)

редактировать: пример производительности:

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);
    }
}

Это займет 1,8 секунды на моем компьютере для выполнения (обратите внимание, что на самом деле происходит только распределение - без передачи параметров)

если Foo1 изменяется со структуры на класс, выполнение занимает 8,9 секунды! это в пять раз медленнее

Ответы [ 5 ]

9 голосов
/ 09 января 2010

Хотя в общем случае верно, что объекты всегда размещаются в куче, C # позволяет вам опуститься до уровня указателя для интенсивного взаимодействия или для очень высокопроизводительного критического кода.

В небезопасных блоках вы можете использовать stackalloc для выделения объектов в стеке и использования их в качестве указателей.

Процитирую их пример:

// 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]);
   }
}

Обратите внимание, однако, что вам не нужно объявлять весь метод небезопасным, вы можете просто использовать для него блок unsafe {...}.

6 голосов
/ 09 января 2010

Ваше объяснение того, куда идут типы значений и ссылочные типы (стек против кучи), не совсем корректно.

Структуры также могут быть размещены в куче, если они, например, являются членами ссылочного типа. Или, если вы поместили их в коробку при передаче через ссылку на объект.

Вы должны прочитать http://www.yoda.arachsys.com/csharp/memory.html, чтобы лучше понять, где на самом деле размещены различные типы.

В отдельном примечании, в .Net вы действительно не должны заботиться о том, где размещены типы - как пишет Эрик Липперт: стек - это деталь реализации . Вам лучше понять семантику передачи типов (по значению, ссылкам и т. Д.).

Кроме того, вы, похоже, подразумеваете, что выделение объекта в куче обходится дороже, чем в стеке. На самом деле, я бы сказал, что затраты на копирование типов значений по производительности перевешивают выгоду от любой экономии при немного более быстром распределении в стеке. Самое большое различие между стеком и кучей заключается в том, что на большинстве архитектур ЦП стек с большей вероятностью будет храниться в кеше ЦП и, таким образом, избегать ошибок в кеше.

Это не самая важная проблема, связанная с . Вы должны решить, должен ли тип иметь семантику передачи по значению или нет. Если это не так - тогда, возможно, это должен быть ссылочный тип.

2 голосов
/ 09 января 2010

Не обманывайте себя ключевым словом new, это не обязательно для структур.

В C # существует управляемый мир, где вы наслаждаетесь сборщиком мусора и Type-Safety и не беспокоитесь о многих деталях памяти. Разница между стеком и кучей не имеет значения, речь идет о семантике копирования.

В тех редких случаях, когда вам нужен контроль, есть небезопасная (неуправляемая) часть C # с реальными указателями и всем остальным.

Но стоимость вещей в C # отличается от стоимости в C ++, так что не охотьтесь за призраками, неуправляемые, недолговечные объекты очень дешевы. И компилятор может размещать небольшие массивы в стеке в качестве оптимизации, вы не сможете этого сказать, и вас это не должно волновать.

1 голос
/ 09 января 2010

Это неправильный способ просмотра структур и классов в C #. В C # разница между структурой и классом заключается не в том, где он расположен, а в семантике копирования. Структура имеет семантику значений, а класс имеет семантику ссылок. Программисты C ++ склонны больше в этом разбираться, поскольку они используются для объектов в стеке, имеющих семантику значений, и объектов в куче, имеющих семантику ссылок.

Как эта память распределяется - деталь реализации среды выполнения. Среда выполнения может использовать стеки, кучи или любую другую гибридную схему распределения, которая ей нравится. Хотя это правда, что обычно структуры будут размещаться в стеке, а классы - в куче, это не обязательно. Например, класс, выделенный в функции и не выходящий за пределы области действия функции, может быть легко размещен в стеке.

1 голос
/ 09 января 2010

Не беспокойтесь об этом - вы все еще находитесь в мире c / c ++, где многое может иметь значение. В команде CLR есть куча действительно умных людей, которые целый день беспокоятся о том, как сделать это волшебным образом.

Есть некоторые ошибки в c # и использование памяти, обычно связанное с созданием большого количества крошечных объектов в результате несчастных случаев (выполнение string = string + другая строка в цикле является классическим)

Существует memprofiler, который будет показывать вам, что происходит, если вы действительно думаете, что у вас есть проблемы с производительностью, вызванные управлением памятью

Я написал много кода с высокой производительностью (клиенты визуализации графики, сетевые серверы) на C #, и мне никогда не приходилось беспокоиться об этом

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...