2016-09-25 2 views
1

정수 계산을 병렬 처리하기 위해 HW를 사용하려면 Vector을 실험 해 왔습니다. 벡터 작업으로 오버플로 검사를 사용할 수있는 방법이 있습니까?벡터로 체크 된 산술을 할 수 있습니까? <T>

예를 들어 int의 두 열 (길이가 동일한 배열)을 함께 추가하는 것입니다. 여기 c=a+bc[0] = a[0] + b[0], c[1] = a[1] + b[1], 의미 등

나는 이런 식으로 뭔가를 할 수있는 가정 :

overflow[i] = b[i] >= 0 ? c[i] < a[i] : c[i] >= a[i]; 

그러나이 닷넷의 자동 오버 플로우 검사보다 더 느려질 수 있습니다 (분기) 및 성능 이점을 부정 할 수 Vector<T>을 사용 중입니다.

우리는 가장 일반적으로 사용되는 연산 인 승산, 빼기, 더 작은 정도의 정수 나누기를 최적화하려고합니다.

편집 : 이것에 대해 조금 더 생각해 보았습니다. 확인되지 않은 벡터 추가보다 2.5 배 느립니다. 추가적인 오버 헤드가 많은 것 같습니다.

public Vector<int> Calc(Vector<int> a, Vector<int> b) 
    { 
     var result = a + b; 
     var overflowFlag = Vector.GreaterThan(b, Vector<int>.Zero) * Vector.LessThan(result,a) 
      + Vector.LessThan(b,Vector<int>.Zero) * Vector.GreaterThan(result, a); 

     // It makes no sense to add the flags to the result, but haven't decided what to do with them yet, 
     // and don't want the compiler to optimise the overflow calculation away 
     return result + overflowFlag; 
    } 

타이밍 :

  • 일반 추가 (100,000 배열 한 쌍의 추가 4K 반복) : 618ms
  • 일반 검사 추가 : 1092ms
  • 벡터 추가 : 208ms
  • 벡터 검사 추가 : 536ms
+0

아니요. 물론 직접 확인할 수도 있습니다. 더 많은 맥락에서 나는 좀 더 타겟 된 추천을 줄 수 있었다. – harold

+0

@Harold - 좀 더 자세한 내용을 추가했습니다. 오버플로가 발생했는지 여부를 결정하는 효율적인 방법이 있습니까? – Rob

+0

분기 할 필요는 없지만 실제로는 할 수 없습니다. ConditionalSelect를 사용할 수 있습니다. 곱셈은 ​​확대되지 않고 까다 롭고 넓어지는 것은 성가신 일입니다. 나는 그것에 대해 생각할 것입니다. 나중에 제대로 대답하기 위해 다시 돌아올 것입니다. – harold

답변

1

일부 속임수를 빌려서 사용함 해커의 기쁨 (2 장의 오버 플로우 감지), 여기 (안 테스트) 약간의 오버 플로우 조건 :

서명 추가 :

var sum = a + b; 
var ovf = (sum^a) & (sum^b); 

결과는 징후가 아닌 전체 마스크입니다. 어쩌면 그 정도면 충분할 것입니다. 그렇다면 정상적으로 바뀌는 것을 권하고 싶지만 Vector<T>에 올바른 변화가 없습니다 (너무 많은 내용이 빠져 있음). 당신은 0과 비교할 수 있습니다.

부호없는 추가 : 완전을 원하십니까?

var sum = a + b; 
var ovf = Vector.LessThan(sum, a); 

곱셈 :

지금까지 내가 그것을 할 단지 합리적인 방법이 없다 말할 수있는

. 그것은 네이티브 SSE에서도 조금 짜증나지만, pmuldq과 약간의 셔플 링이 그렇게 나쁘지는 않다.
C# SIMD에서 이것은 단지 절망적으로 보입니다. 높은 mul (16-bit int를 제외하고는 native SSE에서 빠져 나옴, 성가신 것)도없고, 곱셈을 확장하지도 않고 (어쨌든 결과를 좁힐 방법도 없음), 사전에 넓힐 수있는 합리적인 방법이 없습니다. 심지어 확장 할 수 있다고해도 (API에 심각하게 추가 할 수는 있겠지만), multiplying 64bit integers with SSE is annoying, 스칼라 산수로 처리하는 것이 나쁜 옵션이 아니므로 요점을 없앨 수 있습니다.

그래서 적어도 C#이 아닌 SIMD에서는 그렇게하지 않을 것을 권장 할 것입니다.

그렇다고 내장 된 오버플로 감지 기능을 사용할 필요는 없습니다. 오버플로가 치명적인 오류라면 적절하지만, 일반적인 것이고 예상되는 경우 부작용이 느려지고 부울 플래그로 오버플로 상태를 원할뿐입니다. 이 경우, 당신은 사용할 수 있습니다

서명 곱셈 :

long ext_prod = (long)a * b; 
int prod = (int)ext_prod; 
bool ovf = (prod >> 31) != (int)(ext_prod >> 32); 

부호없는 곱셈 :

ulong ext_prod = (ulong)a * b; 
uint prod = (uint)ext_prod; 
bool ovf = (ext_prod >> 32) != 0; 

는 SIMD 그것은 즉 높은 반인지 여부를 테스트 본질적으로 같은 방식으로 일을 한 것 부호의 복사본 (부호없는 경우의 정의는 0)으로 채워지지만, 확장으로 인해 기본 SIMD에서는 성가 시게되고 C# SIMD에서는 절망적입니다.

+0

'ext_prod == prod'를 사용하여 오버플로를 확인하는 것이 더 빠르지 않습니까? 적어도 내 빠른 벤치 마크에있는 것 같습니다. – Rob

+0

그 결과가 그렇다면 @Rob 그래, 분명히 :) – harold

관련 문제