Почему 16 байтов рекомендуемый размер для структуры в С#?

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

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

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

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

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

Ответ 1

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

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 раза быстрее)

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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