Почему кешгринд не является полностью детерминированным?
Вдохновленный SQLite, я смотрю на использование инструмента cachegrind от valgrind, чтобы сделать воспроизводимый сравнительный анализ производительности. Числа, которые он выводит, намного более стабильны, чем любой другой метод синхронизации, который я нашел, но они все еще не являются детерминированными. В качестве примера, вот простая программа на C:
int main() {
volatile int x;
while (x < 1000000) {
x++;
}
}
Если я скомпилирую его и запусту в cachegrind, я получу следующие результаты:
$ gcc -O2 x.c -o x
$ valgrind --tool=cachegrind ./x
==11949== Cachegrind, a cache and branch-prediction profiler
==11949== Copyright (C) 2002-2015, and GNU GPL'd, by Nicholas Nethercote et al.
==11949== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info
==11949== Command: ./x
==11949==
--11949-- warning: L3 cache found, using its data for the LL simulation.
==11949==
==11949== I refs: 11,158,333
==11949== I1 misses: 3,565
==11949== LLi misses: 2,611
==11949== I1 miss rate: 0.03%
==11949== LLi miss rate: 0.02%
==11949==
==11949== D refs: 4,116,700 (3,552,970 rd + 563,730 wr)
==11949== D1 misses: 21,119 ( 19,041 rd + 2,078 wr)
==11949== LLd misses: 7,487 ( 6,148 rd + 1,339 wr)
==11949== D1 miss rate: 0.5% ( 0.5% + 0.4% )
==11949== LLd miss rate: 0.2% ( 0.2% + 0.2% )
==11949==
==11949== LL refs: 24,684 ( 22,606 rd + 2,078 wr)
==11949== LL misses: 10,098 ( 8,759 rd + 1,339 wr)
==11949== LL miss rate: 0.1% ( 0.1% + 0.2% )
$ valgrind --tool=cachegrind ./x
==11982== Cachegrind, a cache and branch-prediction profiler
==11982== Copyright (C) 2002-2015, and GNU GPL'd, by Nicholas Nethercote et al.
==11982== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info
==11982== Command: ./x
==11982==
--11982-- warning: L3 cache found, using its data for the LL simulation.
==11982==
==11982== I refs: 11,159,225
==11982== I1 misses: 3,611
==11982== LLi misses: 2,611
==11982== I1 miss rate: 0.03%
==11982== LLi miss rate: 0.02%
==11982==
==11982== D refs: 4,117,029 (3,553,176 rd + 563,853 wr)
==11982== D1 misses: 21,174 ( 19,090 rd + 2,084 wr)
==11982== LLd misses: 7,496 ( 6,154 rd + 1,342 wr)
==11982== D1 miss rate: 0.5% ( 0.5% + 0.4% )
==11982== LLd miss rate: 0.2% ( 0.2% + 0.2% )
==11982==
==11982== LL refs: 24,785 ( 22,701 rd + 2,084 wr)
==11982== LL misses: 10,107 ( 8,765 rd + 1,342 wr)
==11982== LL miss rate: 0.1% ( 0.1% + 0.2% )
$
В этом случае "I refs" отличается только на 0,008% между двумя прогонами, но я все еще задаюсь вопросом, почему они отличаются. В более сложных программах (десятки миллисекунд) они могут варьироваться в большей степени. Есть ли способ сделать трассы полностью воспроизводимыми?
1 ответ
В конце http://comments.gmane.org/gmane.comp.debugging.valgrind/6199 Николас Нетеркот (разработчик Mozilla, работающий в команде разработчиков Valgrind) говорит, что незначительные вариации распространены при использовании Cachegrind (и я могу сделать вывод, что они не приведут к серьезным проблемам),
Руководство Cachegrind упоминает, что программа очень чувствительна. Например, в Linux рандомизация адресного пространства (используемая для повышения безопасности) может быть источником недетерминизма.
Стоит также отметить, что результаты очень чувствительны. Изменение размера профилируемого исполняемого файла или размеров любой из общих библиотек, которые он использует, или даже длины имен их файлов, может повлиять на результаты. Вариации будут небольшими, но не ожидайте абсолютно повторяемых результатов, если ваша программа вообще изменится.
Более поздние дистрибутивы GNU/Linux осуществляют рандомизацию адресного пространства, в которой идентичные запуски одной и той же программы загружают общие библиотеки в разных местах в качестве меры безопасности. Это также мешает результатам.
Хотя эти факторы означают, что вы не должны доверять результатам, чтобы быть сверхточными, они должны быть достаточно близкими, чтобы быть полезными.