[t:/]$ 지식_

왜 실수 연산이 더 빠른게냐?

2017/08/23

예전 어딘가의 기사에서 수퍼스칼라라든가, 하스웰 같은 최신 CPU 에서는 FPU 파이프를 따로 파놨다는 것을 보았다. 학교 다닐때 마이크로 아키텍쳐 열심히 봤다면 이것이 의미하는 바를 상상해 볼 수 있다. 즉슨, 정수 연산 하면서 실수 연산을 동시에 하는 것이 정수 연산 다 끝내고 실수 연산 하는 것 보다 빠르지 않을까?

오후에 졸려 죽겠는데 하기 싫은거 억지로 코딩해봤다. 코드가 걸레같은 것은 기분탓입니다. 박카스가 필요하다.

#include <stdio.h>
#include <stdlib.h>

long kb[10000000];
long kc[10000000];
double fb[10000000];
double fc[10000000];
int main()
{
  int i;
  long m;
  long ki;
  long ji = 0;
  double jf = 0.0;
  int kk;

  for(i = 0; i < 10000000; i++) {
    kb[i] = rand();
    kc[i] = kb[i] - rand();
    fb[i] = (double)kb[i];
    fc[i] = fb[i] - (double)rand();
    kc[i] = kc[i] < 0 ? kc[i] * -1 : kc[i];
    fc[i] = fc[i] < 0.0 ? fc[i] * -1.0 : fc[i];
  }

   for (kk = 0; kk < 1000; kk++) {
     for (i = 0; i < 10000000; i++) {
        ji += kb[i] / kc[i];
//        jf += fb[i] / fc[i];

     }
   }
//   printf("%lf\n", jf);
   printf("%ld\n", ji);

}

O3 빌드를 하시고요..

주석처리 부가 있는데 실수만, 정수만 연산 할 수 있다. #if 처리하.. 졸려요 졸리다고 억지로 하고 있ㅇㅁ.

해보니까 정수 연산만 하면 1분 27초가 나오고, 실수 연산만 하면 23초가 나온다. 그런데 실수 연산 only 모드는 실제로는 실수 연산만 하는 것이 아니다. 2중 루프의 인덱스 변수를 증가시키기 위해 정수 연산 2개 + 실수 연산 1개를 동시에 시행하고 있는 셈이다. 물론 중간에 컴페어 브랜치가 들어가서 실제 파이프라인을 어떻게 탈지는 오리무중이 된 상태다. 왜 이렇게 차이가 나나 해서 어셈블리를 까봤다.

어셈블리를 까보니 정수 연산은 날삽질을 하고 있는 반면에 실수 연산은 SIMD를 쓰고 있다. 아.. 그냥 마법이 풀렸다. 그러면 왜 실수 연산만 컴파일러가 SIMD로 바꿔줬을까? 애초에 정수 연산은 SIMD에 의한 나누기가 없다. 즉슨, 정수연산이 무조건 빠르지 않다는 것이다. 나눗셈은 실수만을 위한 것... 워우어어어어..

그래서 소스를 SIMD에 공평히 있는 뺄셈으로 바꿨다.


   for (kk = 0; kk < 1000; kk++) {
     for (i = 0; i < 10000000; i++) {
        ji += kb[i] - kc[i];
//        jf += fb[i] - fc[i];

     }
   }
//   printf("%lf\n", jf);
   printf("%ld\n", ji);

이렇게 바꾼다음 연산이 너무 빨리 끝나서 루핑횟수를 증가시켰다. 결과는 실수 연산 1분, 정수 연산 55초 .. 비슷하고 근소하게 정수연산이 빠르다. 결국 SIMD가 관건이었음. 어셈 까보면 둘다 SIMD를 잘쓰고 있다. 결과적으로 파이프 라이닝에 대한 건은 확인 못해봤다. 통상적인 실험 방법으로는 안 될 것 같고, 어셈 까보면서 다시 실험 계획을 세울 필요가 있겠다.

슈퍼스칼라 아키텍쳐에 대한 짤을 하나 가져왔다. pic

https://www.slideshare.net/DilumBandara/11-performance-enhancements

그림을 보면 정수 유닛 2개와 실수 유닛 1개가 파이프 스테이지상 한 턴에... 자세히는 모르겠지만 실수 계산을 하는 루핑에 있어서 인덱스 변수 2개를 쓴다면 파이프 스테이지 1턴에 처리가 가능할 것 같은 느낌적 느낌이 든다.

원래가 나눗셈은 뺄셈과 시프팅으로.. 생략.

그건 그렇고 ARM NEON은 SIMD지만 나눗셈이 아예 안 보인다. 대신 역수 만들기가 있다. 얘를 쓰면 되는데 찾아보기론 역수 만들기 명령어도 실수만 지원하는 것 같다.

https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/ARM-NEON-Intrinsics.html

졸린데 애썼따..

끗.









[t:/] is not "technology - root". dawnsea, rss