Подтвердить что ты не робот

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

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

В моем последнем тесте я измеряю общее время работы задачи с интенсивным использованием ЦП, когда регистрация активна, а когда нет. Затем я могу сравнить время, чтобы определить, сколько накладных расходов у моей библиотеки. Эта гистограмма показывает разницу по сравнению с моим базовым случаем без регистрации.

performance chart

Как вы можете видеть, моя библиотека ( "безрассудная" ) добавляет отрицательные накладные расходы (если все 4 ядра процессора заняты). Программа работает на полсекунды быстрее, когда регистрация включена, чем когда она отключена.

Я знаю, что я должен попытаться изолировать это в более простом случае, а не спрашивать о программе на 4000 строк. Но есть так много мест для того, что нужно удалить, и без гипотезы я просто упущу проблему, пытаясь ее изолировать. Возможно, я проведу еще год, просто сделаю это. Я надеюсь, что коллективная экспертиза Qaru сделает это гораздо более мелкой проблемой или что причина будет очевидна для тех, у кого больше опыта, чем у меня.

Некоторые факты о моей библиотеке и эталонах:

  • Библиотека состоит из интерфейсного API, который толкает аргументы журнала в незанятую очередь (Boost.Lockless) и обратный поток, который выполняет форматирование строк и записывает записи журнала на диск.
  • Время основано на простое вызов std::chrono::steady_clock::now() в начале и конце программы и печать разницы.
  • Тест тестируется на четырехъядерном процессоре Intel (i7-3770K).
  • Эталонная программа вычисляет фрактальную фрактальную фигуру Mandelbrot 1024x1024 и регистрирует статистику о каждом пикселе, то есть записывает около миллиона записей журнала.
  • Общее время работы составляет около 35 секунд для отдельного случая рабочего потока. Таким образом, увеличение скорости составляет около 1,5%.
  • Тест производит выходной файл (это не часть временного кода), который содержит сгенерированный фрактал Мандельброта. Я проверил, что тот же вывод создается, когда регистрация включена и выключена.
  • Тест выполняется 100 раз (со всеми бенчмарками, это занимает около 10 часов). На гистограмме отображается среднее время, а полосы ошибок показывают интерквартильный диапазон.
  • Исходный код для вычисления Мандельброта
  • Исходный код для эталона.
  • Корень репозитория кода и документации.

Мой вопрос в том, как я могу объяснить очевидное увеличение скорости, когда включена моя библиотека ведения журнала?

Изменить. Это было решено после того, как вы попробовали предложения, приведенные в комментариях. Мой объект журнала создается на строке 24 тестового теста. По-видимому, когда LOG_INIT() касается объекта журнала, он вызывает ошибку страницы, которая заставляет некоторые или все страницы буфера изображения отображаться в физическую память. Я все еще не уверен, почему это улучшает производительность почти на полсекунды; даже без объекта журнала, первое, что происходит в функции mandelbrot_thread(), - это запись в нижнюю часть буфера изображения, которая должна иметь аналогичный эффект. Но, в любом случае, очистка буфера с помощью memset() перед началом теста делает все более разумным. Текущие эталоны здесь

Другие вещи, которые я пробовал:

  • Запустите его с профилировщиком oprofile. Я никогда не мог заставить его регистрировать любое время в замках, даже после увеличения работы, чтобы он работал около 10 минут. Почти все время находилось во внутреннем цикле вычисления Мандельброта. Но, может быть, теперь я смогу интерпретировать их по-другому, потому что знаю об ошибках страницы. Я не думал проверять, занималась ли запись изображения непропорциональным количеством времени.
  • Удаление блокировок. Это оказало значительное влияние на производительность, но результаты все еще были странными, и в любом случае я не мог изменить ни один из многопоточных вариантов.
  • Сравните сгенерированный код сборки. Были различия, но сборка протоколов явно делала больше вещей. Ничто не выглядело как очевидный убийца производительности.
4b9b3361

Ответ 1

При первом доступе к неинициализированной памяти ошибки страницы будут влиять на время.

Итак, перед первым вызовом std::chrono::steady_clock::now() инициализируйте память, запустив memset() в sample_buffer.