Что происходит между Application.Run и Form.Load?

У меня есть приложение WinForms, написанное на VB.NET для Framework 4.5. Я заметил, что время запуска приложения необычно велико (другие написанные мной приложения, которые выполняют еще больше работы при запуске, запускаются почти мгновенно, этому приложению требуется> 5 секунд). Время запуска не меняется после нескольких запусков, так что я думаю это не случай некэшированного кода CLR при первом запуске приложения.

Я провел некоторое тестирование, записав время во время запуска:

Module modMain
    Public MyLog As System.Text.StringBuilder

    <STAThread>
    Public Sub Main()
        MyLog = New System.Text.StringBuilder

        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        MyLog.AppendLine("Before run: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
        Application.Run(frmMain)
    End Sub
End Module

Sub Main() — точка входа приложений. Он запускает frmMain, и первое, что я могу контролировать, это Sub InitializeComponent(), сгенерированный дизайнером:

<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
    MyLog.AppendLine("Init Start: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
    'All the control initializations
    MyLog.AppendLine("Init End: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
End Sub

И, наконец, я прибываю на мероприятие Form.Load

Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    MyLog.AppendLine("Form_Load Start: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
    '...
    MyLog.AppendLine("Form_Load End: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
End Sub

Теперь вывод MyLog выглядит следующим образом:

Before run: 15.12.2014 19:56:47,579
Init Start: 15.12.2014 19:56:51,451
Init End: 15.12.2014 19:56:51,521
Form_Load Start: 15.12.2014 19:56:51,544
Form_Load End: 15.12.2014 19:56:51,547

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

Итак, мой вопрос: что именно происходит между Application.Run и моментом, когда я восстанавливаю контроль над своим кодом, и могу ли я что-то сделать, чтобы ускорить его? Связана ли выполненная там работа с компонентами формы?

Я уже пробовал использовать frmMain.ShowDialog() вместо Application.Run(frmMain), но это привело к тем же результатам. Я использую Visual Studio Express, поэтому, к сожалению, я не могу использовать более глубокий профилировщик производительности.

Отметив это как C#, так и VB.NET, потому что очень приветствуются ответы на обоих языках.

EDIT
Я провел еще несколько тестов, включая предложенное решение в ответе SLaks. Использование NGEN для предварительной компиляции сборки не дало заметного эффекта. Так что я думаю, что это не JIT-компиляция кода InitializeComponent.

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

Windows 7: Slow start
Windows 8.1: Fast start
Windows Server 2008: Fast start

Это просто дополнительные подсказки, я действительно не знаю, полезно ли это для ответа.

EDIT 2
Глядя на ProcMon во время запуска, я обнаружил, что выполнение зависает на следующих строках:

"15:56:29.3547260","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources.dll","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3548019","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources\Electrochemical Calculator.resources.dll","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3548612","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources.exe","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3549519","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources\Electrochemical Calculator.resources.exe","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:32.8796760","Electrochemical Calculator.exe","5972","CreateFile","C:\Windows\Fonts\StaticCache.dat","SUCCESS","Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened"
"15:56:32.8797088","Electrochemical Calculator.exe","5972","QueryStandardInformationFile","C:\Windows\Fonts\StaticCache.dat","SUCCESS","AllocationSize: 9,633,792, EndOfFile: 9,633,792, NumberOfLinks: 1, DeletePending: False, Directory: False"
"15:56:32.8797218","Electrochemical Calculator.exe","5972","ReadFile","C:\Windows\Fonts\StaticCache.dat","SUCCESS","Offset: 0, Length: 60, Priority: Normal"
"15:56:32.8797429","Electrochemical Calculator.exe","5972","CreateFileMapping","C:\Windows\Fonts\StaticCache.dat","FILE LOCKED WITH ONLY READERS","SyncType: SyncTypeCreateSection, PageProtection: "

В дальнейшем проблема возникает только в сборке Release и только тогда, когда я запускаю программу непосредственно из проводника Windows. Сборка отладки запускается мгновенно (0,3 секунды по сравнению с 5-10 секундами), как и сборка выпуска при запуске из Visual Studio.


person Jens    schedule 15.12.2014    source источник
comment
Добавьте вызов журнала в Sub New и посмотрите, куда он падает.   -  person SLaks    schedule 15.12.2014
comment
msdn.microsoft.com/en-us/magazine/cc163655.aspx   -  person Hans Passant    schedule 15.12.2014
comment
Вы можете попробовать запустить Process Monitor, чтобы увидеть, что происходит на нижнем уровне. уровне в течение этих пяти секунд.   -  person Damien_The_Unbeliever    schedule 18.12.2014
comment
Бывает много. Если вы действительно хотите знать, что происходит, взгляните на эту книгу «Внутреннее устройство Windows» noreferrer">amazon.com/Windows-Internals-Edition-Developer-Reference/dp/   -  person user743414    schedule 18.12.2014
comment
Глядя на ваш Edit 2, становится очевидным, что есть некоторые проблемы при доступе к ресурсам (либо из-за того, что они не находятся на правильном пути, либо из-за того, что пользователю отказано в доступе к ним), это может вызывать исключения (которые, вероятно, попали в ловушку), которые обычно вызывают такие задержки. Убедитесь, что все файлы в порядке, и пользователь, запускающий их, имеет разрешения на папку, в которой вы их запускаете.   -  person Jcl    schedule 18.12.2014
comment
Под какую разрядность компилируете? И видите ли вы разницу, если вы «запускаете сборку релиза от имени администратора» из проводника?   -  person Geoff    schedule 18.12.2014
comment
Я использую AnyCPU и работа от имени администратора не имеет значения.   -  person Jens    schedule 18.12.2014


Ответы (3)


Что ж, вы устранили все нормальные источники задержки запуска. Это определенно не имеет ничего общего с Application.Run(), который не запускается до после завершения работы метода InitializeComponent() формы. И вы устранили накладные расходы, используя Ngen.exe. Обязательно различайте задержки холодного и теплого запуска, если он медленный только при первом запуске программы, то это аппаратная проблема, вам нужен более быстрый диск. Тот факт, что он работает медленно только на некоторых машинах, убедительно указывает на проблему с окружающей средой.

Запускается кодом, который выполняется в InitializeComponent(). Другими словами, все конструкторы и установщики свойств элементов управления в форме. Обычно это занимает очень мало времени, но, безусловно, вокруг есть нарушители спокойствия. Вам нужно искать нетривиальный элемент управления, особенно такой, который использует COM (также известный как ActiveX) под капотом. Как веб-браузер. Есть еще много возможностей, все, чье имя класса начинается с "Axe". Такие элементы управления могут загружать множество других библиотек DLL и могут быть интересны программному обеспечению безопасности.

Некоторые советы по отладке:

  • Убедитесь, что у вас есть хорошая резервная копия, и начните удалять элементы управления из формы, начиная с нетривиальных.
  • 5-секундной задержки достаточно, чтобы ее можно было заметить при пошаговом выполнении метода InitializeComponent(). Мгновенно сообщает вам, какой конкретный элемент управления и инструкция вызывают задержку.
  • Переключение отладчика в неуправляемый режим дает гораздо больше информации о том, какие другие библиотеки DLL загружаются в вашу программу. Project + Properties, вкладка «Отладка», установите флажок «Включить отладку собственного кода». Следите за окном вывода во время отладки, вы увидите уведомления о загрузке любой неуправляемой библиотеки DLL. Это может точно указать конкретную DLL, которая вызывает задержку.
  • По возможности отключайте защиту от вредоносных программ, чтобы избежать задержек из-за несоответствующих сканирований DLL.
  • Утилита TcpView от SysInternals отлично подходит для обнаружения сетевых задержек. Будьте осторожны, когда вы видите, что ваша программа связывается с сервером CRL, запросы списка отзыва сертификатов могут быть медленными.
  • Утилита SysInternals Process Monitor отлично подходит для отслеживания задержек, вызванных практически любыми причинами. еще. Трассировка может быть очень большой, сохраните ее и разместите на сайте для обмена файлами, если вам нужна другая группа глаз.
person Hans Passant    schedule 18.12.2014
comment
Спасибо за хороший ответ. Я впервые заметил сейчас, что проблема возникает только в режиме Release Build и только при запуске из проводника (из VS тоже быстро). Кажется, он зависает на одной конкретной строке, связанной с созданием какого-то файла ресурсов, когда я смотрю на ProcMon (добавлю строки в свой вопрос). - person Jens; 18.12.2014
comment
О, но в любом случае это как-то связано со сложностью формы. Когда я удаляю элементы управления, он ускоряется. Таким образом, Дизайнер в некотором роде может быть виновником. Однако я не могу разглядеть существенной разницы в сгенерированном коде (смотря на ILSpy) между двумя версиями. Однако шаг оптимизации определенно вызывает разницу (я отключил его в сборке Release, и он снова работает быстро). - person Jens; 18.12.2014
comment
Сильный намек на использование антивирусного ПО. - person Hans Passant; 18.12.2014
comment
Поскольку награда заканчивается, я награжу этот ответ, так как он обеспечивает наиболее подробный вид. Мне пока не удалось отследить источник проблемы, но я продолжу тестирование. Anti-Malware все еще может быть виновником, хотя я пытался избежать этого, но я могу проверить это дальше. Еще раз спасибо за ваш вклад! - person Jens; 24.12.2014

Это время тратится на загрузку каждой сборки, используемой элементами управления вашей формы, и на JIT-компиляцию метода InitializeComponent.

person SLaks    schedule 15.12.2014
comment
Я вижу, это имеет смысл, так как у меня довольно много элементов управления в форме, размещенных в различных TableLayoutPanels, поэтому код для InitializeComponent довольно длинный. С другой стороны, я ссылаюсь только на 7 сборок .NET и ничего более, поэтому я думаю, что виновником является JIT-компиляция. Есть ли способ ускорить его, или мне просто нужно добавить заставку и жить с этим? - person Jens; 15.12.2014
comment
Вы можете ngen сборки. - person SLaks; 15.12.2014
comment
Я сейчас попробовал это, но это не дало заметного эффекта. Пожалуйста, смотрите отредактированный вопрос, если хотите. - person Jens; 18.12.2014

Как сказал Ганс, вы уже устранили очевидное, и мне кажется, что проблема связана с окружающей средой... так как же устранить неполадки в этом, учитывая экстремальные условия...

Поскольку процесс постоянно занимает 5 или более секунд (при запуске из проводника), вот некоторые из подходов, которые я мог бы использовать в этой ситуации:

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

  • Используйте Procdump, чтобы запустить процесс, и выполните n полный< /strong> дамп после того, как Elapsed Time счетчик производительности для процессов достигает некоторого значения (например, 2, 3 или 4). Затем вы можете открыть эти дампы в windbg (большая часть информации, сложная в использовании) или VS.Net (проще в использовании, но может показать или не показать то, что вы хотите найти, даже если оно там есть). Используйте отладку в смешанном режиме, когда открытие дампа).

  • В том же духе, но менее мощный, более сложный в настройке. Настройте Process Explorer чтобы показать собственные трассировки стека при просмотре свойств процесса... Как только вы запустите процесс, переключитесь в ProcessExplorer и просмотрите трассировки стека для различных потоков в процессе. Это сложнее, потому что в зависимости от того, сколько потоки есть, ручной просмотр их трассировки стека может вообще не работать. Возможно, стоит попробовать или два, если это сделает проблему очевидной с наименьшими усилиями.

В любом случае убедитесь, что ваши символы настроены на общедоступные серверы символов Microsoft. Так что вы получите большую часть информации из собственных трассировок стека.

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

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

person Vikas Gupta    schedule 19.12.2014