Подтвердить что ты не робот

Как работают realloc и memcpy?

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

  • Сделайте realloc() и memcpy() скопируйте записи в массив в другой быстрее, чем просто итерации для каждого элемента O(N)? Если ответ "да", то какова, по вашему мнению, его сложность?

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

4b9b3361

Ответ 1

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 для подробностей glibc. "В нескольких реализациях выделения, делая блок меньшим, иногда требуется копировать его"

Ответ 2

Давайте немного поглядим на memcpy и, пока мы на нем, на обозначениях "большой O" или Ландау.

Во-первых, большой-O. Как я уже говорил в другом месте, стоит вспомнить определение большой 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 - это не асимптотически быстрее, но реализация выполняется так же быстро, как кто-то может сделать это на этой конкретной архитектуре.

Ответ 3

  • Абсолютно невозможно скопировать N элементов быстрее, чем O (N). Тем не менее, он может копировать сразу несколько элементов или использовать специальные инструкции процессора, поэтому он может быть быстрее, чем вы могли бы сделать это самостоятельно.
  • Я не знаю точно, но я бы предположил, что память полностью перераспределена. Это самое безопасное предположение, и это, вероятно, зависит от реализации.

Ответ 4

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

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

Ответ 5

  • Можно предположить, что memcpy можно записать так, чтобы он перемещал большое количество бит. например Полностью можно скопировать данные с помощью инструкций SSE, если это выгодно.

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

Ответ 6

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

malloc.c

memcpy.c

Как я его читал, ответы были бы следующими:

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

Ответ 7

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

Те, которые используются для копирования mem, - movsb/movsw в комбинации с командой rep.

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

Настройки регистров с адресами src/trg и int count и от вас.

Ответ 8

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

  • Функция realloc() должна изменять размер объекта памяти, на который указывает ptr, до размера, указанного размером.

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

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

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

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

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