Можно ли заблокировать некоторые данные в кэше процессора? - PullRequest
6 голосов
/ 06 октября 2009

У меня проблема .... Я записываю данные в массив в цикле while. И дело в том, что я делаю это очень часто. Похоже, что это написание теперь является узким местом в коде. Так что, как я полагаю, это вызвано записью в память. Этот массив не очень большой (что-то вроде 300 элементов). Вопрос в том, можно ли это сделать таким образом: сохранить его в кеше и обновить в памяти только после завершения цикла while?

[править - скопировано из ответа, добавленного Алексом]

double* array1  = new double[1000000]; // this array has elements  
unsigned long* array2  = unsigned long[300];
double varX,t,sum=0;
int iter=0,i=0;
while(i<=max_steps)
{
   varX+=difX;
   nm0 =  int(varX);
   if(nm1!=nm0)
   {
        array2[iter] = nm0;  // if you comment this string application works more then 2 times faster :)
        nm1=nm0;
        t = array1[nm0]; // if you comment this string , there is almost no change in time 
        ++iter;
   }
   sum+=t;
   ++i;
}

Во-первых, я хотел бы поблагодарить всех вас за ответы. Действительно, было немного глупо не размещать код. Поэтому я решил сделать это сейчас.

double* array1  = new double[1000000]; // this array has elements  
unsigned long* array2  = unsigned long[300];
double varX,t,sum=0;
int iter=0,i=0;
while(i<=max_steps)
{
   varX+=difX;
   nm0 =  int(varX);
   if(nm1!=nm0)
   {
        array2[iter] = nm0;  // if you comment this string application works more then 2 times faster :)
        nm1=nm0;
        t = array1[nm0]; // if you comment this string , there is almost no change in time 
        ++iter;
   }
   sum+=t;
   ++i;
}

Так вот и все. Было бы неплохо, если у кого-то будут какие-то идеи. Еще раз большое спасибо.

С уважением Alex

Ответы [ 12 ]

13 голосов
/ 06 октября 2009

Не намеренно, нет. Помимо всего прочего, вы не представляете, насколько большой кэш, поэтому вы не знаете, что будет соответствовать. Кроме того, если приложению было разрешено заблокировать часть кеша, влияние на ОС может иметь разрушительные последствия для общей производительности системы. Это попадает прямо в мой список «вы не можете сделать это, потому что вы не должны делать это. Всегда».

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

Без дополнительной информации о вашем приложении я не думаю, что можно дать более конкретный совет.

7 голосов
/ 06 октября 2009

Процессор обычно не обеспечивает детализированное управление кешем, вы не можете выбирать, что выселено, или закреплять вещи в кеше. У вас есть несколько операций кэширования на некоторых процессорах. Немного информации о том, что вы можете сделать: Вот несколько интересных инструкций, связанных с кэшем, на более новых процессорах x86 {-64} (такие вещи делают переносимость адом, но я подумал, что вам может быть интересно)

Программное обеспечение Data Prefecth

Временная инструкция префечнта, которая извлекает данные в кэш второго уровня, сведение к минимуму загрязнения кеша.

Временные инструкции как следующим образом:

* prefetcht0 – fetches the data into all cache levels, that is, to the

кэш второго уровня для процессора Pentium® 4.

* prefetcht1 – Identical to prefetcht0

* prefetcht2 – Identical to prefetcht0

Кроме того, существует набор инструкций для доступа к данным в памяти, но явно указывается процессору не вставлять данные в кэш. Это так называемые невременные инструкции. Пример одного здесь: MOVNTI .

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

3 голосов
/ 06 октября 2009

У меня проблема .... Я записываю данные в массив в цикле while. И дело в том, что я делаю это очень часто. Похоже, что это написание теперь является узким местом в коде. Так что, как я полагаю, это вызвано записью в память. Этот массив не очень большой (что-то вроде 300 элементов). Вопрос в том, возможно ли это сделать таким образом: сохранить его в кеше и обновить в памяти только после завершения цикла while?

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

Кроме того, массив из 300 элементов должен помещаться в кэш без проблем (при условии, что размер элемента не слишком сумасшедший), поэтому, скорее всего, ваши данные уже в кэше.

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

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

И, наконец, два очевидных инструмента, которые вы должны использовать, а не гадать о поведении кэша:

  • Профилировщик и cachegrind, если доступно. Хороший профилировщик может рассказать вам много статистики о промахах кэша, а cachegrind также даст вам много информации.
  • Мы здесь, в StackOverflow. Если вы опубликуете свой код цикла и спросите, как можно улучшить его производительность, я уверен, что многие из нас сочтут это интересным испытанием.

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

3 голосов
/ 06 октября 2009

Если ваш код не делает что-то совершенно другое между записью в массив, тогда большая часть массива, вероятно, будет храниться в кэше.

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

2 голосов
/ 07 октября 2009

В этом случае array2 будет довольно «горячим» и останется в кеше только по этой причине. Хитрость заключается в том, что array1 остается вне кеша (!). Вы читаете это только один раз, поэтому нет смысла кэшировать его. Инструкция SSE для этого MOVNTPD, внутренняя void_mm_stream_pd(double *destination, __m128i source)

2 голосов
/ 06 октября 2009

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

1 голос
/ 01 февраля 2011

На ранних этапах загрузки CoreBoot (ранее LinuxBIOS), поскольку у них еще нет доступа к ОЗУ (мы говорим о коде BIOS, и, следовательно, ОЗУ еще не инициализировано), они настраивают то, что они называют Cache-as -RAM (CAR), т. Е. Они используют кэш-память процессора в качестве ОЗУ, даже если оно не поддерживается реальной ОЗУ.

1 голос
/ 06 октября 2009

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

1 голос
/ 06 октября 2009

Если у вас проблемы с производительностью, ничего не предполагайте, сначала измерьте. Например, закомментируйте записи и посмотрите, отличается ли производительность.

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

1 голос
/ 06 октября 2009

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

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

...