Презентацию к лекции Вы можете скачать здесь.
Векторизация в рамках 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, компилятор генерирует векторизованный код для этой функции.
Исключены побочные эффекты.
Функции отображаются параллельно!
for, while, do, goto.и другие.
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]);
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]);
});
Операция редукции применяется к сечению массива. Её результат – скалярное значение.
Примеры:
__sec_reduce(f, a[:]) __sec_reduce_add(a[:])
Базовые типы C поддерживаются следующими операциями редукции:
add mul max max_ind min min_ind all_zero all_non_zero any_nonzero
Есть возможность определить пользовательскую операцию приведения:
type fn(type in1, type in2); out = __sec_reduce(fn, identity_value, in[x:y:z]);
#include <iostream>
unsigned int bitwise_and(unsigned int x, unsigned int y) {
return (x y);
}
int main() {
unsigned int a[4] = {5,7,13,15};
unsigned int b = 0;
std::cout << "Display a:\n" << a[:] << " ";
std::cout << std::endl << std::endl;
b = __sec_reduce(bitwise_and, 0xffffffff, a[:]);
std::cout << "b:\n" << b << std::endl;
return(0);
}
float sum = __sec_reduce_add(a[i:n]);
#pragma simd reduction(+:sum)
float sum=0;
for( int i=0; i<n; ++i )
sum += a[i];
cilk::reducer_opadd<float> sum = 0;
cilk_for( int i=0; i<n; ++i )
sum += a[i];
... = sum.get_value();
enumerable_thread_specific<float> sum;
parallel_for( 0, n, []( int i ) {
sum.local() += a[i];
});
... = sum.combine(std::plus<float>());
sum = parallel_reduce(
blocked_range<int>(0,n),
0.f,
[](blocked_range<int> r, float s) -> float
{
for( int i=r.begin(); i!=r.end(); ++i )
s += a[i];
return s;
},
std::plus<float>()
Функции ППИ позволяют управлять поведением программы.
Функции прикладного программного интерфейса (ППИ) используются с заголовочным файлом cilk/cilk_api.h
int __cilkrts_set_param(const char* name, const char* value);
Эта функция используется для управления некоторыми параметрами системы исполнения Cilk.
Первые 2 параметра строковые.
nworkers – значение определяет количество исполнителей. Если данная функция не используется, количество исполнителей задаётся с помощью переменной окружения CILK_NWORKERS или, по умолчанию, оно равно количеству ядер.
Данная функция действует только до первого использования cilk_spawn или cilk_for.
int __cilkrts_get_nworkers(void);
Эта функция возвращает количество потоков-исполнителей и фиксирует его так, что оно не может быть изменено вызовом функции __cilkrts_set_param.
пользуется для управления некоторыми параметрами системы исполнения Cilk.
Идентификаторы исполнителей не обязательно прнимают непрерывный (последовательный) ряд значений.
int __cilkrts_get_worker_number(void);
Эта функция возвращает целое значение, показывающее исполнителя, который выполняет функцию.
int __cilkrts_get_total_workers(void);
Эта функция возвращает суммарное количество потоков-исполнителей, включая неактивные.
Оптимизируйте в первую очередь последовательный код.
Выбор зернистости:
Оптимизируйте кэш-эффективность.
Пример false-sharing:
volatile int x[32];
void f(volatile int *p)
{
for (int i = 0; i < 100000000; i++)
{
++p[0];
++p[16];
}
}
int main()
{
cilk_spawn f(x[0]);
cilk_spawn f(x[1]);
cilk_spawn f(x[2]);
cilk_spawn f(x[3]);
cilk_sync;
return 0;
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.