Лучше ли распределять память во власти двух?

Когда мы используем malloc() для выделения памяти, мы должны указать размер, который находится у власти двух? Или мы просто даем точный размер, который нам нужен?
Как

//char *ptr= malloc( 200 ); 
char *ptr= malloc( 256 );//instead of 200 we use 256

Если лучше указать размер, который находится во власти двух, в чем причина этого? Почему это лучше?

Спасибо

Edit

Причиной моей путаницы является следующая цитата из блога Джоэля Назад к основам

Смарт-программисты минимизируют потенциальное распределение malloc на всегда выделяя блоки памяти которые имеют степень 2. Вы знаете, 4 байта, 8 байтов, 16 байт, 18446744073709551616 байт и т.д. Для причины, которые должны быть интуитивно понятны любой, кто играет с Lego, это сводит к минимуму количество странных фрагментация, которая продолжается в цепь. Хотя это может показаться пространство для отходов, также легко увидеть как он никогда не тратит больше 50% космос. Таким образом, ваша программа не использует более чем в два раза больше необходимо, что не так важно сделка.

Извините, я должен был опубликовать вышеприведенную цитату ранее. Мои извинения!

Большинство ответов, до сих пор, говорят, что выделение памяти во власти двух - плохая идея, тогда в каком сценарии лучше следовать за Джоэлом о malloc()? Почему он так сказал? Является ли приведенное выше цитируемое предложение устаревшим сейчас?

Просьба объяснить это.
Благодаря

Ответ 1

Просто укажите точный размер, который вам нужен. Единственная причина, по которой размер "большой мощности" может быть "лучше", - это обеспечить более быстрое распределение и/или избежать фрагментации памяти.

Однако любая нетривиальная реализация malloc, которая относится к эффективности, будет внутренне охватывать распределения таким образом, если и когда это целесообразно. Вам не нужно беспокоиться о "помощи" malloc; malloc может отлично справиться самостоятельно.

Edit:

В ответ на вашу цитату из статьи Joel on Software, Joel указывает на этот раздел (который трудно правильно распознать без контекста, следующего за абзацем, который вы цитировали) состоит в том, что если вы ожидаете часто перераспределять буфера, лучше сделать это мультипликативно, а не аддитивно. На самом деле это именно то, что делают классы std::string и std::vector в С++ (среди прочих).

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

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

Он упоминает попутно, что это помогает уменьшить "фрагментацию в свободной цепочке", но причина этого в большей степени связана с количеством и единообразием распределений, а не их точным размером. Во-первых, чем больше раз вы выделяете и освобождаете память, тем более вероятно, что вы должны фрагментировать кучу, независимо от того, какой размер вы выделяете. Во-вторых, если у вас есть несколько буферов, которые вы динамически изменяете с использованием одного и того же мультипликативного алгоритма изменения размера, то, скорее всего, если он изменится с 32 до 64, а другой изменится с 16 до 32, то второе перераспределение может поместиться прямо там, где первый раньше был. Это было бы не так, если бы размер был изменен от 25 до 60, а другой - от 16 до 26.

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

Ответ 2

Просто чтобы играть адвоката дьявола, вот как Qt делает это:

Предположим, что мы добавляем 15000 символов в строку QString. затем следующие 18 перераспределений (из возможный 15000) возникают, когда QString заканчивается из пространства: 4, 8, 12, 16, 20, 52, 116, 244, 500, 1012, 2036, 4084, 6132, 8180, 10228, 12276, 14324, 16372. В конце QString содержит 16372 символов Unicode, 15000 из которых заняты.

Значения выше могут показаться немного странно, но вот руководящие принципы:

QString выделяет 4 символа на время до достижения размера 20. От 20 до 4084 года, он продвигается путем удвоения размер каждый раз. Точнее, это продвигается к следующей силе двух, минус 12. ( Некоторые распределители памяти выполнять худшее, когда требуется точное полномочия двух, потому что они используют несколько байт на блок для ведения бухгалтерского учета.) С 4084 года он продвигается блоками 2048 символов (4096 байт). Эта имеет смысл, потому что современный системы не копируют все данные при перераспределении буфера; страницы физической памяти просто переупорядочено, и только данные по первая и последняя страницы на самом деле скопированы.

Мне нравится, как они ожидают функции операционной системы в коде, который предназначен для работы от смартфонов до ферм серверов. Учитывая, что они умнее людей, чем я, я бы предположил, что эта функция доступна во всех современных ОС.

Ответ 3

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

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

Есть слишком много программ, которые распутывают ресурсы - не делайте из них один из них.

Ответ 4

Это несколько неуместно.

Malloc фактически выделяет немного больше памяти, чем вы запрашиваете, потому что у нее есть собственные заголовки для работы. Поэтому оптимальное хранилище, вероятно, похоже на 4k-12 байты... но это зависит от реализации.

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

Ответ 5

Вы можете выделять память с точки зрения размера слова процессора; не будет никакой старой силы 2.

Если процессор имеет 32-разрядное слово (4 байта), то выделяют в единицах 4 байта. Выделение в 2 байта может оказаться нецелесообразным, поскольку процессор предпочитает данные начинать с 4-байтовой границы.

С другой стороны, это может быть микро-оптимизация. Большинство библиотек распределения памяти настроены для возврата памяти, которая выровнена в правильном положении и оставит наименьшее количество фрагментации. Если вы выделите 15 байт, библиотека может выложить и выделить 16 байт. Некоторые распределители памяти имеют разные пулы на основе размера распределения.

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

Ответ 6

Когда я выделяю буфер, который, возможно, нужно продолжать выращивать для размещения данных еще неизвестного размера, я начинаю с мощности 2 минус 1, и каждый раз, когда у него заканчивается свободное пространство, я перераспределяю дважды предыдущий размер плюс 1. Это делает так, что мне никогда не придется беспокоиться о переполнении целого числа; размер может переполняться только тогда, когда предыдущий размер был SIZE_MAX, и в этот момент распределение уже не удалось, а 2*SIZE_MAX+1 == SIZE_MAX в любом случае.

В отличие от этого, если я только использовал мощность 2 и удваивал ее каждый раз, я мог бы успешно получить буфер размером 2 ^ 31 байта, а затем перераспределить буфер 0 байтов в следующий раз, когда я удвоил размер.

Как некоторые люди прокомментировали, что power-of-2-minus-12 хороши для определенных реализаций malloc, можно в равной степени начать с мощности 2 минус 12, затем удвоить его и добавить 12 на каждом шаге...

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

Ответ 7

Это полностью зависит от данной libc реализации malloc(3). Это до этой реализации, чтобы зарезервировать кучи кучи в любом порядке, который он считает нужным.

Чтобы ответить на вопрос - нет, это не "лучше" (здесь "лучше" вы имеете в виду...?). Если размер, который вы запрашиваете, слишком мал, malloc(3) будет резервировать большую часть внутри, поэтому просто придерживайтесь своего точного размера.

Ответ 8

С сегодняшним объемом памяти и ее скоростью я больше не думаю, что это актуально.

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

Ответ 9

Всегда есть тестирование...

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

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

Ответ 10

Если вы выделяете какой-то расширяемый буфер, где вам нужно выбрать некоторое количество для начальных распределений, то да, полномочия 2 - это хорошие числа, которые нужно выбрать. Если вам нужно выделить память для struct foo, тогда просто malloc (sizeof (struct foo)). Рекомендация по распределению мощности 2 объясняется неэффективностью внутренней фрагментации, но современные реализации malloc, предназначенные для многопроцессорных систем, начинают использовать локальные пулы CPU для достаточно малых ресурсов, чтобы это имело значение, что предотвращает конфликт блокировок, который использовался для результат, когда несколько потоков будут пытаться malloc в одно и то же время и тратить больше времени на блокировку из-за фрагментации.

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

Преждевременная оптимизация - это корень всего зла.

Ответ 11

При перераспределении вы должны использовать realloc() вместо malloc(). http://www.cplusplus.com/reference/clibrary/cstdlib/realloc/

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