이 질문은 최근 " C ++ vs Fortran for HPC " 에 대한 답변에서 최근에 제기 된 두 가지 토론의 확장입니다 . 그리고 그것은 질문보다 조금 더 도전입니다 ...
Fortran에 유리한 주장 중 하나는 컴파일러가 더 좋다는 것입니다. 대부분의 C / Fortran 컴파일러는 동일한 백엔드를 공유하므로 두 언어에서 의미 적으로 동등한 프로그램에 대해 생성 된 코드는 동일해야합니다. 그러나 C / Fortran은 컴파일러가 최적화하기가 훨씬 쉽다고 주장 할 수 있습니다.
그래서 간단한 테스트를하기로 결정했습니다 : daxpy.f 와 daxpy.c 의 사본을 가지고 gfortran / gcc로 컴파일했습니다.
이제 daxpy.c는 daxpy.f (자동으로 생성 된 코드, 못생긴 것처럼)의 f2c 번역 일 뿐이므로 해당 코드를 가져 와서 조금 정리했습니다 (daxpy_c를 충족).
for ( i = 0 ; i < n ; i++ )
dy[i] += da * dx[i];
마지막으로 gcc의 벡터 구문을 사용하여 다시 작성했습니다 (daxpy_cvec 입력).
#define vector(elcount, type) __attribute__((vector_size((elcount)*sizeof(type)))) type
vector(2,double) va = { da , da }, *vx, *vy;
vx = (void *)dx; vy = (void *)dy;
for ( i = 0 ; i < (n/2 & ~1) ; i += 2 ) {
vy[i] += va * vx[i];
vy[i+1] += va * vx[i+1];
}
for ( i = n & ~3 ; i < n ; i++ )
dy[i] += da * dx[i];
길이가 2 인 벡터 (모든 SSE2가 허용하는)를 사용하고 한 번에 두 개의 벡터를 처리합니다. 많은 아키텍처에서 벡터 요소보다 곱셈 단위가 더 많을 수 있기 때문입니다.
모든 코드는 "-O3 -Wall -msse2 -march = native -ffast-math -fomit-frame-pointer -malign-double -fstrict-aliasing"플래그와 함께 gfortran / gcc 버전 4.5를 사용하여 컴파일되었습니다. 내 랩탑 (Intel Core i5 CPU, M560, 2.67GHz)에서 다음과 같은 출력을 얻었습니다.
pedro@laika:~/work/fvsc$ ./test 1000000 10000
timing 1000000 runs with a vector of length 10000.
daxpy_f took 8156.7 ms.
daxpy_f2c took 10568.1 ms.
daxpy_c took 7912.8 ms.
daxpy_cvec took 5670.8 ms.
따라서 원래 포트란 코드는 8.1 초보다 조금 더 걸리고, 자동 변환은 10.5 초가 걸리고, 순진한 C 구현은 7.9에서 수행하고 명시 적으로 벡터화 된 코드는 5.6에서 약간 줄어 듭니다.
그것은 포트란이 순진한 C 구현보다 약간 느리고 벡터화 된 C 구현보다 50 % 느립니다.
질문은 다음과 같습니다. 저는 네이티브 C 프로그래머이므로 해당 코드를 잘 처리했다고 확신하지만 Fortran 코드는 1993 년에 마지막으로 다루어 졌으므로 약간 오래된 것일 수 있습니다. Fortran에서 다른 사람들처럼 편안한 코딩을 느끼지 못하기 때문에 누구나 두 가지 C 버전에 비해 더 나은 작업을 수행 할 수 있습니까?
또한 누구나 icc / ifort로이 테스트를 시도 할 수 있습니까? 벡터 구문이 작동하지 않을 수도 있지만 순진한 C 버전이 어떻게 작동하는지 궁금합니다. xlc / xlf가있는 사람도 마찬가지입니다.
소스와 Makefile을 여기에 업로드했습니다 . 정확한 타이밍을 얻으려면 test.c의 CPU_TPS를 CPU의 Hz 수로 설정하십시오. 버전에 대한 개선 사항이 있으면 여기에 게시하십시오!
최신 정보:
온라인으로 파일에 stali의 테스트 코드를 추가하고 C 버전으로 보완했습니다. 이전 테스트와 일치하도록 길이가 10'000 인 벡터에서 1'000'000 루프를 수행하도록 프로그램을 수정했습니다. 암호). 숫자가 조금 작아 -par-threshold:50
졌으므로 컴파일러를 병렬화 할 수있는 옵션 을 사용했습니다 . 사용 된 icc / ifort 버전은 12.1.2 20111128이며 결과는 다음과 같습니다.
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_c
3.27user 0.00system 0:03.27elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_f
3.29user 0.00system 0:03.29elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_c
4.89user 0.00system 0:02.60elapsed 188%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_f
4.91user 0.00system 0:02.60elapsed 188%CPU
요약하면 결과는 모든 실제적인 목적으로 C 및 Fortran 버전에서 동일하며 두 코드는 자동으로 병렬화됩니다. 이전 테스트에 비해 빠른 시간은 단 정밀도 부동 소수점 산술을 사용하기 때문입니다!
최신 정보:
증거의 부담이 어디로 가는지는 마음에 들지 않지만, stali의 행렬 곱셈 예제 를 C로 다시 코딩 하여 웹 의 파일에 추가했습니다 . 다음은 하나 및 두 개의 CPU에 대한 트리플 루프의 결과입니다.
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
triple do time 3.46421700000000
3.63user 0.06system 0:03.70elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_c 2500
triple do time 3.431997791385768
3.58user 0.10system 0:03.69elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
triple do time 5.09631900000000
5.26user 0.06system 0:02.81elapsed 189%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_c 2500
triple do time 2.298916975280899
4.78user 0.08system 0:02.62elapsed 184%CPU
참고 cpu_time
포트란은 CPU 시간이 아닌 벽 시계 시간을 measuers, 그래서에서 전화를 감싸 time
2 개의 CPU 그들을 비교. C 버전이 두 개의 코어에서 약간 나아진다는 점을 제외하고는 결과간에 실제 차이가 없습니다.
이 matmul
명령은 C에서 사용할 수 없으므로 Fortran에서만 명령 을 수행하십시오 .
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
matmul time 23.6494780000000
23.80user 0.08system 0:23.91elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
matmul time 26.6176640000000
26.75user 0.10system 0:13.62elapsed 197%CPU
와. 그것은 절대적으로 끔찍합니다. 아무도 내가 뭘 잘못하고 있는지 알 수 있습니까, 아니면 왜이 본질적인 것이 여전히 좋은지 설명 할 수 있습니까?
dgemm
인텔 MKL에서 동일한 기능에 대한 라이브러리 호출이므로 벤치 마크에 호출을 추가하지 않았습니다 .
향후 테스트를 위해 C에서 Fortran보다 느린 것으로 알려진 예제를 제안 할 수 있습니까?
최신 정보
matmul
작은 행렬 에서 내장 함수가 명시 적 행렬 곱보다 빠르다는 stali의 주장을 확인 하기 위해 필자는 자체 코드를 수정하여 각 방법을 사용하여 100x100 크기의 행렬을 각각 10,000 회 곱했습니다. 하나와 두 개의 CPU에서 결과는 다음과 같습니다.
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 10000 100
matmul time 3.61222500000000
triple do time 3.54022200000000
7.15user 0.00system 0:07.16elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 10000 100
matmul time 4.54428400000000
triple do time 4.31626900000000
8.86user 0.00system 0:04.60elapsed 192%CPU
최신 정보
Grisu는 최적화없이 gcc가 복잡한 숫자에 대한 연산을 라이브러리 함수 호출로 변환하는 반면 gfortran은 몇 가지 명령으로 인라인을 수행한다는 점을 지적합니다.
옵션 -fcx-limited-range
이 설정 되면 C 컴파일러는 동일한 소형 코드를 생성합니다 . 즉, 컴파일러는 중간 값에서 오버 플로우 / 언더 플로우 가능성을 무시하도록 지시받습니다. 이 옵션은 gfortran에서 기본적으로 설정되며 잘못된 결과를 초래할 수 있습니다. -fno-cx-limited-range
gfortran을 강요 해도 아무런 변화가 없었습니다.
따라서 이것은 실제로 수치 계산에 gfortran을 사용 하는 것에 반대 하는 주장입니다 . 정확한 결과가 부동 소수점 범위 내에 있더라도 복잡한 값에 대한 연산이 오버 플로우 / 언더 플로 일 수 있습니다. 이것은 실제로 포트란 표준입니다. gcc 또는 일반적으로 C99에서 달리 지정되지 않는 한 기본값은 엄격하게 (IEEE-754 호환) 읽습니다.
주의 사항 : 주요 질문은 Fortran 컴파일러가 C 컴파일러보다 더 나은 코드를 생성하는지 여부입니다. 이것은 한 언어의 일반적인 장점과 다른 언어의 장점에 관한 토론의 장소가 아닙니다. 내가 정말로 관심을 가질만한 것은 누군가가 명시 적 벡터화를 사용하여 C에서와 같이 효율적으로 덱스 피를 생성하기 위해 gfortran을 동축하는 방법을 찾을 수 있다면 SIMD 최적화를 위해 독점적으로 컴파일러에 의존 해야하는 문제를 보여 주거나 Fortran 컴파일러가 C를 능가하는 경우.