Почему все используют 2 ^ n номера для распределения? → новый StringBuilder (256)

15 лет назад, программируя с Pascal, я понял, почему использовать мощность двух для распределения памяти. Но это все еще похоже на современное состояние.

Примеры С#:

new StringBuilder(256);
new byte[1024];
int bufferSize = 1 << 12;

Я все еще вижу это тысячи раз, я использую это сам, и я все еще спрашиваю:

Нужно ли нам это в современных языках программирования и современном оборудовании?
Я думаю, его хорошая практика, но какая причина?

ИЗМЕНИТЬ
Например, массив byte[], как указано в ответах здесь, мощность 2 не имеет смысла: сам массив будет использовать 16 байтов (?), Поэтому имеет смысл использовать 240 (= 256-16) для размер для 256 байтов?

Ответ 1

Нужно ли нам это в современных языках программирования и современном оборудовании? Я думаю, его хорошая практика, но какая причина?

Это зависит. Здесь есть две вещи:

  • Для размеров, меньших размера страницы памяти, нет заметной разницы между мощностью-2 и произвольным числом для выделения пространства;
  • В основном вы используете управляемые структуры данных с С#, поэтому вы даже не знаете, сколько байтов действительно выделено под ним.

Предполагая, что вы выполняете низкоуровневое распределение с помощью malloc(), использование кратных размера страницы будет считаться хорошей идеей, то есть 4096 или 8192; это связано с тем, что он обеспечивает более эффективное управление памятью.

Моим советом было бы просто выделить то, что вам нужно, и позволить С# обрабатывать управление и распределение памяти для вас.

Ответ 2

К сожалению, это довольно глупо, если вы хотите сохранить блок памяти на одной странице памяти 4k... И люди даже не знают об этом:-) (я не до 10 минут назад... У меня была только догадка)... Пример... Это небезопасный код и зависящий от реализации (с использованием .NET 4.5 на 32/64 бит)

byte[] arr = new byte[4096];

fixed (byte* p = arr)
{
    int size = ((int*)p)[IntPtr.Size == 4 ? -1 : -2];
}

Таким образом, CLR выделил не менее 4096 + (1 или 2) sizeof (int)... Таким образом, он перешел на одну страницу памяти 4k. Это логично... Он должен хранить размер массива где-то, и держать его вместе с массивом - самая разумная вещь (для тех, кто знает, что Pascal Strings и BSTR, да, это тот же принцип)

Я добавлю, что все объекты в .NET имеют номер синхронизации и RuntimeType... Они не менее int, если не IntPtr, поэтому в общей сложности от 8 до 16 байт/объект (Это объясняется в разных местах... попробуйте найти .net object header, если вы заинтересованы)

Ответ 3

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

Например, могут быть случаи, когда вы хотите использовать ровно 8 бит информации (1 байт) для адресации таблицы.

В этом случае я бы позволил таблице иметь размер 2 ^ 8.

Object table = new Object[256];

Таким образом, вы сможете обращаться к любому объекту таблицы, используя только один byte.

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

Object table = new Object[100];

И затем кто-то (возможно, кто-то другой) обращается к нему с байтовым значением вне диапазона таблицы.

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

Вероятно, учитывая увеличение умности современных компиляторов, это не единственная хорошая практика.

Ответ 4

ИМХО, все, что заканчивается точной мощностью двух арифметических операций, похоже на быстрый путь. низкоуровневая арифметическая операция для мощности двух принимает меньшее количество поворотов и манипуляций с битами, чем любые другие числа, требуют дополнительной работы для процессора.

И нашел этот возможный дубликат: Лучше ли распределять память в два раза?

Ответ 5

Да, это хорошая практика, и по крайней мере одна причина. Современные процессоры имеют размер кеша L1 размером 64 байта, и если вы будете использовать размер буфера как 2 ^ n (например, 1024, 4096,..), вы будете использовать полностью кеш-строку, не теряя пространства. В некоторых случаях это поможет предотвратить проблему ложного обмена (http://en.wikipedia.org/wiki/False_sharing).