FortranコンパイラはCコンパイラよりも実際に高速なコードを生成しますか?


17

大学で勉強していたとき、Fortranコンパイラは同等のプログラムのCコンパイラよりも速いコードを生成するという考えをよく耳にしました。

主な理由は次のとおりです。Fortranコンパイラはコード1行あたり平均1,1プロセッサ命令を発行しますが、Cコンパイラはコード1行あたり平均1,6プロセッサ命令を発行します -正確な数字は覚えていませんが、アイデアは、Cコンパイラが著しく多くのマシンコードを出力し、そのためより遅いプログラムを生成するというものでした。

そのような比較はどの程度有効ですか?FortranコンパイラーはCコンパイラーよりも高速なプログラムを生成する、またはその逆と言えますか?なぜこの違いが存在するのですか?


19
つまり、FortranプログラムはCよりも冗長です...意味のある比較を行うには、両方の言語で同じ機能を実装し、結果のマシンコード(サイズと速度)を比較する必要があります。
ペテルトレック

また、生成されたコードは並列実行をサポートしていますか?

@PéterTörök、つまり、FortranのBLASおよびLAPACKは、C / C ++ポートよりもはるかに優れたパフォーマンスを発揮していたということです。現在、ギャップは急速に縮小しています。
SKロジック

6
両方の言語で100%同等のプログラムがあり、コンパイラーを知っており、パフォーマンスを考慮できる専門家によって書かれている場合、1つのコンパイラーがより高速なコードを生成するということだけを主張できます。
ファルコン

前のFortranは再帰をサポートしていなかったため、各関数の引数に静的に割り当てられたスペースがあるため、必ずしも関数呼び出し引数をスタックにプッシュする必要はありませんでした。これは、それがより高速だった理由の1つです。:私はあなたがここでより完全な答えを見つけることが推測amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/...
ペドロ・ロロ

回答:


36

IIRCは、Fortranがより高速であると言われている主な理由の1つは、ポインターエイリアシングがないことです。したがって、Cコンパイラでは使用できない最適化を使用できます。

FORTRANでは、関数の引数は相互にエイリアスしない場合があり、コンパイラーはエイリアスしないと仮定します。これにより、優れた最適化が可能になり、高速言語としてのFORTRANの評判の主な理由の1つです。(エイリアスはまだFORTRAN関数内で発生する可能性があります。たとえば、Aが配列で、iとjが同じ値を持つインデックスである場合、A [i]とA [j]は2つの異なる名前です。同じメモリ位置。幸い、ベース配列は同じ名前でなければならないため、A [i]とA [j]がエイリアスできない場合を判断するためにインデックス分析を行うことができます。

しかし、私はここで他の人に同意します:コード行に対して生成されたアセンブラー命令の平均数を比較することは完全にナンセンスです。たとえば、最新のx86コアは、同じレジスタにアクセスしない場合、2つの命令を並行して実行できます。したがって、(理論上)命令を並べ替えるだけで、命令の同じセットのパフォーマンスが100%向上します。優れたコンパイラーは、より高速なコードを取得するために、多くのアセンブリ命令を生成することもよくあります(ループの展開、インライン化を考えてください)。アセンブラー命令の総数は、コードのパフォーマンスについてほとんど語っていません。


最適化を改善するもう1つの理由は、複素数のネイティブサポートです。
SKロジック

Fortran IVについては間違いなく正しい。現代のFORTRANがまだポインターや動的な記憶などを持たないかどうかはわかりません。
Ingo

2
これは、ゲーム業界でCおよびC ++で開発する際に、しばしばインラインアセンブリに少し落としたのと同じ理由です。人々は「コンパイラは人間がアセンブリを書くよりも最適化できる」と好きなだけ主張することができます。手作業で記述できるコードは、ポインターのエイリアシングについて何もしないので、コンパイラーが出力することは技術的に違法です。
Carson63000

5
Cのrestrictキーワードを使用すると、関数の作成者はポインターにエイリアスがないことを指定できます。これは違いに対処するのに十分ですか、それ以上ありますか?
bk。

@bk .: Cの「制限」攻撃は「問題の半分」です。特定のポインターは、その存続期間内に他の何もエイリアスしないと言うことができますが、関数にアドレスが渡されたオブジェクトがその関数が返されると何もエイリアスされないことをコンパイラーに伝える方法はありません。
スーパーキャット

8

完全に無効な比較。

まず、@PéterTörökが指摘しているように、FortranとCの同等のプログラムの行数を比較して、生成される行数を有効に比較する必要があります。

第二に、コードの以下の行は、常に速くプログラムを等しくありません。すべてのマシン命令が同じサイクル数で実行されるわけではありませんが、メモリアクセスキャッシュなど、他の問題もあります。

その上、実行コードの数が少なくなるため(つまり、Line Count!= Executed Line Count)、長いコードの実行は高速になります。


5

ダンは正しい、長いプログラムは遅いプログラムを意味しない。彼らが何をしているかに大きく依存します。

私はFortranの専門家ではありません。少し知っています。それらを比較すると、よく書かれたCは、Fortranよりも複雑なデータ構造と機能でパフォーマンスがはるかに良くなると思います。私がここで間違っている場合、誰かが私を修正してください。しかし、FortranはCよりも「低レベル」にあると思います。

別のこととして、一見したところ、コンパイラが高速であるかどうかを尋ねていると思いました。実際、Fortranは一般に同じ量のコードに対してより速くコンパイルされると思いますが、結果のプログラムとその実行方法は異なる話になるでしょう。解析する方が簡単です。


2
複雑なデータ構造を使用している場合、FORTRANはおそらく間違った選択です。FORTRANは、単純な数値計算を非常に高速に実行するように最適化されています。
ザカリーK

4

その一部は、FORTRANコンパイラがいくつかのタイプの数学を非常に高速に実行するように設計されていることだと思います。できるだけ早く計算を行うために、人々がFORTRANを使用する理由の一種です


4

この声明は、Cがまだ初期段階にあり、Fortranがすべての主要メーカーによってサポートされ、高度に最適化されていた昔(70年代後半)に当てはまる場合があります。初期のFortranはIBMアーキテクチャーに基づいていたため、算術演算のような単純なものは、アセンブリ命令ごとに1つのステートメントであったことは確かです。これは、Data GeneralやPrimeなどの3つの方法でジャンプする古いマシンにも当てはまります。これは、3方向ジャンプのない最新の命令セットでは機能しません。

コードの行は、コードのステートメントと等しくありません。Fortranの以前のバージョンでは、1行につき1つのステートメントのみが許可されていました。Fortranの以降のバージョンでは、1行に複数のステートメントを使用できます。Cは行ごとに複数のステートメントを持つことができます。IntelのIVF(以前のCVF、MS Powerstation)やIntelのCのような高速のプロダクションコンパイラでは、実際には2つの間に違いはありません。これらのコンパイラは高度に最適化されています。


4

古いスタイルのFORTRANでは、配列の一部を関数で使用できるようにしたいプログラマーは、配列全体への参照と、開始添え字と終了添え字またはアイテムの数を指定する1つ以上の整数値を渡す必要がありました。 。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
}

値をロードまたは計算するすべての操作には、その値とその値を使用する次の操作の間に少なくとも1つ以上の操作があることに注意してください。プロセッサの中には、このような条件が満たされたときに異なる操作の処理をオーバーラップできるものがあり、パフォーマンスが向上します。ただし、Cコンパイラには、共通配列の部分的に重複する領域へのポインタがコードに渡されないことを知る方法がないため、Cコンパイラは上記の変換を行うことができません。ただし、同等のコードが与えられたFORTRANコンパイラは、このような変換を行うことができ、実際に行いました。

Cプログラマーは、ループを展開し、隣接するパスの操作をオーバーラップするコードを明示的に記述することで同等のパフォーマンスを達成しようとすることができますが、コンパイラーはメモリ。FORTRANコンパイラのオプティマイザは、特定のシナリオでどのようなインターリーブ形式が最適なパフォーマンスをもたらすかについて、プログラマーよりも多くのことを知っていると思われます。C99は、多少追加することにより、Cの状況を改善しようとしたもののrestrict場合のみ、ここで使用することができることを、修飾子をdest[]両方から別々の配列だったsrc1[]src2[]、あるいはプログラマがすべてのケース処理するために、ループの別のバージョンを追加した場合destからばらばらだったがsrc1そしてsrc2、どこsrc1[]dest等しかったとsrc2互いに素であり、ここで、src2[]dest[]等しかったとsrc1互いに素であり、すべての3つの配列は同じであった場合。対照的に、FORTRANは、同じソースコードと同じマシンコードを使用して、4つのケースすべてを問題なく処理できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.