Как стать автором
Обновить

Комментарии 19

Никто ведь особо не мешает написать темплейт-обертку над интринсиками. Должно быть достаточно для довольно простых алгоритмов, где единственный плюс новых инструкций — увеличенный размер регистра (который растет раз лет в 8). Да и если появляется что-то важное, темплейты всегда можно специализировать.

Да, такой код будет менее читаем, чем автовекторизуемый C, но не будет привязан к компилятору и его возможности векторизировать.

Такое делают и на чистом ассемблере давно, но это может быстро превратиться в макросовый ад.
Вы не правы — увеличение размера регистра далеко не единственный плюс новых наборов. Там появляются именно новые инструкции, упрощающие векторизацию. Например, загрузка в регистр непоследовательных данных и сохранение по маске.
Кроме того, темплейт будет работать медленнее интринсика по любому, а для некоторых приложений это критично
Я не зря сказал «для довольно простых алгоритмов». Вы не поверите, но довольно большому количеству рутин достаточно SSE2 набора инструкций. Да, конечно, полезные инструкции есть везде, но не всегда они нужны.

А насчет того, что темплейт медленней интринсика, вы не правы. Рантайм цена темплейтов в принципе нулевая, если они инлайнятся. А если не инлайнятся, то всегда есть __forceinline.
По поводу роста раз в 8 лет — это долго на SSE «топтались», уже значительно быстрее. Скажем, посмотрите на рост с 256 до 512. И, как уже было сказано, это не единственный плюс. Для каких то простых алгоритмов — возможно. Но речь то в целом не об этом. Давайте вы будете писать код на темплейтах с интринсиками, а я векторизую цикл через прагму, и сравним время, которое мы затратим на это. Кроме того, ещё и уровень опыта, предъявляемый к разработчику. Ну и будущие изменения в коде, которые так или иначе, затронут вас.
Да никто не спорит, что C код писать, чаще всего, быстрее и проще. И он может быть даже векторизуется при нужном положении луны нужным компилятором. Не оптимально, но векторизируется.

Просто мы пока ну очень далеко от состояния, когда можно будет отдать всё компилятору. Вы когда-нибудь видели автовекторизатор, использующий pmaddwd? А сложные шаффлы с pshufb? А punpck**? Может, это обработка видео такая странная, но у меня от автовекторизаторов сплошное разочарование.

Но речь не об этом. Я просто хотел сказать, что интринсики, да и чистый асм — не абсолютное зло, и не надо их бояться. Если есть желание писать на них универскальный код — это можно сделать. Да, он будет менее читаем, он будет требовать от вас больше знаний, его будет сложнее «поддерживать». Зато без привязки к компилятору (ну разве что иногда производительно интринсиков в vc хромает), без дополнительных зависимостей, без платных тулзов.

Ну и насчет переписывания кода под новые инструкции — мне кажется, вы преувеличиваете. Даже сейчас многие пишут чистый SSE2-4, ибо огромная база пользователей нормальную реализацию AVX(2) не увидит еще несколько лет (с быстрыми vgather, например). Если уж критично использовать всегда последний сэт инструкций — почему бы не OpenCL? Там и перекомпилировать не придется, всё в рантайме.
Кстати, изменения эти будут затрагивать вас всё чаще и чаще, потому фокус и смещается от интринсиков к компилятору. Хотя до сих пор, чтобы получить максимальную производительность, лучше использовать интринсики, весь вопрос в целесообразности.
А целесообразность использования расширений поддерживаемых ажно одним компилятором ажно для одной платформы типа никого не смущает.

Ню-ню. Нет, я не спорю, где-то кому-то как-то Inte® Cilk™ Plus может и пригодится, но я бы его всё-таки отнёс в красную зону, а не в синюю. Ибо все эти прагмы влияют только на скорость (даже если векторизовать вообще ничего не удалось, то хоть как-то код работать будет), а вот начиная с Inte® Cilk™ Plus у вас появляется ситуация, когда программа есть, а запустить вы её не можете вообще.

Вот когда поддержка появится в GCC/Clang'е — уже будет о чём поговорить (например обсудить работоспособность этих чудес на самых быстрых в мире процессорах POWER), а пока — рано.

P.S. Я знаю что Intel работает и с разработчиками GCC и Clang'а. Уж три года как работает. В релизах пока ничего нету. Я верю, что рано или поздно оно случится, но когда это будет? А когда вам проект сдавать? В 2020м или чуть пораньше? Вот отсюда и исходите…
Для ясности — директива pragma simd, с июля этого года часть стандарта OpenMP 4.0 (pragma omp simd), а значит никакой привязки к компилятору. Синтаксис для работы с массивами — так же часть этого стандарта (Array Sections).
Собственно, как мы видим, всё это весьма быстро переходит из частного для компилятора Intel в общее для всех, ввиду распространенности OpenMP и явного стремления того же gcc его поддерживать.

«начиная с Inte® Cilk™ Plus у вас появляется ситуация, когда программа есть, а запустить вы её не можете вообще.»
Это весьма странное утверждение, потому что мы всегда можем сгенерировать дефолтную ветку с поддерживаемыми инструкциями на всех и не «интеловских» процессорах. Хотелось бы понять, что вкладывается в «вообще»?
Ассемблер, на мой взгляд — самый красивый и чистый способ векторизации.

Знаю, нехорошо в блоге Intel оставлять комментарии об ARMе, но я всё же приведу пример.
inline void TransposeSIMD(float32x4_t &x, float32x4_t &y, float32x4_t &z, float32x4_t &w)
{
	__asm__ __volatile__ (
		"vzip.f32 %q0, %q1\n"
		"vzip.f32 %q2, %q3\n"
		"vswp %f0, %e2\n"
		"vswp %f1, %e3\n"
		"vswp %q1, %q2\n"
		: "+w" (x), "+w" (y), "+w" (z), "+w" (w)
	);
}

Боюсь себе представить, как бы это выгядело с кастами к float32x2_t и без vswp.
> самый красивый и чистый способ
У каждого понятия «красоты» и «чистоты» свои. Понятия «простота разработки/поддержки» и «скорость работы» более объективны.

Сразу всплывающие проблемы для Вашего примера кода.
1. Он теперь только для ARM. Если бы этот пример был для IA-32 — та же проблема: код оказывается только для одной архитектуры. У меня были проблемы при попытке переноса огромной массы кода, написанной на асме (ядро двоичного транслятора) даже для случая IA-32 -> Intel 64. Вроде бы одно является развитием другого, а вот фиг асм переиспользуешь.

2. Поправьте меня, если мои знания устарели, но у ARM всего один вид SIMD — это 128-битный NEON. У Интеловской же архитектуры за всё время накопилось векторных расширений: 64-битный MMX, 128-битный SSE/SSE2/SSE3/SSE4.1/SSE4.2, 128-битный AVX, 256-битный AVX2. А на MIC'ах уже есть 512-битный AVX3.x. И для какой же ширины регистров и набора инструкций писать асм в таком случае, если у пользователя может оказаться совершенно разное железо (разве что MMX можно уже забыть как страшный сон)? Придётся писать 3-4 варианта всех процедур на асме. Я бы предпочёл, чтобы это за меня сделал компилятор.

А как вы собираетесь через интринсики писать процессоронезависимый код?

А насчёт скорости (которая для меня — главное, и зачастую она следует как раз из простоты и чистоты) — обёртки над интринсиками тоже не сильно процессоронезависимые, для максимальной производительности всё равно придётся оптимизировать под конкретный процессор.
Я не предлагал писать что-то через интринсики. Интринсики — это тот же самый асм, только завёрнутый более аккуратно в синтаксис использующего его языка высокого уровня. Вместо ассемблер-специфичных clobber-list, двойных процентов у имён регистров и прочее код оформляется как вызов inline-функций. Сахар, не более.
Так про то и разговор. А компилятор даёт более универсальное решение, и мы стараемся как можно сильнее минимизировать разницу между написанием кода на чистых интринсиках и тем, что выдаёт компилятор. Вот только речь не об автовекторизации, а о векторизации, контролируемой разработчиком. Кроме того, ещё и о специальном синтаксисе, при использовании которого вы гарантированно получите векторный код. Раньше таких гарантий компилятор не давал. Вот и вопрос встаёт, насколько вам критично получать дополнительный прирост производительности от более тонкого написания кода на интринсиках, и последующего его переписывания при выходе новых наборов инструкций, или же написать код, который точно будет векторизован сейчас и на всех последующих архитектурах. Скажу сразу, что есть организации, и их не так уж и мало, которые выбрали и выберут интринсики. Но для большинства это будет лишними «расходами».
А зачем касты, если есть vzipq_f32 и vswp тоже можно подумать, чем заменить
Последние векторные инструкции в процессорах Intel мне всё больше напоминают некоторые функции стандарта MPI (message passing interface). Тут тебе и gather/scatter, и stride-access. Интересно, скоро ли напишут вариант реализации MPI для систем с общей памятью прямо на интринсиках :-)

Интересно есть ли решение которое бы давало векторный код между разными CPU архитектурами, как пары ARM/x86 актуальной для Андроид. Для одной архитектуры оптимально наверное использовать intrinsic. Если мы идем наверх в сторону «легкии» то встречаем Intel Cilk Plus, который мог бы быть оптимальным решением, однако скорей всего будет работать под одну архитектуру.
Чуть выше в треде привели пример решения — pragma simd, описываемая стандартом. Дело теперь за компиляторами — надо её поддержать для всех архитектур, тогда и векторный код будет генерироваться.
Спасибо за обзор.
На практике встречал сообщения компилятора о векторизации цикла и пробовал использовать интринсики (на предмете текущего семестра). Нужно будет посмотреть Inte® Cilk™ Plus — планируется его включение в GCC 4.9.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий