2014-04-09 6 views
2

우리는 나는 다음과 같은 방법에 제공된 프로파일 우리의 프로젝트와 함께 약간의 성능 최적화를하고있다 :이 방법은 ~ 1μs의 실행 당과 꽤 빨리 이미바이트 파싱을 빠르게 할 수 있습니까?

private int CalculateAdcValues(byte lowIndex) 
{ 
    byte middleIndex = (byte)(lowIndex + 1); 
    byte highIndex = (byte)(lowIndex + 2); 

    // samples is a byte[] 
    retrun (int)((int)(samples[highIndex] << 24) 
     + (int)(samples[middleIndex] << 16) + (int)(samples[lowIndex] << 8)); 
} 

하지만 초당 ~ 100.000 배라고합니다 따라서 CPU의 10 %를 차지합니다.

누구나이 방법을 개선하는 방법에 대해 알고 있습니까?

편집 :

현재 솔루션 :

fixed (byte* p = samples) 
{ 
    for (; loopIndex < 61; loopIndex += 3) 
    { 
     adcValues[k++] = *((int*)(p + loopIndex)) << 8; 
    } 
} 

이 그 전에 시간의 < 40 %를 소요 (지금은 전에 호출 당 "전체 방법"했다 ~ 35μs와 ~ 13μs) . actualy 이제 다음 calcualtion 더 많은 시간이 소요 -loop for ...

+0

왜 캐스트'byte'에? 인덱스 mod 256을 줄여야합니까? 'int'로의 캐스트는 이미 값을 int로 확장했기 때문에 쓸모가 없습니다. – CodesInChaos

답변

3

당신이 친절 엔디안을 가지고 보면, 약 30 % 빠른 86 일 unsafe

unsafe int CalculateAdcValuesFast1(int lowIndex) 
{ 
    fixed (byte* p = &samples[lowIndex]) 
    { 
    return *(int*)p << 8; 
    } 
} 

이동합니다. 내가 기대했던 것만 큼은 아니었다. x64의 경우 약 40 %.

@CodeInChaos에 의해 제안 :

var bounds = samples.Length - 3; 
    fixed (byte* p = samples) 
    { 
    for (int i = 0; i < 1000000000; i++) 
    { 
     var r = CalculateAdcValuesFast2(p, i % bounds); // about 2x faster 
     // or inlined: 
     var r = *((int*)(p + i % bounds)) << 8; // about 3x faster 
     // do something 
    } 
    } 


unsafe object CalculateAdcValuesFast2(byte* p1, int p2) 
{ 
    return *((int*)(p1 + p2)) << 8; 
} 
+1

콜 계층 구조에서 한 레벨 위로 고정하면 이득이 더 커집니다. – CodesInChaos

+0

OP의 코드와 달리'lowIndex == samples.Length-3' 코드는 정의되지 않은 동작을합니다. 또한 리틀 엔디안 인 시스템에 의존합니다. – CodesInChaos

+0

@CodesInChaos : 왜 내가 엔디안에게 친절하다고 말했지 :) 그리고, 정의되지 않은 동작은 보이지 않습니다. 안전한 버전처럼 경계를 벗어나면 예외가 발생하지 않습니다. 하지만 이미 'for'상태에서 돌보아 주어야합니다. – leppie

5

나는 강하게 byte에 캐스팅 한 후, 당신의 인덱스가 배열 인덱스 연산에 사용되는 어쨌든 다시 int로 전환되고 있다는 것을 생각한다. 그것은 싸게 될 것이지만 완전히 자유롭지는 않을 것입니다. 따라서 byte으로의 변환을 사용하여 0..255 범위의 인덱스를 효과적으로 얻지 않는 한 캐스트를 제거하십시오. 이 시점에서 별도의 지역 변수도 제거 할 수 있습니다.

또한 int 이상의 유형에서만 시프트 연산이 정의되므로 int으로의 형 변환은 no-opps입니다. ?

private int CalculateAdcValues(byte lowIndex) 
{ 
    return (samples[lowIndex + 2] << 24) | 
      (samples[lowIndex + 1] << 16) | 
      (samples[lowIndex] << 8); 

} 

(이유는 아래 8 비트에서 아무것도 의도적인가요 samples[lowIndex + 2] 그 위에있는 경우 결과가 부정적 결국 않습니다 :

마지막으로, |를 사용하여 빠른 +보다 수 있습니다 비트 세트 - 괜찮습니까?)

+2

~ 50 % 빠르지 만 이전보다 더 빨리 빠르기 때문에 우리는 leppie의'unsafe' 솔루션을 사용했습니다 ... – ChrFin

+0

@ "왜 아래쪽 8 비트에는 아무 것도 없습니까?": 32 비트 정수는 없지만 하드웨어에서 상위 24 비트를 확보하면 대역폭을 절약 할 수 있습니다. – ChrFin

0

다음이 조금 더 빠를 수 있습니다. 정수로 캐스팅을 제거했습니다.

 var middleIndex = (byte)(lowIndex + 1); 
     var highIndex = (byte)(lowIndex + 2); 

     return (this.samples[highIndex] << 24) + (this.samples[middleIndex] << 16) + (this.samples[lowIndex] << 8); 
관련 문제