Векторизируемый код и Эльбрус.

Jun 22, 2024 16:35

Тут вдруг пришло мне в голову, что Эльбрус логично сравнить с SIMD блоками процессоров AMD SSE/ARM NEON.

Сравним вот такой кусок кода:




Этот кусок кода переводит grayscale 8 бит на пиксель картинку в однобитную картинку. Т.е. 8 байт на входе преобразуются в 1 байт на выходе, берутся только старшие биты от каждого байта.

gcc при компиляции с флагом -O3 генерирует какой-то код для sse/neon. Если компилировать с флагом -Os то генерирует код на обычных целочисленных регистрах.

Ещё у меня завалялся код скопированный со StackOverflow, который делает ту-же операцию на neon, но написанный человеком.
Так что будем сравнивать 3 платформы.
1 - AMD Rysen 5 3600 (4126 MHz)
2 - Cortex-A7 (Maix III armv7-a 800 MHz)
3 - Elbrus (Elbrus-8C, 1200 MHz)

Прежде всего о размерах кода.
PlatformTime
AMD -O3 784 байта
AMD -Os 133 байта
Cortex-A7 -O3 678 байт
Cortex-A7 -Os 106 байт
Cortex-A7 Human neon 98 байт
Elbrus -O3 1104 байт

*Human neon - это код, который написан человеком. Он самый оптимальный по размерам, и обрабатывает сразу по 128 байт. Причем человек хитрое животное, автор извернулся и написал код, который не требует, что-бы size был кратен 128 байтам. Достаточно кратности 8-ми байтам.
*Для Эльбрус опции -O3 и -Os генерируют одинаковый по размеру и скорости код.

В предыдущих постах объём кода генерируемого для Elbrus мне казался огромным, в сравнению с кодом для процессора. Но вот если сравнивать с кодом генерируемым для SIMD варианта, то он уже лишь "немного больше, чем на остальных платформах".

Теперь о производительности. (size = 256*864 = 216 КБ)
PlatformTimeTakts per byte
AMD -O3 15 us0.28
AMD -Os 150 us2.8
Cortex-A7 -O3 360 us1.3
Cortex-A7 -Os 1617 us5.8
Cortex-A7 Human neon 177 us0.64
Elbrus -O3 70 us0.37

Пара мыслей.
Писать на ASM всё ещё есть смысл, хотя и редко. Под AMD код отлично векторизовался и не уверен, что человек лучше напишет. Под Cortex-A7 человеческий код двухкратно быстрее, чем сгенерированный gcc. И заодно в 6 раз меньше. Слова __restrict__ и __builtin_assume_aligned имеет смысл писать. __builtin_assume_aligned немного уменьшает количество генерируемого кода.

Elbrus медленнее, чем AMD примерно пропорционально своей тактовой частоте. Но на этой задаче Elbrus сильно быстрее, чем Cortex-A7.

Вобщем народ постарался, очень постарался, что-бы Эльбрус работал за разумное время.

И вот кстати пара мыслей, чего не хватает в современных языках программирования (и немного в asm не хватает).
Первое - не хватает saturated арифметики. Т.е. что-бы был какой-то тип uint8s_t для которого 200+200 = 255, а соотвественно 100-200=0.
Второе - нет swizzle. Т.е. это операция, которая берёт 2 входных операнда uint8_t и делает из них uint16_t в котором битики первого и второго операнда чередуются. Это одна из базовых для оптимального использования кеша процессора для двухмерных массивов. Но к сожалению на процессоры её "не завезли".

Previous post Next post
Up