성능 : 일반에서 파생 된 유형
내가 이해할 수없는 한 가지 성능 문제가 발생했습니다. 나는 그것을 고치는 방법을 알고 있지만 왜 그런 일이 발생하는지 이해하지 못합니다. 단지 재미를위한 것입니다!
코드에 대해 이야기합시다. 문제를 재현 할 수 있도록 코드를 최대한 단순화했습니다.
제네릭 클래스가 있다고 가정합니다. 내부에 빈 목록이 있고 T
생성자에서 뭔가를 수행 합니다. 그것은이 Run
호출 방법 IEnumerable<T>
예를 들어 목록에 방법을 Any()
.
public class BaseClass<T>
{
private List<T> _list = new List<T>();
public BaseClass()
{
Enumerable.Empty<T>();
// or Enumerable.Repeat(new T(), 10);
// or even new T();
// or foreach (var item in _list) {}
}
public void Run()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
// or if (_list.Count() > 0)
// or if (_list.FirstOrDefault() != null)
// or if (_list.SingleOrDefault() != null)
// or other IEnumerable<T> method
{
return;
}
}
}
}
그런 다음 비어있는 파생 클래스가 있습니다.
public class DerivedClass : BaseClass<object>
{
}
ClassBase<T>.Run
두 클래스에서 실행중인 메소드 의 성능을 측정 해 보겠습니다 . 파생 유형에서 액세스하는 것은 기본 클래스에서보다 4 배 느립니다. 왜 그런 일이 일어나는지 이해할 수 없습니다. Release 모드에서 컴파일 된 결과는 워밍업과 동일합니다. .NET 4.5에서만 발생합니다.
public class Program
{
public static void Main()
{
Measure(new DerivedClass());
Measure(new BaseClass<object>());
}
private static void Measure(BaseClass<object> baseClass)
{
var sw = Stopwatch.StartNew();
baseClass.Run();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
요점에 대한 전체 목록
업데이트 : Microsoft Connect
의 CLR 팀의 답변이 있습니다.
공유 제네릭 코드의 사전 조회와 관련이 있습니다. 런타임 및 JIT의 휴리스틱은이 특정 테스트에서 제대로 작동하지 않습니다. 우리는 그것에 대해 무엇을 할 수 있는지 살펴볼 것입니다.
그 동안 BaseClass에 두 개의 더미 메서드를 추가하여 문제를 해결할 수 있습니다 (호출 할 필요도 없음). 휴리스틱이 예상대로 작동하도록합니다.
원본 :
JIT 실패입니다.
이 미친 것으로 고칠 수 있습니다.
public class BaseClass<T>
{
private List<T> _list = new List<T>();
public BaseClass()
{
Enumerable.Empty<T>();
// or Enumerable.Repeat(new T(), 10);
// or even new T();
// or foreach (var item in _list) {}
}
public void Run()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
{
return;
}
}
}
public void Run2()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
{
return;
}
}
}
public void Run3()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
{
return;
}
}
}
}
Run2 () / Run3 ()은 어디에서나 호출 되지 않습니다 . 그러나 Run2 또는 Run3 메서드를 주석 처리하면 이전과 같이 성능이 저하됩니다.
스택 정렬 또는 메서드 테이블의 크기와 관련된 것이 있습니다.
PS 당신은 대체 할 수 있습니다
Enumerable.Empty<T>();
// with
var x = new Func<IEnumerable<T>>(Enumerable.Empty<T>);
여전히 같은 버그입니다.
몇 가지 실험 끝에 Enumerable.Empty<T>
T가 클래스 유형일 때 항상 느린 것을 발견했습니다 . 값 유형이면 더 빠르지 만 구조체 크기에 따라 다릅니다. 나는 object, string, int, PointF, RectangleF, DateTime, Guid를 테스트했습니다.
구현 방법을 살펴보면서 다른 대안을 시도했고 빠르게 작동하는 일부를 찾았습니다.
Enumerable.Empty<T>
내부 클래스 EmptyEnumerable<TElement>
의 Instance
정적 속성 에 의존 합니다 .
이 속성은 작은 일을합니다.
- Checks if a private static volatile field is null.
- Assigns an empty array to the field once (only if empty).
- Returns the value of the field.
Then, what Enumerable.Empty<T>
is really doing is only return an empty array of T.
Trying different approaches, I found that the slowness is caused by both the property and the volatile modifier.
Adopting a static field initialized to T[0] instead of Enumerable.Empty<T>
like
public static readonly T[] EmptyArray = new T[0];
the problem disappeared. Note that the readonly modifier is not determinant. Having the same static field declared with volatile or accessed through a property causes the problem.
Regards, Daniele.
It seems there is an CLR optimizator issue. Turn off the "Optimize Code" on the Build tab and try to run your test again.
참고URL : https://stackoverflow.com/questions/27176159/performance-type-derived-from-generic
'Program Tip' 카테고리의 다른 글
배우 기반 시스템 구축을위한 디자인 패턴 / 모범 사례 (0) | 2020.11.25 |
---|---|
파이썬 db-api : fetchone vs fetchmany vs fetchall (0) | 2020.11.25 |
AVAssetWriter 및 AVAssetWriterInputs를 통해 비디오 + 오디오를 작성하는이 코드가 작동하지 않습니다. (0) | 2020.11.25 |
웹 개발 및 디자인에 유용한 Vim 플러그인 (php, html, css, javascript)? (0) | 2020.11.25 |
Angularjs-간단한 양식 제출 (0) | 2020.11.25 |