Другие функции
Имя функции | Операция | Инструкция |
_mm_shuffle_ps | перестановка упакованных значений | SHUFPS |
_mm_shuffle_pi16 | перестановка упакованных значений | PSHUFW |
_mm_unpackhi_ps | выборка старших значений | UNPCKHPS |
_mm_unpacklo_ps | выборка младших значений | UNPCKLPS |
_mm_loadh_pi | загрузка старших значений | MOVHPS reg, mem |
_mm_storeh_pi | сохранение старших значений | MOVHPS mem, reg |
_mm_movehl_ps | копирование старшей половины в младшую | MOVHLPS |
_mm_movelh_ps | копирование младшей половины в старшую | MOVLHPS |
_mm_loadl_pi | загрузка младших значений | MOVLPS reg, mem |
_mm_storel_pi | сохранение младших значений | MOVLPS mem, reg |
_mm_movemask_ps | создание знаковой маски | MOVMSKPS |
_mm_getcsr | сохранить регистр состояния | STMXCSR |
_mm_setcsr | установить регистр состояния | LDMXCSR |
Команды для инициализации и работы с памятью
Инициализация памяти
Имя функции | Операция | Инструкция |
_mm_load_ss | загрузить младшее значение и очистить остальные три значения | MOVSS |
_mm_load1_ps | загрузить одно значение во все четыре позиции | MOVSS + Shuffling |
_mm_load_ps | Загрузить четыре значения по выровненному адресу | MOVAPS |
_mm_loadu_ps | Загрузить четыре значения по невыровненному адресу | MOVUPS |
_mm_loadr_ps | Загрузить четыре значения в обратном порядке | MOVAPS + Shuffling |
Инициализация значений
Имя функции | Операция | Инструкция |
_mm_set_ss | устанавливает самое младшее значение и обнуляет три остальных | составная |
_mm_set1_ps | устанавливает четыре позиции в одно значение | составная |
_mm_set_ps | устанавливает четыре значения, выровненные по адресу | составная |
_mm_setr_ps | устанавливает четыре значения в обратном порядке | составная |
_mm_setzero_ps | Обнуляет все четыре значения | составная |
Операции записи
Имя функции | Операция | Инструкция |
_mm_store_ss | записать младшее значение | MOVSS |
_mm_store1_ps | записать младшее значение во все четыре позиции | MOVSS + Shuffling |
_mm_store_ps | записать четыре значения по выровненному адресу | MOVAPS |
_mm_storeu_ps | записать четыре значения по невыровненному адресу | MOVUPS |
_mm_storer_ps | записать четыре значения в обратном порядке | MOVAPS + Shuffling |
_mm_move_ss | записать младшее значение и оставить без изменения три остальных значения | MOVSS |
Поддержка кэш-памяти в SSE
Имя функции | Операция | Инструкция |
_mm_prefetch | Загружает одну кэш-строку по указанному адресу в кэш-память | PREFETCH |
_mm_stream_pi | Записывает данные в память без записи в кэш | MOVNTQ |
_mm_stream_ps | Записывает данные в память без записи в кэш по адресу, выровненному по 16 байт | MOVNTPS |
_mm_sfence | Гарантирует, что все предшествующие записи в память завершатся до следующей записи. | SFENCE |
3. Использование встроенных функций SSE в программе на языке Си
// скалярное произведение векторов
#include <stdio.h>
#include <xmmintrin.h>
#define N 10000000
// «обычная» функция
float inner1(float *x,float *y,int n)
{
float s;
int i;
s=0;
for(i=0;i<n;i++)
s+=x[i]*y[i];
return s;
}
// функция с использованием SSE intrinsics
float inner2(float *x,float *y,int n)
{
float sum;
int i;
__m128 *xx,*yy;
__m128 p,s;
xx=(__m128 *)x;
yy=(__m128 *)y;
s=_mm_set_ps1(0);
for (i=0;i<n/4;i++)
{
p=_mm_mul_ps(xx[i], yy[i]); // векторное умножение четырех чисел
s=_mm_add_ps(s,p); // векторное сложение четырех чисел
}
p=_mm_movehl_ps(p,s); // перемещение двух старших значений s в младшие p
s=_mm_add_ps(s,p); // векторное сложение
p=_mm_shuffle_ps(s,s,1);//перемещение второго значения в s в младшую позицию в p
s=_mm_add_ss(s,p); // скалярное сложение
_mm_store_ss(&sum,s); // запись младшего значения в память
return sum;
}
int main()
{
float *x,*y, s;
long t;
int i;
// выделение памяти с выравниванием
x=(float *)_mm_malloc(N*sizeof(float),16);
y=(float *)_mm_malloc(N*sizeof(float),16);
for (i=0;i<N;i++)
{
x[i]=10*i/N;
y[i]=10*(N-i-1)/N;
}
// Using x87
s=inner1(x,y,N);
printf("Result: %f\n",s);
// Using SSE
s=inner2(x,y,N);
printf("Result: %f\n",s);
_mm_free(x);
_mm_free(y);
return 0;
}
Задание.
1. Реализовать процедуру умножения квадратных матриц (размером кратным четырём) без использования специальных расширений и с использованием расширений SSE, сравнить время выполнения этих реализаций (Обязательное задание – 10 баллов).
2. В соответствии с вариантом задания реализовать матрично-векторную (с одинаковым размером матриц и векторов кратным четырём) процедуру с использованием расширений SSE (Дополнительное задание – 7 баллов).
3. С использованием инструкции cpuid определить наличие расширения SSE (Дополнительное задание – 3 балла).
4. Крайний срок сдачи – 7 мая 2011 года.
Варианты.
В предложенных вариантах предполагается, что
– скаляры, – векторы, – матрицы:1. Операция
.2. Операция
.3. Операция
.4. Операция
.5. Операция
.6. Операция
.7. Операция
.8. Операция
.9. Операция
.10. Операция
.Лабораторная работа № 4
Программирование многоядерных архитектур
Цель работы. Использование интерфейса OpenMP для программирования простых многопоточных приложений.
Методические указания.
1. Интерфейс OpenMP
OpenMP – интерфейс прикладного программирования (API) для масштабируемых SMP-систем (симметричные мультипроцессорные системы) в модели общей памяти.
Исполняемый процесс в памяти может состоять из множественных нитей, которые имеют общее адресное пространство, но разные потоки команд и раздельные стэки. В простейшем случае, процесс состоит из одной нити, выполняющую функцию main. Нити иногда называют также потоками, легковесными процессами, LWP (light-weight processes). OpenMP основан на существовании множественных потоков в общедоступной памяти [3]. Схема процесса представлена на рисунке.
Рисунок 8.
Все программы OpenMP начинаются как единственный процесс с главным потоком. Главный поток выполняется последовательно, пока не сталкиваются с первой областью параллельной конструкции. Создание нескольких потоков (FORK) и объединение (JOIN) проиллюстрировано на рисунке.
Рисунок 9.
2. Примеры программ с использованием OpenMP
2.1. Определение и печать номера потока
#include <omp.h>
#include <stdio.h>
void main ()
{
int nthreads, tid;
/* Fork a team of threads giving them their own copies of variables */
#pragma omp parallel private(tid)
{
/* Obtain and print thread id */
tid = omp_get_thread_num();
printf("Hello World from thread = %d\n", tid);
/* Only master thread does this */
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Number of threads = %d\n", nthreads);
}
} /* All threads join master thread and terminate */
}
2.2. Распределение работы
#include <stdio.h>
#include <omp.h>
#define CHUNKSIZE 100
#define N 1000
void main ()
{
int i, chunk;
float a[N], b[N], c[N];
/* Some initializations */