векторные инструкции в стандартной библиотеке

Дмитрий Назаров
Дмитрий Назаров

В настоящее время на множестве платформ доступны векторные инструкции для сложения, вычитания, умножения и т.д. В ряде компиляторов (gcc, clang, msvc) уже имеются соответствующие расширения для работы с такими инструкциями.

// пример из документации на gcc
typedef int v4si __attribute__ ((vector_size (16)));

v4si a, b, c;

c = a + b;

Было бы неплохо иметь в стандарте соответствующие функции для работы с этими расширениями. С одной стороны, компиляторы и так в праве использовать их при оптимизации кода. С другой - применение таких функций будет дополнительной подсказкой компилятору о намерениях разработчика. На платформах, которые не поддерживают векторные расширения, можно использовать программную эмуляцию в виде цикла. В стандарте уже применяется аналогичный подход с эмуляцией std::atomic через использование мьютексов. 

5
рейтинг
7 комментариев
yndx-antoshkka
Есть уже proposal на эту тему open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0214r5.pdf

Ознакомьтесь, и если есть замечания - мы их передадим комитету.
yndx-antoshkka
Дмитрий Назаров
Спасибо. Я не знал об этом предложении.
Само предложение расписано достаточно подробно. Но я бы добавил constexpr к операциям, где это возможно. Например ничто не мешает компилятору создавать объект типа simd из std::initializer_list и сразу же загружать в соответствующий регистр.
Это может показаться надуманно, но представим себе следующий пример: pixel perfect rendering с использованием графических API. Например, в OpenGL при отрисовке используется "diamond exit rule". Одним из решений для получения требуемого эффекта выровненных пикселей является добавления некоторого смещения к координатам выводимых на экран точек. Если таких точек много, то оптимальным решением на стороне процессора было бы использовать simd инструкции. Если бы у simd был бы constexpr конструктор, то ничего бы не мешало нам написать contexpr функцию по добавлению смещения к вектору.

имеется

static constexpr float offset = { 0.0f, 1.0f, 4.0f, 5.0f};

constexpr simd retranslate(const simd &point_)
{
return point_ + simd(offset);
}


Возможно это является экономией на спичках, но как по мне, если есть шанс что-то посчитать на этапе компиляции, то почему бы и нет.
Дмитрий Назаров
yndx-antoshkka
> Но я бы добавил constexpr

Я бы тоже. Однако с этим придётся подождать, так как большая часть simd операций может использовать asm вставки, которые в данный момент невозможно сделать constexpr. Даже простой констуруктор может иметь вставку наподобие "pxor xmm0, xmm0", разгружающую конвеер.

Cтоит дождаться принятия "if (constexpr())". Тогда можно будет делать simd constexpr, не боясь что-то замедлить.
yndx-antoshkka
Сергей Прейс
Дмитрий Назаров, вот здесь есть обсуждение на тему реализации векторных constexpr: clang-developers.42468.n3.nabble.com/Allow-constexpr-in-vector-extensions-td4057969.html

Общий смысл в том, что как минимум в некоторых случаях от constexpr будет страдать производительность. В частности если бы константа в вашем случае была, скажем {1.0f,1.0f,1.0f,1.0f} то добавить её на этапе исполнения было бы быстрее, чем загрузить значение посчитанное на этапе компиляции. Однако общее заключение, что это не повод не делать constexpr simd.
Сергей Прейс
Дмитрий Назаров
Сергей Прейс, в обсуждении как раз приводится пример желаемого результата.

typedef int v4si __attribute__ ((vector_size (16)));

int main()
{
constexpr v4si a = {1,2,3,4};
constexpr v4si b = {2,0,0,0};
v4si c = a + b;
...
}

Возможно обходным решением было бы неявное преобразование таких векторов в какой-то другой тип, который бы можно было использовать в качестве constexpr, и обратно. Но это требует хотя бы constexpr конструктора. Вообще говоря, возможно именно конструктор то и не обязан быть simd. Основное требование к векторам, насколько я понимаю, это выравнивание.
Дмитрий Назаров
Сергей Прейс
Дмитрий Назаров, там как раз написано, что constexpr результат (загрузка константы из памяти) будет медленнее того, что сгенерировал бы компилятор непосредственно в коде для конкретного случая в обсуждении без constexpr-сложения.

Выравнивание важно для производительности, но не только оно. Некоторые операции и последовательности операций в simd выполняются быстрее, чем другие. эквивалентные. Кроме того, в векторных типах требования к типизации строже. Такте вещи как integral promotion, конверсии float->double в simd зачастую стоят гораздо дороже, требуют нетривиальных операций и просто вредны.
Но это всё (включая выравнивание) имеет мало отношения к constexpr - там будут совсем другие проблемы. Для поддержки constexpr придётся встраивать в компилятор simd-интерпретатор и без JIT он в нетривиальных случаях просто будет медленным. Впрочем в clang уже обсуждают JIT для скалярных consexpr-выражений.
Сергей Прейс
Сергей Прейс
Я, как разработчик векторизатора в компиляторе, всегда скептически относился к такому способу описания векторного (simd) исполнения. Несомненный плюс этого подхода по отношению к более высокоуровневым программным моделям - это относительная предсказуемость кодогенерации. Минусами, однако, являются завязка кода на особенности аппаратуры (размеры векторных регистров, фиксированная последовательность вычислений, оптимальная на конкретной аппаратуре) и уменьшение пространства для оптимизации (преобразование низкоуровневой последовательности записанной в коде в оптимальную может быть сложнее, чем оптимизация алгоритма записанного высокоуровневыми средствами).
Поэтому я предпочитаю более высокоуровневые подходы к проблеме - execution_policies, for_each и т.п.
Но последнее предложение от Matthias Kretz (ссылку дали выше) выглядит достаточно стройным и продуманным, а ABI tags кажутся вообще очень хорошей и универсальной концепцией для гетерогенного программирования на С++, так что я теперь скорее ЗА.
Сергей Прейс
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).