В предыдущей статье Жирные программы-факторы скорости были набросаны наметки по теме — почему программы "тормозят".
Был выделен фактор памяти, как важный, но не было идей по его тестированию его “жирности” в зависимости от фреймворка. И вот, приняв на праздниках спокойное правильное состояние, я такую идею сгенерировал.
Была написана утилита, которая позволяет измерить время загрузки программы и связанные с этим расходы — дисковый ввод/вывод и потребляемую память. Ссылка в конце статьи.
Некоторые тесты под катом.
Возьмем три похожие программы — мини-IDE, написанные соответственно на разных языках и использующие разные фреймворки — в таблице
Программа | Язык | Фреймворк | Ссылка |
---|---|---|---|
DrJava | Java8 | Swing+JGoodies | http://www.drjava.org |
Notepad++ | C++ | WinAPI+Scintilla | https://notepad-plus-plus.org |
SharpDevelop | C# | .NET 4.5 | http://www.icsharpcode.net/OpenSource/SD/Default.aspx |
Каждая программа тестируется после загрузки виртуальной системы — замеряем время первого и повторного запусков. Конфигурация виртуальной машины — чистая Windows 7, 1Gb памяти (340Мб используется под нужды ОС), 2 ядра. Хост — i5 3.2GHz, 8Gb памяти.
Тест 1 — время до открытия окна и запуска очереди сообщений
Смотрим Starting Time. После запуска утилита stime каждые 3 секунды выводит статистику по памяти и дисковому вводу выводу (ошибки страниц), что позволяет мониторить динамическую подгрузку модулей фреймворка.
stime.exe "C:\Program Files\Notepad++\notepad++.exe"
DiskIO: 15.922MB WorkingSetSize: 13.613MB PagefileUsage: 7.5117MB
Starting time is 1333.03 ms, Повторный запуск: 196.751 ms
stime javaw -jar D:\Downloads\drjava-beta-20160913-225446.jar
DiskIO: 96.953MB WorkingSetSize: 72.559MB PagefileUsage: 82.148MB
DiskIO: 103.76MB WorkingSetSize: 73.621MB PagefileUsage: 91.965MB
Starting time is 5963.89 ms, Повторный запуск: 4541.6 ms
stime.exe -splash "C:\Program Files\SharpDevelop\5.1\bin\SharpDevelop.exe"
DiskIO: 85.934MB WorkingSetSize: 54.414MB PagefileUsage: 45.117MB
DiskIO: 136.74MB WorkingSetSize: 71.609MB PagefileUsage: 66.969MB
DiskIO: 137.43MB WorkingSetSize: 72.09MB PagefileUsage: 67.035MB
Starting time is 7863.89 ms, Повторный запуск: 1405.49 ms
Была замечена небольшая некорректность со временем старта — время засекается функцией, контролирующей начало обработки очереди сообщений — но некоторые программы, например DrJava, начинают ее обрабатывать в фоновом режиме, хотя загрузка еще продолжается очень долго.
Потому для Явы используется обходной путь — ожидаем дополнительно появления видимого окна с непустым заголовком.
Аналогично, для SharpDevelop ожидание создания главного окна и подавление сплеш включено специальной опцией. И даже после появления главного окна SharpDevelop еще прилично подгружался — что видно по DiskIO через 3с после старта.
Еще интересный момент — когда виртуальная машина была сконфигурирована однопроцессорной — время загрузки Явы вырастало втрое (~35c), для С++ и .NET программ это не влияло.
Тест 2 — время загрузки файла исходного кода и отработки команды выхода
При запуске с опцией -quit, утилита stime сразу после запуска дочернего процесса посылает команду закрытия WM_QUIT. Смотрим Full run time — засечку по выгрузке процесса из памяти.
stime.exe -quit "C:\Program Files\Notepad++\notepad++.exe" stime.cpp
DiskIO: 16.734MB WorkingSetSize: 14.32MB PagefileUsage: 8.0664MB
Full run time is 1611.67 ms, Повторный запуск: 240.524 ms
stime.exe -quit javaw -jar drjava-beta-20160913-225446.jar JsonIterator.java
DiskIO: 97.992MB WorkingSetSize: 75.52MB PagefileUsage: 100.41MB
Full run time is 6860.405 ms, Повторный запуск: 4533.613 ms
stime.exe -splash -quit "C:\Program Files\SharpDevelop\5.1\bin\SharpDevelop.exe" kernel.cs
DiskIO: 127.4MB WorkingSetSize: 68.148MB PagefileUsage: 53.133MB
Full run time is 9752.181 ms, Повторный запуск: 3066.784 ms
Здесь тоже приходится применять обходной путь для Явы — после того, как окно приложения закрылось, пристреливаем javaw.exe
> Исходники программы stime и бинарник на GitHub
Выводы — загрузка фреймворка — очень дорогая операция, поскольку стоит большого количества рандомного ввода-вывода. Хотите быструю программу — делайте ее компактными средствами.
Ну а для подробной диагностики, на забываем про более подробные утилиты Руссиновича и средства профилирования для своего набора инструментов.
Комментарии (9)
a1888877
10.05.2017 21:48+4Т.е. вы взяли три разных программы, по одной от Java/C++/C# и по скорости их загрузки делаете выводы о среде выполнения? Вам не кажеться, что между «экспериментом» и выводом причинно следственных связей нет? Без относительно того, сколько environment java/.net отжирает?
Siemargl
10.05.2017 21:52+1Мне кажется, что связь все-таки есть.
Чтобы запустить свою небольшую программу — приходится загрузить с диска 100Мб => (следовательно) загрузка медленная.
Инструмент приложен — можно пробовать подтвердить или опровергнуть гипотезу. На своих примерах.
cranium256
11.05.2017 02:46Проблема в том, что сейчас редко кто уделяет внимание эффективности программ. Об этом просто не заморачиваются.
ssh24
11.05.2017 09:12Любопытно, почему в своем тесте вы выбрали DrJava, а например не jEdit?
С другой стороны, чтобы посильнее задвинуть джаву, то надо было брать сразу Eclipse.
Siemargl
11.05.2017 10:09-1Например, потому что не знал про него. Симпатичная программка.
jEdit, Java8, Swing
Тест 1
DiskIO: 58.906MB WorkingSetSize: 50.844MB PagefileUsage: 59.063MB
DiskIO: 61.371MB WorkingSetSize: 52.121MB PagefileUsage: 79.465MB
Starting time is 5577.73 ms Повторный запуск: 2917.75 ms
Тест 2
DiskIO: 58.496MB WorkingSetSize: 51.059MB PagefileUsage: 59.078MB
Full run time is 5420.59 ms Повторный запуск: 2581.902 ms
Компактнее и быстрее, чем DrJava
aleksandy
18.05.2017 12:46Не Eclipse, а Netbeans или Idea.
Eclipse на swt писан, который весь UI делегирует нативным библиотекам. А вот Netbeans/Idea — это свинговые приложения.
remzalp
Bernard Goossens уже несколько лет пилит идею профайлера, который будет выдавать всегда повторимые результаты времени выполнения при одинаковых входных данных и любом количестве перезапусков. Идея в том, чтобы запускать программу на эмуляторе процессора, который аккуратно будет отчитываться в логи. В этом случае убирается воздействие кода операционной системы, который выполняется параллельно.
Пока что только ARM архитектура поддерживается.
Подозреваю, аналогичным решением можно и остальное профайлить гораздло более суровым методом
lany
Ну можно как минимум hardware performance counters заиспользовать, они стабильнее. Количество выполненных инструкций, например, считать. Хотя суперстабильности всё равно не будет. JIT-компилятор в Java, например, легко выдаст неустойчивый результат в зависимости от гонки между потоками. То есть гонка на ранней стадии работы программы повлияет на сгенерированный ассемблерный код, который будет с вами до конца работы программы и может дать вполне ощутимую разницу в производительности.
А тут ещё в IO многое упирается. На рамдиске наверняка будут существенно другие результаты. Поэтому замеры чисто процессорного времени непрактичны.