Как бы вы в общем случае определяли ассоциативность строки кэша из кода режима пользователя?

Я собираю небольшой патч для инструмент 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)
);

Затем используйте информацию в соответствии с документом в ссылке, упомянутой выше.