Библиотека сайта rus-linux.net
Что каждый программист должен знать о памяти.
Часть 9: Приложения и библиография
| Назад | Оглавление | Вперед |
9.3 Измерение издержек, связанных с общим использованием кэш-строк
В этом разделе приводится тестовая программа, которая используется для измерения дополнительных затрат, связанных с использованием одной и той же кэш-строки, в сравнении с случаем, когда используются различные кэш-строк.
#include <error.h>
#include <pthread.h>
#include <stdlib.h>
#define N (atomic ? 10000000 : 500000000)
static int atomic;
static unsigned nthreads;
static unsigned disp;
static long **reads;
static pthread_barrier_t b;
static void *
tf(void *arg)
{
long *p = arg;
if (atomic)
for (int n = 0; n < N; ++n)
__sync_add_and_fetch(p, 1);
else
for (int n = 0; n < N; ++n)
{
*p += 1;
asm volatile("" : : "m" (*p));
}
return NULL;
}
int
main(int argc, char *argv[])
{
if (argc < 2)
disp = 0;
else
disp = atol(argv[1]);
if (argc < 3)
nthreads = 2;
else
nthreads = atol(argv[2]) ?: 1;
if (argc < 4)
atomic = 1;
else
atomic = atol(argv[3]);
pthread_barrier_init(&b, NULL, nthreads);
void *p;
posix_memalign(&p, 64, (nthreads * disp ?: 1) * sizeof(long));
long *mem = p;
pthread_t th[nthreads];
pthread_attr_t a;
pthread_attr_init(&a);
cpu_set_t c;
for (unsigned i = 1; i < nthreads; ++i)
{
CPU_ZERO(&c);
CPU_SET(i, &c);
pthread_attr_setaffinity_np(&a, sizeof(c), &c);
mem[i * disp] = 0;
pthread_create(&th[i], &a, tf, &mem[i * disp]);
}
CPU_ZERO(&c);
CPU_SET(0, &c);
pthread_setaffinity_np(pthread_self(), sizeof(c), &c);
mem[0] = 0;
tf(&mem[0]);
if ((disp == 0 && mem[0] != nthreads * N)
|| (disp != 0 && mem[0] != N))
error(1,0,"mem[0] wrong: %ld instead of %d",
mem[0], disp == 0 ? nthreads * N : N);
for (unsigned i = 1; i < nthreads; ++i)
{
pthread_join(th[i], NULL);
if (disp != 0 && mem[i * disp] != N)
error(1,0,"mem[%u] wrong: %ld instead of %d", i, mem[i * disp], N);
}
return 0;
}
Код, представляемый здесь, следует, большей частью, рассматривать в качестве иллюстрации создания программы, в которой измеряются такие эффекты, как затраты, связанные с использованием кэш-строк. Интерес представляет тело циклов в tf. Внутренняя инструкция __sync_add_and_fetch, известная компилятору, создает атомарную инструкцию добавления. Во втором цикле мы должны "употребить" результат приращения (с помощью инлайновой инструкции asm). Реальный код вместо инструкции asm не подставляется; благодаря этой инструкции компилятор не удаляет из цикла счетчик цикла.
Вторая интересная особенность в том, что программа размещает потоки на конкретных процессорах. В программе предполагается, что процессоры пронумерованы от 0 и до 3, что обычно бывает в случае, если в машине используется четыре или большее число логических процессоров. В программе можно было бы для определения числа используемых процессоров воспользоваться интерфейсами из libNUMA, но это программа-тест предназначена для широкого применения без добавления указанной зависимости. Так или иначе, это достаточно легко исправить.
| Назад | Оглавление | Вперед |
