Чтобы найти или написать процедуру быстрого копирования памяти, мы должны понять, как работают процессоры.
Процессоры, начиная с Intel Pentium Pro, выполняют «выполнение вне очереди». Они могут выполнять много инструкций параллельно, если инструкции не имеют зависимостей. Но это только тот случай, когда инструкции работают только с регистрами. Если они работают с памятью, используются дополнительные модули ЦП, называемые «загрузочными блоками» (для чтения данных из памяти) и «хранилищами» (для записи данных в память). Большинство процессоров имеют два загрузочных модуля и один запоминающий модуль, то есть они могут параллельно выполнять две инструкции, которые читают из памяти, и одну инструкцию, которая записывает в память (опять же, если они не влияют друг на друга). Размер этих блоков обычно совпадает с максимальным размером регистров - если у процессора есть регистры XMM (SSE) - это 16 байтов, если у него есть регистры YMM (AVX) - это 32 байта и так далее. Все инструкции, которые читают или записывают память, переводятся в микрооперации (микрооперации), которые переходят в общий пул микроопераций и ждут там, пока модули загрузки и хранения смогут их обслуживать. Одна единица загрузки или хранения может обслуживать только одну микрооперацию за раз, независимо от размера данных, которые необходимо загрузить или сохранить, будь то 1 байт или 32 байта.
Таким образом, самая быстрая копия памяти будет перемещаться в регистры и из них с максимальным размером. Для процессоров с поддержкой AVX самым быстрым способом копирования памяти будет повторение следующей последовательности, развернутой в цикле:
vmovdqa ymm0,ymmword ptr [rcx]
vmovdqa ymm1,ymmword ptr [rcx+20h]
vmovdqa ymmword ptr [rdx],ymm0
vmovdqa ymmword ptr [rdx+20h],ymm1
Код Google, опубликованный ранее hplbsh, не очень хорош, потому что они используют все 8 регистров xmm для хранения данных до того, как они начнут записывать их обратно, хотя это и не нужно - так как у нас всего две единицы загрузки и одно хранилище Блок. Так что только два регистра дают лучшие результаты. Использование такого количества регистров никоим образом не повышает производительность.
Процедура копирования памяти также может использовать некоторые «продвинутые» методы, такие как «предварительная выборка», чтобы дать указание процессору заранее загружать память в кэш и «невременные записи» (если вы копируете очень большие фрагменты памяти и не необходимо, чтобы данные из выходного буфера были немедленно прочитаны), выровнены и не выровнены записи и т. д.
Современные процессоры, выпущенные с 2013 года, если у них есть бит ERMS в CPUID, имеют так называемый «расширенный rep movsb», поэтому для большой копии памяти может использоваться «rep movsb» - копия будет очень быстро, даже быстрее, чем с регистрами ymm, и он будет правильно работать с кешем. Однако затраты на запуск этой инструкции очень высоки - около 35 циклов, поэтому она рассчитывается только на больших блоках памяти.
Надеюсь, теперь вам будет проще выбрать или написать лучшую процедуру копирования памяти, необходимую для вашего дела.
Вы можете даже сохранить стандартную memcpy / memmove, но получите свою собственную специальную largememcpy () для своих нужд.