포트란 컴파일러는 실제로 C 컴파일러보다 더 빠른 코드를 생성합니까?


17

대학에서 공부할 때 종종 포트란 컴파일러가 동등한 프로그램을 위해 C 컴파일러보다 빠른 코드를 생성한다는 생각을 들었습니다.

핵심 이유는 다음과 같습니다. Fortran 컴파일러는 코드 라인 당 평균 1,1 개의 프로세서 명령어를 생성하는 반면 C 컴파일러는 코드 라인 당 평균 1,6 개의 프로세서 명령어를 생성 합니다. 정확한 숫자는 기억 나지 않지만 아이디어는 C 컴파일러가 눈에 띄게 더 많은 머신 코드를 방출하여 더 느린 프로그램을 생성한다는 것입니다.

그러한 비교는 얼마나 유효합니까? 포트란 컴파일러가 C 컴파일러보다 더 빠른 프로그램을 생성한다고 할 수 있습니까?


19
이는 단순히 포트란 프로그램이 C보다 더 장황하다는 것을 의미 할 수 있습니다. 의미있는 비교는 두 언어로 동일한 기능 을 구현 하고 결과 기계 코드 (크기 및 속도)를 비교해야만 가능합니다 .
Péter Török

또한 생성 된 코드가 병렬 실행을 지원합니까?

@Peter Török은 Fortran의 BLAS와 LAPACK이 C / C ++ 포트보다 훨씬 나은 성능을 발휘했음을 의미합니다. 이제 격차가 빠르게 줄어들고 있습니다.
SK-logic

6
컴파일러를 알고 있고 성능을 설명 할 수있는 전문가가 작성한 두 언어로 된 100 % 동등한 프로그램이있는 경우 하나의 컴파일러가 더 빠른 코드를 생성한다고 주장 할 수 있습니다.
팔콘

이전 포트란은 재귀를 지원하지 않았기 때문에 각 함수의 인수에 대해 정적으로 할당 된 공간이 있기 때문에 함수 호출 인수를 스택에 푸시 할 필요는 없었습니다. 이것이 더 빠른 이유 중 하나입니다. amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Pedro Rolo

답변:


36

IItran Fortran이 더 빠른 이유 중 하나는 포인터 앨리어싱 이 없기 때문에 C 컴파일러가 사용할 수없는 최적화를 사용할 수 있다는 것입니다.

FORTRAN에서 함수 인수는 서로 별명을 지정할 수 없으며 컴파일러는 그렇지 않다고 가정합니다. 이는 탁월한 최적화를 가능하게하며 빠른 언어로 FORTRAN의 명성을 얻는 주요 이유 중 하나입니다. (예를 들어, A가 배열이고 i와 j가 동일한 값을 갖는 인덱스 인 경우 별칭은 FORTRAN 함수 내에서 여전히 발생할 수 있습니다. A [i]와 A [j]는 기본 배열의 이름이 같아야하므로 인덱스 분석을 수행하여 A [i] 및 A [j]가 별칭을 지정할 수없는 경우를 확인할 수 있습니다.

그러나 나는 다른 사람들에게 동의합니다 : 한 줄의 코드에 대해 생성 된 평균 어셈블러 명령어 수를 비교하는 것은 말도 안됩니다. 예를 들어 최신 x86 코어는 동일한 레지스터에 액세스하지 않으면 두 개의 명령을 병렬로 실행할 수 있습니다. 따라서 이론적으로 명령 을 다시 정렬 하여 동일한 명령 세트에 대해 100 %의 성능 향상을 얻을 수 있습니다 . 좋은 컴파일러는 종종 더 많은 코드를 얻기 위해 더 많은 어셈블리 명령어를 생성 합니다 (루프 언 롤링, 인라인 생각). 총 어셈블러 명령어 수는 코드의 성능에 대해서는 거의 언급하지 않습니다.


더 나은 최적화를위한 또 다른 이유는 복소수에 대한 기본 지원입니다.
SK-logic

Fortran IV 정도는 확실합니다. 최신 FORTRAN에 여전히 포인터, 동적 추론 등이 없는지 확실하지 않습니다.
Ingo

2
이것이 우리가 게임 산업에서 C 및 C ++로 개발할 때 종종 약간의 인라인 어셈블리로 떨어 졌던 것과 같은 이유입니다. 사람들은 "컴파일러가 인간이 어셈블리를 작성하는 것보다 더 잘 최적화 할 수있다"고 자주 주장 할 수있다. 사실, 포인터 앨리어싱은 종종 그들이 할 수 없다는 것을 의미한다 . 우리가 직접 작성할 수있는 코드는 컴파일러가 포인터 앨리어싱에 대해 아무 것도하지 않는다는 것을 알면 기술적으로 불법입니다.
Carson63000

5
C의 restrict키워드를 사용하면 함수 작성자가 포인터에 별칭이 없음을 지정할 수 있습니다. 이것이 차이를 해결하기에 충분합니까, 아니면 더 있습니까?
bk.

@bk .: C의 "제한"공격은 "문제 절반"; 특정 포인터가 수명 내에서 다른 것을 별칭으로 지정할 수는 없지만, 함수에 주소가 전달 된 객체는 일단 함수가 반환되면 별칭이 지정되지 않는다는 것을 컴파일러에게 알리는 방법이 없습니다.
supercat

8

완전히 잘못된 비교입니다.

먼저 @ Péter Török이 지적했듯이 먼저 Fortran과 C의 동등한 프로그램에서 줄 수를 비교해야 생산 라인 수를 올바르게 비교할 수 있습니다.

둘째, 적은 코드 줄이 항상 더 빠른 프로그램과 같지는 않습니다 . 모든 기계 명령어 가 실행 하는 데 동일한 주기를 수행하는 것은 아니지만 메모리 액세스 와 같은 다른 문제도 있습니다 , 캐싱 .

게다가, 긴 코드 실행은 실행 라인 수가 적으므로 (즉, Line Count! = Executed Line Count ) 더 빠를 수 있습니다 .


5

Dan은 정확합니다. 프로그램이 길다고해서 프로그램이 느려지는 것은 아닙니다. 그들이하는 일에 크게 의존합니다.

나는 포트란에 대한 전문가가 아닙니다. 그것들을 비교하면 잘 작성된 C가 Fortran보다 더 복잡한 데이터 구조와 기능으로 성능이 훨씬 뛰어나다 고 생각합니다. 내가 틀렸다면 누군가 (제발) 나를 수정하지만, Fortran이 C보다 다소 '낮은 수준'이라고 생각합니다. 그렇다면 Fortran에서 일부 문제가 더 빨리 나올 것이라고 확신합니다.

또 다른 점은 언뜻보기에 컴파일러가 더 빠른지 묻는 것입니다. 실제로 Fortran은 비슷한 양의 코드에 대해 더 빨리 컴파일 할 것이라고 생각하지만 결과 프로그램과 실행 방법은 다른 이야기입니다. 파싱하는 것이 더 간단합니다.


2
복잡한 데이터 구조를 사용하는 경우 FORTRAN이 잘못된 선택 일 수 있습니다. FORTRAN은 간단한 숫자 처리를 매우 빠르게 수행하도록 최적화되었습니다.
Zachary K

4

FORTRAN 컴파일러는 일부 유형의 수학을 매우 빠르게 수행하도록 설계되었다고 생각합니다. 사람들이 FORTRAN을 사용하여 가능한 빨리 계산을 수행하는 이유


4

이 진술은 C가 초기 단계에 있었던 옛날 (70 년대 후반)에 맞았을 수 있으며, 포트란은 모든 주요 제조업체의 지원을 받아 고도로 최적화되었습니다. 초기 Fortrans는 IBM 아키텍처를 기반으로 했으므로 어셈블리 명령어 당 하나의 명령문이었던 산술과 같은 간단한 것들입니다. 이는 Data General 및 Prime과 같은 오래된 시스템에서 3 방향으로 점프 한 경우에 해당됩니다. 3 방향 점프가없는 최신 명령어 세트에서는 작동하지 않습니다.

코드 줄이 코드 설명과 같지 않습니다. 이전 버전의 포트란은 한 줄에 하나의 문장 만 허용했습니다. 이후 버전의 포트란은 한 줄에 여러 명령문을 사용할 수 있습니다. C는 한 줄에 여러 문장을 가질 수 있습니다. 인텔의 IVF (이전의 CVF, MS Powerstation) 및 인텔의 C와 같은 더 빠른 프로덕션 컴파일러에서는이 둘 사이에 실제로 차이가 없습니다. 이 컴파일러는 고도로 최적화되어 있습니다.


4

구식 FORTRAN은 배열의 일부를 함수에 사용 가능하게하려는 프로그래머는 시작 첨자와 종료 첨자 또는 항목 수를 지정하는 하나 이상의 정수 값과 함께 전체 배열에 대한 참조를 전달해야했습니다. . C는 요소 수와 함께 관심있는 부분의 시작 부분에 포인터를 전달하여 이것을 단순화 할 수 있습니다. 직접적으로 말하면, 이것은 일을 더 빨리 할 것입니다 (3 개가 아닌 2 개를 통과). 그러나 간접적으로 컴파일러가 수행 할 수있는 최적화 종류를 제한하여 속도를 늦출 수 있습니다.

기능을 고려하십시오.

void diff(float dest[], float src1[], float src2[], int n)
{
  for (int i=0; i<n; i++)
    dest[i] = src1[i] - src2[i];
}

컴파일러가 각 포인터가 배열의 시작을 식별한다는 것을 알고 있다면, x! = y에 대해 dest [x에 대한 연산 때문에 배열의 요소에 대해 병렬 또는 임의의 순서로 작동하는 코드를 생성 할 수 있습니다. ]는 src1 [y] 또는 src2 [y]에 영향을 미치지 않습니다. 예를 들어, 일부 시스템에서 컴파일러는 다음과 같은 코드를 생성하여 이점을 얻을 수 있습니다.

void dif(float dest[], float src1[], float src2[], int n)
{
  int i=0;
  float t1a,t1b,t2a,t2b,tsa,tsb;
  if (n > 2)
  {
    n-=4;
    t1a = src1[n+3]; t1b = src2[n+3]; t1b=src2[n+2]; t2b = src2[n+2];
    do
    {
      tsa = t1a-t2a;
      t1a = src1[n+1]; t2a = src2[n+1]; 
      tsb = t2b-t2b;
      dest[n+3] = tsa;
      t1b = src1[n]; t2b = src2[n]; 
      n-=2;
      dest[n+4] = tsb;
    } while(n >= 0);
    ... add some extra code to handle cleanup
  }
  else
    ... add some extra code to handle small values of n
}

값을로드하거나 계산하는 모든 작업에는 해당 값과 해당 값을 사용하는 다음 작업 사이에 하나 이상의 작업이 있습니다. 일부 프로세서는 이러한 조건이 충족 될 때 다른 작업 처리와 겹칠 수 있으므로 성능이 향상됩니다. 그러나 C 컴파일러는 코드 에 공통 배열의 부분적으로 겹치는 영역에 대한 포인터가 전달되지 않음을 알 수 없으므로 C 컴파일러는 위의 변환을 수행 할 수 없습니다. 그러나 동등한 코드가 지정된 FORTRAN 컴파일러는 그러한 변환을 수행 할 수 있고 수행했습니다.

C 프로그래머는 루프를 풀고 인접한 패스의 연산과 겹치는 코드를 명시 적으로 작성하여 비슷한 성능을 달성하려고 시도 할 수 있지만, 컴파일러가 "유출"해야하는 자동 변수를 너무 많이 사용하면 이러한 코드가 성능을 쉽게 저하시킬 수 있습니다. 기억. FORTRAN 컴파일러의 옵티마이 저는 특정 시나리오에서 어떤 형태의 인터리빙이 최적의 성능을 제공 할 수 있는지 프로그래머보다 더 많이 알고있을 것입니다. 이러한 결정은 종종 그러한 컴파일러에 맡기는 것이 가장 좋습니다. C99는 restrict한정자 를 추가하여 C의 상황을 어느 정도 개선하려고 시도했지만 ,이 dest[]둘은와 src1[]와 분리 된 배열 src2[]이거나 프로그래머가 분리 된 버전의 루프를 추가하여 모두 dest분리 된 경우를 처리하는 경우에만 사용할 수 있습니다.src1src2, 어디src1[]dest동일했다 및 src2해체했다 src2[]하고 dest[]있었 읍니다과 src1해체, 그리고 어디에서 세 가지 배열은 동일했다. 반대로 FORTRAN은 동일한 소스 코드와 동일한 머신 코드를 사용하는 데 어려움없이 4 가지 경우를 모두 처리 할 수 ​​있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.