Конференция Highload++ / 7 ноября 2016 / Спикер - Антон Орлов, занимается исследованием аппаратных компонентов, пригодных к использованию в платформе фильтрации трафика Qrator Labs.
В пересчёте на количество транзисторов оперативная память занимает в современном сервере не менее 85% (если добавить сюда внутрипроцессорные кэши, то и сильно за 90%). Все эти транзисторы оплачены, они греются. Хотелось бы использовать их по максимуму. При этом уже с середины 90-х годов именно скорость доступа к данным ограничивает производительность большинства вычислений (фоннеймановское узкое горло, стена памяти).
Мы так привыкли к слову RAM, что порой принимаем название random access за чистую монету. Однако во что на самом деле обходится доступ в память? И как это узнать? И что потом с этим делать?
11. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
12. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
13. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
14. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
15. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
16. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
17. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
18. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
19. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
20. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
21. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
12.32s
22. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
12.32s
#define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
23. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
12.32s
#define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
24. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
12.32s
#define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
25. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
26. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
27. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
28. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
29. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
30. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
31. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
32. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
33. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
34. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
35. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
36. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
37. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
38. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
12.32s
#define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS/2; i++)
mat[i][j] = (char)j;
}
for (int j = 0; j < ROWS; j++) {
for (int i = COLUMNS/2; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
1.96s
49. Адресация в 8-канальном
32КБ кэше
06124863
64
8
• Кэш-линия: 64 = 26
байт
• Всего кэш-линий: 32 КБ / 64 Б = 512
• Бакетов: 512 / 8 = 64 = 26
tag idx
50. Адресация в 8-канальном
32КБ кэше
06124863
64
8
• Кэш-линия: 64 = 26
байт
• Всего кэш-линий: 32 КБ / 64 Б = 512
• Бакетов: 512 / 8 = 64 = 26
• В каждом бакете:
у адресов совпадают 6 бит idx
tag idx
51. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS);
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
12.32s
52. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS + 64*i) + 64*i;
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
53. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS + 64*i) + 64*i;
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
54. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS + 64*i) + 64*i;
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
55. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS + 64*i) + 64*i;
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
56. #define COLUMNS 10
#define ROWS (1<<28) // 256MB
int main () {
char* mat[COLUMNS];
for (int i = 0; i < COLUMNS; i++)
mat[i] = (char*)malloc(ROWS + 64*i) + 64*i;
for (int j = 0; j < ROWS; j++) {
for (int i = 0; i < COLUMNS; i++)
mat[i][j] = (char)j;
}
int res = 0;
for (int i = 0; i < COLUMNS; i++)
res += mat[i][i];
return res;
}
2.12s
57. Кэш L1 и виртуальная память
06124863
64
8
Виртуальный адрес:
tag idx
58. Кэш L1 и виртуальная память
06124863
64
8
Виртуальный адрес:
tag idx
Страница: 4096 байт (12 бит адреса)
59. Кэш L1 и виртуальная память
06124863
64
8
Виртуальный адрес:
tag idx
061245
Физический адрес:
tag idx
Страница: 4096 байт (12 бит адреса)
60. Кэш L1 и виртуальная память
06124863
64
8
Виртуальный адрес:
tag idx
061245
Физический адрес:
tag idx
Страница: 4096 байт (12 бит адреса)
TLB: кэш для трансляции адресов
76. Summary
• Меньше памяти — лучше!
• Доступ в память — не O(1)
• Меньше промахов в кэши и TLB
• Меньше задержки на линках (RDIMM, LRDIMM, ECC)
77. Summary
• Меньше памяти — лучше!
• Доступ в память — не O(1)
• Меньше промахов в кэши и TLB
• Меньше задержки на линках (RDIMM, LRDIMM, ECC)
• Важно помнить про иерархию
• Cчетчики
• Через уровни можно прыгать
80. NVRAM в качестве основной памяти?
1: node = nvm_alloc(sizeof(node_t));
2: node->value = val;
3: node->next = head;
4: head = node;
Source: http://www.hpl.hp.com/techreports/2012/HPL-2012-236.pdf
81. NVRAM в качестве основной памяти?
1: node = nvm_alloc(sizeof(node_t));
2: node->value = val;
3: node->next = head;
4: head = node;
Intel:
● CLWB (Cache Line Write Back)
● PCOMMIT (to Persistent storage COMMIT)
Source: http://www.hpl.hp.com/techreports/2012/HPL-2012-236.pdf
82. NVRAM в качестве основной памяти?
1: node = nvm_alloc(sizeof(node_t));
2: node->value = val;
3: node->next = head;
4: head = node;
Intel:
● CLWB (Cache Line Write Back)
● PCOMMIT (to Persistent storage COMMIT)
Source: http://www.hpl.hp.com/techreports/2012/HPL-2012-236.pdf
83. Спасибо за внимание!
Вопросы?
“What Every Programmer Should Know About Memory”
Ulrich Drepper, 2007
https://www.akkadia.org/drepper/cpumemory.pdf
84. Другие полезные ссылки
• Dick Sites, Datacenter Computers: modern challenges in CPU design
Видео: https://www.youtube.com/watch?v=QBu2Ae8-8LM
Слайды: http://www.pdl.cmu.edu/SDI/2015/slides/DatacenterComputers.pdf
• What's new in CPUs since the 80s and how does it affect programmers?
http://danluu.com/new-cpu-features/
• Row hammer Android exploit https://www.vusec.net/projects/drammer/
• Kumud Bhandari, Dhruva R. Chakrabarti, Hans-J. Boehm, Implications of CPU
Caching on Byte-addressable Non-Volatile Memory Programming
http://www.hpl.hp.com/techreports/2012/HPL-2012-236.pdf