Чи точне пошуку джерела повільніше, ніж просто читання вмісту файлів?


13

Я написав модуль PowerShell, який втягує визначення функцій з різних вихідних файлів (наприклад, один .ps1 файл на функцію). Це дозволяє нам (як команда) паралельно працювати над різними функціями. Модуль (.psm1 файл) отримує список доступних .ps1 файлів ...

$Functions = Get-ChildItem -Path $FunctionPath *.ps1

... потім перебирає список і виводить у кожне визначення функції за допомогою точкового пошуку:

foreach($Function in $Functions) {
  . $Function.Fullname                                     # Can be slow
}

Проблема: Ми помітили, що швидкість, з якою це завершується, може змінюватись в значній мірі, від 10 до 180 секунд для приблизно 50 вихідних файлів, залежно від того, на якій машині ми перевіряємо. Ми не можемо пояснити велику різницю часу, що займається, і вважаємо, що ми контролювали такі змінні, як тип машини, ОС, обліковий запис користувача, дозволи адміністратора, профіль PS, версія PS тощо. Зайнятий час може змінюватися для одного хоста для одного і того ж хоста користувач від одного дня до другого.

Нам було цікаво, чи це проблема з доступом до диска, і перевірили, наскільки швидко ми можемо просто читати з диска. Виявляється, що перегляд Get-Contentусіх цих файлів пройшов дуже швидко, чим ми скористалися для вирішення проблеми:

foreach($Function in $Functions) {
  Invoke-Expression (Get-Content $Function.Fullname -Raw)  # Is quick
}

Чому додавання цих функцій за допомогою пошуку точок відбувається набагато повільніше, ніж читання та виконання вмісту файлу?

Відповіді:


17

Налагодження науки

По-перше, кілька сценаріїв, які допоможуть нам перевірити це. Це генерує 2000 файлів скриптів, кожен з яких має одну маленьку функцію:

1..2000 | % { "Function Test$_(`$someArg) { Return `$someArg * $_ }" > "test$_.ps1" }

Цього повинно бути достатньо, щоб нормальний пусковий наклад не мав великого значення. Ви можете додати більше, якщо хочете. Це завантажує їх усіх за допомогою точкового пошуку:

dir test*.ps1 | % {. $_.FullName}

Це завантажує їх усіх, спочатку читаючи їхній вміст:

dir test*.ps1 | % {iex (gc $_.FullName -Raw)}

Тепер нам потрібно провести серйозну перевірку того, як працює PowerShell. Мені подобається JetBrains dotPeek для декомпілятора. Якщо ви коли-небудь намагалися вставити PowerShell в додаток .NET , ви побачите, що збірка, яка включає більшість відповідних матеріалів, є System.Management.Automation. Декомпілюйте цю програму в проект та PDB.

Щоб побачити, де проводиться весь цей загадковий час, ми скористаємося профілером. Мені подобається вбудований у Visual Studio. Це дуже просто у використанні . Додайте папку, що містить PDB, до місць символів . Тепер ми можемо виконати профільний екземпляр PowerShell, який просто запускає один із тестових сценаріїв. (Встановіть параметри командного рядка для -Fileповного шляху першого сценарію для спроби. Встановіть місце запуску в папку, що містить усі крихітні сценарії.) Після того, як це буде зроблено, відкрийте Властивості для powershell.exeзапису в розділі Цілі та змініть аргументи для використання іншого сценарію. Потім клацніть правою кнопкою миші найвищий елемент у програмі «Провідник продуктивності» та виберіть « Почати профілювання». Профілер знову запускається за допомогою іншого сценарію. Тепер ми можемо порівняти. Переконайтесь, що ви натискаєте "Показати весь код", якщо вам надана можливість; для мене це відображається в області сповіщень у підсумковому перегляді зразка звітності про моделювання.

Результати приходять

На моїй машині у Get-Contentверсії 2000 сценаріїв знадобилося 9 секунд. Важливими функціями на "Гарячому шляху" були:

Microsoft.PowerShell.Commands.GetContentCommand.ProcessRecord
Microsoft.PowerShell.Commands.InvokeExpressionCommand.ProcessRecord

Це має багато сенсу: нам потрібно чекати, Get-Contentщоб прочитати вміст з диска, і нам доведеться чекати, Invoke-Expressionщоб скористатися цим вмістом.

У версії дот-джерела моя машина витратила трохи більше 15 секунд на обробку цих файлів. Цього разу функції на "Гарячому шляху" були нативними методами:

WinVerifyTrust
CodeAuthzFullyQualifyFilename

Другий там, як видається, недокументований, але WinVerifyTrust"виконує дію перевірки довіри на визначеному об'єкті". Це приблизно настільки розпливчасто, як ви можете отримати, але іншими словами, ця функція перевіряє справжність даного ресурсу за допомогою даного постачальника. Зауважте, що для PowerShell я не ввімкнув будь-яких фантазійних питань безпеки, і моя політика виконання сценарію така Unrestricted.

Що це означає

Коротше кажучи, ви чекаєте, коли кожен файл якимось чином перевірятиметься, ймовірно, перевіряється на підпис, хоча це не потрібно, коли ви не обмежуєте сценарії, дозволені до запуску. Коли ви, gcа потім iexвміст, це як би ви набрали функції на консолі, тому немає ресурсу для перевірки.


2
Бен, дякую за цю чудову відповідь. Вразило, що ви зайшли до декомпіляції, що є кроком понад усе, що я спробував. Я побачу, чи є якийсь спосіб я дотримуватися вашого методу тестування на одній із машин, де ця проблема є найгострішою. Це може зайняти багато часу, тому не затримуйте дихання!
Чарлі Джойнт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.