Я розглядав можливість використання Lazy<T>
властивостей, щоб поліпшити продуктивність власного коду (і дізнатися трохи більше про нього). Я прийшов сюди, шукаючи відповіді про те, коли його використовувати, але здається, що скрізь, де я заходжу, є фрази на кшталт:
Використовуйте ледачу ініціалізацію, щоб відкласти створення великого або ресурсомісткого об’єкта або виконання ресурсомісткого завдання, особливо коли таке створення або виконання може не відбуватися протягом життя програми.
від класу MSDN Lazy <T>
Я трохи розгублений, бо не знаю, де провести лінію. Наприклад, я розглядаю лінійну інтерполяцію як досить швидке обчислення, але якщо мені не потрібно цього робити, чи може лінива ініціалізація допомогти мені уникнути цього і чи варто цього?
Врешті-решт я вирішила спробувати власний тест, і я подумала, що поділюсь результатами тут. На жаль, я не дуже досвідчений в проведенні подібних тестів, тому я радий отримати коментарі, які пропонують вдосконалення.
Опис
У моєму випадку мені було особливо цікаво побачити, чи може Lazy Properties допомогти покращити частину мого коду, яка робить багато інтерполяції (більшість з них не використовується), і тому я створив тест, який порівнював 3 підходи.
Я створив окремий тестовий клас з 20 тестовими властивостями (давайте назвати їх t-властивостями) для кожного підходу.
- Клас GetInterp: Запускає лінійну інтерполяцію щоразу, коли отримується t-властивість.
- Клас InitInterp: ініціалізує t-властивості, запустивши лінійну інтерполяцію для кожного з конструкторів. Отримати лише повертає дубль.
- Клас InitLazy: встановлює t-властивості як властивості Lazy, щоб лінійна інтерполяція була запущена один раз, коли властивість вперше отримана. Подальше отримання повинно просто повернути вже обчислений подвійний.
Результати тесту вимірюються в мс і становлять в середньому 50 екземплярів або 20 отриманих властивостей. Кожен тест потім проводили 5 разів.
Результати тесту 1: Моментальне (середнє 50 екземплярів)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.005668 0.005722 0.006704 0.006652 0.005572 0.0060636 6.72
InitInterp 0.08481 0.084908 0.099328 0.098626 0.083774 0.0902892 100.00
InitLazy 0.058436 0.05891 0.068046 0.068108 0.060648 0.0628296 69.59
Результати тесту 2: Перше отримання (в середньому 20 об'єктів отримує)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.263 0.268725 0.31373 0.263745 0.279675 0.277775 54.38
InitInterp 0.16316 0.161845 0.18675 0.163535 0.173625 0.169783 33.24
InitLazy 0.46932 0.55299 0.54726 0.47878 0.505635 0.510797 100.00
Результати тесту 3: Друге отримання (в середньому 20 об'єктів отримує)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.08184 0.129325 0.112035 0.097575 0.098695 0.103894 85.30
InitInterp 0.102755 0.128865 0.111335 0.10137 0.106045 0.110074 90.37
InitLazy 0.19603 0.105715 0.107975 0.10034 0.098935 0.121799 100.00
Спостереження
GetInterp
найшвидше створити так, як очікувалося, оскільки воно нічого не робить. InitLazy
швидше уявити, ніж InitInterp
припускати, що накладні витрати в налаштуванні лінивих властивостей швидше, ніж мій лінійний розрахунок інтерполяції. Однак я трохи заплутався тут, тому що InitInterp
слід робити 20 лінійних інтерполяцій (щоб встановити його t-властивості), але для отримання екземпляра потрібно лише 0,09 мс (тест 1), порівняно з GetInterp
яким потрібно лише 0,28 мс, щоб зробити лише одну лінійну інтерполяцію перший раз (тест 2) і 0,1 мс, щоб зробити це вдруге (тест 3).
Це займає InitLazy
майже в 2 рази довше, ніж GetInterp
перший раз отримати майно, хоча InitInterp
воно найшвидше, оскільки воно заселяло його властивості під час інстанції. (Принаймні, це було б зробити, але чому це результат інстанції був набагато швидшим, ніж одна лінійна інтерполяція? Коли саме це робить ці інтерполяції?)
На жаль, схоже, що в моїх тестах відбувається деяка автоматична оптимізація коду. GetInterp
Щоб отримати об’єкт нерухомості потрібно в один і той же час, як і вдруге, але він відображається як більш ніж у 2 рази швидше. Схоже, що така оптимізація впливає і на інші класи, оскільки для тестування 3. вони займають приблизно стільки ж часу, проте такі оптимізації можуть мати місце і в моєму виробничому коді, що також може бути важливим фактором.
Висновки
Хоча деякі результати, як очікується, є й дуже цікаві несподівані результати, ймовірно, завдяки оптимізації коду. Навіть для класів, схожих на те, що вони роблять багато роботи в конструкторі, результати інстанції показують, що вони все ще можуть бути дуже швидкими для створення, порівняно з отриманням подвійного властивості. Хоча фахівці в цій галузі, можливо, зможуть прокоментувати та дослідити більш ретельно, моє особисте відчуття полягає в тому, що мені потрібно зробити цей тест ще раз, але на своєму виробничому коді, щоб вивчити, які саме оптимізації можуть відбуватися і там. Однак я очікую, що це InitInterp
може бути шлях.
get { if (foo == null) foo = new Foo(); return foo; }
. І є мільйони можливих місць для його використання ...