Неинтуитивные результаты производительности при перегрузке кэша, связанной с ассоциативностью кэша - PullRequest
0 голосов
/ 15 октября 2018

Я пытаюсь продемонстрировать ассоциативность кэша на процессорах Intel, обращаясь к памяти с шагом, равным степени двух.Я получаю доступ к матрице двойных значений K x N по столбцам (в C ++) и ожидаю, что при K> 8 производительность будет ухудшаться, если N - степень двух (потому что я работаю на процессорах с 8-сторонним интерфейсом).установить ассоциативный кеш L1).Поэтому я строю график доступа к памяти для K = 1,40, для N = 2 ^ 20, N = 2 ^ 20 + 8 и N = 2 ^ 20 + 64, таким образом делая заполнение строк кэша 0, 1 и 8соответственно.

#include <cstdio>
#include <cstdlib>
#include <chrono>

double buffer[((1 << 20) + 64) * 40];

void measure_flops(int N, int K) {
    // Warm-up.
    for (int j = 0; j < 10; ++j)
        for (int i = 0; i < N * K; ++i)
            buffer[i] += 1e-10 * i + 0.123 * j;

    // Iterate.
    int repeat = 500 / K;
    auto start = std::chrono::steady_clock::now();
    for (int r = 0; r < repeat; ++r)
        for (int i = 0; i < N; ++i)
            for (int j = 0; j < K; ++j)
                buffer[j * N + i] += 1.0123;
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> diff = end - start;

    volatile double tmp = buffer[rand() % (N * K)]; (void)tmp;

    // Report.
    double flops = (double)repeat * N * K / diff.count();
    printf("%d  %2d  %.4lf\n", N, K, flops * 1e-9);
    fflush(stdout);
}

void run(int N) {
    printf("      N   K  GFLOPS\n");
    for (int K = 1; K <= 40; ++K)
        measure_flops(N, K);
    printf("\n\n");
}

int main() {
    const int N = 1 << 20;

    run(N);
    run(N + 64 / sizeof(double));
    run(N + 512 / sizeof(double));

    return 0;
}

Я запускаю тест на следующих процессорах:

  • Intel Xeon E5-2680 v3 (Haswell, ассоциативность равна 8, 8, 20 для L1, L2и L3 соответственно)
  • Intel Xeon E5-2670 v3 (Haswell, ассоциативность составляет 8, 8, 20)
  • Intel Xeon Gold 6150 (Skylake, ассоциативность составляет 8, 16, 11)

... и я получаю противоречивые результаты и некоторые формы, которые я не понимаю.

enter image description here

Мои вопросы:

1) Почему производительность не восстановилась для заполнения = 64 B для E5-2680, если для E5-2670?Производительность восстанавливается для заполнения = 512 B, но почему?(Возможно ли, что prefetcher удаляет кэш?)

EDIT: протестировано также на E5-2690 и E5-2650, на другом кластере.E5-2690 ведет себя как E5-2680 (плохая производительность для заполнения = 64 B), в то время как E5-2650 ведет себя как E5-2670 (хорошая производительность).Таким образом, кажется, что между 2670 и 2680 действительно что-то изменилось.

2) Почему производительность падает так рано, до того, как К достигает 8?(Я подозреваю, что это «фиктивная стойка переадресации магазина», как объяснено в несколько иной конфигурации здесь , но я не уверен.)

3) Почему тогда снова падает производительностьдля K = ~ 32 для E5-2680 и для K = ~ 35 для золота 6150?(perf stat -ddd показывает увеличенное количество пропусков кэша L3, но я не вижу точной причины, потому что K не соответствует ассоциативности L3.)


Подробности настройки:

E5-2680 и Gold 6150 работают в кластере, где я использую полный узел, чтобы убедиться, что нет помех другим пользователям.Узел содержит 2 ЦП в обоих случаях, но я в любом случае использую только одно ядро.E5-2670 находится на общей машине с одним ЦП (не может получить полный узел), но запустил тест, когда он был неактивен.

Я скомпилировал код с g++ -O1 -std=c++11 (версия 6.3.0 для E5-2680 и Gold, версия 7.3.0 для E5-2670).Все результаты воспроизводимы.Изменение компилятора (или, например, добавление -O3 или добавление volatile к buffer) изменяет более или менее только результаты для низкого K, например, K = 2 становится быстрее / медленнее.

...