Элементные функции. Функции прикладного программного интерфейса
Презентацию к лекции Вы можете скачать здесь.
Эффективная векторизация – как её добиться?
Векторизация в рамках C/C++. Недостаток – не всегда удобная реализация.
Использование расширения процессорных инструкций SSE (Streaming SIMD Extension). Недостаток – фиксированная длина вектора.
Использование элементных операций/функций Intel® CilkTM Plus.
Элементные функции
Элементные функции формируют результат вычисления скалярной функции для каждого элемента массива (вызов скалярной функции с векторным аргументом формирует массив значений, конформный аргументу):
__declspec(vector) <сигнатура функции>
Отображение заменяет цикл последовательной программы.
Примеры:
a[:] = sin(b[:]); a[:] = pow(b[:], c); // b[:]**c a[:] = pow(c, b[:]); // c**b[:] f(b[:])
При компиляции кода с вызовом элементных функций компилятор генерирует обращения к векторизованным функциям.
Компилятор может генерировать многопоточный код.
Если функция определена как elemental, компилятор генерирует векторизованный код для этой функции.
Исключены побочные эффекты.
Функции отображаются параллельно!
Ограничения
- Допускается использование только следующих типов:
- signed/unsigned 8/16/32/64 битовые целые;
- 32 или 64 битовые с плавающей точкой;
- 64 или 128 битовые комплексные;
- указатель или ссылка C++.
- Не допускается использование ключевых слов for, while, do, goto.
- Не допускается использование операторов выбора.
- Не допускается использование ассемблерных вставок.
- В функциях не допускается многопоточность, реализованная с помощью , OpenMP, cilk_spawn/cilk_for.
- Не допускаются виртуальные функции и указатели на функции.
- В функциях не допускается использование выражений с индексной нотацией.
и другие.
Пример
float saxpy(float a, float *x, float *y); void foo(float *x, float *y, float a, int len) { for(int i = 0; i < len; i++) saxpy(a, x[i], y[i]); } void saxpy(float a, float *x, float *y) { *y += a * (*x); }
__declspec(vector(scalar(a),linear(x),linear(y))) void saxpy(float a, float *x, float *y); void foo(float *restrict x, float *restrict y, float a, int len) { saxpy(a, x[0:len], y[0:len]); }
Компилятор сгенерирует вот такой код:
void saxpy_4(float a, float x[4], float y[4]) { y[:] += a * x[:]; } for(i = 0; i < len-3; i += 4) { saxpy_4(a, &x[i], &y[i]); } for(; i < len; i++) { saxpy(a, &x[i], &y[i]); }
Примеры применения (по предметным областям):
- обработка изображений (гамма-коррекция и т.д.);
- преобразование между цветовыми пространствами;
- моделирование методом Монте-Карло.
Дополнительные примеры использования отображения функций
Параллелизм потоков
cilk_for( int i=0; i<n; ++i ) a[i] = f(b[i]);
Векторный параллелизм
a[0:n] = f(b[i:n]); #pragma simd for( int i=0; i<n; ++i ) a[i] = f(b[i]);
Пример использования отображения функций в Intel® Threading Building Blocks
parallel_for( 0, n, [&]( int i ) { a[i] = f(b[i]); });
parallel_for( blocked_range<int>(0,n), [&](blocked_range<int> r ) { for( int i=r.begin(); i!=r.end(); ++i ) a[i] = f(b[i]); });