부동 소수점 덧셈과 부동 소수점 덧셈의 상대 속도는 무엇입니까?부동 소수점 곱하기

StackOverflow https://stackoverflow.com/questions/1146455

문제

10~20년 전에는 곱셈과 나눗셈을 사용하지 않고 대신 덧셈과 뺄셈을 사용하도록 숫자 코드를 작성하는 것이 가치가 있었습니다.좋은 예는 다음과 같습니다. 앞으로의 차이 다항식을 직접 계산하는 대신 다항식 곡선을 평가합니다.

이것이 여전히 사실입니까, 아니면 현대 컴퓨터 아키텍처가 *,/가 더 이상 +,-보다 몇 배 느리지 않을 정도로 발전했습니까?

구체적으로 말하면, 나는 소프트웨어에서 FP를 수행하려는 작은 마이크로가 아니라 광범위한 온보드 부동 소수점 하드웨어를 갖춘 현대의 일반적인 x86 칩에서 실행되는 컴파일된 C/C++ 코드에 관심이 있습니다.파이프라이닝 및 기타 아키텍처 개선으로 인해 특정 주기 수가 제외된다는 점을 알고 있지만 여전히 유용한 직관을 얻고 싶습니다.

도움이 되었습니까?

해결책

또한 명령어 혼합에 따라 다릅니다.귀하의 프로세서에는 언제든지 대기 중인 여러 계산 장치가 있으며, 모든 계산 장치가 항상 채워지면 최대 처리량을 얻을 수 있습니다.따라서 mul의 루프를 실행하는 것은 루프를 실행하거나 추가하는 것만큼 빠르지만 표현식이 더 복잡해지면 동일하지 않습니다.

예를 들어 다음 루프를 살펴보세요.

for(int j=0;j<NUMITER;j++) {
  for(int i=1;i<NUMEL;i++) {
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ;
  }
}

NUMITER=10^7, NUMEL=10^2의 경우 두 배열 모두 작은 양수로 초기화되었습니다(NaN은 훨씬 느림). 64비트 proc에서 double을 사용하면 6.0초가 걸립니다.루프를 다음으로 교체하면

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ;

1.7초밖에 안걸려요..그래서 우리가 추가를 "과도하게 수행"했기 때문에 mul은 본질적으로 무료였습니다.추가 항목을 줄이는 것이 도움이 되었습니다.더 혼란스러워집니다.

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ;

-- 동일한 mul/add 분포이지만 이제 상수를 곱하는 대신 추가합니다. -- 3.7초가 걸립니다.귀하의 프로세서는 일반적인 수치 계산을 보다 효율적으로 수행하도록 최적화되어 있을 가능성이 높습니다.따라서 멀의 합과 스케일링된 합과 같은 내적은 얻을 수 있는 만큼 좋습니다.상수를 추가하는 것은 흔하지 않으므로 속도가 느려집니다.

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/

다시 1.7초가 걸립니다.

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/

(초기 루프와 동일하지만 비용이 많이 드는 상수 추가가 없음:2.1초)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/

(대부분 멀이지만 한 가지 추가: 1.9초)

그래서 기본적으로;어느 것이 더 빠른지 말하기는 어렵지만 병목 현상을 피하려면 더 중요한 것은 제대로 혼합하고, NaN 또는 INF를 피하고, 상수를 추가하지 않는 것입니다.무엇을 하든지 다양한 컴파일러 설정을 테스트하고 테스트해야 합니다. 종종 작은 변경만으로도 차이가 생길 수 있기 때문입니다.

추가 사례:

bla *= someval; // someval very near 1.0; takes 2.1 seconds
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86

다른 팁

이론적으로 정보는 다음과 같습니다.

Intel®64 및 IA-32 아키텍처 최적화 참조 설명서, 부록 C 명령 대기 시간 및 처리량

나열된 모든 프로세서에 대해 FMUL의 대기 시간은 FADD 또는 FDIV의 대기 시간과 매우 유사합니다.일부 구형 프로세서에서는 FDIV가 그보다 2~3배 느린 반면, 최신 프로세서에서는 FMUL과 동일합니다.

주의사항:

  1. 실제로 내가 연결한 문서에는 프로세서가 올바른 경우 작업 속도를 높이기 위해 원하는 작업을 수행하므로 실제 생활에서는 이러한 숫자에 의존할 수 없다고 나와 있습니다.

  2. 컴파일러가 부동 소수점 곱셈/나누기를 사용할 수 있는 최신 명령어 세트 중 하나를 사용하기로 결정할 가능성이 높습니다.

  3. 이 문서는 컴파일러 작성자만 읽어야 하는 복잡한 문서이므로 제가 잘못 읽었을 수도 있습니다.일부 CPU에서 FDIV 대기 시간 숫자가 완전히 누락된 이유가 확실하지 않은 것처럼 말입니다.

이 질문에 답하는 가장 좋은 방법은 수행해야 하는 처리에 대한 벤치마크/프로파일을 실제로 작성하는 것입니다.가능하면 이론적인 것보다 경험적인 것을 사용해야 합니다.특히 쉽게 얻을 수 있는 경우에는 더욱 그렇습니다.

수행해야 하는 수학의 다양한 구현을 이미 알고 있는 경우 수학의 몇 가지 다른 코드 전송을 작성하고 성능이 최고조에 달하는 위치를 확인할 수 있습니다.이렇게 하면 프로세서/컴파일러가 다양한 실행 스트림을 생성하여 프로세서 파이프라인을 채우고 답변에 대한 구체적인 답변을 제공할 수 있습니다.

특히 DIV/MUL/ADD/SUB 유형 명령어의 성능에 관심이 있는 경우 일부 인라인 어셈블리를 사용하여 이러한 명령어의 어떤 변형이 실행되는지 구체적으로 제어할 수도 있습니다.그러나 시스템이 수행할 수 있는 성능에 대한 좋은 아이디어를 얻으려면 여러 실행 단위를 계속 사용하고 있는지 확인해야 합니다.

또한 이와 같은 작업을 수행하면 동일한 프로그램을 실행하여 다양한 프로세서 변형의 성능을 비교할 수 있으며 마더보드 차이점을 고려할 수도 있습니다.

편집하다:

+-의 기본 아키텍처는 동일합니다.따라서 논리적으로 계산하는 데 동일한 시간이 걸립니다.* 반면에 단일 작업을 완료하려면 일반적으로 "전체 가산기"로 구성된 여러 레이어가 필요합니다.이는 주기마다 파이프라인에 *를 발행할 수 있지만 더하기/빼기 회로보다 대기 시간이 더 길다는 것을 보장합니다.fp / 연산은 일반적으로 시간이 지남에 따라 정답을 향해 반복적으로 수렴하는 근사 방법을 사용하여 구현됩니다.이러한 유형의 근사는 일반적으로 곱셈을 통해 구현됩니다.따라서 부동 소수점의 경우 곱셈(이미 그 자체로 큰 회로임)을 여러 곱셈기 회로의 파이프라인으로 "펼치는" 것이 비현실적이기 때문에 일반적으로 나누기가 더 오래 걸릴 것이라고 가정할 수 있습니다.여전히 특정 시스템의 성능은 테스트를 통해 가장 잘 측정됩니다.

확실한 참조를 찾을 수는 없지만 광범위한 실험에 따르면 요즘 부동 소수점 곱셈은 덧셈 및 뺄셈과 거의 같은 속도인 반면 나눗셈은 그렇지 않습니다(그러나 "여러 배" 느리지도 않음).자신만의 실험을 실행해야만 원하는 직관을 얻을 수 있습니다. 미리 난수(수백만 개)를 생성하고 타이밍을 시작하기 전에 이를 읽고 CPU의 성능 카운터를 사용하는 것을 기억하십시오(다른 프로세스가 실행되지 않음). 최대한 막아주세요) 정확한 측정을 위해!

* / 대 + -의 속도 차이는 프로세서 아키텍처에 따라 다릅니다.일반적으로, 특히 x86의 경우 최신 프로세서에서는 속도 차이가 줄어들었습니다.*는 의심스러운 경우 +에 가까워야 합니다.그냥 실험해 보세요.많은 FP 작업으로 인해 정말 어려운 문제가 있는 경우 벡터 프로세서로 작동하는 GPU(GeForce, ...) 사용을 고려해 보세요.

아마도 곱셈과 덧셈 사이의 시간 차이는 거의 없을 것입니다.반면에 나눗셈은 재귀적 특성으로 인해 곱셈보다 훨씬 느립니다.최신 x86 아키텍처에서는 fpu를 사용하는 대신 부동 소수점 연산을 수행할 때 sse 명령어를 고려해야 합니다. 하지만 좋은 C/C++ 컴파일러는 fpu 대신 sse를 사용할 수 있는 옵션을 제공해야 합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top