Я нашел эту статью о Lazy
: лень в C # 4.0 - ленивый
Какова лучшая практика, чтобы иметь лучшую производительность, используя ленивые объекты? Может ли кто-нибудь указать мне на практическое использование в реальном приложении? Другими словами, когда я должен использовать это?
c#
.net
lazy-evaluation
danyolgiax
источник
источник
get { if (foo == null) foo = new Foo(); return foo; }
. И есть миллионы возможных мест, чтобы использовать это ...get { if (foo == null) foo = new Foo(); return foo; }
не является потокобезопасным, хотяLazy<T>
по умолчанию является потокобезопасным.Ответы:
Вы обычно используете это, когда хотите создать экземпляр чего-либо, когда оно действительно используется. Это задерживает стоимость его создания до тех пор, пока / когда это необходимо, вместо того, чтобы всегда нести расходы.
Обычно это предпочтительнее, когда объект может или не может быть использован, и стоимость его строительства нетривиальна.
источник
Lazy<T>
. Однако для создания каждого свойства я делаю линейную интерполяцию (или билинейную интерполяцию), которая довольно тривиальна, но имеет определенную стоимость. (Собираетесь ли вы предложить мне пойти и провести собственный эксперимент?)Вы должны стараться избегать использования Singletons, но, если вам это когда-нибудь понадобится,
Lazy<T>
облегчает реализацию отложенных, поточно-ориентированных синглетонов:источник
Отличный реальный пример того, как полезна ленивая загрузка, - это ORM (Object Relation Mappers), такие как Entity Framework и NHibernate.
Допустим, у вас есть объект Customer, у которого есть свойства для Name, PhoneNumber и Orders. Name и PhoneNumber являются обычными строками, но Orders - это свойство навигации, которое возвращает список всех заказов, которые когда-либо делал клиент.
Вам часто может понадобиться просмотреть всех ваших клиентов и получить их имя и номер телефона, чтобы позвонить им. Это очень быстрая и простая задача, но представьте, что каждый раз, когда вы создавали клиента, он автоматически переходил к сложному объединению, чтобы вернуть тысячи заказов. Хуже всего то, что вы даже не собираетесь использовать заказы, так что это полная трата ресурсов!
Это идеальное место для ленивой загрузки, потому что если свойство Order является ленивым, оно не пойдет за все заказы клиента, если они вам действительно не нужны. Вы можете перечислить объекты Customer, получая только их Имя и Номер телефона, пока свойство Order терпеливо спит и готово, когда вам это нужно.
источник
Db.Customers.Include("Orders")
. Это приведет к тому, что объединение заказов будет выполнено в тот момент, а не приCustomer.Orders
первом использовании свойства. Ленивая загрузка также может быть отключена через DbContext.Я подумывал об использовании
Lazy<T>
свойств, чтобы помочь улучшить производительность моего собственного кода (и узнать немного больше об этом). Я пришел сюда в поисках ответов о том, когда его использовать, но, кажется, везде, где я бываю, есть такие фразы:из MSDN Lazy <T> класса
Я немного растерялся, потому что не уверен, где провести черту. Например, я рассматриваю линейную интерполяцию как довольно быстрое вычисление, но если мне не нужно это делать, то может ли ленивая инициализация помочь мне избежать этого и стоит ли это того?
В конце концов я решил попробовать свой собственный тест и решил поделиться результатами здесь. К сожалению, я не очень разбираюсь в подобных тестах и поэтому рад получить комментарии, которые предлагают улучшения.
Описание
В моем случае мне было особенно интересно посмотреть, смогут ли Lazy Properties улучшить часть моего кода, которая выполняет большую часть интерполяции (большая часть которой не используется), и поэтому я создал тест, который сравнил 3 подхода.
Я создал отдельный тестовый класс с 20 тестовыми свойствами (давайте назовем их t-свойствами) для каждого подхода.
Результаты теста измеряются в мс и представляют собой среднее значение 50 экземпляров или 20 полученных значений. Затем каждый тест был выполнен 5 раз.
Результаты теста 1: Реализация (в среднем 50 экземпляров)
Результаты теста 2: Первый получить (в среднем 20 объектов получает)
Результаты теста 3: второе получение (в среднем 20 приобретений недвижимости)
наблюдения
GetInterp
быстрее всего создать экземпляр, как и ожидалось, потому что он ничего не делает.InitLazy
быстрее создать экземпляр, чемInitInterp
предполагать, что накладные расходы при настройке отложенных свойств быстрее, чем мои вычисления с линейной интерполяцией. Тем не менее, я немного запутался, потому чтоInitInterp
должен выполнить 20 линейных интерполяций (чтобы установить его t-свойства), но для его создания требуется всего 0,09 мс (тест 1), по сравнению сGetInterp
0,28 мс, чтобы выполнить только одну линейную интерполяцию в первый раз (тест 2) и 0,1 мс, чтобы сделать это во второй раз (тест 3).Требуется
InitLazy
почти в 2 раза больше времени, чемGetInterp
для получения свойства в первый раз, и при этомInitInterp
является самым быстрым, потому что он заполняет свои свойства во время создания экземпляра. (По крайней мере, это то, что он должен был сделать, но почему его результат был гораздо быстрее, чем одна линейная интерполяция? Когда именно он выполняет эти интерполяции?)К сожалению, похоже, что в моих тестах происходит некоторая автоматическая оптимизация кода. Он должен принять
GetInterp
то же самое время , чтобы получить собственность в первый раз , как это делает во второй раз, но он показывает , как более чем в 2 раза быстрее. Похоже, что эта оптимизация также влияет на другие классы, так как все они занимают примерно одинаковое количество времени для теста 3. Однако такая оптимизация может также иметь место в моем собственном рабочем коде, что также может быть важным фактором.Выводы
Хотя некоторые результаты ожидаемые, есть и некоторые очень интересные неожиданные результаты, вероятно, из-за оптимизации кода. Даже для классов, которые выглядят так, как будто они выполняют большую часть работы в конструкторе, результаты создания экземпляров показывают, что они все еще могут быть созданы очень быстро по сравнению с получением двойного свойства. Хотя специалисты в этой области могут комментировать и исследовать более тщательно, я лично считаю, что мне нужно повторить этот тест снова, но с моим рабочим кодом, чтобы выяснить, какая оптимизация может происходить там же. Тем не менее, я ожидаю, что это
InitInterp
может быть путь.источник
lazy
что нужно сделать некоторые дополнительные бухгалтерии,InitLazy
будет использовать больше памяти, чем другие решения. Он также может иметь незначительное снижение производительности при каждом доступе, в то время как он проверяет, имеет ли он уже значение или нет; умные трюки могут убрать эти издержки, но для этого потребуется специальная поддержка в IL. (Haskell делает это, превращая каждое ленивое значение в вызов функции; после того, как значение сгенерировано, оно заменяется функцией, которая каждый раз возвращает это значение.)Просто чтобы указать на пример, опубликованный Мэтью
до рождения Ленивого мы бы сделали это так:
источник
Из MSDN:
В дополнение к ответу Джеймса Майкла Хэра, Lazy обеспечивает поточно-ориентированную инициализацию вашего значения. Взгляните на запись MSDN перечисления LazyThreadSafetyMode, описывающую различные типы режимов безопасности потоков для этого класса.
источник
Вы должны посмотреть этот пример, чтобы понять архитектуру Lazy Loading
-> вывод -> 0 1 2
но если этот код не напишите "list.Value.Add (0);"
вывод -> значение не создано
источник