2014-11-05 4 views
3

Я читал о кешировании в многоядерных системах, и мне интересно, можно ли управлять некоторыми страницами в кеше или нет, когда мы программируем на C/C++.Управление кешем в C

Например, я знаю, что мы можем использовать встроенную функцию __builtin_prefetch, чтобы переместить данные в кеш, чтобы уменьшить промахи в кеше и, следовательно, латентность. я нашел его здесь: https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html#Other-Builtins

Я также нашел, что это для x86 и x86-64 Intel:

#include <xmmintrin.h> 
enum _mm_hint 
{ 
    _MM_HINT_T0 = 3, 
    _MM_HINT_T1 = 2, 
    _MM_HINT_T2 = 1, 
    _MM_HINT_NTA = 0 
}; 
void _mm_prefetch(void *p, enum _mm_hint h); 

Здесь: http://lwn.net/Articles/255364/

Какие другие функции, которые мы можем использовать, которые дают нам некоторый кэш «контроль». Например, можем ли мы сделать что-либо, связанное с заменой страницы кэша? Или это исключительная функция ОС?

Большое спасибо!

ответ

4

Рекомендации по предварительной выборке кеша обычно испускают специальную инструкцию prefetch, которая советует prefetcher, что эта часть памяти понадобится в ближайшем будущем. Префект может (или не может) принять этот совет. Таким образом, в этом смысле предварительная выборка программного обеспечения не связана с «управлением кешем» или «управлением кешем».

Насколько мне известно, ни одна широко распространенная архитектура набора инструкций не содержит инструкций по выходу определенной строки кэша или, например, для приведения определенной части памяти в строку кэша. Точка кэша в большинстве современных архитектур должна быть прозрачной для программиста.

Вы можете, однако, писать программы, которые не относятся к кешированию. Это все о пространственной и временной локальности обоих данных и инструкции:

  • Пространственная локальность в отношении данных означает, что вы должны стремиться к последовательным доступ к памяти, не слишком далеко друг от друга , Это самая естественная вещь для оптимизации.

  • Пространственная локальность в отношении инструкций означает, что прыжки и ветви не должны заходить слишком далеко в код. Составители и линкеры должны попытаться это сделать.

  • Временная локальность относительно данных означает доступ к одним и тем же ячейкам памяти (хотя, возможно, не близко друг к другу) за один временной срез. Определение того, как долго этот фрагмент времени может быть сложным;

  • Временная локальность в отношении инструкций означает, что даже если код перескакивает на большие расстояния, он перескакивает через одни и те же места в одном временном фрагменте. Это, как правило, очень неинтуитивно и не очень полезно для оптимизации.

Как правило, вы должны оптимизировать данные и не столько инструкции местности. В большинстве программ, чувствительных к производительности, объем данных намного превышает объем кода.

Кроме того, в отношении нескольких сердечников вы должны стараться избегать ложного обмена и максимально использовать поточное локальное хранилище. Имейте в виду, что каждое ядро ​​ЦП имеет свой собственный выделенный кеш, и такие вещи, как перехват строки кэша между кешами ядер, могут иметь очень отрицательный эффект.

Чтобы проиллюстрировать ложное разделение, рассмотрим следующий код:

int counts[NUM_THREADS]; // a global array where each thread writes to its slot 

... 

for (int i = 0; i < NUM_THREADS; ++i) { 
    spawn_thread(thread_start); 
} 

... 

void thread_start(void) 
{ 
    for (a_large_number_of_iterations) { 
     int some_condition = some_calculation(); 

     if (some_condition) { 
      counts[THREAD_ID]++; 
     } 
    } 
} 

Каждая из нитей модифицирует элемент массива counts с высокой частотой. Проблема состоит в том, что отдельные элементы массива смежны, а большие группы из них будут падать на одну линию кэша. При типичной кэш-строке 64 байта, а типичный размер int составляет 4 байта, это означает, что одна строка кеша может освободить место для 16 элементов. Когда несколько ядер обновляют только их 4-байтовый счет, они также недействительны для соответствующей строки кэша в других ядрах, что приведет к отскоку линии кэша между ядрами, хотя потоки, по-видимому, используют независимые ячейки памяти.

+0

Привет, спасибо большое! Об этом _false sharing_: Я полагаю, что то, что мы можем сделать на уровне программирования, связано с механизмами синхронизации ... Можете ли вы дать мне пример с ложным совместным использованием? – franco

+0

Что касается вашей второй пули: программист может помочь компилятору/компоновщику, написав кэш-код, компактный и, по существу, встроенный в него с несколькими прыжками и вызовами внутри него. Что касается четвертой пули: это непросто, но это отнюдь не невозможно. –

0

Другие аспекты операций кэша промывка и недействительности:

  • Промывка означает запись обновлений, которые находятся на рассмотрении в кэшей в ОЗУ. За флеш может и не может последовать недействительность.
  • Недействительные средства означают, что строки кеша пустыми. Недействительный редко используется без предшествующего флеша.

В x86 эти операции, как правило, выполняются прозрачно с помощью кэшей. Они также могут быть вызваны в качестве инструкций программиста. См. Соответствующие ссылки на набор инструкций.

Смежные вопросы