Как работает внутренняя реализация memcpy?
Как работает стандартная функция C 'memcpy'? Он должен скопировать (большой) кусок оперативной памяти в другую область в оперативной памяти. Поскольку я знаю, что вы не можете перемещаться прямо из ОЗУ в ОЗУ в сборке (с помощью инструкции mov), я предполагаю, что при копировании используется регистр ЦП в качестве промежуточной памяти?
Но как это копировать? По блокам (как это будет копировать по блокам?), По отдельным байтам (символам) или по наибольшему типу данных, который у них есть (копирование в длинные длинные двойные - что в моей системе составляет 12 байт).
РЕДАКТИРОВАТЬ: Хорошо, очевидно, вы можете перемещать данные из ОЗУ в ОЗУ напрямую, я не эксперт по сборке, и все, что я узнал о сборке, это из этого документа ( руководство по сборке X86), в котором упоминается в разделе об инструкции mov, из которой вы не можете перейти ОЗУ в ОЗУ. Видимо, это не так.
3 ответа
Зависит. В общем, вы не могли физически скопировать что-либо большее, чем самый большой используемый регистр в одном цикле, но это не совсем так, как работают машины в наши дни. На практике вы действительно меньше заботитесь о том, что делает процессор, а больше о характеристиках DRAM. Иерархия памяти машины будет играть решающую определяющую роль в выполнении этого копирования самым быстрым способом (например, загружаете ли вы целые строки кэша? Каков размер строки DRAM относительно операции копирования?). Реализация может вместо этого использовать некоторые векторные инструкции для реализации memcpy
, Без ссылки на конкретную реализацию, это фактически побайтная копия с одноместным буфером.
Вот забавная статья, которая описывает приключение одного человека в оптимизации memcpy
, Главная точка отсчета заключается в том, что она всегда будет ориентирована на конкретную архитектуру и среду на основе инструкций, которые вы можете выполнить недорого.
Реализация memcpy
очень специфичен для системы, в которой он реализован. Реализации часто аппаратные.
Инструкции mov из памяти в память не так уж и редки - они существуют, по крайней мере, PDP-11
времена, когда вы могли бы написать что-то вроде этого:
MOV FROM, R2
MOV TO, R3
MOV R2, R4
ADD LEN, R4
CP: MOV (R2+), (R3+) ; "(Rx+)" means "*Rx++" in C
CMP R2, R4
BNE CP
Закомментированная строка примерно эквивалентна C
*to++ = *from++;
Современные процессоры имеют инструкции, которые реализуют memcpy
напрямую: вы загружаете специальные регистры с адресами источника и назначения, вызываете команду копирования памяти и позволяете процессору делать все остальное.
Тривиальная реализация memcpy
является:
while (n--) *s2++ = *s1++;
Но glibc
обычно использует некоторые умные реализации в ассемблерном коде. memcpy
звонки обычно встроены.
На x86 код проверяет, является ли параметр размера кратным литералу 2
или кратно 4
(с помощью gcc
встроенные функции) и использует цикл с movl
инструкция (копия 4
байт) в противном случае это вызывает общий случай.
В общем случае используется сборка быстрого блочного копирования с использованием rep
а также movsl
инструкции.