2015-01-21 1 views
15

VB.NET 2010을 사용하여 계산 집약적 인 프로그램을 작성 중이며 속도를 최적화하고 싶습니다. 운영자 AndAlsoOrElse은 작업 결과가 클래스 수준 변수에 할당 된 경우 비정상적으로 느린 것으로 나타났습니다. 예를 들어, 문또한 OrElse는 비정상적으로 느릴 수 있습니다.

a = _b AndAlso _c 
_a = a 

는 컴파일 된 EXE에

_a = _b AndAlso _c 

은 약 80 기계 사이클이 소요되는 하나의 문을 사이 약 6 기계 사이클을 가지고있다. 여기서 _a, _b_cForm1의 Private Boolean 변수이며 해당 문은 Form1의 인스턴스 프로 시저에 있으며이 중 a은 로컬 부울 변수입니다.

단일 명령문이 오래 걸리는 이유를 찾을 수 없습니다.

Instruction    Explanation        Stack 
00: ldarg.0    Push Me (ref to current inst of Form1) Me 
01: ldarg.0    Push Me         Me, Me 
02: ldfld bool Form1::_b Pop Me, read _b and push it    _b, Me 
07: brfalse.s 11   Pop _b; if false, branch to 11   Me 
09: ldarg.0    (_b true) Push Me      Me, Me 
0a: ldfld bool Form1::_c (_b true) Pop Me, read _c and push it _c, Me 
0f: brtrue.s 14   (_b true) Pop _c; if true, branch to 14 Me 
11: ldc.i4.0    (_b, _c not both true) Push result 0  result, Me 
12: br.s 15    Jump unconditionally to 15    result, Me 
----- 
14: ldc.i4.1    (_b, _c both true) Push result 1   result, Me 
15: stfld bool Form1::_a Pop result and Me; write result to _a (empty) 
1a: 

사람이 문 _a = _b AndAlso _c 80 기계 사이클 대신 예측 5 정도 소요 이유에 어떤 빛이 있나 : 나는 좋은 보이는 CIL 코드의 수준에 NetReflector을 사용하여 살펴 보았다?

.NET 4.0 및 Visual Studio Express 2010과 함께 Windows XP를 사용하고 있습니다. 근본적으로 Stopwatch 개체를 사용하는 솔직히 더러운 스 니펫으로 시간을 측정하여 For-Next 루프 시간을 측정합니다. 코드를 질문하고 빈 For-Next 루프와 비교하십시오. 몇 사이클을 낭비하고 프로세서 스톨을 방지하기 위해 두 루프에 하나의 쓸모없는 명령어가 포함되어 있습니다. 내 목적을 위해 충분하지만 좋은.

+0

죄송합니다.이 의견이 다소 복잡해 보이지만 계산 속도와주기를 주기적으로 찾고 있다면 일반적으로 VB.NET 또는 .NET을 사용하지 않을 것입니다. – TyCobb

+0

코드를 더 게시하면 효율성 향상을위한 다른 제안을 제공 할 수 있습니다. – Jeremy

+0

그냥 AND를 사용해 보셨습니까? 특히 Booleans 변수로 작업하고 있기 때문에 특히 그렇습니다. –

답변

12

이 코드를 느리게하는 두 가지 요인이 있습니다. 당신은 일리노이에서 이것을 볼 수 없으며 기계 코드 만이 당신에게 통찰력을 줄 수 있습니다.


먼저 AndAlso 연산자와 관련된 일반적인 것이 있습니다. 단락 연산자이며 왼쪽 피연산자가 False로 평가되면 오른쪽 피연산자는 평가되지 않습니다. 이를 위해서는 기계 코드에 분기가 있어야합니다. 브랜치는 프로세서가 수행 할 수있는 가장 느린 작업 중 하나입니다. 은 파이프 라인을 플러시해야하는 위험을 피하기 위해 분기 시작 부분에서으로 추측해야합니다. 잘못 추측 한 경우 주요 성능 저하가 발생합니다. 매우 잘 덮여 this post. a 변수가 매우 랜덤하고 분기가 잘못 예측되는 경우 일반적인 성능 손실은 약 500 %입니다.

대신 And 연산자를 사용하면이 위험을 피할 수 있으며 컴퓨터 코드에 분기가 필요하지 않습니다. 그것은 단지 하나의 명령어이고, 프로세서에 의해 구현됩니다. 그런 식으로 AndAlso를 선호하는 데는 아무런 포인트가 없습니다. 오른쪽 피연산자가 평가되면 아무 문제가 없습니다. 여기서는 적용 할 수 없지만 IL에 분기가 표시되는 경우에도 지터는 CMOV 명령어 (조건부 이동)를 사용하여 분기 코드없이 분기 코드를 생성 할 수 있습니다.


가장 중요한 점은 Form 클래스가 MarshalByRefObject 클래스에서 상속된다는 것입니다. 상속 체인은 MarshalByRefObject> 구성 요소> 컨트롤> ScrollableControl> ContainerControl> 폼입니다.

MBRO는 Just-in-Time 컴파일러로 특별히 처리되므로 코드가 실제 개체가 다른 AppDomain 또는 다른 컴퓨터에있는 클래스 개체에 대한 프록시로 작동 할 수 있습니다.프록시는 클래스의 거의 모든 종류의 멤버에 대한 지터에 투명하며 간단한 메소드 호출로 구현됩니다. 필드를 제외하고는 메소드 호출이 아닌 메모리 읽기/쓰기로 필드에 대한 액세스가 이루어지기 때문에 필드를 제외하고는 프록시를 사용할 수 없습니다. 지터가 개체가 로컬임을 증명할 수 없다면 JIT_GetFieldXxx() 및 JIT_SetFieldXxx()라는 도우미 메서드를 사용하여 강제로 CLR을 호출해야합니다. CLR은 개체 참조가 프록시인지 실제 거래인지 여부를 알고 차이점을 처리합니다. 오버 헤드는 꽤 상당하다. 80 사이클은 오른쪽에 대해 들린다.

변수가 Form 클래스의 멤버 인 경우 많은 작업을 수행 할 수 있습니다. 헬퍼 클래스로 이동하면 해결 방법입니다.

+0

니스! 당신의 똑똑함에 감사드립니다 :) – Jeremy

+3

Wonderful! 정말 고맙습니다. 변수를 도우미 클래스로 옮길 때 명령어는 80 대신 평균 3½ 사이클이 걸린다. –

+2

좋은 결과. Q + A는 이보다 나아질 수 없습니다. 질문을 업데이트하고 측정 방법에 대해 몇 마디 말씀 드리며, 충분한 프로그래머가 아닙니다. –