Нужно ли Skylake vzeroupper для турбо-часов для восстановления после 512-битной инструкции, которая читает только регистр ZMM, записывая маску? - PullRequest
5 голосов
/ 26 октября 2019

Запись в регистр ZMM может привести к тому, что процессор Skylake-X (или аналогичный) может бесконечно долго находиться в состоянии пониженного макс-турбо. ( SIMD-инструкции, понижающие частоту процессора и Динамическое определение того, где выполняется мошенническая инструкция AVX-512 ) Предположительно, Ice Lake похож.

( Обходной путь: нетпроблема для zmm16..31 , согласно комментариям @ BeeOnRope, которые я цитировал в Полезно ли использовать VZEROUPPER, если ваша программа + библиотеки не содержат инструкций SSE? Так что этот strlen может просто использовать vpxord xmm16,xmm16,xmm16 и vpcmpeqb с zmm16.)

Как проверить это, если у вас есть оборудование:

@ BeeOnRope опубликовал тестовый код в потоке RWT : заменить vbroadcastsd zmm15, [zero_dp]с vpcmpeqb k0, zmm0, [rdi] в качестве команды «загрязнения» и посмотрите, будет ли цикл после этого медленным или быстрым.


Я предполагаю, что выполнение любого 512-битного мопа временно вызовет пониженное турбо (наряду с отключением порта1 для векторных ALU-мопов, в то время как 512-битный моп фактически находится на заднем плане), но вопрос в следующем: восстановится ли ЦП самостоятельно, если вы никогда не будете использовать vzeroupper после всего лишь чтения ZMMрегистр?

(И / или будут ли последующие инструкции SSE или AVX иметь штрафы за переход или ложные зависимости?)

В частности, strlen, использующему такие insns, нужно vzeroupperперед возвратом? (На практике на любом реальном процессоре и / или в соответствии с документацией Intel для получения рекомендаций на будущее.) Предположим, что более поздние инструкции могут включать не VEX SSE и / или кодировку VEX AVX1 / 2, а нетолько целое число GP, в случае, если это имеет отношение к ситуации «грязный верхний-256», сохраняющей турбо сокращение.

; check 64 bytes for zero, strlen building block.
    vpxor     xmm0,xmm0,xmm0    ; zmm0 = 0 using AVX1 implicit zero-extension
    vpcmpeqb  k0, zmm0, [rdi]   ; 512-bit load + ALU, not micro-fused
    ;kortestq k0,k0 / jnz or whatever

    kmovq     rax, k0
    tzcnt     rax, rax

  ;vzeroupper  before lots of code that goes a long time before another 512-bit uop?

(Вдохновленный strlen в AVX512BW: обрабатывать 64-битную маску в 32-битном кодес bsf / tzcnt? , который выглядел бы так, если бы обнуление его вектора reg было должным образом оптимизировано для использования более короткого VEX вместо инструкции EVEX.)


Ключевой инструкцией является vpcmpeqb k0, zmm0, [rdi], котораядекодирует в SKX или CNL до 2 отдельных мопов ( без микросинхронизации: retire-slots = 2.0 ): 512-битная загрузка (в 512-битный физический регистр?) и сравнение ALU в маскезарегистрируйтесь.

но нет архитектурный Регистр ZMM всегда записывается явно, только для чтения. Таким образом, предположительно, по крайней мере, xsave / xrstor очистит любое «грязное верхнее» условие, если оно существует после этого. (Это не произойдет в Linux, если нет фактического переключения контекста на другой процесс пользовательского пространства на этом ядре или поток мигрирует; простой ввод в ядро ​​для прерываний не вызовет его. Так что это на самом деле все еще тестируется подосновная ОС, если у вас есть оборудование; у меня нет.)

Возможности, которые я могу представить для SKX / CNL и / или Ice Lake:

  • Нет долгосрочного эффекта: max-turbo восстанавливается так же быстро, как и с vzeroupper
  • Макс. Turbo ограничивается 512-битной скоростью до переключения контекста. (xrstor или эквивалентный снимает любой флаг состояния «грязный верхний», потому что архитектурные регистры чистые).
  • Максимальное турбо ограничение ограничено 512-битной скоростью даже при переключении контекста, как если бы вы запускали vaddps zmm0,zmm0,zmm0,(Грязный верхний флаг устанавливается в сохраненном и восстановленном в архитектурном состоянии.) Вероятно, потому что xsaveopt пропускает сохранение верхних 128 или 256 векторных регистров, если известно, что они чистые.

Полагаю, kmovq не уменьшит максимальное турбо или не вызовет другие 512-битные эффекты UOP. Старшие 32 бита регистров маски обычно вступают в игру с AVX512BW только для 64-байтовых векторов, но, по-видимому, они не обеспечивают питание верхних 32 битов регистров маски отдельно, только верхние 32 байта векторных рег. Существуют варианты использования, например, использование kshift или kunpack для работы с 64-битными порциями масок (для загрузки / сохранения или передачи в целочисленные регистры), даже если вы только когда-либо генерируете или используете их 32 битами одновременно с AVX512VLс рег. YMM или XMM


PS: Xeon Phi не подвержен этим эффектам;он не предназначен для работы выше тяжелого AVX512 при запуске другого кода, потому что он создан для запуска AVX512. И на самом деле vzeroupper очень медленный и не рекомендуется для KNL / KNM.

Тот факт, что мой пример использует AVX512BW, действительно не имеет отношения к вопросу, но все основные (не Xeon Phi) процессоры с AVX512 имеютAVX512BW. Это просто хороший пример реального использования, и тот факт, что использование AVX512BW исключает KNL, не имеет значения.

1 Ответ

2 голосов
/ 27 октября 2019

Нет , vpcmpeqb в регистр маски не запускает медленный режим, если вы используете регистр zmm в качестве одного из сравнений, по крайней мере, для SKX.

Этотакже справедливо для любой другой инструкции (насколько я тестировал), которая только читает ключевые 512-битные регистры (ключевые регистры zmm0 - zmm15). Например, vpxord zmm16, zmm0, zmm1 также не загрязняет верхние элементы, потому что, хотя он включает в себя zmm1 и zmm0, которые являются регистрами ключей, он только читает из них при записи zmm16, который не является регистром ключей. .

Я протестировал это, используя avx-turbo на Xeon W-2104, который имеет номинальную скорость 3,2 ГГц, лицензию L1 turbo (AVX2 turbo) 2,8 ГГц и L2лицензия (AVX-512 Turbo) 2,4 ГГц. Я использовал опцию --dirty-upper, чтобы загрязнять верх перед каждым тестом с vpxord zmm15, zmm14, zmm15. Это приводит к тому, что любой тест, который вообще использует любые SIMD-регистры (включая скалярную SSE FP), работает на более медленной скорости 2,8 ГГц, как показано в этих результатах (посмотрите на столбец A / M-MHz для частоты процессора):

CPUID highest leaf  : [16h]
Running as root     : [YES]
MSR reads supported : [YES]
CPU pinning enabled : [YES]
CPU supports AVX2   : [YES]
CPU supports AVX-512: [YES]
cpuid = eax = 2, ebx = 266, ecx = 0, edx = 0
cpu: family = 6, model = 85, stepping = 4
tsc_freq = 3191.8 MHz (from calibration loop)
CPU brand string: Intel(R) Xeon(R) W-2104 CPU @ 3.20GHz
4 available CPUs: [0, 1, 2, 3]
4 physical cores: [0, 1, 2, 3]
Will test up to 1 CPUs
Cores | ID                  | Description                     | OVRLP1 | OVRLP2 | OVRLP3 | Mops | A/M-ratio | A/M-MHz | M/tsc-ratio
1     | pause_only          | pause instruction               |  1.000 |  1.000 | 1.000  | 2256 |      0.99 |    3173 | 1.00       
1     | ucomis_clean        | scalar ucomis (w/ vzeroupper)   |  1.000 |  1.000 | 1.000  |  790 |      1.00 |    3192 | 1.00       
1     | ucomis_dirty        | scalar ucomis (no vzeroupper)   |  1.000 |  1.000 | 1.000  |  466 |      0.88 |    2793 | 1.00       
1     | scalar_iadd         | Scalar integer adds             |  1.000 |  1.000 | 1.000  | 3192 |      0.99 |    3165 | 1.00       
1     | avx128_iadd         | 128-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_iadd         | 256-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 2793 |      0.87 |    2793 | 1.00       
1     | avx512_iadd         | 512-bit integer adds            |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_iadd_t       | 128-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 8380 |      0.88 |    2793 | 1.00       
1     | avx256_iadd_t       | 256-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 8380 |      0.88 |    2793 | 1.00       
1     | avx128_mov_sparse   | 128-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_mov_sparse   | 256-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_mov_sparse   | 512-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2794 |      0.87 |    2793 | 1.00       
1     | avx128_merge_sparse | 128-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_merge_sparse | 256-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_merge_sparse | 512-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_vshift       | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_vshift       | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_vshift       | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_vshift_t     | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 5587 |      0.88 |    2793 | 1.00       
1     | avx256_vshift_t     | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 5588 |      0.88 |    2793 | 1.00       
1     | avx512_vshift_t     | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_imul         | 128-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx256_imul         | 256-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx512_imul         | 512-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx128_fma_sparse   | 128-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_fma_sparse   | 256-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_fma_sparse   | 512-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx128_fma          | 128-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.88 |    2793 | 1.00       
1     | avx256_fma          | 256-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.87 |    2793 | 1.00       
1     | avx512_fma          | 512-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.88 |    2793 | 1.00       
1     | avx128_fma_t        | 128-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 4789 |      0.75 |    2394 | 1.00       
1     | avx256_fma_t        | 256-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 4790 |      0.75 |    2394 | 1.00       
1     | avx512_fma_t        | 512-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 2394 |      0.75 |    2394 | 1.00       
1     | avx512_vpermw       | 512-bit serial WORD permute     |  1.000 |  1.000 | 1.000  |  466 |      0.88 |    2793 | 1.00       
1     | avx512_vpermw_t     | 512-bit parallel WORD permute   |  1.000 |  1.000 | 1.000  | 1397 |      0.87 |    2793 | 1.00       
1     | avx512_vpermd       | 512-bit serial DWORD permute    |  1.000 |  1.000 | 1.000  |  931 |      0.87 |    2793 | 1.00       
1     | avx512_vpermd_t     | 512-bit parallel DWORD permute  |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       

Единственными тестами, которые выполнялись на полной скорости, были Scalar integer adds, который вообще не использует регистр SSE / AVX, и scalar ucomis (w/ vzeroupper), который имеет явное vzeroupper перед каждым тестом, поэтому не выполняется с грязными верхними строчками.

Затем я изменил инструкцию по очистке на интересующую вас инструкцию vpcmpeqb k0, zmm0, [rsp]. Новые результаты:

Cores | ID                  | Description                     | OVRLP1 | OVRLP2 | OVRLP3 | Mops | A/M-ratio | A/M-MHz | M/tsc-ratio
1     | pause_only          | pause instruction               |  1.000 |  1.000 | 1.000  | 2256 |      1.00 |    3192 | 1.00       
1     | ucomis_clean        | scalar ucomis (w/ vzeroupper)   |  1.000 |  1.000 | 1.000  |  790 |      1.00 |    3192 | 1.00       
1     | ucomis_dirty        | scalar ucomis (no vzeroupper)   |  1.000 |  1.000 | 1.000  |  790 |      1.00 |    3192 | 1.00       
1     | scalar_iadd         | Scalar integer adds             |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx128_iadd         | 128-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3190 | 1.00       
1     | avx256_iadd         | 256-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_iadd         | 512-bit integer adds            |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_iadd_t       | 128-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 9575 |      1.00 |    3192 | 1.00       
1     | avx256_iadd_t       | 256-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 9577 |      1.00 |    3192 | 1.00       
1     | avx128_mov_sparse   | 128-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_mov_sparse   | 256-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_mov_sparse   | 512-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx128_merge_sparse | 128-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_merge_sparse | 256-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_merge_sparse | 512-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx128_vshift       | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_vshift       | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_vshift       | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_vshift_t     | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 6386 |      1.00 |    3192 | 1.00       
1     | avx256_vshift_t     | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 6386 |      1.00 |    3192 | 1.00       
1     | avx512_vshift_t     | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_imul         | 128-bit integer muls            |  1.000 |  1.000 | 1.000  |  638 |      1.00 |    3192 | 1.00       
1     | avx256_imul         | 256-bit integer muls            |  1.000 |  1.000 | 1.000  |  639 |      1.00 |    3192 | 1.00       
1     | avx512_imul         | 512-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx128_fma_sparse   | 128-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_fma_sparse   | 256-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_fma_sparse   | 512-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.87 |    2793 | 1.00       
1     | avx128_fma          | 128-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  798 |      1.00 |    3192 | 1.00       
1     | avx256_fma          | 256-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  798 |      1.00 |    3192 | 1.00       
1     | avx512_fma          | 512-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.88 |    2793 | 1.00       
1     | avx128_fma_t        | 128-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 6384 |      1.00 |    3192 | 1.00       
1     | avx256_fma_t        | 256-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 5587 |      0.87 |    2793 | 1.00       
1     | avx512_fma_t        | 512-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 2394 |      0.75 |    2394 | 1.00       
1     | avx512_vpermw       | 512-bit serial WORD permute     |  1.000 |  1.000 | 1.000  |  466 |      0.87 |    2793 | 1.00       
1     | avx512_vpermw_t     | 512-bit parallel WORD permute   |  1.000 |  1.000 | 1.000  | 1397 |      0.88 |    2793 | 1.00       
1     | avx512_vpermd       | 512-bit serial DWORD permute    |  1.000 |  1.000 | 1.000  |  931 |      0.88 |    2793 | 1.00       
1     | avx512_vpermd_t     | 512-bit parallel DWORD permute  |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00     

Большинство тестов теперь выполняются на полной скорости. Те, которые все еще работают на частоте 2,8 ГГц (или в одном случае 2,4 ГГц для параллельных 512-битных FMA), - это те, которые фактически используют 512-битные векторы или используют 256-битные векторы и тяжелые инструкции FP, такие как FMA, как и ожидалось.

...