На KbL i7-8550U
он ведет себя так, как будто нет частичных обращений на строку кэша, а не 32-байтовую область .
На самом деле я провел гораздо больше экспериментов, чем описано ниже, но это здесь невозможно разместить их все.
В Руководстве по оптимизации Intel указано, что кэш-память UOP включает L1i:
Декодированный ICache фактически включен в кэш инструкций и ITLB .
Рассмотрим следующее
Пример 1.
;edi = 1 << 31
align 32
test_uop_cache_hit:
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
;More 8 * nop ax blocks
dec edi
jnz test_uop_cache_hit
ret
Сбор счетчиков icache_64b.iftag_hit
, idq.dsb_uops
, idq.mite_uops
у нас есть следующий сюжет
![enter image description here](https://i.stack.imgur.com/JEOHc.png)
Сюжет Uops является разумным. Все мопы доставлены из DSB.
Первый график показывает, что для каждой строки кэша L1i имеется только один поиск тега, размер которого составляет 64 байта. Поиск тега необходим для того, чтобы найти запись в кэше UOP.
Пример 2.
Добавление jmp в середину 8 * nop ax
блоков в одной строке кэша.
;edi = 1 << 31
align 64
test_uop_cache_hit:
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
jmp test_uop_cache_hit_1
align 32
test_uop_cache_hit_1:
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
nop ax
dec edi
jnz test_uop_cache_hit
ret
Мы имеем следующие сюжеты:
![enter image description here](https://i.stack.imgur.com/xuNAS.png)
Удельный график снова оправдан. Из icache_64b.iftag_hit
я пришел к выводу, что предсказанные ветвления вызывают поиск тега li1, чтобы найти соответствующую запись в кэше uop (даже если источник и цель ветвления принадлежат одной и той же строке). С этим наблюдением Intel Optimization Manual/2.5.5.2
После доставки микроопераций из устаревшего конвейера выборка микроопераций из декодированного ICache может возобновиться только после следующей микрооперации ветвления.
выглядит довольно разумно для меня.
Теперь рассмотрим немного более интересный
Пример 3.
Я буду использовать псевдо-сборку код для экономии места
align 64
test_uop_cache_hit:
8 * nop ax
19 * nop
jmp test_uop_cache_hit_1
align 32:
test_uop_cache_hit_1: ;new line starts here
;more 8 * nop ax 19 * nop jmp blocks
dec edi
jnz test_uop_cache_hit
ret
У нас есть следующие результаты
![enter image description here](https://i.stack.imgur.com/0twTI.png)
Что интересно здесь даже несмотря на то, что микрооперация ветвления уже установлена и 8 * nop ax
идеально подходит для кэша UOP, они не доставляются из кэша UOP . Как видно на графиках , единственная микрооперация, доставленная из кэша UOP, была слитой макросом dec-jnz
.
В результате я понял, что если какая-то 32-байтовая область не В случае установки UOP-кэша вся строка кэша помечается как не содержащаяся в UOP-кэше, и в следующий раз, когда любая 32-байтовая его часть запрашивается, она будет доставлена из Legacy Decoded Pipeline.
Является ли ветвь микро Нужно ли переходить с Legacy Decoded Pipeline? Для проверки рассмотрим
Пример 4.
align 32
test_uop_cache_hit:
32 * nop
test_uop_cache_hit_0: ;new line start here
16 * nop ax
;more 16 * nop ax
dec edi ;new line start here
jnz test_uop_cache_hit
ret
Вот результат для DSB
![enter image description here](https://i.stack.imgur.com/jxKdW.png)
Ясно, что все мопы были доставлены из унаследованного декодированного конвейера.
Рассмотрим несколько более сложные примеры, чтобы проверить, работает ли предположение, сделанное в Example 3.
:
I.
align 32
test_uop_cache_hit:
6 * nop ax
test edi, 0x1
;ends 64 byte region, misses due to erratum
;does not matter for the example
jnz test_uop_cache_hit_1
32 * nop
test_uop_cache_hit_1:
dec edi
jnz test_uop_cache_hit
ret
Результаты
1 075 981 881 idq.dsb_uops
50 341 922 587 idq.mite_uops
Результаты вполне разумны. Когда ветвь не занята и 32 * nop
доставлены, становится ясно, что они не могут поместиться в кэш uop. После 32 * nop
макрос слитый dec-jnz
доставляется из Legacy Decode Pipeline. Он соответствует кешу uop и, следовательно, в следующий раз, когда ветка будет занята, он будет доставлен из DSB.
Результат очень близок к ожидаемому: (1 << 31)/2 = 1073741824
II.
Более сложный пример, чем предыдущий
align 32
test_uop_cache_hit:
test edi, 0x1
jnz test_uop_cache_hit_2
jmp test_uop_cache_hit_1
;starts new cache line
align 32
test_uop_cache_hit_1:
8 * nop ax
; 32 byte aligned
test_uop_cache_hit_2:
6 * nop ax
nop dword [eax + 1 * eax + 0x1]
;End of 32 bytes region
;misses due to erratum
;Important here
jmp test_uop_cache_hit_3
test_uop_cache_hit_3:
dec edi
jnz test_uop_cache_hit
ret
Вот результат:
5 385 033 285 idq.dsb_uops
25 815 684 426 idq.mite_uops
Результат ожидаемый. Каждый раз, когда берется dec edi - jnz test_uop_cache_hit_2
, он переходит в 32-байтовую область, содержащую jmp
в конце. Так что будет скучать по DSB. В следующий раз dec edi - jnz test_uop_cache_hit_2
не берется jmp test_uop_cache_hit_1
берется. Обычно он попадает в DSB, поскольку 8 * nop ax
подходит ему идеально, но помните, что на предыдущей итерации l oop jmp
в конце 32-байтовой области вызывал промах. Они оба принадлежат одной и той же строке кэша, и поэтому промах dsb происходит на каждой итерации.
Результат близок к ожидаемому: (1 << 31) + (1 << 31)/2 + (1 << 31) = 5368709120
.
Удаление только одного nop ax
из 32-байтовой области с jmp
на конец при сохранении test_uop_cache_hit_3
32-байтовое выравнивание приводит к доставке всех мопов из dsb:
29 081 868 658 idq.dsb_uops
8 887 726 idq.mite_uops
Примечание: Если есть 2 ветки, которые, как ожидается, будут взяты на кэш В итоге результаты оказались весьма непредсказуемыми, поэтому сложно дать разумную оценку. Мне не понятно почему.