ABI как часть языка

Сергей Прейс
Сергей Прейс

Я приведу примеры для OpenMP 4.0 потому как делал для него поддержку в компиляторе и знаю его проблему наиболее близко, но проблемы в С++AMP и прочих расширениях аналогичны описанным.

Итак, OpenMP 4.0 позволяет описать описать функцию, совместимую по ABI с SIMD-исполнением цикла (она будет принимать сразу несколько значений параметров в SIMD-регистрах, SIMD-обрабатывать их и возвращать на SIMD регистре).

#pragma omp declare simd simdlen(4)
#pragma omp declare simd simdlen(8)
double mul(double x, double y);

Теперь функция сможет обрабатывать 4 или 8 значений параллельно (будет сделано несколько версий) и ее можно использовать в цикле

#pragma omp simd
for (int i = 0; i < N; i++) {
    a[i] = mul(b[i], c[i]);
}

Однако, если мы решим усложнить программу с использованием современного C++ мы быстро столкнемся с проблемами. '#omp declare simd' - это не часть типа и потому:

- Попытка завернуть функцию в std::function утратит ABI сразу

- Взятие адреса и передача в другую функцию - тоже (нет возможности описать тип функции и фунционального указателся с учетом ABI)

- std::bind тоже не будет работать - она деконструирует и реконструирует тип, ABI потеряется.

То есть даже имея политику исполнения SIMD (unseq) мы не сможем передать в алгоритм

std::transform(unseq, b, b+N, c, a, mul);

функцию совместимую с этой политикой по ABI. Если нам повезет mul проинлайнится мы получим нужный эффект, иначе - увы. БОльшая часть расширений такого рода полностью опираются на инлайнинг и ограничивают использование C++ конструкций с аннотированными функциями.

Предлагается ввести понятие ABI в систему типов как дополнительный аттрибут со значениями либо поддержанными компилятором либо возвращающими ошибку.

Что-то вроде

double mul(double x, double y);
double mul(double x, double y) abi(simd4);
double mul(double x, double y) abi(simd8);

или даже

template<abi_tag a = default>
double mul(double x, double y) abi(a);

Cоответственно abi будет частью функционального типа и будет применимо в т.ч. к указателям. Также abi должно стать частью execution policy. Тогда станет возможно, например, исполнять алгоритмы на сопроцессорах, GPU и т.п. даже если ядро алгоритма не инлайнится в алгоритм.

 

 

-1
рейтинг
6 комментариев
Павел Корозевцев
почему это нельзя сделать с помощью шаблонного тега. зачем нужен спецификатор `abi(...)` в сигнатуре функции?
еще мне непонятно, как всё-таки будет выявляться нужная перегрузка в `std::transform(unseq, b, b+N, c, a, mul);`
Павел Корозевцев
Сергей Прейс
за ABI отвечает компилятор. Он отвечает и за корректное порождение кода в соответствии с ABI и за корректное оформление вызова. Шаблонные теги прямо сейчас не предполагают смены ABI - все инстанциации шаблона имеют один и тот же ABI и я уверен, что менять это поведение неправильно. Нужная перегрузка будет выявляться компилятором: он увидит, что специализация алгоритма для конкретной политики требует вызова со специфическим ABI и подставит нужный вызов.
Сергей Прейс
ru.night.beast
если и делать, то через атрибуты [[...]]
и без перегрузки.
ru.night.beast
Сергей Прейс
ru.night.beast, атрибуты (по крайней мере сейчас) не являются обязательными (их допустимо игнорировать) и потому менять через них ABI не получится. Кроме того, атрибуты - это как раз не часть типа и именно то, что теряется в std::function, std::bind и просто при взятии адреса. По сути все существующие реализации и пытаются обойтись атрибутами и их аналогами, но как результат для них недоступна существенная часть возможностей современного C++.
В частности, начиная с версии 17.0 в компиляторе Intel доступны указатели на SIMD-функции (в синтаксисе OpenMP), но доступны они только в C. Есть специальная оговорка для C++ связанная именно с выводом типов: указатели на такие функции не совместимы с обычными, а "необычность" легко теряется.
В с++amp Microsoft делает попытку сделать restrict() частью системы типов, но std::bind даже в их собственной реализации работать не будет - restrict() просто не будет захвачен при деконструции типа. Ну и вопрос с указателями до конца не решен - описать их вроде как можно, но работать будут только для случая restruct(cpu) то есть когда они просто совпадают с обычными.
Сергей Прейс
ru.night.beast
Сергей Прейс, сейчас есть возможность для функций указывать CC (cdecl, thiscall, etc)
зачем изобретать еще дополнительный способ?
ru.night.beast
Сергей Прейс
ru.night.beast, Во-первых СС не часть С++. Во-вторых многие из них делают по сути из C++ функций C-функции. То есть напрочь убивают перегрузку, я уж молчу про std::function и std::bind.
Сергей Прейс
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).