Cachegrind выходной интерпретации
Это часть вывода cachegrind. Эта часть кода была выполнена 1224 раза. elmg1 - это массив без знака long размером 16 x 20. Размер кэша L1 моей машины составляет 32 КБ, размер строки кэша 64 Б и ассоциативность с 8-позиционным набором.
- для (i = 0; i <20; i ++) 78,336 2,448 2 50,184 0 0 1,224 0 0
- {
- telm01 = elmg1 [i]; 146 880 0 0 73,440 0 0 24,480 0 0
- telm31 = (telm01 << 3) ^ val1; 97,920 0 0 48,960 0 0 24,480 0 0
- telm21 = (telm01 << 2) ^ (val1 >> 1); 146,880 1,224 1 48,960 0 0 24,480 0 0
- telm11 = (telm01 << 1) ^ (val1 >> 2); 146 880 0 0 48,960 0 0 24,480 0 0
- }
О. Причина, по которой я это изложил, заключается в том, что в 3-й строке цикла for я вижу несколько пропусков I1 (также один промах L2). Это несколько сбивает с толку, и я не мог догадаться, почему?
Б. Я пытаюсь оптимизировать (время) часть кода. Выше просто небольшой фрагмент. Я думаю, что в моей памяти программ доступ к стоимости мне дорого. Как и в приведенном выше примере, elmg1 - это массив беззнаковых длин 16 x 20. Когда я пытаюсь использовать его в коде, всегда есть некоторые промахи, и в моей программе эти переменные встречаются часто. Какие-либо предложения?
C. Мне нужно выделить и (иногда инициализировать) эти неподписанные лонги. Можете ли вы предложить, какой из них я бы предпочел, calloc или объявление массива, а затем явную инициализацию. Кстати, будет ли разница в том, как кеш обрабатывает их?
Благодарю.
1 ответ
Вы пытались развернуть петлю?
- Я не буду беспокоиться о промахах L1 прямо сейчас. Также допустимо одно промах L2 из 1224 раз, процессор должен загрузить значения в кеш в какой-то момент.
- Какой процент пропущенного L2 стоит этот код по сравнению с остальной частью программы?
- Используйте calloc(), если размер массива всегда одинаков и вы используете константы для размера, тогда компилятор может оптимизировать обнуление массива. Также единственное, что может повлиять на использование строк кэша, это выравнивание, а не то, как оно было инициировано.
редактировать: число, где трудно читать таким образом и читать их неправильно в первый раз.
давайте удостоверимся, что я читаю числа прямо для строки 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-way set ассоциативная" означает, что каждый адрес памяти отображается только в 8 из этих 512 строк кэша. Если бы вся программа, которую вы профилировали, представляла собой один непрерывный блок из 32 Кбайт памяти, то все это поместилось бы в кэш I1, и ни одна из них не была бы извлечена. Скорее всего, это не тот случай, и для одних и тех же 8 строк кэша будет более 8 64-байтовых блоков кода. Допустим, что вся ваша программа имеет 1 Мбайт кода (включая библиотеки), тогда каждая группа из 8 строк кэша будет иметь около 32 (1 Мбайт /32 Кбайт) фрагментов кода для тех же 8 строк кэша.
Прочитайте эту статью на lwn.net, чтобы узнать все подробности о кешах процессора
Компилятор не всегда может определить, какие функции программы будут горячими точками (вызываемыми много раз), а какие будут кодовыми точками (то есть кодом обработчика ошибок, который почти никогда не запускается). GCC имеет атрибуты функции hot/cold, которые позволят вам помечать функции как hot/cold, что позволит компилятору сгруппировать горячие функции вместе в одном блоке памяти, чтобы лучше использовать кэш (т. Е. Холодный код не будет выталкивать hotcode из из кешей).
В любом случае, эти промахи I1 действительно не стоят времени, чтобы беспокоиться.