- PVSM.RU - https://www.pvsm.ru -
«Проводник тратит 700 мс на то, чтобы открыть контекстное меню панели задач. 75% этого времени он выполняет 114 801 операцию считывания из одного файла, средний объём считываемых данных 68 байт.
Мне стоит написать пост об этом, или достаточно саркастичного твита?»
За компьютером я работаю быстро, и поэтому меня раздражает, когда приходится ждать завершения операции, которая должна выполняться мгновенно. Постоянной помехой на моём сверхмощном домашнем ноутбуке стало медленное закрывание окон на панели задач. Я нажимаю правой клавишей на значок, жду, пока откроется меню, а затем выбираю «Закрыть окно». Самым медленным в этом процессе должны быть перемещения мыши, но выясняется, что наиболее долгим компонентом оказывается задержка перед появлением меню.
Это напрягало меня уже давно, но я проявлял нехарактерный мне самоконтроль и удерживался от раздражения. Так было до сегодняшнего дня, когда я наконец сорвался и схватился за трассировщик ETW [1].
Этот пост написан как проверка скоростного блогинга. От момента нахождения проблемы и саркастичного твита [2] о ней до публикации поста прошло примерно 90 минут.
Трассировщик ETW фиксировал, как я нажимаю правой клавишей мыши на панели задач и закрываю два окна «Проводника». Я использовал функцию UIforETW [3] Tracing to file с опциями по умолчанию, получив в результате лог диагностики на 20,9 МБ [4].
Иногда самое трудное в анализе трассировки заключается в поиске места возникновения проблемы, но в данном случае эта часть анализа оказалась тривиальной. Было три недвусмысленных сигнала, указывавших на нужное место, и болезненно очевидный виновник преступления.
Первым сигналом были события ввода. UIforETW содержит интегрированный логгер ввода [5] (достаточно анонимизированный, чтобы я случайно не украл пароли или личную информацию), поэтому мне достаточно было подробно изучить события MouseUp и Button Type со значением 2, соответствующим правой клавише мыши. При этом на временной шкале WPA [6] появляются отметки моментов, когда происходят эти события — см. вертикальные линии на скриншоте:
Это дало мне понять, что когда я отпускаю правую клавишу мыши, то спустя 600 мс происходит смена фокуса окна, что, по моей догадке, соответствует моменту отображения меню. Кроме того, в RuntimeBroker.exe присутствует чёткий блок активности ЦП между событиями отпускания клавиши мыши и смены фокуса окна.
Не доказано, что смена фокуса окна и активность ЦП связаны, но сделав измерения при помощи приложения записи экрана, я увидел, что на отображение меню тратится примерно 660 мс, поэтому я склонен этому доверять.
Следующим шагом будет изучение того, чем занят RuntimeBroker.exe. Хотя CPU Usage (Precise) [7] отлично помогает увидеть, сколько времени ЦП использует процесс, и почему он простаивает, подходящим инструментом для выяснения того, на что тратится время ЦП, является таблица CPU Usage (Sampled) [8]. Я внимательно её изучил и быстро обнаружил, что 264 сэмпла приходится на KernelBase.dll!ReadFile:
Поискав ещё немного, я нашёл другие стеки вызовов, которые тоже затрагивали эту функцию, поэтому я нажал правой клавишей мыши и выбрал View Callers-> By Function. Включившийся при этом режим (с инвертированными стеками) показал, что из 899 сэмплов этого процесса 628 сэмплов, или 70%, из разных стеков вызовов проходили через эту функцию:
271 сэмпла в этом потоке не проходили через функцию, а оставшиеся сэмплы (не показаны) находились в других потоках.
Заметьте, что 899 сэмплов в потоке 10 252 обозначают два щелчка мыши, то есть примерно 450 сэмплов или 450 мс (при стандартной частоте сэмплирования 1 кГц) на один щелчок мыши.
CPU Usage (Sampled) показывает время ЦП, поэтому дисковый ввод-вывод здесь обычно не отображается, ведь в эти моменты поток засыпает и ждёт диск. То, что операции ввода-вывода отображаются как время ЦП, означает, что все считывания пришлись на кэш системы и время ЦП было лишними ресурсами ядра (см. ntoskrnl.exe в первом сэмплированном стеке вызовов), потраченными на получение данных из кэша.
Теперь, когда под подозрение попал файловый ввод-вывод, нам необходимо перейти в Graph Explorer-> Storage-> File I/O. Немного настроив внешний вид столбцов, мы получили следующий впечатляющий результат:
Он сообщает нам, что в потоке 10 252 за два щелчка мыши RuntimeBroker.exe создал 229 604 вызова ReadFile, считав в сумме 15 686 586 байт. То есть в среднем каждый раз считывалось по 68 байт.
Задумайтесь об этом на секунду.
Не забывайте, что это вызовы к операционной системе — вызовы ядра. Это значит, что между RuntimeBroker.exe и этим файлом отсутствует кэширование. На самом деле сам файл имеет длину всего 4 027 904 байт, то есть каждый раз, когда я нажимаю на значок проводника в панели задач, файл считывается 1,9 раза самым медленным из возможных способов.
Подстрекаемый своими читателями, я решил удалить этот файл и теперь меню списка переходов проводника появляются почти мгновенно. Я не рекомендую этого делать, но удаление файла может помочь и вам (ценой потери истории проводника):
%appdata%MicrosoftWindowsRecentAutomaticDestinationsf01b4d95cf55d32a.automaticDestinations-ms
Выигрыш от удаления файла показан на этом скриншоте WPA. Тонкие вертикальные линии — это нажатия правой клавиши мыши, а прямоугольники в нижней строке обозначают момент отображения меню. Я удалил файл после первых двух кликов и при последних двух кликах задержка значительно снизилась:
Или же «можно воспользоваться официальным прибамбасом из „Параметров“, чтобы очистить (нажать дважды) или отключить (нажать один раз) списки переходов в меню „Пуск“/панели задач. Чтобы ещё больше повысить скорость, отключите также анимации». Спасибо, Рафаэль [9]!
Похоже, что эту проблему можно решить. Снизив задержку отображения меню панели задач проводника, было бы здорово рассмотреть способы устранения других задержек, чтобы все меню панели задач появлялись мгновенно, а не спустя сотни миллисекунд.
Автор: PatientZero
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/optimizatsiya-koda/329623
Ссылки в тексте:
[1] ETW: https://randomascii.wordpress.com/2015/09/24/etw-central/
[2] саркастичного твита: https://twitter.com/BruceDawson0xB/status/1170924849004367873
[3] UIforETW: https://randomascii.wordpress.com/2015/09/01/xperf-basics-recording-a-trace-the-ultimate-easy-way/
[4] лог диагностики на 20,9 МБ: https://github.com/randomascii/blogstuff/tree/master/TaskbarLatency
[5] логгер ввода: https://github.com/google/UIforETW/blob/master/UIforETW/KeyLoggerThread.cpp
[6] WPA: https://randomascii.wordpress.com/2012/06/19/wpaxperf-trace-analysis-reimagined/
[7] CPU Usage (Precise): https://randomascii.wordpress.com/2012/05/05/xperf-wait-analysisfinding-idle-time/
[8] таблица CPU Usage (Sampled): https://randomascii.wordpress.com/2013/04/23/xperf-for-excess-cpu-consumption-wpa-edition/
[9] Спасибо, Рафаэль: https://twitter.com/WithinRafael/status/1171228479251025920
[10] базы данных выполняют считывание по 4 кибибайта: https://randomascii.wordpress.com/2012/08/19/fixing-another-photo-gallery-performance-bug/
[11] https://aka.ms/AA60dfg: https://aka.ms/AA60dfg
[12] shift+правой клавишей мыши на значки: https://twitter.com/ericlaw/status/1171030922755530753
[13] здесь: https://news.ycombinator.com/item?id=20916072#20919612
[14] здесь: https://www.reddit.com/r/programming/comments/d1ofql/taskbar_latency_and_kernel_calls/
[15] здесь: https://twitter.com/BruceDawson0xB/status/1171107806373539841
[16] Источник: https://habr.com/ru/post/466941/?utm_campaign=466941&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.