필드 대 속성. 성능 최적화
성능과 관련된이 질문에 유의하십시오. 디자인 지침, 철학, 호환성, 이식성 및 순수한 성능과 관련이없는 모든 것을 건너 뛸 수 있습니다. 감사합니다.
이제 질문입니다. 나는 항상 C # 게터 / 세터가 실제로 위장 된 메서드이기 때문에 퍼블릭 필드를 읽는 것이 게터를 호출하는 것보다 빠르다고 생각했습니다.
그래서 내가 테스트를했는지 확인하기 위해 (아래 코드). 그러나이 테스트는 Visual Studio 내에서 실행하는 경우 예상되는 결과 만 생성합니다 (즉, 필드가 게터보다 34 % 빠름 ) .
명령 줄에서 실행하면 거의 동일한 타이밍이 표시됩니다.
유일한 설명은 CLR이 추가 최적화를 수행한다는 것입니다 (여기에서 내가 틀렸다면 수정).
이러한 속성이 훨씬 더 정교한 방식으로 사용되는 실제 응용 프로그램에서는 동일한 방식으로 최적화 될 것이라고 생각하지 않습니다.
실생활에서 부동산이 분야보다 느리다는 생각을 증명하거나 반증하도록 도와주세요.
질문은-공개 필드가 게터를 능가하도록 CLR 변경 동작을 만들기 위해 테스트 클래스를 수정하는 방법입니다. 또는 내부 논리가없는 속성이 필드와 동일하게 수행된다는 것을 보여줍니다 (적어도 게터에서는).
편집 : 릴리스 x64 빌드에 대해서만 이야기하고 있습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace PropertyVsField
{
class Program
{
static int LEN = 20000000;
static void Main(string[] args)
{
List<A> a = new List<A>(LEN);
List<B> b = new List<B>(LEN);
Random r = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < LEN; i++)
{
double p = r.NextDouble();
a.Add(new A() { P = p });
b.Add(new B() { P = p });
}
Stopwatch sw = new Stopwatch();
double d = 0.0;
sw.Restart();
for (int i = 0; i < LEN; i++)
{
d += a[i].P;
}
sw.Stop();
Console.WriteLine("auto getter. {0}. {1}.", sw.ElapsedTicks, d);
sw.Restart();
for (int i = 0; i < LEN; i++)
{
d += b[i].P;
}
sw.Stop();
Console.WriteLine(" field. {0}. {1}.", sw.ElapsedTicks, d);
Console.ReadLine();
}
}
class A
{
public double P { get; set; }
}
class B
{
public double P;
}
}
다른 사람들이 이미 언급했듯이 getter는 인라인 됩니다.
인라이닝을 피하려면
자동 속성을 수동 속성으로 바꿉니다.
class A { private double p; public double P { get { return p; } set { p = value; } } }
컴파일러에게 getter를 인라인하지 않도록 지시합니다 (또는 원한다면 둘 다).
[MethodImpl(MethodImplOptions.NoInlining)] get { return p; }
첫 번째 변경은 성능에 영향을주지 않는 반면 두 번째 변경은 명확한 메서드 호출 오버 헤드를 보여줍니다.
수동 속성 :
auto getter. 519005. 10000971,0237547.
field. 514235. 20001942,0475098.
게터 인라인 없음 :
auto getter. 785997. 10000476,0385552.
field. 531552. 20000952,077111.
속성 대 필드를 살펴보십시오 – 왜 중요한가? (Jonathan Aneja) MSDN의 VB 팀 구성원 중 한 명이 작성한 블로그 기사. 그는 속성 대 필드 인수를 설명하고 다음과 같이 간단한 속성도 설명합니다.
속성보다 필드를 사용하는 것에 대해 들었던 한 가지 주장은 "필드가 더 빠르다"는 것입니다. 그러나 실제로 사실이 아닌 사소한 속성의 경우 CLR의 JIT (Just-In-Time) 컴파일러가 속성 액세스를 인라인하고 다음과 같은 코드를 생성합니다. 필드에 직접 액세스하는 것만 큼 효율적입니다.
JIT는 내부 메트릭이 더 빨리 인라인 될 것이라고 결정하는 모든 메소드 (게터뿐만 아니라)를 인라인합니다. 표준 속성이 주어지면 return _Property;
모든 경우에 인라인됩니다.
다른 동작이 나타나는 이유는 디버거가 연결된 디버그 모드에서 JIT가 상당히 장애가있어 모든 스택 위치가 코드에서 기대하는 것과 일치하는지 확인하기 때문입니다.
당신은 또한 최고의 성능 규칙을 잊고 있습니다. 테스트는 생각보다 뛰어납니다. 예를 들어 빠른 정렬이 삽입 정렬보다 점근 적으로 빠르더라도 실제로는 매우 작은 입력의 경우 삽입 정렬이 더 빠릅니다.
유일한 설명은 CLR이 추가 최적화를 수행한다는 것입니다 (여기에서 내가 틀렸다면 수정).
예, 인라이닝이라고합니다. 컴파일러에서 수행됩니다 (기계 코드 수준-즉 JIT). getter / setter가 사소하기 때문에 (즉, 매우 간단한 코드) 메서드 호출이 파괴되고 getter / setter가 주변 코드에 기록됩니다.
이것은 디버깅을 지원하기 위해 디버그 모드에서 발생하지 않습니다 (즉, getter 또는 setter에서 중단 점을 설정하는 기능).
Visual Studio에서는 디버거에서이를 수행 할 수있는 방법이 없습니다. 릴리스를 컴파일하고 연결된 디버거없이 실행하면 전체 최적화를 얻을 수 있습니다.
이러한 속성이 훨씬 더 정교한 방식으로 사용되는 실제 응용 프로그램에서는 동일한 방식으로 최적화 될 것이라고 생각하지 않습니다.
세상은 잘못된 환상으로 가득 차 있습니다. 그들은 여전히 사소하기 때문에 최적화 될 것입니다 (즉, 간단한 코드이므로 인라인 됨).
Visual Studio에서 "실제"성능을 볼 수 있다는 점에 유의해야합니다.
- 최적화가 활성화 된 릴리스 모드에서 컴파일합니다.
- 디버그-> 옵션 및 설정으로 이동하여 "모듈로드시 JIT 최적화 억제 (관리 전용)"를 선택 취소하십시오.
- 선택적으로 "내 코드 만 사용"을 선택 취소하십시오. 그렇지 않으면 코드를 입력하지 못할 수 있습니다.
이제 디버거가 연결된 경우에도 jitted 어셈블리가 동일하므로 원하는 경우 최적화 된 디스 어셈블리를 실행할 수 있습니다. 이것은 CLR이 코드를 최적화하는 방법을 이해하는 데 필수적입니다.
모든 기사를 읽은 후 다음 코드로 벤치 마크를 만들기로 결정했습니다.
[TestMethod]
public void TestFieldVsProperty()
{
const int COUNT = 0x7fffffff;
A a1 = new A();
A a2 = new A();
B b1 = new B();
B b2 = new B();
C c1 = new C();
C c2 = new C();
D d1 = new D();
D d2 = new D();
Stopwatch sw = new Stopwatch();
long t1, t2, t3, t4;
sw.Restart();
for (int i = COUNT - 1; i >= 0; i--)
{
a1.P = a2.P;
}
sw.Stop();
t1 = sw.ElapsedTicks;
sw.Restart();
for (int i = COUNT - 1; i >= 0; i--)
{
b1.P = b2.P;
}
sw.Stop();
t2 = sw.ElapsedTicks;
sw.Restart();
for (int i = COUNT - 1; i >= 0; i--)
{
c1.P = c2.P;
}
sw.Stop();
t3 = sw.ElapsedTicks;
sw.Restart();
for (int i = COUNT - 1; i >= 0; i--)
{
d1.P = d2.P;
}
sw.Stop();
t4 = sw.ElapsedTicks;
long max = Math.Max(Math.Max(t1, t2), Math.Max(t3, t4));
Console.WriteLine($"auto: {t1}, {max * 100d / t1:00.00}%.");
Console.WriteLine($"field: {t2}, {max * 100d / t2:00.00}%.");
Console.WriteLine($"manual: {t3}, {max * 100d / t3:00.00}%.");
Console.WriteLine($"no inlining: {t4}, {max * 100d / t4:00.00}%.");
}
class A
{
public double P { get; set; }
}
class B
{
public double P;
}
class C
{
private double p;
public double P
{
get => p;
set => p = value;
}
}
class D
{
public double P
{
[MethodImpl(MethodImplOptions.NoInlining)]
get;
[MethodImpl(MethodImplOptions.NoInlining)]
set;
}
}
디버그 모드에서 테스트 할 때 다음과 같은 결과를 얻었습니다.
auto: 35142496, 100.78%.
field: 10451823, 338.87%.
manual: 35183121, 100.67%.
no inlining: 35417844, 100.00%.
그러나 릴리스 모드로 전환하면 결과가 이전과 다릅니다.
auto: 2161291, 873.91%.
field: 2886444, 654.36%.
manual: 2252287, 838.60%.
no inlining: 18887768, 100.00%.
자동차 속성이 더 나은 방법 인 것 같습니다.
참고URL : https://stackoverflow.com/questions/9842917/field-vs-property-optimisation-of-performance
'Program Tip' 카테고리의 다른 글
동일한 열에서 여러 WHERE 조건으로 선택 (0) | 2020.11.21 |
---|---|
javascript / html5로 즉석에서 사운드 생성 (0) | 2020.11.21 |
PHP의 "easter egg"URL을 비활성화하려면 어떻게해야합니까? (0) | 2020.11.21 |
Angular의 $ q.reject () 대 deferred.reject () (0) | 2020.11.21 |
AngularJS 컨트롤러를 동적으로로드 (0) | 2020.11.21 |