Как работают realloc и memcpy? - PullRequest
24 голосов
/ 12 декабря 2008

У меня два вопроса.

  1. Копируют ли realloc() и memcpy() записи в массиве в другой быстрее, чем просто итерируют каждый элемент O(N)? Если ответ «да», то как вы думаете, в чем его сложность?

  2. Если выделенный размер меньше исходного размера, realloc() копирует записи в другое место или просто оставляет их, поскольку они уменьшают размер массива?

Ответы [ 8 ]

19 голосов
/ 12 декабря 2008

1 - Нет. Они копируют блок за раз. Смотрите http://www.embedded.com/design/configurable-systems/4024961/Optimizing-Memcpy-improves-speed для довольно хорошего анализа.

2 - Это зависит от реализации. Смотрите http://www.gnu.org/software/libtool/manual/libc/Changing-Block-Size.html для получения подробной информации. «В нескольких реализациях выделения памяти уменьшение блока иногда требует его копирования»

18 голосов
/ 27 декабря 2008

Давайте немного подробнее посмотрим на memcpy и, пока мы это делаем, на нотацию "большого О" или Ландау.

Во-первых, биг-о. Как я уже говорил в другом месте, стоит запомнить определение большого O, которое заключается в том, что некоторая функция g (n) называется O (f (n)) , когда существует константа k , для которой g (n) & le; кф (п) . То, что делает константа, позволяет вам игнорировать мелкие детали в пользу важной части. Как все отметили, memcpy из n байтов будет O (n) в большинстве любой обычной архитектуры, потому что независимо от того, что вы должны переместить эти n байты, один кусок за раз. Итак, первая наивная реализация memcpy в C может быть написана

unsigned char *
memcpy(unsigned char * s1, unsigned char * s2, long size){
    long ix;
    for(ix=0; ix < size; ix++)
        s1[ix] = s2[ix];
    return s1;
}

Это на самом деле O (n) , и это может заставить вас задуматься, почему мы вообще беспокоимся о рутинной библиотеке. однако, функция libc заключается в том, что они являются местом, где пишутся специфичные для платформы утилиты; если вы хотите оптимизировать архитектуру, это одно из мест, где вы можете это сделать. Итак, в зависимости от архитектуры , возможны более эффективные варианты реализации; например, в архитектуре IBM 360 есть инструкция MOVL, которая перемещает данные большими кусками, используя очень высоко оптимизированный микрокод. Таким образом, вместо этого цикла реализация memcpy 360 вместо этого может выглядеть примерно так:

LR 3,S1      LOAD S1 ADDR in Register 3
LR 4,S2      
MOVL 3,4,SIZE

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

Что на самом деле происходит, так это то, что он выполняет O (n) микро инструкции под крышками. То, что отличается между ними, является константой k ; потому что микрокод намного быстрее, и поскольку в инструкциях всего три шага декодирования, он значительно быстрее, чем наивная версия, но все равно O (n) - это просто константа меньше.

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

5 голосов
/ 12 декабря 2008
  1. Производительность memcpy не может быть лучше, чем O (N), но ее можно оптимизировать, чтобы она превосходила ручное копирование; например, он может скопировать 4 байта за то время, которое требуется для копирования 1 байта. Многие реализации memcpy написаны на ассемблере с использованием оптимизированных инструкций, которые могут копировать несколько элементов одновременно, что обычно быстрее, чем копирование данных по одному байту за раз.

  2. Я не совсем понимаю этот вопрос, если вы используете realloc, чтобы уменьшить размер памяти, и это успешно (возвращает не NULL), новое местоположение будет содержать те же данные, что и старое местоположение вверх к размеру нового запроса. Если расположение памяти было изменено в результате вызова realloc (что обычно не происходит при уменьшении размера), содержимое будет скопировано, в противном случае копирование не требуется, поскольку память не перемещена.

5 голосов
/ 12 декабря 2008
  1. Абсолютно невозможно скопировать N элементов быстрее, чем O (N). Тем не менее, он может копировать несколько элементов одновременно или использовать специальные инструкции процессора - так что это может быть быстрее, чем вы могли бы сделать это самостоятельно.
  2. Не знаю точно, но я бы предположил, что память полностью перераспределена. Это самое безопасное предположение, и в любом случае оно, вероятно, зависит от реализации.
2 голосов
/ 17 декабря 2008
  1. Можно предположить, что memcpy мог бы быть написан так, чтобы он перемещал большое количество битов. например Полностью возможно копировать данные, используя инструкции SSE, если это выгодно.

Как уже говорилось, это не будет быстрее, чем O (n), но системы памяти часто имеют предпочтительный размер блока, а также возможно, скажем, записать размер строки кэша за раз. *

0 голосов
/ 18 февраля 2012

Некоторые важные моменты, связанные с realloc (проверьте на dev c ++): void * realloc (void * ptr, size_t size);

  1. Функция realloc () изменяет размер объекта памяти, на который указывает ptr, на размер, указанный в size.

  2. Содержимое объекта должно оставаться неизменным вплоть до меньшего из новых и старых размеров.

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

  4. Если размер равен 0 и ptr не является нулевым указателем, указанный объект освобождается.

  5. Если ptr является нулевым указателем, realloc () должен быть эквивалентен malloc () для указанного размера.

  6. Если ptr не соответствует указателю, возвращенному ранее calloc (), malloc () или realloc (), или если пространство ранее было освобождено вызовом free () или realloc (), поведение не определено.

0 голосов
/ 27 декабря 2008

В x86 есть специальные инструкции для сканирования и сопоставления байта / слова в блоке памяти, а также инструкции, которые можно использовать для копирования блока памяти (в конце концов, это CISC-процессор). Многие компиляторы C, которые реализуют встроенный язык ассемблера и прагму для встраивания целых функций, уже много лет используют это в своих библиотечных функциях.

В качестве копии копии используются movsb / movsw в сочетании с инструкцией rep.

CMPS/MOVS/SCAS/STOS
REP, REPE, REPNE, REPNZ, REPZ

Настройка регистров с адресами src / trg и числом int, и все, что вам нужно.

0 голосов
/ 27 декабря 2008

Предполагая, что вы говорите о glibc, и поскольку ваши вопросы зависят от реализации, вероятно, лучше всего просто проверить источник:

malloc.c

memcpy.c

То, как я это читаю, ответит:

  1. O (N) --- нет способа скопировать элементы лучше, чем линейное время.
  2. Иногда большие элементы будут копироваться, когда для их сжатия используется realloc ().
...