Эффективное программирование современных …ssd.sscc.ru ›...
Transcript of Эффективное программирование современных …ssd.sscc.ru ›...
Эффективное программирование современных микропроцессоров
и мультипроцессоров
Лекция 2. Эффективная работа с памятью
Новосибирский государственный университетФакультет информационных технологий
Кафедра параллельных вычислений
Преподаватели:Киреев С.Е.Калгин К.В.
Иерархия памяти
Оп
ерат
ивн
ая п
амят
ь
КэшL3
КэшL2Кэш
L1Регистры
Предвыборка
Буферизация
Другие потоки
Другие ядра
Другие процессоры
На что следует обращать внимание при работе с памятью
• Выравнивание данных
• Плотность размещения данных
• Объём данных
• Порядок обхода данных
• Доступ к памяти нескольких потоков
ВЫРАВНИВАНИЕ ДАННЫХ
Выравнивание данных
• Память делится на блоки
в соответствии с размером страницы, кэш-строки, сектора кэша, шины данных, …
• Элементы данных могут пересекать границы блоков
Это приводит к нескольким операциям чтения/записи
вместо одной.
• Пример:
Выравнивание данных
• Естественное выравнивание– По размеру элемента данных (до машинного слова)
– Применяется компилятором автоматически
• Выравнивание вручную
int x[N] __attribute__((aligned(64)));
err = posix_memalign(&ptr, 64, size); // Posix
ptr = _mm_malloc(size, 64); // Intel compiler
ptr = malloc(size + 64);ptra = (ptr % 64) ? (ptr & 0xffffffc0) + 64 : ptr;
Выравнивание данных
• Выравнивание элементов структур:а) struct S1 { int a; double x; int b; };
б) struct S2 { double x; int a; int b; };
• Управление выравниванием в компиляторе#pragma pack(…)
ПЛОТНОЕ И РАЗРЕЖЕННОЕ РАЗМЕЩЕНИЕ ДАННЫХ
Плотное и разреженное размещение данных
• Память делится на блокив соответствии с размером страницы, кэш-строки, сектора кэша, шины данных, …
• Загрузка блока занимает время и ресурсы (кэши, шины передачи данных)
• Плотно размещенные данные используют меньше блоков
адреса в памяти
a) Плотное размещение данных
б Разреженное размещение данных)
адреса в памяти
Данные используются
Данные не используются
Плотное и разреженное размещение данных
• Пример: два варианта размещения данных
– Какое размещение выбрать для следующих программ?
Вариант A Вариант Б
struct point { float x, y; };struct point points[N];
float x[N];float y[N];
б)
а) X1 X2 X3 XNY1 Y2 Y3 YN
X1 X2 X3 XN Y1 Y2 Y3 YN
for (i=0;i<N;i++)
process(x[i],y[i]);
for (i=0;i<N;i++) process(x[i]);
for (i=0;i<N;i++) process(y[i]);
ОБЪЁМ ДАННЫХ
Объём данных• Среднее время обращения к данным зависит от объема
обрабатываемых данных:
Объём данных
Доступ к памяти в многопроцессорных системах• Тест: копирование данных (8 МБ) на различное
расстояние в памяти
0
500000
1000000
1500000
2000000
2500000
3000000
8
144
280
416
552
688
824
960
1096
1232
1368
1504
1640
1776
1912
вр
ем
я, м
се
к
расстояние, Мб
0
50000
100000
150000
200000
250000
вр
ем
я, м
ксек
расстояние, Мб
SMP – однородный доступ к памяти NUMA – неоднородный доступ к памяти
2 × Alpha 21264 2 × Opteron 244
Объём данных
• Для увеличения скорости работы с памятью используются специальные структуры данных и алгоритмы
• Cache-oblivious algorithms, Блочные (Tiled), …• Параметр – размер блока, зависит от размера кэша• Примеры:
• Идея в основе этих алгоритмов:– то, что загрузили, использовать по максимуму
Блочные матричные алгоритмы
Развернутый связный список
Объём данных
Kazushige Goto, Robert A. van de Geijn. Anatomy of High-Performance Matrix Multiplication.// ACM Trans. Math. Softw., Vol. 34, No. 3., 2008.
Пример: Планирование размещения данных в иерархии памяти для умножения матриц
Объём данных
• Типичная зависимость времени работы программы от степени разбиения данных/алгоритма на блоки:
Степень разбиения
Вр
ем
я р
або
ты
Оптимум – зависит от объема кэша
Блоки не входят в кэш –обрабатываются долго
Нужно перебиратьмного блоков – долго
Быстрая обработка небольшого числа
блоков
Объём данных
• Особенности виртуальной памяти– Виртуальная память разделена на страницы
• стандартный размер для x86: 4 КБ
– Есть таблица страниц• отображает виртуальные адреса страниц в физические• хранится в виртуальной памяти
– Есть TLB – кэш таблицы страниц• многоуровневый (L1/L2), данных/команд• множественно-ассоциативный
– Обращение к новой странице, адрес которой ещё не загружен в TLB, требует минимум два обращения в память – т.е. минимум в два раза дольше
• Как учитывать особенности виртуальной памяти– Хранить и использовать данные компактно– Использовать большие страницы (huge pages)
Объём данных
• Особенности виртуальной памяти– Виртуальная память разделена на страницы
• стандартный размер для x86: 4 КБ
– Есть таблица страниц• отображает виртуальные адреса страниц в физические• хранится в виртуальной памяти
– Есть TLB – кэш таблицы страниц• многоуровневый (L1/L2), данных/команд• множественно-ассоциативный
– Обращение к новой странице, адрес которой ещё не загружен в TLB, требует минимум два обращения в память – т.е. минимум в два раза дольше
• Как учитывать особенности виртуальной памяти– Хранить и использовать данные компактно– Использовать большие страницы (huge pages)
ПОРЯДОК ОБХОДА ДАННЫХ
Порядок обхода данных
• Подсистема памяти оптимизирована для последовательного обхода
– Блочное хранение и передача данных
– Аппаратная предвыборка данных
Порядок обхода данных
Блочное хранение и передача данных
Непоследовательный обход Последовательный обход
Порядок обхода данных
Блочное хранение и передача данных
Аппаратная предвыборка
данных
Непоследовательный обход Последовательный обходБез предвыборки С предвыборкой
Порядок обхода данных
• Расположение данных в памяти: пример
Порядок обхода данных
Пример: умножение матриц
for(i=0;i<Nx;i++)
for(k=0;k<Nz;k++)
for(j=0;j<Ny;j++)
C[i][k]+=A[i][j]*B[j][k];
1
0
Ny
j
jkijik BAC
for(i=0;i<Nx;i++)
for(j=0;j<Ny;j++)
for(k=0;k<Nz;k++)
C[i][k]+=A[i][j]*B[j][k];
×=
Cik Aij Bjk
×=
Cik Aij Bjk
Время перемножения матриц 1000×1000
120.67 с Alpha 6.24 с
16.67 с Opteron 6.3 с
по формуле: переставим циклы
Порядок обхода данных
• Пример: умножение матрицfor (i=0; i<N; i++)
for (j=0; j<N; j++)
for (k=0; k<N; k++)
C[i][j] += A[i][k] * B[k][j];
0
20000000
40000000
60000000
80000000
100000000
120000000
вр
ем
я, м
кс
варианты перемножения
Перемножение матриц Alpha
Opteron
Порядок обхода данных
• Специальный случай обхода:
кэш-буксование (cache-thrashing)Обход элементов с шагом кратным
размеру банка = размер кэша / степень ассоциативности
Порядок обхода данных
• Простой пример 1 (один массив):double a[4096000], sum[4096];
int i, j;
for (i=0; i<4096; i++) {
sum[i] = 0;
for(j=0; j<1000; j++) sum[i] += a[i + j*4096];
}
• Простой пример 2 (несколько массивов):double a[4096], b[4096], c[4096];
int i;
for (i=0; i<4096; i++) c[i] = a[i] + b[i];
Порядок обхода данных
• Стандартные способы избежать кэш-буксования
– Изменить порядок обхода
– Изменить расстояние между элементами
• Пример:
double a[4096], b[4096], c[4096];int i;for (i=0; i<4096; i++) с[i]=a[i]+b[i];
Порядок обхода данных
• Стандартные способы избежать кэш-буксования
– Изменить порядок обхода
– Изменить расстояние между элементами
• Пример:
double a[4096 +4], b[4096 +4], c[4096 +4];int i;for (i=0; i<4096; i++) c[i]=a[i]+b[i];
Пример: «буксование» кэша – семиточечная схема
X[Nz][Ny][Nx];
Nx × NyNx2D слой: Nx × Ny = 32Кб
32Кб 32КбМедленно:
2D слой: Nx × Ny + d Nx
Быстрее: 32Кб + d 32Кб + d
Nx × Ny + d
Порядок обхода данных
Пример: «буксование» кэша – семиточечная схема
Порядок обхода данных
0
1
2
3
4
5
6
7
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
Величина размерностей массива (N)
вр
ем
я, с
Размер 2D слоя для N=128: 128×128 = 16384 элементов = 64 Кб
Размер 3D массива: N × N × N
ДОСТУП К ПАМЯТИ НЕСКОЛЬКИХ ПОТОКОВ
Доступ к памяти нескольких потоков
Особенности:• Совместное использование ядрами кэш-памяти
• Доступный потоку объем кэш-памяти меньше
• Совместное использование каналов доступа к памяти
• Доступ потока к данным в памяти медленнее
• Поддержка когерентности кэш-памяти• Доступ потока к совместным данным медленнее• Возможно ложное разделение кэш-строк
Доступ к памяти нескольких потоков
• Пример: Совместное использование канала доступа к памяти при работе MPI-программы на нескольких ядрах
0
20
40
60
80
100
120
1 2 4 8 16 32 64
Число MPI-процессов
Вр
ем
я,
с
0
20
40
60
80
100
120
140
160
180
1 2 4 8 16 32 64
Число MPI-процессов
Вр
ем
я,
с8 ядер в узле
8 ядер в узле
Itanium2 1.6 GHzXeon X5365 3.0 GHz
Память – узкое место этой системы Производительности памяти хватает для работы данного процессора
Доступ к памяти нескольких потоков
• Проблема:• при совместном использовании канала доступа
к памяти доступ конкретного потока к данным в памяти происходит медленнее
• Решение• Реже обращаться в оперативную память –
использовать кэш-память более эффективно
(использовать блочные алгоритмы и т.п.)
Доступ к памяти нескольких потоков
• Пример: Совместное использование канала доступа к памяти
0
20
40
60
80
100
120
140
160
180
1 2 4 8 16 32 64
Число MPI-процессов
Вр
ем
я,
с
0
20
40
60
80
100
120
1 2 4 8 16 32 64
Число MPI-процессов
Вр
ем
я,
с
Обычный
Конвейер по строкам
Преодолели узкое местоза счёт использования
кэш-эффективного алгоритма
Itanium2 1.6 GHzXeon X5365 3.0 GHz
Доступ к памяти нескольких потоков
• Поддержка когерентности кэш-памяти• Проблема:
• При многократном обращении нескольких потоков к одним и тем же данным требуется обновление данных на ближайшем общем уровне иерархии памяти – кэш на более низких уровнях работает неэффективно.
• Пример:int i, s;#pragma omp parallel forfor (i=0; i<N; i++)
#pragma omp atomics += i;
• Решение:• модифицируйте алгоритм, чтобы такой ситуации не было
Доступ к памяти нескольких потоков
• Поддержка когерентности кэш-памяти• Проблема: ложное разделение кэш-строк
• Синхронизация данных происходит кэш-строками• Различные данные, размещенные в одной кэш-строке, будут тоже
синхронизироваться
• Пример:const int nth = 4;int i, s[nth];#pragma omp parallel num_threads(nth){ int ith = omp_get_thread_num();#pragma omp forfor (i=0; i<N; i++) s[ith] += i;
}
• Решение:• разнесите данные на расстояние не меньше одной кэш-строки
Доступ к памяти нескольких потоков
• Тест: доступ двух потоков к данным• Время работы одного из потоков в зависимости от
расстояния между данными потоков:
СРЕДСТВА «ПРОДВИНУТОЙ» РАБОТЫ С ПАМЯТЬЮ
Средства «продвинутой» работы с памятью
• Программная предвыборка данных в кэш• _mm_prefetch(ptr, _MM_HINT_NTA);
• Куда загружать: _MM_HINT_T0, _MM_HINT_T1, _MM_HINT_T2, _MM_HINT_NTA
• https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=4056,5203,4056&text=_mm_prefetch
• Запись в память без записи в кэш• _mm_stream_ps(ptr, v128);• https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=4056,5203,4056,5203&text=_mm_stream
Средства «продвинутой» работы с памятью
• Аппаратная предвыборка данныхfor (i=0; i<N; i++)
{
process(x[i]);
}
• Программная предвыборка данныхfor (i=0; i<N; i++)
{
_mm_prefetch(&x[i+d], 0);
process(x[i]);
}
// d – дистанция предвыборки
4 5 6 7 8 9X[]:
обработкаx[i]
аппаратная предвыборка
дистанция предвыборки
0 1 2 3 4 5 6 7 8 9X[]:
программная предвыборка x[i+d]
d – дистанция предвыборки
обработкаx[i]
Средства «продвинутой» работы с памятью
• Запись в память без записи в кэш
• Пример: копирование массиваfor (i=0; i<N; i++)
{
_mm_prefetch(&x[i+d], _MM_HINT_NTA);
_mm_stream_ps(&y[i], x[i]);
}
КэшL2
КэшL3
КэшL1
ОП
Загрузка с предвыборкой в кэши:все каналы работают одновременно
Запись напрямую в память