Что может заставить программу работать намного быстрее во второй раз? - PullRequest
29 голосов
/ 27 сентября 2011

Что я заметил при тестировании кода, который я пишу, так это то, что длительные операции имеют тенденцию запускаться на намного дольше при первом запуске программы, чем при последующих, иногда в 10 и более раз,Очевидно, что здесь есть какая-то проблема холодного / теплого кэша, но я не могу понять, что это такое.

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

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

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

РЕДАКТИРОВАТЬ: Для пояснения, я пишу в Delphi, хотя я не думаю, что этоэто специфичная для Delphi проблемаНо это означает, что, какой бы ни была проблема, она не связана с проблемами JIT, проблемами со сборкой мусора или любым другим багажом, который сопровождает управляемый код.И я не имею дело с сетевыми подключениями.Это чисто процессорная обработка.

Один пример: компилятор скрипта.Он работает так:

  • Загрузка всего файла в память с диска
  • Lex весь файл в очередь токенов
  • Анализ очереди в дереве
  • Запустите codegen на дереве, чтобы получить байт-код

Если я загрузлю его огромный файл сценария (~ 100 тыс. Строк) после загрузки всей вещи с диска в память, шаг lex займет около15 секунд при первом запуске и 2 секунды при последующих запусках.(И да, я знаю, что это еще долго. Я работаю над этим ...) Я хотел бы знать, откуда происходит это замедление и что я могу с этим поделать.

Ответы [ 9 ]

13 голосов
/ 27 сентября 2011

Три вещи, чтобы попробовать:

  • Запустите его в профилировщике выборки, включая «холодный» запуск (первым делом после перезагрузки). Обычно должно быть достаточно.
  • Проверьте использование памяти, не станет ли оно настолько высоким (даже временным), что операционная система должна будет выгружать вещи из ОЗУ, чтобы освободить место для вашего приложения? Одно это может быть объяснением того, что вы видите. Также посмотрите, сколько свободной оперативной памяти у вас есть при запуске приложения.
  • Включите инструменты для повышения производительности системы и проверьте счетчики ввода / вывода или доступ к файлам и убедитесь, что в FileMon / Process Explorer у вас нет доступа к файлам или сети, о которых вы забыли (оставшийся журнал / тестовый код)
5 голосов
/ 27 сентября 2011

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

Я бы тоже так легко не отказывался от кеша процессора. Кэш уровня 0 очень важен для внутренних циклов, но гораздо меньше для второго запуска того же приложения. В моей дешевой системе Athlon 2 X4 645 кэш-память уровня 0 64 КБ + 64 КБ (данные + инструкция) на ядро ​​- не совсем большой объем памяти. Уровень кеша 1 уровня составляет II12 512 Кбайт на ядро, поэтому вероятность того, что он будет неактивен из-за кода O / S, необходимого для запуска нового запуска программы, обращения к службам операционной системы и стандартным библиотекам и т. Д., Кэш уровня 2 на процессорах, у которых он есть - у моего Athlon 2 нет, IIRC) еще больше, и может быть какой-то еще более высокий уровень и больший кэш, предоставляемый материнской платой / чипсетом.

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

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

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

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

4 голосов
/ 27 сентября 2011

Я думаю, это все ваши библиотеки / DLL. Обычно они загружаются по требованию во время выполнения, поэтому при первом запуске вашей программы ОС должна будет прочитать их все с диска. Однако после прочтения они останутся загруженными, если в вашей системе не останется мало памяти. Поэтому, если вы запускаете одну и ту же программу несколько раз подряд, первый запуск берет на себя основное время загрузки, а другие запускаются с использованием предварительно загруженных библиотек.

4 голосов
/ 27 сентября 2011

Я обычно испытывал обратное: для интенсивных вычислений (если антивирус не работает), между вызовами у меня разница только в 5-10%.Например, 6 000 000 регрессионных тестов, запущенных для нашей платформы, имеют очень постоянное время выполнения, и это требует очень много работы с дисками и процессорами.

Я действительно не верю в проблему с кэшем ЦП или конвейерным прогнозированием / ветвлением.либо, поскольку обработанные данные и код, как вы писали, кажутся согласованными.Если антивирус отключен, это может быть связано с настройками потока ОС: вы пытались изменить привязку и приоритет процессора процесса?

Это должно быть очень специфично для процесса, который вы запускаете.Без какого-либо реального исходного кода для его воспроизведения почти невозможно сказать, что с вами происходит.Сколько там потоков?Какова конфигурация HW (нет ли повышения мощности процессора Intel - используете ли вы ноутбук, и каковы ваши настройки энергии)?Использует ли он CPU / FPU / MMX / SSE2 (например, MMX и FPU не смешиваются)?Это перемещает много данных, или обрабатывает некоторые существующие данные?Зависит ли ваше ПО от внешних библиотек (даже некоторым библиотекам Windows может потребоваться некоторое время для инициализации)?Как вы используете память (вы пытались предварительно выделить память; или в многопоточном приложении вы пытались использовать масштабирующий MM вместо FastMM4)?

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

AFAIK всегда было написано, что,при проведении сравнительного анализа первый запуск приложения никогда не учитывается.В наши дни компьютерные системы настолько сложны, что в первый раз необходимо очистить всю внутреннюю (SW и HW) водопроводную систему - поэтому вы не должны пить первую воду, выходящую из вашего крана, когда вы вернетесь из 1-месячного путешествия.;)

2 голосов
/ 27 сентября 2011

Просто случайное предположение ...

Поддерживает ли ваш процессор адаптивную частоту?Возможно, это просто процессор, который не успевает адаптировать свою частоту при первом запуске и работает на полной скорости во втором.

2 голосов
/ 27 сентября 2011

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

Возможно, это как-то связано с физическим макетом страницы? Насколько я знаю, каждый доступ к памяти проходит через записи таблицы страниц MMU, поэтому рассредоточенные физические страницы могут быть медленнее, чем последовательные страницы. (Просто дикое предположение, это)

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

Кстати, как вы определяете «первый запуск»? Может быть, вы только что скомпилировали исполняемый файл? В этом случае (и я просто еще раз догадываюсь здесь), какой-то процесс (либо ОС, либо антивирусный сканер, либо даже какой-нибудь руткит) может быть занят анализом поведения вашего исполняемого файла, который может быть пропущен после того, как исполняемый файл был анализировали раньше. Вы могли бы попытаться доказать это, изменив некоторый случайный неважный байт вашего исполняемого файла между запусками, и посмотреть, не повлияет ли это снова на время выполнения?

Пожалуйста, опубликуйте резюме, как только вы выяснили причину (и) - это может помочь и другим. Ура!

1 голос
/ 27 сентября 2011

Угадай, что ты используешь .net, если я не прав, ты можешь игнорировать большинство моих идей ...

Пул соединений, JIT-компиляция, отражение, IO Кэширование списка продолжается и продолжается ....

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

Вы можете попробовать создать свои сборки, так как это удалит JIT-компиляцию.

1 голос
/ 27 сентября 2011

Есть много вещей, которые могут вызвать это. В качестве одного примера: если вы используете ADO.NET для доступа к данным с включенным пулом соединений (который используется по умолчанию), то при первом запуске вашего приложения будет удастся создать соединение с базой данных. Когда ваше приложение закрыто, соединение поддерживается в его открытом состоянии на ADO.NET, поэтому в следующий раз, когда ваше приложение запустится и получит доступ к данным, ему не нужно будет создавать экземпляр соединения, и, следовательно, оно будет появляться быстрее.

0 голосов
/ 27 сентября 2011

откуда приходит это замедление и что я могу с этим поделать.

Я бы говорил о быстром выполнении в следующий раз из кеширования производительности

  • Кэш-память на диске (8 МБ или более)
  • Зависимости приложения Windows (в виде DLL) / основной кэш
  • Кэш ЦП L3 (или L2, если какой-то цикл программирования достаточно мал)

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

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