Cachegrind выходной интерпретации - PullRequest
3 голосов
/ 01 ноября 2010

Это часть вывода cachegrind.Эта часть кода была выполнена 1224 раза.elmg1 - это массив без знака long размером 16 x 20. Размер кэша L1 моей машины составляет 32 КБ, размер строки кэша 64 Б и ассоциативность с 8-позиционным набором.

  1. для (i = 0; i <20; i ++) 78,336 2,448 2 50,184 0 0 1,224 0 0 </li>
  2. {
  3. telm01 = elmg1 [i];146 880 0 0 73,440 0 0 24,480 0 0
  4. telm31 = (telm01 << 3) ^ val1;97 920 0 0 48 960 0 0 24 48080 0 0 </li>
  5. telm21 = (telm01 << 2) ^ (val1 >> 1);146,880 1,224 1 48,960 0 0 24,480 0 0
  6. telm11 = (telm01 << 1) ^ (val1 >> 2);146 880 0 0 48,960 0 0 24,480 0 0
  7. }

A.Причина, по которой я изложил это здесь, заключается в том, что в 3-й строке цикла for я вижу несколько пропусков I1 (также один промах L2).Это несколько сбивает с толку, и я не мог догадаться, почему?

B.Я пытаюсь оптимизировать (время) часть кода.Выше просто небольшой фрагмент.Я думаю, что в моей памяти программы доступ стоит мне много.Как и в приведенном выше примере, elmg1 - это массив размером 16 х 20 без знака.Когда я пытаюсь использовать его в коде, всегда есть некоторые промахи, и в моей программе эти переменные встречаются часто.Есть предложения?

C.Мне нужно выделить и (иногда инициализировать) эти неподписанные long.Можете ли вы предложить, какой из них мне лучше выбрать, calloc или объявление массива, а затем явную инициализацию.Кстати, будет ли какая-то разница в том, как кеш обрабатывает их?

Спасибо.

1 Ответ

3 голосов
/ 01 ноября 2010

Вы пытались развернуть цикл?

  1. Я бы не стал беспокоиться о промахах L1 прямо сейчас. Также допустимо одно промах L2 из 1224 раз, процессор должен загрузить значения в кеш в какой-то момент.
  2. Какой процент пропущенного L2 стоит этот код по сравнению с остальной частью программы?
  3. Используйте calloc (), если размер массива всегда одинаков и вы используете константы для размера, тогда компилятор может оптимизировать обнуление массива. Также единственное, что может повлиять на использование строк кэша, это выравнивание, а не то, как оно было инициировано.

edit: число, которое трудно прочитать таким образом и читать неправильно с первого раза.

давайте удостоверимся, что я читаю цифры прямо для строки 5:

Ir    146,880
I1mr  1,224
ILmr  1
Dr    48,960
D1mr  0
DLmr  0
Dw    24,480
D1mw  0
DLmw  0

Кэш-память L1 разделена на два 32-килобайтных кэша: один для кода I1 и один для данных D1. IL & DL - это кэш L2 или L3, который используется как данными, так и инструкциями.

Большое количество I1mr - пропуски инструкций, а не пропуски данных, это означает, что код цикла извлекается из кэша инструкций I1.

I1 пропускает в строке 1 и 5 всего 3672, то есть 3 раза по 1224, поэтому каждый раз при запуске цикла вы получаете 3 пропуска I1 кеша с 64-битными строками кеша, что означает, что размер цикла в цикле составляет 128-192 байта для покрытия 3 строки кэша. Таким образом, эти I1 пропускаются в строке 5, потому что именно там код цикла пересекает последнюю строку кэша.

Я бы рекомендовал использовать KCachegrind для просмотра результатов из cachegrind

Редактировать: Подробнее о строках кэша.

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

Кэш-память I1 объемом 32 КБ разделена на 512 строк кэша (по 64 байта каждая). Часть «8-полосный набор ассоциативных» означает, что каждый адрес памяти отображается только в 8 из этих 512 строк кэша. Если бы вся программа, которую вы профилировали, представляла собой один непрерывный блок из 32 Кбайт памяти, то все это поместилось бы в кэш I1, и ни одна из них не была бы извлечена. Скорее всего, это не тот случай, и для одних и тех же 8 строк кэша будет более 8 64-байтовых блоков кода. Допустим, вся ваша программа имеет 1 Мбайт кода (включая библиотеки), тогда каждая группа из 8 строк кэша будет иметь около 32 (1 Мбайт / 32 Кбайт) фрагментов кода для тех же 8 строк кэша.

Прочтите эту статью на lwn.net, чтобы узнать все подробности о кешах процессора

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

В любом случае, эти промахи I1 действительно не стоят того времени, чтобы о них беспокоиться.

...