Прочитав следующий документ https://people.freebsd.org/~lstewart/articles/cpumemory.pdf («Что должен знать каждый программист о памяти») Я хотел попробовать один из авторских тестов, то есть измерить влияние TLB на окончательное время выполнения.
Я работаю над Samsung Galaxy S3, который включает Cortex-A9.
По документации:
у нас есть два микро TLB для кеширования инструкций и данных в L1 (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0388e/Chddiifa.html)
Главный TLB находится в L2 (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0388e/Chddiifa.html)
Микро TLB данных имеет 32 записи (инструкция micro TLB имеет 32 или 64 записи)
- L1 'size == 32 Кбайт
- Строка кэша L1 == 32 байта
- L2 'размер == 1 МБ
Я написал небольшую программу, которая выделяет массив структур с N записями. Размер каждой записи == 32 байта, поэтому она умещается в строке кэша. Я выполняю несколько обращений для чтения и измеряю время выполнения.
typedef struct {
int elmt; // sizeof(int) == 4 bytes
char padding[28]; // 4 + 28 = 32B == cache line size
}entry;
volatile entry ** entries = NULL;
//Allocate memory and init to 0
entries = calloc(NB_ENTRIES, sizeof(entry *));
if(entries == NULL) perror("calloc failed"); exit(1);
for(i = 0; i < NB_ENTRIES; i++)
{
entries[i] = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if(entries[i] == MAP_FAILED) perror("mmap failed"); exit(1);
}
entries[LAST_ELEMENT]->elmt = -1
//Randomly access and init with random values
n = -1;
i = 0;
while(++n < NB_ENTRIES -1)
{
//init with random value
entries[i]->elmt = rand() % NB_ENTRIES;
//loop till we reach the last element
while(entries[entries[i]->elmt]->elmt != -1)
{
entries[i]->elmt++;
if(entries[i]->elmt == NB_ENTRIES)
entries[i]->elmt = 0;
}
i = entries[i]->elmt;
}
gettimeofday(&tStart, NULL);
for(i = 0; i < NB_LOOPS; i++)
{
j = 0;
while(j != -1)
{
j = entries[j]->elmt
}
}
gettimeofday(&tEnd, NULL);
time = (tEnd.tv_sec - tStart.tv_sec);
time *= 1000000;
time += tEnd.tv_usec - tStart.tv_usec;
time *= 100000
time /= (NB_ENTRIES * NBLOOPS);
fprintf(stdout, "%d %3lld.%02lld\n", NB_ENTRIES, time / 100, time % 100);
У меня есть внешний цикл, который изменяет NB_ENTRIES от 4 до 1024.
Как видно на рисунке ниже, пока NB_ENTRIES == 256 записей, время выполнения больше.
Когда NB_ENTRIES == 404 появляется сообщение «Недостаточно памяти» (почему? Превышены микро TLB? Превышены основные TLB? Превышены таблицы страниц? Превышена виртуальная память для процесса?)
Может кто-нибудь объяснить мне, пожалуйста, что на самом деле происходит от 4 до 256 записей, а затем от 257 до 404 записей?
ИЗМЕНИТЬ 1
Как было предложено, я выполнил мембенч (src code) и ниже результаты:
ИЗМЕНИТЬ 2
В следующей статье (стр. 3) они были выполнены (я полагаю ) тот же тест. Но разные шаги хорошо видны по их сюжетам, что не в моем случае.
Прямо сейчас, по их результатам и объяснениям, я могу выделить лишь несколько вещей.
- графики подтверждают, что размер строки кэша L1 составляет 32 байта, потому что, как они сказали
«как только размер массива превышает размер кэша данных (32 КБ), чтения начинают генерировать промахи [...] точка перегиба возникает, когда каждое чтение генерирует промахи».
В моем случае самая первая точка перегиба появляется, когда шаг == 32 байта. - График показывает, что у нас кеш второго уровня (L2). Я думаю, что это изображено желтой линией (1 МБ == размер L2) - поэтому два последних графика над последним, вероятно, отражают задержку при доступе к основной памяти (+ TLB?).
Однако из этого теста я не могу идентифицировать:
- ассоциативность кеша. Обычно D-Cache и I-Cache являются четырехсторонними ассоциативными (Cortex-A9 TRM).
- Эффекты TLB. Как они сказали,
в большинстве систем вторичное увеличение задержки указывает на TLB, который кэширует ограниченное количество виртуальных преобразований в физические. [..] Отсутствие увеличения задержки, связанного с TLB, указывает на то, что [...] "
вероятно, были использованы / реализованы большие размеры страниц.
ИЗМЕНИТЬ 3
Эта ссылка объясняет эффекты TLB от другого графа-элемента. Фактически можно получить те же эффекты на моем графике.
В страничной системе 4 КБ, по мере того, как вы набираете успех, пока они все еще <4 КБ, вы будете все меньше и меньше пользоваться каждой страницей [...] вам придется обращаться к TLB 2-го уровня при каждом доступе [ ...]
Cortex-A9 поддерживает страницы размером 4 КБ режим. Действительно, как видно на моем графике до шагов == 4K, задержки увеличиваются, а затем, когда они достигают 4K
вы внезапно снова начинаете получать выгоду, поскольку фактически пропускаете целые страницы.
InitWithRandomValues
, поскольку он, кажется, определяет, как вы получаете доступ к памяти. - person ElderBug   schedule 29.05.2015mmap()
для распределения новых таблиц MMU. Есть cachebench, membersch и т. Д., Которые дадут более точные измерения. Этот код не является хорошим набором тестов. - person artless noise   schedule 29.05.2015if(entries[i] == MAP_FAILED) perror("mmap failed"); exit(1);
. Без фигурных скобок вокруг блока кода это означает, что вы всегда выходите. - person Sjlver   schedule 21.06.2016The main TLB is located in L2
. Это распространенное заблуждение. L2TLB - это второй уровень для L1TLB, точно так же, как кэш L2 является вторым уровнем для кешей L1D и L1I. L2TLB не использует записи и не заботится о содержимом обычного кэша L2. Связанное описание звучит так, как Intel реализует многоуровневые TLB на мощных процессорах x86, таких как Haswell, с небольшими / быстрыми TLB L1I и L1D и большим общим пулом, который не является полностью ассоциативным. Схема в статье Кантера должна быть полезной. - person Peter Cordes   schedule 23.08.2016AnonHugePages:
в/proc/meminfo
, отличную от нуля. - person Peter Cordes   schedule 23.08.2016