engine icon indicating copy to clipboard operation
engine copied to clipboard

Streebog dynamic dispatch + import of SSE4.1 & MMX implementations

Open vt-alt opened this issue 3 years ago • 13 comments
trafficstars

Идея такая:

  • В файле gosthash2012_dispatch.h создаются разные версии g() под разные микроархтектуры (sse2, sse4.1, и ref).
  • Они переключаются динамически во время выполнения (сейчас для определения микроархитектуры используется __builtin_cpu_supports() и __has_builtin() (так что это работает только на свежих версиях gcc), но потом можно сделать определение через CPUID "как у всех").
  • Если dynamic dispatch невозможен, то все должно работать по старому (только sse2 и ref).
  • Переключение с помощью if — это наиболее быстрый вариант из доступных.

ps. gosthash2012: Fix '_mm_empty' call - надо в stable ветки перегнать, это багфикс.

vt-alt avatar Nov 26 '21 23:11 vt-alt

А диспетчер менее адским сделать не получается, да? :(

beldmit avatar Nov 27 '21 20:11 beldmit

Там всё рационально, только непривычно такое количество логики на препроцессоре. Но, оно нужно, чтоб сохранить уже существующую портабельность. Рассмотрим gosthash2012_dispatch.h как 2 смысловые части:

  1. Создание разных версий g(). -- Этот вариант вносит наименьшее кол-во изменений в существующий код (не раскрывает все макросы и не создаёт повторов одного и того-же как в php-stribog). Не меняет способ сборки (иначе придется перенести усложнение в CMakeLists.txt, а там это может оказаться ещё "страшнее"). Так же у нас уже поддержка нескольких компиляторов, которую надо было оставить.
  2. Сам диспетчер (top level g()) вполне обычный. Вот пример для BLAKE3: https://github.com/BLAKE3-team/BLAKE3/blob/master/c/blake3_dispatch.c Если есть идеи, то предлагай.

vt-alt avatar Nov 27 '21 20:11 vt-alt

# if defined(__clang__)
#  pragma clang attribute push (__attribute__((target("mmx"))), apply_to = function)
# elif defined(__GNUC__)
#  pragma GCC push_options
#  pragma GCC target("mmx")
# endif

...

# if defined(__clang__)
#  pragma clang attribute pop
# elif defined(__GNUC__)
#  pragma GCC pop_options
# endif

Вот этот фрагмент скорее всего можно заменить на 1 макрос устанавливающий __attribute__((target("mmx"))), а потом или прилеплять его сразу к функции g, или спрятать в другой (компиляторо-зависимый) макрос.

vt-alt avatar Nov 27 '21 20:11 vt-alt

# undef XLOAD
# undef STORE
# undef TRANSPOSE
# undef XTRANSPOSE
# undef XLPS32
# undef XLPS
# undef __GOST3411_USE_MMX__
# undef g

Такое всё равно придется делать при темплейтировании и в gosthash2012_mmx.h это не убрать, так как эти мкросы ещё нужны в момент include gosthash2012_g.h. Но может можно это загнать в один макрос.

vt-alt avatar Nov 27 '21 20:11 vt-alt

Пример диспетчера в libntru: https://github.com/tbuktu/libntru/blob/master/src/poly.c#L1082 общая схема такая-же, но всё проще так как этот код только под x86_64. Нам же так нельзя.

А что именно показалось адом - темплейт или сам диспетчер? Темплейт ещё можно попытаться украсить или сделать без него (но это не без своих серьезных минусов), а диспетчер мне кажется таким и должен быть.

vt-alt avatar Nov 28 '21 00:11 vt-alt

Пример из BIKE https://github.com/open-quantum-safe/liboqs/blob/main/src/kem/bike/additional_r3/decode_internal.h#L61 Все равно в _init есть сложная логика. И далее они устанавливают callbacks. g() через pointers будет работать значительно медленнее для такой часто вызываемой функции,. Вот соавтор BLAKE3 рассуждает о ifunc и поинтерах: https://github.com/BLAKE3-team/BLAKE3/pull/92#issuecomment-647158735 Другой эксперт по performance мне так же советовал из трех вариантов - if(), pointer, ifunc - использовать if() для переключения (для кода в библиотеках).

vt-alt avatar Nov 28 '21 00:11 vt-alt

Мало кто делает темплейт. Можно пойти на дублирование кода и перенести копии функции g() (она не такая большая) в gosthash2012_{ref,mmx,sse2,sse41}.h тогда все #undef и attribute target() можно перенести в них же. А в gosthash2012_dispatch.h будет только 4 #include этих микроархитектурно зависимых хедеров и диспетчер.

Это можно дальше развить и переименовать все эти gosthash2012_sse2.h в gosthash2012_sse2.c и собирать их отдельно (заодно выкинуть #undef). А gosthash2012_dispatch.h вернуть обратно в gosthash2012.c.

vt-alt avatar Nov 28 '21 02:11 vt-alt

LGTM

beldmit avatar Nov 28 '21 12:11 beldmit

Только сейчас подумал, что можно же было делать #include "gosthash2012_g.h" внутри gosthash2012_sse41.h и др. Странно мозг работает.

vt-alt avatar Nov 28 '21 19:11 vt-alt

LGTM

Это про что?

vt-alt avatar Nov 29 '21 02:11 vt-alt

Это по совокупности

beldmit avatar Nov 29 '21 06:11 beldmit

Переделал так — убрал прагмы (вместо них attribute target() прямо у функций — для msvc говорят они не нужны), вместо темплейта (с include _g.h) и {ref,sse2,...}.h сделал .c где в каждом своя версия g. Логика выбора реализаций в gosthash2012.h, диспетчер (top-level g остался) в gosthash2012.c. Добавил атрибут _internal для всех g (может это повлияет на оптимизацию.), (А для главной g (диспетчера) заменил static на _internal, чтоб его asm можно было посмотреть в gdb -ex 'disas/s g'" — это можно убрать, но, вроде, не мешает).

vt-alt avatar Nov 30 '21 22:11 vt-alt

Так оно стало гораздо читабельнее.

beldmit avatar Dec 01 '21 15:12 beldmit