Решил посмотреть на новый Intel VTune Amplifier XE 2011 beta и написать статью о примере использования. Правда, в процессе написания акцент частично сместился от использования Amplifier к его тестированию. Но это тоже хорошо, надеюсь, разработчики в Intel учтут пожелания и внесут изменения в следующую версию инструмента. И вообще буду критиковать и себя, и всех :).
Начну немного издалека, иначе возникнут вопросы, зачем мне понадобился код, который мне пришлось оптимизировать. В ходе разработки статического анализатора кода для Си++ мне приходится сталкиваться с весьма экзотическими фрагментами программ. Чего только программисты не придумывают. И даже такие компиляторы, как Visual C++ или Intel C++ нет, нет, да и упадут на очередной "загогулине".
В нашем статическом анализаторе PVS-Studio есть одно не очень хорошее место, связанное с определением типа объектов. Есть, например, конструкция:
typedef int A;
typedef A B;
B myValue;
и необходимо "вычислить" тип B, чтобы узнать, что это не что иное, как int. С подобными примерами, конечно, никаких сложностей нет и все работает. Однако бывают очень запутанные ситуации. Приведу пример кода, демонстрирующего откуда начинают появляться разные сложности:
При попытке узнать тип переменной X мы посмотрим ее тип в namespace B и не найдем его там. Тогда мы увидим, что надо посмотреть в namespace C. И тоже там не найдем. Теперь надо посмотреть и в namespace B... Вечный цикл. Конечно, это простой случай и с ним тоже никаких проблем нет. Но добавьте сюда typedef, шаблоны, наследуемые классы. Иногда получаются удивительно сложные вещи, причем не специально. Особенно это проявляется в коде с шаблонами.
К сожалению, инструмент PVS-Studio иногда входил в вечный цикл на особенно "удачных конструкциях" и делал самоубийство через 5 минут, чтобы было возможно продолжить обработку других файлов. Очень редкая ситуация, но неприятная. Ошибки, конечно, правятся и правятся, но находятся все новые ситуации. Причем не всегда есть возможность узнать, что же это за код такой у пользователя. Было принято решение не полностью обрывать работу анализатора по таймеру, а отказаться от получения типа какого-то объекта, если возникает зацикливание. Лучше пропустить одну переменную, чем весь файл.
Здесь проявляет очень интересный момент несовместимости теории и практики. Теоретически надо написать так, чтобы ошибок не было. Даже пожурить нас можно. Разработчики статического анализатора, писатели статей как писать без ошибок, а сами не могут сделать верный разбор типа переменных. А вот получается, что не можем. И не только мы. Задачи, связанные с компиляцией Си++ кода крайне сложные. И приходится от рассуждений о высоком и прекрасном переходить к созданию заплаток на всякий случай.
Был создан простой, но эффективный механизм остановки. Если при получении типа объекта мы получаем указатель на закодированный тип, с которым уже работали до этого, то стоп. Для начала был создан просто класс, содержащий множество указателей в std::set<const void*> *m_pGuardPointers. Если в множестве уже есть указатель, то значит пора остановиться.
Я не удивился, когда производительность программы после такого изменения упала во много раз. Я ожидал подобного эффекта. Не стал даже мерить скорость, и так видно, что очень медленно и причина понятна. Обычно глубина "вывода" типа не велика, и использовать тяжелую артиллерию для подобных случаев просто глупо:
typedef long LONG;
LONG x;
Сразу был написан класс следующего вида (приводится в сокращенном виде):
class CPointerDuplacateGuard
{
static const size_t QuickMaxSize = 10;
const void *m_ptrs[QuickMaxSize];
size_t m_index;
std::set<const void*> *m_pGuardPointers;
public:
CPointerDuplacateGuard();
CPointerDuplacateGuard(const CPointerDuplacateGuard *parent);
...
};
В начале, сохраняем и ищем указатели в обыкновенном массиве из 10 элементов, а уже затем создаем и начинаем использовать множество. Стало значительно лучше, но все в несколько раз медленнее, чем без этого механизма.
И вот здесь то я и решил, что пришло время посмотреть на Intel VTune Amplifier XE 2011 beta. Очень хороший повод. Здесь профилировщик как нельзя кстати. Он поможет ответить на вопрос, связано падение производительности только с использованием std::set или замедление вносит само использование постоянных проверок указателя. Если основное падение производительности связано с std::set, то нужно увеличивать значение QuickMaxSize. Это отложит использование std::set на самый крайний случай. Если замедляет сам алгоритм, то думать дальше.
Сразу скажу, что как следует поработать с Intel VTune Amplifier XE 2011 beta у меня не хватило терпения. Он вводит невообразимое торможение в работу. Хотя у меня достаточно мощная система (4 ядра, 8 Гбайт памяти), если открыто окно Intel VTune Amplifier XE 2011 beta, то даже простое перемещение по коду делается с натугой и рывками. Причем сам Intel VTune Amplifier XE 2011 ничего не делает. Вернее процессор он нагружает, но не пишет, чем занимается. Чтобы не быть голословным приведу демонстрационные скриншоты.
Для большей наглядности, я прикрепил devenv.exe к 4-тому ядру.
Итак, сейчас у меня открыт проект, ничего не происходит. На рисунке видно, что загрузка четвертого ядра близка к нулю:
Теперь я просто запускаю Intel VTune Amplifier XE 2011. Подчерку, что только запускаю! Я не выполняю анализ проекта и вообще ничего не делаю. Но четвертое ядро уже занято полностью:
Работать становится сразу как-то некомфортно. Среда начинает тормозить везде, где только можно. Если закрыть окно Intel VTune Amplifier XE 2011 то торможение тут же пропадает и загрузка ядра снова становится близка к нулю. Возможно, запущенный Intel VTune Amplifier XE 2011 делает какую-то полезную работу. Но какую непонятно. Если делает - то надо как минимум показать это. У меня сложилось ощущение о какой-то ошибке.
Торможение меня не остановило, и я приступил к исследованию нашей программы. Для начала я выбрал режим анализа, не собирающий информаций о стеке вызова, но позволяющий понять какие функции тратят наибольшее время:
Анализ выполнился без неожиданностей:
И я получил полезный результат:
Большинство времени тратится в std::_Tree, в функциях выделения и освобождения памяти. Программисту сразу станет понятно, что основное замедление связано именно с использованием std::set.
Если запустить Amplifier в режиме Hotspot, то проблемное место станет гораздо очевиднее:
После этого запуска стало возможным просмотреть стек вызовов. Правда, первый мой запуск в этом режиме закончился неудачей. Меня предупреждали, что с включенной галочкой "Collect accurate CPU time" все будет медленнее. Но получилось как-то слишком медленно. Когда я нажал на кнопку раскрытия стека для первой функции, то так и не дождался результата (ждал 15 минут).
Запуск уже без галочки показал нужную мне информацию. Но и здесь не могу не добавить ложку дегтя. Функционально инструмент замечателен, но интерфейс безобразен. Все время что-то разъезжается, не перерисовывается, что-то смазывается. Пользоваться неудобно и неприятно:
Впрочем, подводил не только внешний вид. Иногда почему-то перебрасывает на не тот участок кода, который необходимо.
Однако нужный результат я все-таки достиг. Оказалось, что если установить значение массива в 64, то производительность возвращается приблизительно на прежний уровень. Новая подсистема практически не вносит замедления в работу. Исправление:
static const size_t QuickMaxSize = 64;
Это подтверждается и Amplifier. Теперь на первый план вышли совсем другие функции, такие как strncmp:
Нет счастья в мире программ. Кругом ошибки и недочеты. У и нас в PVS-Studio, и везде. Это не значит, что с ними не надо бороться, но большой шаг хотя бы признать большую сложность программ и что ошибки есть.
Пост получился несколько критичный по отношению к инструменту Intel VTune Amplifier. Видимо это у меня как у разработчика наболело. Предлагают попробовать очередную программу, которая улучшит мир, а как начинаешь с ней работать, то понимаешь, что больше потратили на маркетинг или красивые картинки, чем на контроль качества. Ну что это за профайлер, который сам тормозит? Сапожник без сапог. :)
Надеюсь в Release это, или, по крайней мере, многое, будет поправлено. Сам инструмент весьма мощен. Но, к сожалению, пока рекомендовать его использовать не могу. Конечно, подобное писать в блоге Intel как-то не хорошо, зато честно. Надеюсь, разработчики это оценят.