Имеет ли смысл переписывать memcpy/memcmp/ с SIMD-инструкциями?

Имеет ли смысл переписывать memcpy/memcmp/... с инструкциями SIMD в крупномасштабном программном обеспечении?

Если да, то почему GCC по умолчанию не генерирует SIMD-инструкции для этих библиотечных функций?

Кроме того, есть ли какие-либо другие функции, которые можно улучшить с помощью SIMD?


person limi    schedule 16.03.2011    source источник
comment
Это зависит от того, какую ОС и библиотеки компилятора вы используете. Например. В Mac OS X уже есть memcpy et al, оптимизированный для SIMD. Кроме того, Intel ICC генерирует встроенные memcpy, которые быстрее, чем все, что вы, вероятно, сможете реализовать в библиотеке.   -  person Paul R    schedule 16.03.2011
comment
@Paul: memcpy на самом деле является худшим случаем для встроенной функции SSE, потому что SSE нельзя использовать для крайних случаев. Выдают ли эти компиляторы код SIMD для strlen и memchr?   -  person Ben Voigt    schedule 16.03.2011
comment
@Ben: я только что проверил с помощью ICC 12 - memcpy и strlen испускают встроенный код SSE, strchr - это библиотечная функция, которая выглядит как простой скалярный код.   -  person Paul R    schedule 16.03.2011


Ответы (4)


Да, эти функции намного быстрее с инструкциями SSE. Было бы неплохо, если бы ваша библиотека/компилятор времени выполнения включала в себя оптимизированные версии, но это, похоже, не повсеместно.

У меня есть собственный SIMD memchr, который чертовски быстрее библиотечной версии. Особенно, когда я нахожу первый из 2 или 3 символов (например, я хочу знать, есть ли уравнение в этой строке текста, я ищу первый из =, \n, \r).

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

person Ben Voigt    schedule 16.03.2011
comment
SIMD memcpy обычно будет быстрее только для копий, где источник и/или место назначения уже находятся в кеше, поскольку почти любой полуприличный memcpy должен быть в состоянии насытить доступную пропускную способность DRAM. - person Paul R; 16.03.2011
comment
@Paul: SIMD лучше всегда. Если это не происходит строго быстрее из-за невозможности доступа к памяти, это ядро ​​освобождается для гиперпоточности, энергосбережения или спекулятивного выполнения не по порядку. Как сказал Crashworks, SSE также будет быстрее извлекать данные в кеш из-за подсказки предварительной выборки. Без SSE процессору, возможно, придется чередовать выборку данных и выполнение копирования, при этом SSE выполняется параллельно. - person Ben Voigt; 16.03.2011
comment
в случае memcpy et al в потоке выполнения больше ничего не происходит, так что никакой пользы. Если ваше ядро ​​​​зависло в ожидании доступа к DRAM, вы мало что можете сделать — задержка DRAM может составлять порядка 200 тактов, что представляет собой множество циклов инструкций без каких-либо действий. - person Paul R; 16.03.2011
comment
@Paul: (1) Не все вызовы memcpy предназначены для тысяч байтов. У вас может легко быть вызов memcpy для ~ 20 байтов внутри цикла с другой обработкой. (2) Современные ядра ЦП не ограничиваются обработкой инструкций из одного потока, поэтому я упомянул гиперпоточность. (3) Задержка DRAM менее важна при конвейерной предварительной выборке чтения, важна только пропускная способность. (4) Даже если пропускная способность DRAM тормозит код, все же лучше выполнять копирование эффективно, поскольку ЦП может выполнять работу за то же время и с меньшим энергопотреблением (например, динамически снижая тактовую частоту). - person Ben Voigt; 16.03.2011
comment
Какую дерьмовую библиотеку вы используете, в которой нет хорошего SIMD memchr? У Glibc есть написанные от руки ассемблерные версии memchr / strchr / memmove и так далее для i386 и x86-64 (и большинства других ISA), которые отлично подходят для больших буферов, и многие из них также имеют хорошие стратегии для малых буферов. (С диспетчеризацией времени выполнения через разрешение символов динамического компоновщика, поэтому он может использовать AVX2 на совместимых процессорах даже в двоичных файлах, скомпилированных без -mavx2). Главное, что вы можете получить, это если вы знаете, что ваш буфер выровнен и/или имеет длину не менее 16 байт, чтобы вы могли избежать ветвления для выбора стратегии. - person Peter Cordes; 11.02.2020
comment
например code.woboq.org/userspace/glibc/ sysdeps/x86_64/multiarch/ — это memchr glibc с vpcmpeqb из 4 векторов, затем соедините их все вместе, чтобы сэкономить на vpmovmskb + test операций в секунду, с разветвлением цикла один раз на 2 строки кэша. - person Peter Cordes; 11.02.2020

Это не имеет смысла. Ваш компилятор должен неявно генерировать эти инструкции для встроенных функций memcpy/memcmp/similar, если он вообще может генерировать SIMD.

Возможно, вам потребуется явно указать GCC для генерации кодов операций SSE с помощью например, -msse -msse2; некоторые GCC не включают их по умолчанию. Кроме того, если вы не укажете GCC оптимизировать (т. е. -o2), он даже не попытается создать быстрый код.

Использование кодов операций SIMD для такой работы с памятью может иметь огромное влияние на производительность, поскольку они также включают предварительную выборку из кэша и другие подсказки DMA, которые важны для оптимизации доступа к шине. Но это не значит, что вам нужно создавать их вручную; даже несмотря на то, что большинство компиляторов в целом плохо генерируют SIMD-операции, каждый из тех, что я использовал, по крайней мере, обрабатывает их для основных функций памяти CRT.

Базовые математические функции также могут значительно выиграть от перевода компилятора в режим SSE. Вы можете легко получить 8-кратное ускорение на базовом sqrt(), просто говорит компилятору использовать код операции SSE вместо ужасного старого x87 FPU.

person Crashworks    schedule 16.03.2011
comment
Согласен, что memcpy, скорее всего, будет должным образом оптимизирован. Многие другие функции из <string.h> и <memory.h> также очень полезны и не оптимизированы компилятором. - person Ben Voigt; 16.03.2011
comment
@BenVoigt: GCC не всегда встраивает хорошие версии библиотечных функций, но хорошие библиотеки имеют хороший рукописный ассемблер. например Почему этот код работает в 6,5 раз медленнее с включенной оптимизацией? показывает случай, когда GCC встраивает очень плохой repne scasb strlen в -O1 или сложный 32- побитовый битхак в -O2, который не использует преимущества SSE2. Программа полностью зависит от производительности strlen для огромных буферов, поэтому вызов оптимизированной версии glibc является большой победой. Существует большая разница между библиотекой и встроенным. - person Peter Cordes; 11.02.2020

Это вероятно не имеет значения. ЦП намного быстрее, чем пропускная способность памяти, и реализации memcpy и т. д., предоставляемые библиотекой времени выполнения компилятора, вероятно, достаточно хороши. В «крупномасштабном» программном обеспечении ваша производительность в любом случае не будет зависеть от копирования памяти (вероятно, доминирует ввод-вывод).

Чтобы добиться реального повышения производительности копирования памяти, в некоторых системах имеется специализированная реализация DMA, который можно использовать для копирования из памяти в память. Если требуется существенное увеличение производительности, аппаратное обеспечение — это способ добиться этого.

person Greg Hewgill    schedule 16.03.2011
comment
Это во многом зависит от того, используете ли вы ужасно медленный API-интерфейс ввода-вывода, такой как C++ iostreams. Трудно выполнять какую-либо нетривиальную обработку со скоростью, с которой ОС может обеспечивать ввод-вывод. Кроме того, SIMD быстрее по целому ряду причин, особенно на небольших блоках, где установка механизма прямого доступа к памяти была бы чрезмерно дорогой. Во-первых, SSE использует другой набор регистров ЦП, поэтому ваши рабочие переменные остаются зарегистрированными и не попадают в кеш. - person Ben Voigt; 16.03.2011

на оборудовании x86 это не должно иметь большого значения, с неупорядоченной обработкой. Процессор добьется необходимого ILP и попытается выполнить максимальное количество операций загрузки/сохранения за такт для memcpy, будь то набор инструкций SIMD или Scalar.

person Pari Rajaram    schedule 19.04.2011