Почему 16 байтов рекомендуемый размер для структуры в C #? - PullRequest
33 голосов
/ 09 марта 2010

Я читаю книгу Cwalina (рекомендации по разработке и дизайну приложений .NET).

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

Почему именно это?

И (что более важно) могу ли я иметь большую структуру с той же эффективностью, если я запускаю .NET 3.5 (скоро будет .NET 4.0) 64-разрядное приложение на Core i7 под Windows 7 x64 (это ограничение на основе CPU / OS)?

Просто еще раз подчеркну - мне нужна настолько эффективная структура, насколько это возможно. Я стараюсь держать его в стеке все время. Приложение является многопоточным и работает с интервалом менее миллисекунды, а текущий размер структуры составляет 64 байта.

Ответы [ 7 ]

21 голосов
/ 09 марта 2010

Вы неправильно цитируете книгу (по крайней мере, второе издание). Джеффри Рихтер утверждает, что типы значений могут быть больше 16 байтов, если:

Вы не собираетесь передавать их другим методы или скопировать их и из коллекционный класс.

Кроме того, Эрик Ганнерсон добавляет (в отношении ограничения в 16 байт)

Используйте это руководство как триггер, чтобы сделать дальнейшее расследование.

Это просто неправда, что структура "должна иметь размер менее 16 байт". Все зависит от использования.

Если вы создаете структуру, а также используете ее и беспокоитесь о производительности, используйте профилировщик, сравнивающий структуру struct с классом, чтобы определить, какой из них лучше для вас.

10 голосов
/ 14 марта 2013

JIT-компилятор создает специальный код для копирования структур, которые меньше определенного порога, и несколько более медленную конструкцию общего назначения для больших. Я предположил бы, что, когда этот совет был написан, порог был 16 байтов, но в сегодняшней 32-битной структуре это, кажется, 24 байта; может быть больше для 64-битного кода.

Как уже было сказано, стоимость создания любого объекта класса размера существенно больше, чем стоимость копирования структуры, содержащей те же данные. Если код создает объект класса с полями размером 32 байта, а затем передает или иным образом копирует ссылку на этот объект 1000 раз, экономия времени при копировании 1000 ссылок на объекты вместо необходимости копировать 1000 32-байтовых структур, вероятно, перевесит стоимость создание объекта класса. Однако если экземпляр объекта будет отменен после того, как ссылка будет скопирована только дважды, стоимость создания объекта, вероятно, значительно превысит стоимость копирования 32-байтовой структуры в два раза.

Обратите также внимание, что во многих случаях можно избежать передачи структур по значению или иным образом избыточного их копирования, если кто-либо попытается это сделать. Например, для передачи любого размера структуры в качестве параметра ref методу требуется только передача адреса из одного машинного слова (4 или 8 байтов). Поскольку в .net отсутствует какая-либо концепция const ref, таким образом могут быть переданы только доступные для записи поля или переменные, а коллекции, встроенные в .net - кроме System.Array - не предоставляют средств для доступа к членам с помощью ref. Однако если вы хотите использовать массивы или пользовательские коллекции, даже огромные (100 байт или более) структуры могут быть обработаны очень эффективно. Во многих случаях преимущество в производительности при использовании структуры, а не неизменяемого класса может возрасти с размером инкапсулированных данных.

9 голосов
/ 09 марта 2010

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

class MainClass
{
    static void Main()
    {
        Struct64 s1 = new Struct64();
        Class64 c1 = new Class64();
        DoStuff(s1);
        DoStuff(c1);
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 10000; i++)
        {
            s1 = DoStuff(s1);
        }
        sw.Stop();
        Console.WriteLine("Struct {0}", sw.ElapsedTicks);
        sw.Reset();

        sw.Start();
        for (int i = 0; i < 10000; i++)
        {
            c1 = DoStuff(c1);
        }
        sw.Stop();
        Console.WriteLine("Class {0}", sw.ElapsedTicks);
        sw.Reset();

        Console.ReadLine();
    }
}

с:

public class Class64
{
    public long l1;
    public long l2;
    public long l3;
    public long l4;
    public long l5;
    public long l6;
    public long l7;
    public long l8;
}
public struct Struct64
{
    public long l1;
    public long l2;
    public long l3;
    public long l4;
    public long l5;
    public long l6;
    public long l7;
    public long l8;
}

Попробуйте подобного рода вещи с представительными структурами / классами и посмотрите, какие результаты вы получите. (На моей машине выше тестовый класс кажется в 3 раза быстрее)

4 голосов
/ 09 марта 2010
  • Большая структура не так эффективна, но тогда .... если у вас есть больше данных, у вас есть. Нет смысла говорить об эффективности там.

  • 64 байта должны быть в порядке.

  • Основная причина, возможно, заключается в операциях копирования .... которые замедляют IIRC, если структура больше. И это должно быть скопировано довольно много.

Я бы обычно советовал использовать здесь класс;) Но, не зная содержания структуры, это немного сложно.

3 голосов
/ 09 марта 2010

Одна из вещей, которую я узнал при программировании на ассемблере, заключалась в том, что вы выравниваете свои структуры (и другие данные) по 8 или 16-байтовым границам (на самом деле в MASM есть команда препроцессора). Причиной этого является значительно более быстрый доступ к памяти при хранении или перемещении объектов в памяти. Сохранение ваших структур размером менее 16 байт обеспечит успешное выравнивание памяти, поскольку ничто не будет пересекать границы выравнивания.

Однако я ожидаю, что подобные вещи будут в значительной степени скрыты от вас при использовании .NET Framework. Если бы ваши структуры были больше 16 байтов, я бы ожидал, что инфраструктура (или JIT-компилятор) выровняет ваши данные по 32-байтовым границам и так далее. Было бы интересно увидеть подтверждение комментариев в этих книгах и узнать, в чем разница в скорости.

2 голосов
/ 23 июня 2016

На самом деле, на 64-битной машине 32 байта - это максимальный размер, который нужно использовать для структурных данных. Из моих тестов 32-байтовая структура (четыре длинных) работает примерно так же, как и ссылка на объект.

Вы можете найти тестовый код и запустить тесты самостоятельно здесь: https://github.com/agileharbor/structBenchmark

Последняя пара тестов копирует большое количество структур / объектов из одного массива в другой, и вы можете увидеть, где класс превосходит struct с восемью long, но это примерно так же, как struct с четырьмя long. 1006 *

1 голос
/ 09 марта 2010

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

...