Низкая производительность с CLWB (обратная запись кэширования) в одно и то же место по сравнению с циклическим перебором в несколько строк - PullRequest
4 голосов
/ 12 октября 2019

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

На каждой итерации код изменяет одну из kNumCacheLines строк кэша, записывает строку в DIMM с clwb инструкция, и блокирует, пока хранилище не попадет в контроллер памяти с sfence. В этом примере требуются серверные процессоры Intel Skylake или более новые клиентские процессоры Xeon или IceLake.

#include <stdlib.h>
#include <stdint.h>

#define clwb(addr) \
  asm volatile(".byte 0x66; xsaveopt %0" : "+m"(*(volatile char *)(addr)));

static constexpr size_t kNumCacheLines = 1;

int main() {
  uint8_t *buf = new uint8_t[kNumCacheLines * 64];
  size_t data = 0;
  for (size_t i = 0; i < 10000000; i++) {
    size_t buf_offset = (i % kNumCacheLines) * 64;
    buf[buf_offset] = data++;
    clwb(&buf[buf_offset]);
    asm volatile("sfence" ::: "memory");
  }

  delete [] buf;
}

(примечание редактора: _mm_sfence() и _mm_clwb(void*) позволяют избежать использования встроенного asm, но этот встроенный asm выглядит корректно, включая"memory" clobber).

Вот некоторые показатели производительности на моем аппарате Skylake Xeon, о которых сообщается при запуске time ./bench с различными значениями kNumCacheLines:

kNumCacheLines  Time (seconds)
1               2.00
2               2.14
3               1.74
4               1.82
5               1.00
6               1.17
7               1.04
8               1.06

Интуитивно яможно ожидать, что kNumCacheLines = 1 даст лучшую производительность из-за попаданий в очередь ожидания записи контроллера памяти. Но это один из самых медленных.

В качестве объяснения неинтуитивного замедления, возможно, что, пока контроллер памяти завершает запись в строку кэша, он блокирует другие записи в той же строке кэша. Я подозреваю, что увеличение kNumCacheLines увеличивает производительность из-за более высокого параллелизма, доступного контроллеру памяти. Время работы увеличивается с 1,82 до 1,00 секунды, когда kNumCacheLines изменяется с четырех до пяти. Похоже, это коррелирует с тем фактом, что очередь ожидающих записи контроллера памяти имеет место для 256 байтов из потока [https://arxiv.org/pdf/1908.03583.pdf, Section 5.3].

Обратите внимание, что, поскольку buf меньше 4 КБвсе доступы используют один и тот же модуль DIMM. (Предполагается, что он выровнен и не пересекает границы страницы)

...