Что может заставить программу работать намного быстрее во второй раз?

29

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

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

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

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

EDIT: для уточнения, я пишу в Delphi, хотя я действительно не думаю, что это проблема, связанная с Delphi. Но это означает, что независимо от проблемы, это не связано с проблемами JIT, проблемами сбора мусора или любым другим багажом, который управляет кодом. И я не имею дело с сетевыми подключениями. Это чистая обработка, привязанная к процессору.

Один пример: компилятор script. Он работает следующим образом:

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

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

  • 6
    Вы уверены, что это не кэширование ввода / вывода? Для меня это похоже на уровень кэширования ввода / вывода в ОС. Расчеты CPU не кэшируются, поэтому оно должно быть что - то в памяти где - то ...
  • 0
    Какой язык? Добавить тег
Показать ещё 17 комментариев
Теги:
performance
caching

9 ответов

13

Три вещи, которые нужно попробовать:

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

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

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

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

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

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

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

  • 1
    Я не считаю время загрузки здесь. Все это управляется с помощью графического интерфейса, так что я могу убедиться, что все загружено до того, как начнется весь процесс обработки чисел.
  • 1
    Я должен признать, что, если подумать, надувание от 2 секунд до 15 секунд кажется неправдоподобным для этих эффектов в любом случае.
Показать ещё 4 комментария
4

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

  • 1
    Нет, это все, что происходит после того, как все было загружено с диска. Я сделал все возможное, чтобы учесть это.
  • 1
    Сколько временного пространства использует ваш код? Может быть, дело не в том, что содержимое разбито на страницы, в этом проблема, может быть, дело в том, чтобы разбить страницы , чтобы освободить место. Как только она выгружается и ваша программа завершает работу, система будет иметь много доступной памяти для последующих запусков. Заметили ли вы, что ваш браузер или другие программы имеют тенденцию «колебаться» при первом обращении к ним после выполнения кода?
4

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

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

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

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

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

2

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

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

  • 2
    AFAIK, даже старый AMD Athlon 64 мог менять частоту 30 раз в секунду, так что это не объясняет 13-секундный разрыв.
2

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

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

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

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

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

1

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

  • 0
    Нету. Родной код, нет сетевых подключений.
1

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

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

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

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

  • 0
    Извините, пишу в Delphi. Никакой управляемый байткод-материал, вызывающий проблему здесь.
  • 0
    +1 - все еще хороший ответ, даже если он неправильный.
0

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

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

  • Внутренний кэш диска (8 МБ или более)
  • Зависит от приложения Windows (как DLL)/Core cache
  • Кэш CPU L3 (или L2, если некоторый цикл программирования достаточно мал)

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню