Этот цикл работает на одной итерации в течение 3 циклов на Intel Conroe/Merom, с узким местом по пропускной способности imul, как ожидалось. Но на Haswell/Skylake он работает на одной итерации за 11 циклов, по-видимому, потому, что setnz al имеет зависимость от последнего imul.
; synthetic micro-benchmark to test partial-register renaming
    mov     ecx, 1000000000
.loop:                 ; do{
    imul    eax, eax     ; a dep chain with high latency but also high throughput
    imul    eax, eax
    imul    eax, eax
    dec     ecx          ; set ZF, independent of old ZF.  (Use sub ecx,1 on Silvermont/KNL or P4)
    setnz   al           ; ****** Does this depend on RAX as well as ZF?
    movzx   eax, al
    jnz  .loop         ; }while(ecx);
Если setnz al зависит от rax, последовательность 3ximul/setcc/movzx формирует цепочку зависимостей, связанных с циклом. Если нет, каждая цепочка setcc/movzx/3x imul независима, отбрасывается из dec, которая обновляет счетчик циклов. 11c на итерацию, измеренную на HSW/SKL, отлично объясняется узким местом задержки: 3x3c (imul) + 1c (read-modify-write by setcc) + 1c (movzx в одном регистре).
Отключить тему: избегать этих (преднамеренных) узких мест
Я собирался понять понятное/предсказуемое поведение, чтобы изолировать материал с частичным реестром, а не оптимальную производительность.
Например, xor -zero/set-flags/ setcc лучше в любом случае (в этом случае xor eax,eax/dec ecx/setnz al). Это разбивает dep на eax на всех процессорах (кроме раннего семейства P6, таких как PII и PIII), по-прежнему избегает слияния с частичным регистром и сохраняет 1c задержки movzx. Он также использует еще один ALU uop для процессоров, которые обрабатывают xor-zeroing в стадии переименования регистра. См. Эту ссылку для получения дополнительной информации об использовании xor-zeroing с помощью setcc.
Обратите внимание, что AMD, Intel Silvermont/KNL и P4 вообще не выполняют переименование частичных регистров. Это только функция процессоров семейства Intel P6 и ее потомок семейства Intel Sandybridge, но, похоже, постепенно прекращается.
gcc, к сожалению, имеет тенденцию использовать cmp/setcc al/movzx eax,al, где он мог бы использовать xor вместо movzx (Godbolt пример компилятора-проводника), в то время как clang использует xor-zero/cmp/setcc, если вы не объединяете несколько логических условий, таких как count += (a==b) | (a==~b).
Версия xor/dec/setnz работает на уровне 3.0c за итерацию на Skylake, Haswell и Core2 (узкое место при пропускной способности imul). xor -zeroing нарушает зависимость от старого значения eax от всех процессоров вне порядка, отличных от PPro/PII/PIII/early-Pentium-M (где он по-прежнему избегает штрафов за частичную регистрацию, но не "t сломать dep).  Руководство по микроаргуту Agner Fog описывает это. Замена xor-zeroing на mov eax,0 замедляет его до одного на 4.78 циклов на Core2: 2-3c stall (в интерфейсе?) Для вставки слияния с частичным списанием, когда imul читает eax после setnz al.
Кроме того, я использовал movzx eax, al, который побеждает исключение mov, как это делает mov rax,rax. (IvB, HSW и SKL могут переименовать movzx eax, bl с 0 латентностью, но Core2 не может). Это делает все равным по Core2/SKL, за исключением поведения с частичным регистром.
Поведение Core2 совместимо с Agar Fog microarch guide, но поведение HSW/SKL - нет. Из раздела 11.10 для Skylake, а также для предыдущих Intel uarches:
Различные части регистра общего назначения могут храниться в разных временных регистрах, чтобы удалить ложные зависимости.
Он, к сожалению, не успевает провести детальное тестирование для каждого нового uarch, чтобы перепроверить предположения, поэтому это изменение в поведении проскользнуло через трещины.
Агнер описывает, как встраиваемый uop вставлен (без остановки) для регистров high8 (AH/BH/CH/DH) на Sandybridge через Skylake, а для low8/low16 - на SnB. (Я, к сожалению, распространял неверную информацию в прошлом и говорил, что Хасуэлл может объединить AH бесплатно. Я быстро снял раздел Агнера Хасуэлла и не заметил более поздний абзац о регистрах high8. Сообщите мне, видите ли вы мои неправильные комментарии к другим сообщениям, поэтому я могу удалить их или добавить исправление. Попытаюсь хотя бы найти и отредактировать свои ответы, где я это сказал.)
Мои актуальные вопросы: Как точно ведут себя частичные регистры на Skylake?
Все ли одно и то же от IvyBridge до Skylake, включая дополнительную задержку high8?
Руководство по оптимизации Intel не является специфическим, о том, какие ЦП имеют ложные зависимости для чего (хотя он упоминает, что у некоторых ЦП есть), и оставляет такие как чтение AH/BH/CH/DH (регистры high8), добавляющие дополнительную задержку, даже если они не были изменены.
Если есть какое-либо поведение P6-семейства (Core2/Nehalem), которое руководство по микроархиву Agner Fog не описывает, это тоже было бы интересно, но я, вероятно, должен ограничивать сферу применения этого вопроса только для семейства Skylake или Sandybridge.
  Мои данные теста Skylake, от размещения коротких последовательностей %rep 4 внутри небольшого цикла dec ebp/jnz, который запускает итерации 100M или 1G. Я измерил циклы с Linux perf так же, как в моем ответе здесь, на том же оборудовании (рабочий стол Skylake i7 6700k).
Если не указано иное, каждая команда выполняется как 1 uop с плавным доменом, используя порт выполнения ALU. (Измерено с помощью ocperf.py stat -e ...,uops_issued.any,uops_executed.thread). Это определяет (отсутствие) отмену mov и добавление дополнительных сходов.
Случаи "4 за цикл" - это экстраполяция в бесконечно разведенный случай. Накладные расходы Loop занимают часть пропускной способности интерфейса, но все, что лучше, чем 1 за цикл, является признаком того, что переименование регистра избегало зависимости от записи после записи, и что uop не обрабатывается внутренне как чтение-изменение-запись.
  Запись только в AH: предотвращает выполнение цикла из буфера loopback (также известный как Loop Stream Detector (LSD)). Подсчеты для lsd.uops равны 0 на HSW и крошечные на SKL (около 1,8 тыс.) И не масштабируются с учетом итерации цикла. Вероятно, эти подсчеты взяты из некоторого кода ядра. Когда петли выполняются из LSD, lsd.uops ~= uops_issued с точностью до шума измерения. Некоторые циклы чередуются между LSD или no-LSD (например, когда они могут не вписываться в кеш uop, если декодирование начинается не в том месте), но я не сталкивался с этим при тестировании этого.
-  повторный mov ah, bhи/илиmov ah, blработает с 4 за цикл. Он принимает ALU uop, поэтому он не исключается, какmov eax, ebx.
-  repeat mov ah, [rsi]работает с 2 за цикл (узкое место в пропускной способности).
-  repeat mov ah, 123работает с 1 за цикл. (A dep-breakxor eax,eaxвнутри цикла удаляет узкое место.)
-  повторение setz ahилиsetc ahвыполняется с 1 за цикл. (Отрывная разбивкаxor eax,eaxпозволяет ей узкое место на пропускной способности p06 дляsetccи ветки цикла.)Почему запись ahс инструкцией, которая обычно использует блок выполнения ALU, имеет ложную зависимость от старого значения, тогда какmov r8, r/m8не работает (для reg или memory src)? (А как насчетmov r/m8, r8? Конечно, неважно, какой из двух кодов операций, которые вы используете для регр-рег?)
-  Повторяется add ah, 123работает на 1 за цикл, как и ожидалось.
-  repeat add dh, clработает с 1 за цикл.
-  repeat add dh, dhработает с 1 за цикл.
-  повторный add dh, chработает на 0,5 за цикл. Чтение [ABCD] H является особенным, когда они "чисты" (в этом случае RCX совсем недавно не изменен).
  Терминология: все они оставляют AH (или DH) " грязными", т.е. нуждаются в слиянии (с объединением uop), когда остальная часть регистра читать (или в некоторых других случаях). то есть, что AH переименовывается отдельно от RAX, если я правильно понимаю это. " чистый" - это наоборот. Существует много способов очистки грязного регистра, самым простым из которых является inc eax или mov eax, esi.
  Запись только в AL: эти циклы выполняются из LSD: uops_issue.any ~ = lsd.uops.
-  repeat mov al, blработает с 1 за цикл. Случайная дефрагментацияxor eax,eaxна группу позволяет узкому месту выполнения программы на пропускную способность uop, а не задержка.
-  repeat mov al, [rsi]работает с 1 за цикл, в качестве микровыплавленного ALU + load uop. (uops_issued = 4G + накладные расходы цикла, uops_executed = 8G + накладные расходы цикла). Отрывная разбивкаxor eax,eaxперед группой из 4 препятствует ей узкое место при 2 нагрузках за такт.
-  repeat mov al, 123работает с 1 за цикл.
-  repeat mov al, bhработает со скоростью 0,5 за цикл. (1 на 2 цикла). Чтение [ABCD] H является особенным.
-  xor eax,eax+ 6xmov al,bh+dec ebp/jnz: 2c за истребитель, узкое место на 4 часа в час для интерфейса.
-  repeat add dl, chработает со скоростью 0,5 за цикл. (1 на 2 цикла). Чтение [ABCD] H, по-видимому, создает дополнительную задержку дляdl.
-  repeat add dl, clработает от 1 за цикл.
Я думаю, что запись в low-8 reg ведет себя как RMW-смесь в полный рег, например, add eax, 123 будет, но не вызывает слияние, если ah грязно. Таким образом (кроме игнорирования слияния ah) он ведет себя так же, как и на процессорах, которые вообще не переименовывают частичное переименование. Кажется, что AL никогда не переименовывается отдельно от rax?
-  inc al/inc ahпары могут работать параллельно.
-  mov ecx, eaxвставляет слияние uop, еслиah"грязный", но фактическийmovпереименован. Это то, что Agner Fog описывает для IvyBridge и позже.
-  Повторяется movzx eax, ahвыполняется один раз в 2 цикла. (Чтение регистров высокого уровня после записи полных регистров имеет дополнительную задержку.)
-  movzx ecx, alимеет нулевую задержку и не принимает порт выполнения на HSW и SKL. (Как то, что описывает Agner Fog для IvyBridge, но он говорит, что HSW не переименовывает movzx).
-  movzx ecx, clимеет задержку 1 с и принимает порт выполнения. (mov-elim никогда не работает для случаяsame,same, только между различными архитектурными регистрами.)Цикл, который вставляет слияние uop, каждая итерация не может выполняться из LSD (буфер цикла)? 
Я не думаю, что в AL/AH/RAX есть что-то особенное против B *, C *, DL/DH/RDX. Я тестировал некоторые частичные регистры в других регистрах (хотя я обычно показываю AL/ah для согласованности) и никогда не замечал никакой разницы.
Как мы можем объяснить все эти наблюдения разумной моделью того, как микроарх работает внутри?
Связано: проблемы с Partial  отличаются от частичных  register проблем. См. Инструкция INC против ADD 1: имеет ли это значение? для некоторых супер-странных вещей с shr r32,cl (и даже shr r32,2 на Core2/Nehalem: не читайте флаги смены, отличные от 1).
См. также Проблемы с ADC/SBB и INC/DEC в жестких циклах на некоторых процессорах для элементов с частичным флагом в циклах adc.
