Новые Оптимизации Для X86 В Ожидаемой Версии Gcc 5.0

Итак, собственно разработку новых оптимизаций в GCC 5.0 можно считать завершенной.

Продукт GCC 5.0 в настоящее время находится в стадии разработки.

этап3 , то есть уже реализованные оптимизации дорабатываются.

В этой и последующих статьях я расскажу об оптимизациях, реализованных в GCC 5.0 для x86 и их влиянии на производительность программ для процессоров линейки.

Интел Атом И Intel Core .

Сегодня мы поговорим о векторизации группового доступа к памяти.

В последующих статьях я расскажу об ускорении в 32-битном режиме PIC и дополнительных улучшениях векторизации.

Как вы, наверное, уже догадались, в GCC 5.0 значительно улучшена векторизация группового доступа к памяти.

Групповой доступ к памяти представляет собой повторяющуюся последовательность доступов.

Например:

  
  
  
  
   

x = a[i], y = a[i + 1], z = a[i + 2]

итерируемый по я представляет собой группу загрузок памяти длиной 3. Длина группы обращений к памяти — это расстояние между младшим и старшим адресами в группе.

Для примера выше это (я + 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.
Ниже приведены таблицы, позволяющие оценить прирост производительности при использовании GCC 5.0 на байтовых структурах (наибольшем количестве элементов в векторе) по сравнению с GCC 4.9. Для оценки используется следующий цикл:

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 — макросы, определяющие длину группы загрузки из памяти и сохранения в память соответственно.

Параметры компиляции «-Ofast» плюс «-march=slm» для Сильвермонт , "-march=core-avx2" для Хасуэлл и все комбинации -DLDGSIZE={1,2,3,4,8} -DSTGSIZE={1,2,3,4,8} Увеличение производительности GCC 5.0 по сравнению с 4.9 (во сколько раз быстрее, чем больше, тем лучше).

Сильвермонт : Процессор Intel Atom(TM) C2750 @ 2,41 ГГц Увеличение до 6,5 раз!

Новые оптимизации для x86 в ожидаемой версии GCC 5.0

Как видно из таблицы, результаты для групп сохранения памяти длиной 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 раз!

Новые оптимизации для x86 в ожидаемой версии GCC 5.0

В этом случае результаты для групп сохранения памяти длиной 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

Из всего вышесказанного следует вывод: используя GCC 5.0, можно существенно ускорить работу приложений, подобных описанным выше.

Вы можете начать пробовать прямо сейчас! Большую часть изменений планируется перенести в Android NDK. Составители, использованные в измерениях:

Скачать пример, на котором были сняты замеры, можно с сайта оригинальный текст статьи на английском языке .

Теги: #открытый исходный код #GCC #программирование на c #векторизация #программирование #C++

Вместе с данным постом часто просматривают: