이 기사를 찾았습니다 Lazy
: C # 4.0의 게으름 – 게으른
Lazy 객체를 사용하여 최상의 성능을 발휘하는 가장 좋은 방법은 무엇입니까? 누군가 실제 응용 프로그램에서 실용적으로 사용할 수 있습니까? 즉, 언제 사용해야합니까?
get { if (foo == null) foo = new Foo(); return foo; }
스레드로부터 안전하지 않습니다, 동안 Lazy<T>
스레드 안전 기본으로합니다.
이 기사를 찾았습니다 Lazy
: C # 4.0의 게으름 – 게으른
Lazy 객체를 사용하여 최상의 성능을 발휘하는 가장 좋은 방법은 무엇입니까? 누군가 실제 응용 프로그램에서 실용적으로 사용할 수 있습니까? 즉, 언제 사용해야합니까?
get { if (foo == null) foo = new Foo(); return foo; }
스레드로부터 안전하지 않습니다, 동안 Lazy<T>
스레드 안전 기본으로합니다.
답변:
실제로 실제로 사용 된 무언가를 인스턴스화하려는 경우 일반적으로 사용합니다. 이렇게하면 항상 비용이 발생하지 않고 필요할 때 / 필요할 때까지 생성 비용이 지연됩니다.
일반적으로 이것은 객체가 사용되거나 사용되지 않을 수 있고 구성 비용이 사소하지 않을 때 바람직합니다.
Lazy<T>
있습니다. 그러나 각 속성을 만들려면 상당히 사소하지만 약간의 비용이 드는 선형 보간 (또는 이중 선형 보간)을 수행하고 있습니다. (내가 직접 실험 해 보라고 제안하겠습니까?)
싱글 톤 사용을 피해야하지만, 필요한 경우 Lazy<T>
게으르고 스레드로부터 안전한 싱글 톤을 쉽게 구현할 수 있습니다.
public sealed class Singleton
{
// Because Singleton's constructor is private, we must explicitly
// give the Lazy<Singleton> a delegate for creating the Singleton.
static readonly Lazy<Singleton> instanceHolder =
new Lazy<Singleton>(() => new Singleton());
Singleton()
{
// Explicit private constructor to prevent default public constructor.
...
}
public static Singleton Instance => instanceHolder.Value;
}
큰 실제 게으른 로딩이 편리 경우의 예는 엔티티 프레임 워크와 NHibernate에 같은 ORM의 (객체 관계 매퍼)으로한다.
이름, 전화 번호 및 주문에 대한 특성이있는 엔티티 고객이 있다고 가정하십시오. Name 및 PhoneNumber는 일반 문자열이지만 Orders는 고객이 만든 모든 주문 목록을 반환하는 탐색 속성입니다.
당신은 종종 모든 고객을 거치고 그들의 이름과 전화 번호를 얻어서 전화하기를 원할 것입니다. 이것은 매우 빠르고 간단한 작업이지만 고객을 만들 때마다 자동으로 진행되어 복잡한 주문을 처리하여 수천 건의 주문을 반환한다고 상상해보십시오. 최악의 부분은 주문을 사용하지 않아서 완전한 자원 낭비라는 것입니다!
Order 속성이 게으 르면 실제로 필요한 경우가 아니라면 모든 고객의 주문을 가져 오지 않기 때문에 이것은 지연 로딩에 완벽한 장소입니다. Order 속성이 참을성있게 잠자기 상태 일 때 필요할 때 사용할 수 있도록 이름과 전화 번호 만받는 고객 개체를 열거 할 수 있습니다.
Db.Customers.Include("Orders")
. 이로 인해 Customer.Orders
속성을 처음 사용할 때가 아니라 해당 시점에 주문 조인이 실행 됩니다. 지연 로딩은 DbContext를 통해 비활성화 할 수도 있습니다.
Lazy<T>
내 코드의 성능을 향상시키고 약간 더 배우기 위해 속성 사용을 고려하고 있습니다. 나는 그것을 언제 사용 해야하는지에 대한 답을 찾기 위해 여기에 왔지만 어디를 가든지 다음과 같은 문구가있는 것 같습니다.
지연 초기화를 사용하여 특히 프로그램 수명 기간 동안 이러한 생성 또는 실행이 발생하지 않을 때 큰 또는 리소스 집약적 인 개체 생성 또는 리소스 집약적 작업 실행을 연기하십시오.
선을 그릴 위치가 확실하지 않기 때문에 약간 혼란스러워합니다. 예를 들어 선형 보간법을 상당히 빠른 계산으로 생각하지만 필요하지 않으면 지연 초기화를 수행하지 않아도 가치가 있습니까?
결국 나는 내 자신의 테스트를 시도하기로 결정했고 여기에서 결과를 공유 할 것이라고 생각했습니다. 불행히도 나는 이런 종류의 테스트를 수행하는 전문가가 아니므로 개선을 제안하는 의견을 기쁘게 생각합니다.
기술
필자의 경우 Lazy Properties가 많은 보간을 수행하는 코드의 일부를 개선하는 데 도움이되는지 (특히 대부분 사용되지 않음) 관심이 있었으므로 3 가지 접근법을 비교하는 테스트를 만들었습니다.
각 접근 방식마다 20 개의 테스트 속성 (t 속성이라고 함)으로 별도의 테스트 클래스를 만들었습니다.
테스트 결과는 ms 단위로 측정되며 평균 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 속성을 설정해야 함) 여기에서 약간 혼란 스럽지만 인스턴스화 (테스트 1) GetInterp
하는 데 0.09ms 만 걸리는 반면 한 번의 선형 보간을 수행하는 데 0.28ms가 소요됩니다 첫 번째 (테스트 2), 두 번째 (테스트 3)에는 0.1ms입니다.
처음 속성을 얻는 InitLazy
것보다 거의 2 배 더 오래 걸리며 인스턴스화하는 동안 속성을 채웠기 때문에 가장 빠릅니다. (적어도 그것이해야 할 일이지만 왜 단일 선형 보간보다 인스턴스화 결과가 훨씬 빠릅니까? 정확히 언제 보간을 수행합니까?)GetInterp
InitInterp
불행히도 테스트에서 자동 코드 최적화가 진행되는 것처럼 보입니다. GetInterp
두 번째와 마찬가지로 처음으로 속성을 얻는 데는 시간이 걸리지 만 2 배 이상 빠릅니다. 이 최적화는 다른 클래스에도 영향을 미치는 것으로 보입니다. 모두 테스트 3에 거의 동일한 시간이 걸리기 때문입니다. 그러나 이러한 최적화는 또한 내 자신의 프로덕션 코드에서 발생할 수 있으며 중요한 고려 사항이 될 수도 있습니다.
결론
일부 결과는 예상대로이지만 코드 최적화로 인해 예상치 못한 매우 흥미로운 결과도 있습니다. 생성자에서 많은 작업을 수행하는 것처럼 보이는 클래스의 경우에도 인스턴스화 결과에 따르면 이중 속성을 얻는 것보다 훨씬 빠르게 생성 할 수 있습니다. 이 분야의 전문가가 더 철저하게 논평하고 조사 할 수는 있지만 개인적 느낌은이 테스트를 다시 수행해야하지만 프로덕션 코드에서 어떤 종류의 최적화가 수행되고 있는지 조사하기위한 것입니다. 그러나 나는 그것이 InitInterp
갈 길이 라고 기대하고 있습니다.
lazy
몇 가지 추가 부기을 할 수 있으며, InitLazy
다른 솔루션보다 더 많은 메모리를 사용합니다. 또한 액세스 할 때마다 약간의 성능 저하가있을 수 있으며 이미 값이 있는지 확인합니다. 영리한 트릭으로 인해 오버 헤드가 제거 될 수 있지만 IL에서는 특별한 지원이 필요합니다. (Haskell은 모든 게으른 값을 함수 호출로하여이를 수행합니다. 일단 값이 생성되면 매번 해당 값을 반환하는 함수로 대체됩니다.)
Mathew가 게시 한 예를 가리 키기 만하면됩니다.
public sealed class Singleton
{
// Because Singleton's constructor is private, we must explicitly
// give the Lazy<Singleton> a delegate for creating the Singleton.
private static readonly Lazy<Singleton> instanceHolder =
new Lazy<Singleton>(() => new Singleton());
private Singleton()
{
...
}
public static Singleton Instance
{
get { return instanceHolder.Value; }
}
}
게으른 사람이 태어나 기 전에 우리는 이렇게했을 것입니다.
private static object lockingObject = new object();
public static LazySample InstanceCreation()
{
if(lazilyInitObject == null)
{
lock (lockingObject)
{
if(lazilyInitObject == null)
{
lazilyInitObject = new LazySample ();
}
}
}
return lazilyInitObject ;
}
MSDN에서 :
Lazy 인스턴스를 사용하여 특히 프로그램 수명 기간 동안 이러한 작성 또는 실행이 수행되지 않을 때 큰 또는 자원 집약적 오브젝트 작성 또는 자원 집약적 태스크 실행을 지연하십시오.
James Michael Hare의 답변 외에도 Lazy는 값에 대한 스레드 안전 초기화를 제공합니다. 이 클래스에 대한 다양한 유형의 스레드 안전 모드를 설명하는 LazyThreadSafetyMode 열거 MSDN 항목을 살펴보십시오 .
Lazy Loading 아키텍처를 이해하려면이 예제를보아야합니다.
private readonly Lazy<List<int>> list = new Lazy<List<int>>(() =>
{
List<int> configList = new List<int>(Thread.CurrentThread.ManagedThreadId);
return configList;
});
public void Execute()
{
list.Value.Add(0);
if (list.IsValueCreated)
{
list.Value.Add(1);
list.Value.Add(2);
foreach (var item in list.Value)
{
Console.WriteLine(item);
}
}
else
{
Console.WriteLine("Value not created");
}
}
-> 출력-> 0 1 2
그러나이 코드가 "list.Value.Add (0);"을 쓰지 않으면
출력-> 값이 생성되지 않았습니다
get { if (foo == null) foo = new Foo(); return foo; }
. 그리고 그것을 사용할 수있는 수십억의 장소가 있습니다 ...