Как бы вы в общем случае определяли ассоциативность строки кэша из кода режима пользователя?
Я собираю небольшой патч для инструмент cachegrind/callgrind в valgrind, который будет автоматически обнаруживать, используя полностью общий код, CPU команды и конфигурации кэша (сейчас только автоматические настройки x86/x64 и другие архитектуры не обеспечивают конфигурацию типа CPUID для не-привилегированного кода). Этот код должен выполняться целиком в непривилегированном контексте, то есть в коде режима чистого пользователя. Он также должен быть переносимым в самых разных реализациях POSIX, поэтому grokking/proc/cpuinfo не будет работать, поскольку одна из наших систем назначения не имеет такой вещи.
Обнаружение частоты процессора, количества кешей, их размеров и даже размера строки кеша может быть выполнено с использованием 100% общего кода POSIX, который не имеет кодов операций, специфичных для процессора (просто много разумных предположений, таких как так как это добавление двух чисел вместе, если без задержек с памятью или регистром, возможно, будет выполнено за один цикл). Эта часть довольно проста.
Что не так просто, и почему я спрашиваю StackOverflow, как определить ассоциативность кеш-строк для заданного кеша? Ассоциативность - это то, сколько мест в кеше может содержать заданную строку кэша из основной памяти. Я вижу, что ассоциативность L1 кэша может быть обнаружена, но кеш L2? Несомненно, ассоциативность L1 мешает?
Я понимаю, что это, вероятно, проблема, которая не может быть решена. Но я бросаю его в StackOverflow и надеюсь, что кто-то знает что-то, чего у меня нет. Обратите внимание, что если мы потерпим неудачу здесь, я просто буду жестко закодировать по умолчанию по умолчанию, если предположить, что это не будет иметь большого значения для результатов.
Спасибо,
Найл
Ответы
Ответ 1
Здесь схема:
У вас есть шаблон доступа к памяти с шагом S, а число уникальных элементов доступно = N. Тест сначала касается каждого уникального элемента, а затем измеряет среднее время доступа к каждому элементу, путем обращения к одному и тому же шаблону число очень большое.
Пример: для S = 2 и N = 4 шаблон адреса будет 0,2,4,6,0,2,4,6,0,2,4,6,...
Рассмотрим иерархию многоуровневого кэша. Вы можете сделать следующие разумные предположения:
- Размер n + 1-го уровня-кеша - это мощность в два раза больше n-го кеша
- Ассоциативность n + 1-го кэша также является мощью, в два раза превышающей ассоциативность n-го кэша.
Эти 2 предположения позволяют нам сказать, что если два адреса сопоставляются с одним и тем же множеством в n + 1-м кэше (скажем, L2), то они должны сопоставляться с тем же множеством в n-м кеше (скажем, L1).
Скажите, что вы знаете размеры кешей L1, L2. Вам нужно найти ассоциативность кэша L2.
- установить stride S= размер кеша L2 (чтобы каждый доступ сопоставлялся с тем же множеством в L2 и в L1)
- меняет N (по степеням 2)
Вы получаете следующие режимы:
- Режим 1: N <= ассоциативность L1. (Все обращения к HIT в L1)
- Тип 2: ассоциативности L1 < N <= ассоциативность L2 (все получают доступ к пропуску в L1, но HIT в L2)
- Режим 3: N > ассоциативность L2 (все доступ к пропуску в L2)
Итак, если вы планируете среднее время доступа к N (когда S = размер L2), вы увидите шаг-подобный сюжет. Конец нижнего шага дает вам ассоциативность L1. Следующий шаг дает вам ассоциативность L2.
Вы можете повторить ту же процедуру между L2-L3 и так далее. Пожалуйста, дайте мне знать, если это поможет. Способ получения параметров кэша путем изменения шага шаблона доступа к памяти аналогичен методу, используемому эталоном LMBENCH. Я не знаю, относится ли lmbench к ассоциативности.
Ответ 2
Не могли бы вы сделать небольшую программу, которая только обращается к строкам из одного и того же набора? Затем вы можете увеличить расстояние стека между доступом и когда время выполнения резко падает, вы можете предположить, что вы достигли ассоциативности.
Вероятно, он не очень стабилен, но, возможно, это может привести к лидерству, не знаю. Надеюсь, это поможет.
Ответ 3
Для платформы x86 вы можете использовать cpuid
:
Подробнее см. http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html.
Вам нужно что-то вроде:
long _eax,_ebx,_ecx,_edx;
long op = func;
asm ("cpuid"
: "=a" (_eax),
"=b" (_ebx),
"=c" (_ecx),
"=d" (_edx)
: "a" (op)
);
Затем используйте информацию в соответствии с документом в ссылке, упомянутой выше.