Итак, собственно разработку новых оптимизаций в GCC 5.0 можно считать завершенной.
Продукт GCC 5.0 в настоящее время находится в стадии разработки.
этап3 , то есть уже реализованные оптимизации дорабатываются.
В этой и последующих статьях я расскажу об оптимизациях, реализованных в GCC 5.0 для x86 и их влиянии на производительность программ для процессоров линейки.
Интел Атом И Intel Core .
Сегодня мы поговорим о векторизации группового доступа к памяти.
В последующих статьях я расскажу об ускорении в 32-битном режиме PIC и дополнительных улучшениях векторизации.
Как вы, наверное, уже догадались, в GCC 5.0 значительно улучшена векторизация группового доступа к памяти.
Групповой доступ к памяти представляет собой повторяющуюся последовательность доступов.
Например:
итерируемый по я представляет собой группу загрузок памяти длиной 3. Длина группы обращений к памяти — это расстояние между младшим и старшим адресами в группе.x = a[i], y = a[i + 1], z = a[i + 2]
Для примера выше это (я + 2) – (я) + 1 = 3 Количество обращений к памяти в группе не превышает ее длину.
Например: x = a[i], z = a[i + 2]
итерируемый по я представляет собой группу длиной 3, несмотря на то, что имеется только 2 доступа к памяти.
GCC 4.9 векторизует группы, длина которых равна степени 2 (2, 4, 8.).
GCC 5.0 векторизует группы, длина которых равна 3 или степени 2 (2, 4, 8.).
Другие длины не векторизуются, поскольку в реальных приложениях они встречаются очень редко.
Чаще всего векторизация группы обращений к памяти используется при работе с массивами структур.
1. Преобразование изображений, скажем, в RGB-структуру.
Пример .
2. Работа с N-мерными координатами (скажем, для нормализации 3D точек).
Пример .
3. Умножение векторов на постоянную матрицу: a[i][0] = 7 * b[i][0] - 3 * b[i][1];
a[i][1] = 2 * b[i][0] + b[i][1];
В целом в GCC 5.0 (по сравнению с 4,9)
- Появилась векторизация группы обращений к памяти длиной 3
- Значительно улучшена векторизация групп нагрузок из памяти на любую длину.
- Стали использоваться методы векторизации групп обращений к памяти, оптимальные для конкретного процессора x86.
int i, j, k;
byte *in = a, *out = b;
for (i = 0; i < 1024; i++)
{
for (k = 0; k < STGSIZE; k++)
{
byte s = 0;
for (j = 0; j < LDGSIZE; j++)
s += in[j] * c[j][k];
out[k] = s;
}
in += LDGSIZE;
out += STGSIZE;
}
Где:
- с является постоянной матрицей:
const byte c[8][8] = {1, -1, 1, -1, 1, -1, 1, -1,
1, 1, -1, -1, 1, 1, -1, -1,
1, 1, 1, 1, -1, -1, -1, -1,
-1, 1, -1, 1, -1, 1, -1, 1,
-1, -1, 1, 1, -1, -1, 1, 1,
-1, -1, -1, -1, 1, 1, 1, 1,
-1, -1, -1, 1, 1, 1, -1, 1,
1, -1, 1, 1, 1, -1, -1, -1};
Такая матрица используется для минимизации вычислений внутри цикла до относительно быстрых сложений и вычитаний.
- в И вне — указатели на глобальные массивы «a[1024 * LDGSIZE]» и «b[1024 * STGSIZE]»
- байт это беззнаковый символ
- LDGSIZE и STGSIZE — макросы, определяющие длину группы загрузки из памяти и сохранения в память соответственно.
Сильвермонт : Процессор Intel Atom(TM) C2750 @ 2,41 ГГц
Увеличение до 6,5 раз!
Как видно из таблицы, результаты для групп сохранения памяти длиной 3 не очень хорошие.
Это связано с тем, что для такого преобразования требуется 8 инструкций «pshufb», что занимает около 5 тактов для выполнения в Silvermont. Несмотря на это, векторизация других инструкций в цикле может дать больший выигрыш.
Это можно увидеть на примере группы загрузок памяти длиной 2, 3, 4 и 8. Пример ассемблера GCC для группы загрузки памяти длиной 2:
ССЗ 4.9 | ССЗ 5.0 |
movdqa .
LC0(%rip), %xmm7 movdqa . LC1(%rip), %xmm6 movdqa . LC2(%rip), %xmm5 movdqa . LC3(%rip), %xmm4 movdqu a(%rax,%rax), %xmm1 movdqu a+16(%rax,%rax), %xmm0 мовдка %xmm1, %xmm3 пшуфб %xmm7, %xmm3 мовдка %xmm0, %xmm2 пшуфб %xmm5, %xmm1 пшуфб %xmm6, %xmm2 пшуфб %xmm4, %xmm0 пор %xmm2, %xmm3 пор %xmm0, %xmm2 |
movdqa .
LC0(%rip), %xmm3 movdqu a(%rax,%rax), %xmm0 movdqu a+16(%rax,%rax), %xmm1 мовдка %xmm3, %xmm4 пандус %xmm0, %xmm3 psrlw $8, %xmm0 пандус %xmm1, %xmm4 psrlw $8, %xmm1 packageswb %xmm4, %xmm3 packageswb %xmm1, %xmm0 |
LC» будут инвариантами цикла, но только при наличии свободных регистров.
В противном случае их придется поместить прямо в phufb: «pshufb .
LC0, %xmm3».
Такой pshufb будет больше по размеру адреса и потенциально потребует больше времени для выполнения.
Хасуэлл : Процессор Intel Core(TM) i7-4770K @ 3,50 ГГц
Увеличение до 3 раз!
В этом случае результаты для групп сохранения памяти длиной 3 намного лучше, поскольку Хасуэлл Инструкция «pshufb» выполняется за 1 такт. Причем наибольший прирост здесь приходится именно на реализованную векторизацию групп обращений к памяти длиной 3.
Пример ассемблера GCC для группы загрузки памяти длиной 2:
ССЗ 4.9 | ССЗ 5.0 |
vmovdqa .
LC0(%rip), %ymm7 vmovdqa . LC1(%rip), %ymm6 vmovdqa . LC2(%rip), %ymm5 vmovdqa . LC3(%rip), %ymm4 vmovdqu a(%rax,%rax), %ymm0 vmovdqu a+32(%rax,%rax), %ymm2 vpshufb %ymm7, %ymm0, %ymm1 vpshufb %ymm5, %ymm0, %ymm0 vpshufb %ymm6, %ymm2, %ymm3 vpshufb %ymm4, %ymm2, %ymm2 впор %ymm3, %ymm1, %ymm1 впор %ymm2, %ymm0, %ymm0 vpermq $216, %ymm1, %ymm1 vpermq $216, %ymm0, %ymm0 |
vmovdqa .
LC0(%rip), %ymm3 vmovdqu a(%rax,%rax), %ymm0 vmovdqu a+32(%rax,%rax), %ymm2 vpan и %ymm0, %ymm3, %ymm1 vpsrlw $8, %ymm0, %ymm0 vpan и %ymm2, %ymm3, %ymm4 vpsrlw $8, %ymm2, %ymm2 vpackuswb %ymm4, %ymm1, %ymm1 vpermq $216, %ymm1, %ymm1 vpackuswb %ymm2, %ymm0, %ymm0 vpermq $216, %ymm0, %ymm0 |
Вы можете начать пробовать прямо сейчас! Большую часть изменений планируется перенести в Android NDK. Составители, использованные в измерениях:
Скачать пример, на котором были сняты замеры, можно с сайта оригинальный текст статьи на английском языке .Теги: #открытый исходный код #GCC #программирование на c #векторизация #программирование #C++
-
Что Касается Некоторых Блогов
19 Oct, 24 -
Киборги Рядом
19 Oct, 24 -
Форматы Электронной Подписи
19 Oct, 24