INSERTED: Давайте посмотрим на "статистическую значимость".
Предположим, что где-то есть инструкция вызова функции. Вы не можете обязательно видеть это - класс, или макрос, или компилятор, возможно, вставили его.
Рядом есть другие вызовы той же функции, но этот вызов находится в цикле, или его аргументы таковы, что этот вызов занимает много времени. Фактически, так много времени, что если этот вызов может занять нулевое время, то общее время выполнения будет уменьшено на некоторую величину, скажем, на 90%. (Невозможно? Вовсе нет.) Будет ли время точно определять? Нет. Определит ли это график вызовов? Нет. Будет ли кол-во вызовов точно определять это? Нет. Потому что проблема не в уровне функций, а в уровне инструкций вызова.
Каким-то образом программа случайно останавливается в определенный момент времени и проверяется ее состояние. Остановится ли он в те 90% времени, которые будут сохранены, если инструкция может быть "обнулена"? Конечно, с вероятностью 90%, и инструкция будет точно указана в стеке, ожидая завершения своей «работы».
На самом деле, если вы остановите ее случайным образом 20 раз, эта инструкция будет в стеке в среднем 18 раз со стандартным отклонением +/- 1,3 раза.
Это статистически значимо? Вы держите пари, что это так.
Вам нужно было большое количество образцов? Вы держите пари, что вы не сделали.
Предположим, процент небольшой, например, 10% или 5%. Применяется тот же принцип.
Фактически, независимо от того, как мало выборок берется, любая инструкция, которая находится на> 1 выборке, является статистически значимой и представляет собой «горячую точку», «узкое место» или как вы хотите это назвать. Если вы могли бы удалить его, назвать его меньше или как-то уменьшить его, это сэкономит значительное время. (Некоторые из них вы не можете, например, «позвонить _main», но другие вы можете. Вам просто нужно их найти.)
Конечно мой код никогда не был бы таким глупым, не так ли? Ну, тогда, докажите это .
Хорошо, теперь вернемся к истории. , .
ОРИГИНАЛЬНЫЙ ОТВЕТ: Я слышал о профилировщиках, еще когда-то, и я думал, что они должны быть довольно аккуратными, но у меня не было доступа к ним / этому. Я работал над встроенным процессором (чип Intel 8086), который, казалось, занимал ужасно много времени, рисуя некоторые числа с плавающей точкой на экране дисплея. Специалисты по аппаратному обеспечению предложили предоставить из своего большого количества - добавление микросхем таймера, чтобы я мог видеть, сколько времени это займет. Однако в один из выходных я запустил его с внутрисхемным эмулятором Intel Blue Box и запустил его. В то время как это было медленно, я задавался вопросом "Какого черта это делает?" Так что я просто остановился, чтобы выяснить это. ПК был в библиотеке с плавающей запятой (там не было микросхемы FP). Это не было неожиданностью, поскольку оно рисовало числа с плавающей точкой, но я хотел знать больше. Поэтому я (кропотливо) читаю шестнадцатеричную память, чтобы следить за стеком вызовов. Угадай, что? Он находился в процессе получения числа, которое нужно нарисовать, деления его на 10, преобразования в целое число, преобразования обратно в число с плавающей точкой, вычитания и т. Д. , чтобы получить следующую цифру для рисования . Само собой разумеется, были лучшие способы сделать , чем , что привело бы к ускорению примерно в 10 раз.
Это было найдено с одним (1) образцом!
В другом случае, на чипе 68K, была некоторая медлительность. Опять же, профилировщик не был доступен, но отладчик «adb» был, поэтому, пока он работал медленно, я несколько раз останавливал его. ПК был в математической библиотеке, фактически в процедуре умножения 32-разрядных целых чисел. Посмотрев в стек, я нашел этот код:
struct {...} a[...];
int i;
for (i = 0; i < ...; ++i){ ... a[i] ... }
Там нет призыва умножать там - что происходит? Оказывается, для a[i]
компилятор должен умножить i
на размер элемента массива. Поскольку i
32-битный (в этом компиляторе), он генерирует вызов 32-битной процедуры умножения, и стек точно определяет эту инструкцию. Объявляя i
как short
, цикл утроился в скорости!
Какой смысл? Если вы берете сэмплы в случайное время, пока программа работает медленно, ПК сообщит вам, что она делает, но стек вызовов сообщит вам почему и приведет вас к проблеме. Теперь, если проблема не очень серьезная, вам может потребоваться взять несколько образцов. Любое утверждение, которое появляется на> 1 выборке, может вызывать у вас подозрения. Обратите внимание, он точно определяет операторы , инструкции даже не большие куски кода, подобные функциям. Эта техника может быть «быстрой и грязной», но она очень эффективна.
ДОБАВЛЕНО: Если вы делаете это несколько раз, вы можете решить проблему за проблемой в том же программном обеспечении. Например, если вы получили ускорение в 3 раза, небольшие проблемы с производительностью, которые раньше могли быть незначительными, теперь будут в 3 раза больше оставшегося времени. Это значительно облегчает их попадание в образцы. Возможно, вам придется добавить временный внешний цикл, чтобы он работал достаточно долго для выборки. Таким образом, я видел составные коэффициенты ускорения более чем в 40 раз .