Linux C ++: как профилировать время, потраченное впустую из-за промахов кэша? - PullRequest
44 голосов
/ 21 марта 2010

Я знаю, что могу использовать gprof для тестирования моего кода.

Однако у меня есть эта проблема - у меня есть умный указатель, который имеет дополнительный уровень косвенности (думайте об этом как о прокси-объекте).

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

Есть ли способ измерить время, которое мой ЦП теряет из-за пропадания кэша?

Спасибо!

Ответы [ 9 ]

18 голосов
/ 21 марта 2010

Вы можете попробовать cachegrind и его внешний интерфейс kcachegrind.

11 голосов
/ 21 марта 2010

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

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

Был найден быстрый поиск в Google boost::intrusive_ptr, который может вас заинтересовать. Похоже, что он не поддерживает что-то вроде weak_ptr, но преобразование вашей программы может быть тривиальным, и тогда вы наверняка будете знать цену неинтрузивного подсчета ссылок.

10 голосов
/ 12 июля 2013

Linux поддерживает с perf с 2.6.31 и далее. Это позволяет вам делать следующее:

  • скомпилируйте ваш код с -g для включения отладочной информации
  • запустите ваш код, например используя счетчики пропущенных кэшей последнего уровня: perf record -e LLC-loads,LLC-load-misses yourExecutable
  • пробег perf report
    • после подтверждения исходного сообщения выберите строку LLC-load-misses,
    • затем, например, первая функция и
    • , затем annotate. Вы должны увидеть строки (в коде сборки, окруженные исходным исходным кодом) и число, указывающее, какая часть кэша последнего уровня пропущена для строк, в которых произошла ошибка кэша.
6 голосов
/ 28 апреля 2012

Продолжая в соответствии с ответом @ Mike_Dunlavey:

Сначала получите профиль на основе времени, используя ваш любимый инструмент: VTune или PTU или OProf.

Затем получите профиль пропуска кэша. Отсутствует кэш L1, отсутствует кэш L2, или ...

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

Примечание: я часто «сокращаю» данные, суммируя их по функциям или (если у меня есть технология) по циклам. Или по бинам, скажем, 64 байта. Сравнение отдельных программных счетчиков часто бесполезно, потому что счетчики производительности нечеткие - место, где вы видите сообщение об ошибке кэша, часто несколько отличается от того, где это произошло на самом деле.

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

Диаграммы "Айсберг": ось X - это ПК, положительная ось Y - время, отрицательный доступ Y - это пропуски кэша. Поиск мест, которые идут как вверх, так и вниз.

(Диаграммы с чередованием также полезны: та же идея, ось X - это ПК, график и время, и тайник пропуска по оси Y, но с узкими вертикальными линиями разных цветов, обычно красного и синего. Места, где много обоих потраченное время и потери в кеше будут иметь чередующиеся красные и синие линии, почти выглядящие фиолетовыми. Это распространяется на пропуски кеша L2 и L3, все на одном графике. Кстати, вы, вероятно, хотите «нормализовать» числа, либо на% возраст общего времени или пропусков кэша, или, что еще лучше,% возраста максимального момента времени или пропадания кэша. Если вы неправильно укажете масштаб, вы ничего не увидите.)

XY-диаграммы : для каждого контейнера выборки (ПК, или функции, или петли, или ...) построить точку, у которой X-координата - нормализованное время, а Y-координата - это нормализованный кеш пропускает . Если вы получаете много точек данных в верхнем правом углу - большой процент времени возрастает и большой процент кэшей возраста пропадает - это интересное доказательство. Или забудьте количество баллов - если сумма всех процентов в верхнем углу велика ...

Обратите внимание, к сожалению, что вам часто приходится проводить эти анализы самостоятельно. Последний раз я проверял, VTune не делает это для вас. Я использовал gnuplot и Excel. (Предупреждение: Excel умирает выше 64 тысяч точек данных.)


Еще совет:

Если ваш умный указатель встроен, вы можете получить счет повсюду. В идеальном мире вы сможете отследить ПК до исходной строки исходного кода. В этом случае вы можете немного отложить сокращение: посмотрите на все отдельные ПК; отобразить их обратно на строки исходного кода; и затем отобразите их в исходную функцию. Многие компиляторы, например GCC, есть опции таблицы символов, которые позволяют вам сделать это.

Кстати, я подозреваю, что ваша проблема не в том, что умный указатель вызывает перегрузку кэша. Если вы не делаете smart_ptr повсюду. Если вы делаете smart_ptr , а sizeof (Obj) + больше, чем, скажем, 4 * sizeof (Obj *) (и если сам smart_ptr не очень большой), то это не так уж много.

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

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

template<typename T> class refcntptr {
    refcnt_handle<T> handle;
public:
    refcntptr(T*obj) {
        this->handle = new refcnt_handle<T>();
        this->handle->ptr = obj;
        this->handle->count = 1;
    }
};
template<typename T> class refcnt_handle {
    T* ptr;
    int count;
    friend refcnt_ptr<T>;
};

(я бы так не кодировал, но он служит для изложения.)

Двойная косвенность this-> handle-> ptr может быть большой проблемой производительности. Или даже тройная косвенность, это-> handle-> ptr-> field. По крайней мере, на машине с 5 циклами попаданий в кэш L1 каждое поле this-> handle-> ptr-> будет занимать 10 циклов. И будет гораздо сложнее перекрывать, чем погоня за одним указателем. Но, что еще хуже, если каждый из них является пропуском кеша L1, даже если до L2 было всего 20 циклов ... ну, гораздо сложнее скрыть 2 * 20 = 40 циклов задержки кеша, чем одиночное промах L1.

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

1049 * Е.Г. *

template<typename T> class refcntptr {
    refcnt_info<T> info;
    T* ptr;
public:
    refcntptr(T*obj) {
        this->ptr = obj;
        this->info = new refcnt_handle<T>();
        this->info->count = 1;
    }
};
template<typename T> class refcnt_info {
    T* ptr; // perhaps not necessary, but useful.
    int count;
    friend refcnt_ptr<T>;
};

В любом случае, профиль времени - ваш лучший друг.


О да, аппаратное обеспечение Intel EMON также может подсказать, сколько циклов вы ожидали на ПК. Это может отличить большое количество пропусков L1 от небольшого количества пропусков L2.

5 голосов
/ 21 марта 2010

Если вы используете процессор AMD, вы можете получить CodeAnalyst , очевидно, бесплатно, как в пиве.

5 голосов
/ 21 марта 2010

Это зависит от того, какую ОС и процессор вы используете. Например. для Mac OS X и x86 или ppc Shark выполнит профилирование промаха кэша. То же самое для Zoom в Linux.

2 голосов
/ 23 марта 2010

Вот что-то вроде общего ответа .

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

2 голосов
/ 21 марта 2010

Другим инструментом для профилирования производительности на основе счетчика процессора является oprofile . Вы можете просмотреть его результаты, используя kcachegrind.

2 голосов
/ 21 марта 2010

Мой совет - использовать PTU (утилита настройки производительности) от Intel.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...