Cycles/cost для L1 Cache hit vs. Регистрация на x86?

Я помню, полагая, что кэш L1-кеша - это 1 цикл (то есть идентичный времени доступа к регистру) в моем классе архитектуры, но действительно ли это на современных процессорах x86?

Сколько циклов занимает луч L1? Как это сравнить с доступом к регистру?

Ответ 1

Вот отличная статья на эту тему:

http://arstechnica.com/gadgets/reviews/2002/07/caching.ars/1

Чтобы ответить на ваш вопрос - да, кэш-кеш имеет примерно такую ​​же стоимость, как и доступ к регистру. И, конечно, пропустить кеш довольно дорого;)

PS:

Специфика будет отличаться, но эта ссылка содержит несколько хороших цифр:

Приблизительная стоимость доступа к различным кешам и основной памяти?

Core i7 Xeon 5500 Series Data Source Latency (approximate)
L1 CACHE hit, ~4 cycles
L2 CACHE hit, ~10 cycles
L3 CACHE hit, line unshared ~40 cycles
L3 CACHE hit, shared line in another core ~65 cycles
L3 CACHE hit, modified in another core ~75 cycles remote
L3 CACHE ~100-300 cycles
Local DRAM ~30 ns (~120 cycles)
Remote DRAM ~100 ns 

PPS:

Эти цифры представляют собой гораздо более старые, более медленные процессоры, но в основном отношения:

http://arstechnica.com/gadgets/reviews/2002/07/caching.ars/2

Level                    Access Time  Typical Size  Technology    Managed By
-----                    -----------  ------------  ---------     -----------
Registers                1-3 ns       ?1 KB          Custom CMOS  Compiler
Level 1 Cache (on-chip)  2-8 ns       8 KB-128 KB    SRAM         Hardware
Level 2 Cache (off-chip) 5-12 ns      0.5 MB - 8 MB  SRAM         Hardware
Main Memory              10-60 ns     64 MB - 1 GB   DRAM         Operating System
Hard Disk                3M - 10M ns  20 - 100 GB    Magnetic     Operating System/User

Ответ 2

Нет.

Задержка кеша в одном цикле раньше использовалась в простых конвейерах упорядочения при более низких тактовых частотах (поэтому каждый цикл составлял больше наносекунд), особенно с более простыми кэшами (меньшими, не ассоциативными, и с меньшим TLB для кешей, которые не были чисто виртуально адресовано.) Например, классический 5-этапный конвейер RISC, такой как MIPS I, предполагает 1 цикл для доступа к памяти при попадании в кэш, с вычислением адреса в EX и доступом к памяти в MEM, до этапа WB.

Современные высокопроизводительные процессоры делят конвейер на несколько этапов, что позволяет сократить каждый цикл. Это позволяет простым инструкциям, таким как add/or/and работать очень быстро, с задержкой в 1 цикл, но с высокой тактовой частотой.


Для получения дополнительной информации о подсчете циклов и выполнении вне порядка см. Pdf файл Agner Fog microarch и другие ссылки в вики-теге x86.


Время ожидания загрузки Intel Haswell L1 составляет 4 такта для отслеживания указателя, что типично для современных процессоров x86. т.е. как быстро mov eax, [eax] может выполняться в цикле с указателем, который указывает на себя. (Или для связанного списка, который попадает в кеш, легко микробенч с замкнутым циклом). См. Также Есть ли штраф, если база + смещение находятся на странице, отличной от базы? Этот особый случай задержки с 4 циклами применяется только в том случае, если указатель поступает непосредственно от другой нагрузки, в противном случае он равен 5 циклам.

Задержка использования нагрузки на 1 цикл выше для векторов SSE/AVX в процессорах Intel.


Задержка перезагрузки хранилища составляет 5 циклов и не связана с попаданием или пропуском кэша (это переадресация магазина, чтение из буфера хранилища для данных хранилища, которые еще не зафиксированы в кэше L1d).

Как прокомментировал Гарольд, доступ к регистру равен 0 циклам. Так, например:

  • inc eax имеет задержку в 1 цикл (только операция ALU)
  • add dword [mem], 1 имеет 6 циклов задержки, пока не будет готова загрузка из dword [mem]. (АЛУ + пересылка магазина). Например, хранение счетчика цикла в памяти ограничивает цикл одной итерацией на 6 циклов.
  • mov rax, [rsi] имеет 4 цикла задержки от готовности rsi до готовности rax к удару L1 (задержка использования нагрузки L1.)

http://www.7-cpu.com/cpu/Haswell.html содержит таблицу задержек на кэш (которую я скопирую здесь) и некоторые другие экспериментальные числа, включая задержку попадания L2-TLB (при промахе L1DTLB).

Intel i7-4770 (Haswell), 3,4 ГГц (Turbo Boost off), 22 нм. Оперативная память: 32 ГБ (PC3-12800 cl11 cr2).

  • Кэш данных L1 = 32 КБ, 64 B/line, 8-WAY.
  • Кэш инструкций L1 = 32 КБ, 64 B/line, 8-WAY.
  • Кэш-память второго уровня = 256 КБ, 64 B/line, 8-WAY
  • Кэш-память L3 = 8 МБ, 64 B/строка

  • L1 Data Cache Latency = 4 цикла для простого доступа через указатель (mov rax, [rax])

  • L1 Data Cache Latency = 5 циклов для доступа с вычислением сложного адреса (mov rax, [rsi + rax*8]).
  • Задержка кэша L2 = 12 циклов
  • Задержка кэша L3 = 36 циклов
  • Задержка ОЗУ = 36 циклов + 57 нс

Страница бенчмарка верхнего уровня - http://www.7-cpu.com/utils.html, но на самом деле не объясняет, что означают различные размеры тестов, но код доступен. Результаты теста включают Skylake, который почти такой же, как у Haswell в этом тесте.

Ответ @paulsm4 содержит таблицу для нескольких сокетов Nehalem Xeon, включая некоторые номера удаленной (другой сокет) памяти /L3.

Ответ 3

Если я правильно помню это примерно за 1-2 такта, но это оценка и новые кеши могут быть быстрее. Это из книги "Компьютерная архитектура" у меня есть, и это информация для AMD, поэтому Intel может немного отличаться, но я бы связал ее между 5 и 15 тактами, что мне кажется хорошей оценкой.

EDIT: Whoops L2 - 10 циклов с доступом TAG, L1 занимает от 1 до 2 циклов, моя ошибка:\

Ответ 4

Фактически, стоимость попадания кеша L1 почти такая же, как и стоимость доступа к регистру. Для меня это было удивительно, но это правда, по крайней мере для моего процессора (Athlon 64). Некоторое время назад я написал простое тестовое приложение для проверки эффективности доступа к общим данным в многопроцессорной системе. Тело приложения является простой переменной памяти, увеличивающейся в течение предопределенного периода времени. Чтобы сделать comapison, я сначала проверил не разделяемую переменную. И во время этого действия я захватил результат, но затем во время демонтажа приложения я обнаружил, что компилятор был обманут мои ожидания и применил нежелательную оптимизацию к моему коду. Он просто поместил переменную в регистр CPU и увеличил ее итеративно в регистре без доступа к памяти. Но реальный сюрприз был достигнут после того, как я заставил компилятор использовать переменную in-memory вместо переменной register. В обновленном приложении я достиг почти тех же результатов бенчмаркинга. Снижение производительности было действительно незначительным (~ 1-2%) и похоже на некоторый побочный эффект.

В результате:

1) Я думаю, что вы можете рассматривать кеш L1 как неуправляемый пул регистров процессора.

2) Нет никакого смысла применять грубую оптимизацию, заставляя хранилище компилятора часто получать данные в регистрах процессоров. Если они действительно доступны, они будут жить в кеше L1, и из-за этого будут иметь такую ​​же стоимость доступа, как и регистр процессора.