Вопрос

Существуют ли более быстрые альтернативы memcpy() в C ++?

Это было полезно?

Решение

Маловероятно.Ваш компилятор / стандартная библиотека, скорее всего, будет иметь очень эффективную и адаптированную реализацию memcpy.И memcpy - это, по сути, самый низкий API, который существует для копирования одной части памяти в другую.

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

Другие советы

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

Во-вторых, да, есть альтернативы получше.

  • В C ++ используйте std::copy функция.Он делает то же самое, но это 1) безопаснее и 2) потенциально быстрее в некоторых случаях.Это шаблон, означающий, что он может быть специализирован для определенных типов, что делает его потенциально более быстрым, чем обычный C memcpy.
  • Или же вы можете использовать свои превосходные знания в ваш конкретная ситуация.Разработчики memcpy должны были написать его так, чтобы он хорошо работал в каждый кейс.Если у вас есть конкретная информация о ситуации, в которой она вам нужна, возможно, вы сможете написать более быструю версию.Например, сколько памяти вам нужно для копирования?Как это выровнено?Это могло бы позволить вам написать более эффективный memcpy для это конкретный случай.Но это будет не так хорошо в большинстве других случаев (если это вообще сработает).

Этот ответ на очень похожий вопрос (о memset()) применимо и здесь.

В основном это говорит о том, что компиляторы генерируют некоторый очень оптимальный код для memcpy()/memset() - и различный код в зависимости от характера объектов (размер, выравнивание и т.д.).

И помните, только memcpy() Модули на C ++.

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

Процессоры начиная с Intel Pentium Pro выполняют & # 8220; выполнение не по порядку & # 8221 ;. Они могут выполнять много инструкций параллельно, если инструкции не имеют зависимостей. Но это только тот случай, когда инструкции работают только с регистрами. Если они работают с памятью, используются дополнительные модули ЦП, называемые & # 8220; нагрузочные модули & # 8221; (для чтения данных из памяти) и & # 8220; хранить единицы & # 8221; (для записи данных в память). Большинство процессоров имеют два загрузочных модуля и один запоминающий модуль, то есть они могут параллельно выполнять две инструкции, которые читают из памяти, и одну инструкцию, которая записывает в память (опять же, если они не влияют друг на друга). Размер этих блоков обычно совпадает с максимальным размером регистра & # 8211; если процессор имеет регистры XMM (SSE) & # 8211; это 16 байтов, если у него есть регистры YMM (AVX) & # 8211; это 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 для хранения данных до того, как они начнут записывать их обратно, пока они не нужны & # 8211; так как у нас есть только две единицы нагрузки и одна единица магазина. Так что только два регистра дают лучшие результаты. Использование такого количества регистров никоим образом не улучшает производительность.

Подпрограмма копирования памяти также может использовать некоторые "расширенные" такие методы, как & # prefetch & # 8221; дать указание процессору заранее загружать память в кэш и & # 8220; невременные записи & # 8221; (если вы копируете очень большие фрагменты памяти и не нуждаетесь в немедленном считывании данных из выходного буфера), выровненные или не выровненные записи и т. д.

Современные процессоры, выпущенные с 2013 года, если у них есть бит ERMS в CPUID, имеют так называемое & reg; ; можно использовать & # 8211; копия будет очень быстрой, даже быстрее, чем с регистрами ymm, и она будет работать с кэшем должным образом. Однако начальные затраты на эту инструкцию очень высоки & # 8211; около 35 циклов, поэтому он рассчитывается только на больших блоках памяти.

Надеюсь, теперь вам будет проще выбрать или написать лучшую процедуру копирования памяти, необходимую для вашего случая.

Вы даже можете сохранить стандартную memcpy / memmove, но получите свою собственную специальную largememcpy () для своих нужд.

В зависимости от того, что вы пытаетесь сделать ... если это достаточно большой memcpy и вы редко пишете в копию, mmap с MMAP_PRIVATE для создания сопоставления копирования при записи может быть быстрее .

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

Я не уверен, что использование memcpy по умолчанию всегда лучший вариант. Большинство реализаций memcpy, на которые я смотрел, стараются выровнять данные в начале, а затем делать выровненные копии. Если данные уже выровнены или достаточно малы, то это напрасная трата времени.

Иногда полезно иметь специализированную копию слова, половину слова, копию байта memcpy, если это не оказывает слишком негативного влияния на кэши.

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

Тем не менее, в общем, вы бы напрасно пытались оптимизировать memcpy, поскольку, несомненно, в вашем приложении будет много более простых кусочков кода, которые можно ускорить.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top