2016-03-27 2 views
0

Я векторизовал внутренний цикл добавления матрицы, используя инструкцию intrinsics для AVX2, у меня также есть таблица задержек от here. Я ожидаю, что ускорение должно быть в 5 раз, потому что почти 4 задержки происходят в 1024 итерациях более 6 латентностей в 128 итерациях, но ускорение является фактором 3, поэтому вопрос заключается в том, что еще здесь, чего я не вижу. Я использую gcc, кодирование в c, intrinsics, CPU - skylake 6700hqПочему ускорение ниже ожидаемого с помощью AVX2?

Вот c и сборка из внутренней петли.

глобальные данные:

int __attribute__((aligned(32))) a[MAX1][MAX2] ; 
int __attribute__((aligned(32))) b[MAX2][MAX3] ; 
int __attribute__((aligned(32))) c_result[MAX1][MAX3] ; 

последовательные:

for(i = 0 ; i < MAX1 ; i++) 
     for(j = 0 ; j < MAX2 ; j++) 
      c_result[i][j] = a[i][j] + b[i][j]; 

.L16: 
    movl (%r9,%rax), %edx   // latency : 2 , throughput : 0.5 number of execution unit : 4 ALU 
    addl (%r8,%rax), %edx   // latency : dont know , throughput : 0.5  number of execution unit : 4 ALU 
    movl %edx, c_result(%rcx,%rax) // latency : 2 , throughput : 1 number of execution unit : 4 ALU 
    addq $4, %rax 
    cmpq $4096, %rax 
    jne .L16 

AVX2:

for(i = 0 ; i < MAX1 ; i++){ 
    for(j = 0 ; j < MAX2 ; j += 8){ 
     a0_i= _mm256_add_epi32(_mm256_load_si256((__m256i *)&a[i][j]) , _mm256_load_si256((__m256i *)&b[i][j])); 
      _mm256_store_si256((__m256i *)&c_result[i][j], a0_i); 
    }} 

.L22: 
    vmovdqa (%rcx,%rax), %ymm0   // latency : 3 , throughput : 0.5  number of execution unit : 4 ALU 
    vpaddd (%r8,%rax), %ymm0, %ymm0  // latency : dont know , throughput : 0.5 number of execution unit : 3 VEC-ALU 
    vmovdqa %ymm0, c_result(%rdx,%rax) // latency : 3 , throughput : 1 number of execution unit : 4 ALU 
    addq $32, %rax 
    cmpq $4096, %rax 
    jne .L22 
+0

Выравнивание памяти составляет 32 байт, размер строки кеша L1D составляет 64 байт и 8 путей, я все еще исследую. Но мне нужен профессиональный лидер, Да, я знаю его воскресенье. – ADMS

+1

Вы уже пробовали IACA? Это не сделало Skylake, последний раз я посмотрел, но результаты на Хасуэлла могут помочь. Кроме того, ознакомьтесь с таблицами инструкций Agner Fog. (Http://www.agner.org/optimize/instruction_tables.pdf) – jbapple

+0

Ваш код, вероятно, не вычисляется, а привязан к памяти. Вы не можете получить быстрее, чем ваша шина памяти может предоставлять данные. –

ответ

3

Другие, чем счетчик цикла, нет никакой цепи зависимостей петли несут. Таким образом, операции с разных итераций цикла могут быть в полете сразу. Это означает, что латентность не является узким местом, а просто пропускной способностью (исполнительных блоков и интерфейсом (до 4 подключений с плавным доменом на каждый такт)).

Кроме того, ваши номера абсолютно безумны. mov Нагрузки не принимают 4 исполнительных блока ALU! И числа задержки загрузки/хранения ошибочны/бессмысленны (см. Последний раздел).

# Scalar (serial is the wrong word. Both versions are serial, not parallel) 
.L16: 
    movl (%r9,%rax), %edx   // fused-domain uops: 1. Unfused domain: a load port 
    addl (%r8,%rax), %edx   // fused-domain uops: 2 Unfused domain: a load port and any ALU port 
    movl %edx, c_result(%rcx,%rax) // fused-domain uops: 2 Unfused domain: store-address and store-data ports. port7 can't handle 2-reg addresses 
    addq $4, %rax     // fused-domain uops: 1 unfused: any ALU 
    cmpq $4096, %rax    // fused-domain uops: 0 (fused with jcc) 
    jne .L16       // fused-domain uops: 1 unfused: port6 (predicted-taken branch) 

Итого: 7 слита-домен микрооперация означает, что цикл может выдавать из буфера петли на одной итерации на 2с. (не на 1,75 в). Поскольку мы используем сочетание загрузок, хранилищ и модулей ALU, порты выполнения не являются узким местом, а только шириной проблемы с объединенным доменом. Две нагрузки на 2c и один магазин на 2c - это только половина пропускной способности блоков загрузки и хранения.

Обратите внимание, что режимы адресации с двумя регистрами can't micro-fuse on Intel SnB-family. Это не проблема для чистых нагрузок, потому что они равны 1 мкп даже без микро-слияния.

Анализ идентичен для векторного цикла. (vpaddd имеет латентность 1c на Skylake и почти каждый другой процессор. Таблица не перечисляет ничего в столбце задержки для padd с операндом памяти, так как время ожидания нагрузки не зависит от задержки добавления. добавляет один цикл в DEP цепи с участием регистра SRC/Dest, до тех пор, как адрес нагрузки знаю достаточно далеко впереди времени.)


магазина и латентность нагрузки чисел Agner противотуманными являются своего рода поддельным тоже. Он произвольно делит общую задержку в обратном направлении нагрузки (с сохранением-хранением) на число латентности для загрузки и хранения. IDK, почему он не перечислял задержку загрузки, измеренную с помощью теста на сканирование указателя (например, повторный mov (%rsi), %rsi). Это свидетельствует о том, что процессоры Intel SnB-семейства имеют 4 байта задержки загрузки.

Я хотел послать ему записку об этом, но не добрался до него.


Вы должны быть видим AVX2 убыстрение 32/4, т.е. 8х. Размер вашей проблемы составляет только 4096B, что достаточно мало для трех массивов такого размера, чтобы вставить в кеш L1. (РЕДАКТ: вопрос вводит в заблуждение: показан цикл, представляющий собой внутренний цикл вложенного цикла. См. Комментарии: по-видимому, даже с 4k массивами (не 4M), OP все еще только видел 3x ускорение (против 1.5x с 4M массивами), поэтому в версии AVX есть какое-то узкое место.)

Все 3 массива выровнены, так что это не переход к кеш-линии в операнде памяти , который не требует выравнивания (%r8).

Моя другая теория на этом тоже не кажется очень вероятной, но ваши адреса массива смещены друг от друга точно в 4096B? Из Agner противотуманными в microarch PDF:

Это не возможно читать и писать одновременно с адресами , которые разнесены на кратное 4 кбайт

пример показывает магазин затем загрузить, хотя, так IDK, если это действительно объясняется. Даже если аппаратное обеспечение для хранения памяти считает, что загрузка и хранение могут быть на одном и том же адресе, я не уверен, почему это остановит код для поддержки как можно большего количества операций с памятью или почему это повлияет на код AVX2 хуже, чем скалярный код ,

Стоит попытаться компенсировать ваши массивы друг от друга дополнительными 128B или 256B или еще чем-то.

+0

Thank вы, я имею в виду, что есть 4 ALU, которые могут выдавать 'mov' не принимать 4 ALU – ADMS

+1

@Amir: ALU = Арифметико-логический блок. Skylake имеет ALU на портах 0,1,5 и 6.' movl (% r9,% rax),% edx' - чистая нагрузка и не нуждается в ALU. Для этого нужен только порт загрузки, из которых у процессоров семейства SnB два. Вот почему его пропускная способность составляет один на 0,5 c. –

+0

см. [64- IA-32-архитектура оптимизация-руководство] (http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-archi tectures-optimization-manual.html) стр. 34, таблица 2-2, строка 1: блок выполнения: ALU, № элемента: 4, инструкции: добавить, и, cmp или, test, xor, movzx, movsx, mov, (v) movdqu, (v) movdqa, (v) movap *, (v) movup *. Так что это значит? – ADMS

0

Следующее ограничение ограничивает выполнение двух вариантов реализации. Во-первых, кроме счетчика циклов, не существует цепочки зависимостей, связанных с циклом, поэтому операции с различными итерациями цикла могут выполняться сразу, и это означает, что латентность не является основным узким местом, как латентность является важным фактором в HPC. Так как латентности равны, пропускная способность исполнительных блоков более эффективна для обеих реализаций. IACA демонстрирует узкое место пропускной способности для скалярной реализации как «Inter-Iteration», что означает, что существует зависимость между последовательными итерациями цикла и векторизации, что помогает ускорить выполнение кода. Кроме того, vpaddd в векторизованном режиме может быть выдан на портах 5,1, но add использует порты выполнения 1,5,6, когда порт 0 занят в первом цикле. Во-вторых, пропускная способность интерфейсного плавкого домена может влиять на производительность, но в этом алгоритме согласно результатам IACA для обеих реализаций 7 uops для каждой требуемой итерации, а микроархитектура HSW/SKL может выдавать до 4 плавленых- домен uops за такт, поэтому ему требуется 2 цикла на итерацию внутреннего цикла, и это ограничение нарушает реализацию AVX2 больше, чем скалярная реализация. В-третьих, зависимость данных от алгоритма вызывает много промахов в кэше. Уменьшая размер матриц, которые должны быть вписаны в L1D (кеш данных первого уровня), становится фактором 5 (, как бы я не тестировал много времени, чтобы получить 5, но тестирование IDK снова ускорилось - 7,3).

+1

Интересно, что вы получили коэффициент ускорения 5, а не 8, так как задержки и uops одинаковы для скалярных и AVX2. Также обратите внимание, что «total» IACA - это unops-domain uops, что не является полезной суммой. (например, xor-zeroing и исключенные ходы считаются нулями). В вашем случае ответ тот же, потому что ни один из ваших микропроцессоров не может замаскировать микропроцессор, только макро-предохранитель. –

+0

В любом случае руководство по оптимизации Intel в разделе 2.1.3 дает таблицу максимальной и устойчивой пропускной способности для L1, L2 и т. Д. На Skylake. Skylake может поддерживать только ~ 81B/общий цикл в/из кеша L1D. (Таблица Хасуэлла не имеет этого столбца. IDK, если это означает, поддерживает = пик или нет). Однако я просто понял, что это не объясняет ничего о скалярном и векторном для вашего цикла, потому что интерфейс ограничивает ваш код до 96B за 2 цикла **. Я на минуту подумал, что нашел объяснение, но, думаю, нет. –

+0

vpaddd в векторизованном режиме может быть выдан на портах 5,1, но добавить использует порты выполнения 1,5,6, когда порт 0 занят в первом цикле. просто добавил к ответу – ADMS

Смежные вопросы