Я подумывал об использовании Lazy<T>
свойств, чтобы улучшить производительность моего собственного кода (и узнать немного больше об этом).Я пришел сюда в поисках ответов о том, когда его использовать, но, кажется, везде, где я бываю, есть такие фразы:
Используйте отложенную инициализацию, чтобы отложить создание большого или ресурсоемкого объекта, иливыполнение ресурсоемкой задачи, особенно когда такое создание или выполнение может не произойти в течение срока действия программы.
из MSDN Lazy Класс
Я немного растерялся, потому что не уверен, где провести черту.Например, я рассматриваю линейную интерполяцию как довольно быстрое вычисление, но если мне не нужно это делать, то может ли ленивая инициализация помочь мне избежать этого и стоит ли это того?
В конце концов я решилПопробуйте свой собственный тест, и я подумал, что поделюсь результатами здесь.К сожалению, я не очень разбираюсь в подобных тестах, поэтому я рад получить комментарии, которые предлагают улучшения.
Описание
Для моего случая я былособенно интересно узнать, может ли Lazy Properties помочь улучшить часть моего кода, которая выполняет большую часть интерполяции (большая часть которой не используется), и поэтому я создал тест, который сравнил 3 подхода.
Я создал отдельныйТестовый класс с 20 тестовыми свойствами (давайте назовем их t-свойствами) для каждого подхода.
- GetInterp Class: Запускает линейную интерполяцию при каждом получении t-свойства.
- InitInterp Class: Инициализирует t-свойства, выполняя линейную интерполяцию для каждого в конструкторе.Метод get просто возвращает значение типа double.
- InitLazy Class: Устанавливает t-свойства как свойства Lazy, чтобы линейная интерполяция запускалась один раз при первом получении свойства.Последующие получения должны просто возвращать уже рассчитанный double.
Результаты теста измеряются в мс и являются средним значением 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 результата: First Get (в среднем из 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: Second Get (в среднем 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
, предполагая, что накладные расходы при настройке свойств lazy быстрее, чем мои вычисления с линейной интерполяцией.Тем не менее, я немного запутался, потому что InitInterp
должен выполнять 20 линейных интерполяций (чтобы установить его t-свойства), но для его создания (тест 1) требуется всего 0,09 мс, по сравнению с GetInterp
, который занимает 0,28 мсвыполнить только одну линейную интерполяцию в первый раз (тест 2) и 0,1 мс, чтобы сделать это во второй раз (тест 3).
Требуется InitLazy
почти в 2 раза больше, чем GetInterp
, чтобы получитьсвойство в первый раз, тогда как InitInterp
является самым быстрым, потому что оно заполняет свои свойства во время создания экземпляра.(По крайней мере, это то, что он должен был сделать, но почему его результат был гораздо быстрее, чем одиночная линейная интерполяция? Когда именно он выполняет эти интерполяции?)
К сожалению, похоже, что есть некоторая автоматическая оптимизация кодапроисходит в моих тестах.Чтобы получить свойство в первый раз, потребуется GetInterp
, как и во второй раз, но оно показывается более чем в 2 раза быстрее.Похоже, что эта оптимизация также влияет на другие классы, так как все они занимают примерно одинаковое количество времени для теста 3. Однако такая оптимизация может также иметь место в моем собственном рабочем коде, что также может быть важным фактором.
Выводы
Хотя некоторые результаты соответствуют ожидаемым, есть также некоторые очень интересные неожиданные результаты, вероятно, из-за оптимизации кода. Даже для классов, которые выглядят так, как будто они выполняют большую часть работы в конструкторе, результаты создания экземпляров показывают, что они все еще могут быть созданы очень быстро по сравнению с получением двойного свойства. Хотя эксперты в этой области могут комментировать и исследовать более тщательно, я лично считаю, что мне нужно повторить этот тест снова, но с моим рабочим кодом, чтобы проверить, какие оптимизации могут происходить там же. Тем не менее, я ожидаю, что InitInterp
может быть путь.