1. ホーム
  2. c

[解決済み] FortranはC言語よりも重い計算を最適化しやすいですか?

2022-03-23 13:58:08

質問

時々、重い計算にはCよりFortranの方が速い、または速いという記事を読みます。本当にそうなのでしょうか?私はFortranをほとんど知らないのですが、今まで見たFortranのコードでは、Cにない特徴があるようには見えませんでした。

もし本当なら、その理由を教えてください。数字計算をするのに適した言語やLibを教えてください。そのためのアプリやLibを書くつもりはありません。

どのように解決するのですか?

両言語は似たような機能セットを持っています。パフォーマンスの違いは、FortranではEQUIVALENCE文が使われていない限り、エイリアシングは許されないとされていることに起因します。エイリアシングがあるコードはFortranとしては無効ですが、このエラーを検出するのはプログラマーであり、コンパイラーではありません。したがって、Fortranコンパイラはメモリポインタのエイリアシングの可能性を無視し、より効率的なコードを生成できるようにしているのです。この小さな例をCで見てみましょう。

void transform (float *output, float const * input, float const * matrix, int *n)
{
    int i;
    for (i=0; i<*n; i++)
    {
        float x = input[i*2+0];
        float y = input[i*2+1];
        output[i*2+0] = matrix[0] * x + matrix[1] * y;
        output[i*2+1] = matrix[2] * x + matrix[3] * y;
    }
}

この関数は、最適化後のFortranの対応する関数よりも実行速度が遅くなります。なぜでしょうか?出力配列に値を書き込むと、matrixの値を変更する可能性があるからです。結局、ポインタが重なって、メモリの同じチャンクを指す可能性があるのです(その中には int のポインターを使用します!) Cコンパイラは、すべての計算のために、4つの行列の値をメモリから再ロードすることを余儀なくされます。

Fortranでは、コンパイラは行列の値を一度ロードして、レジスタに格納することができます。これは、Fortranコンパイラが、ポインタや配列がメモリ上で重ならないことを前提にしているからです。

幸いなことに restrict キーワードとストリクトエイリアスがC99標準に導入され、この問題に対処しています。最近では、ほとんどのC++コンパイラでも十分にサポートされている。キーワードは、プログラマがポインタが他のポインタとエイリアスしないことを約束するヒントをコンパイラに与えることを可能にします。strict-aliasing は,異なる型のポインタが決して重ならないことをプログラマが約束することを意味し,たとえば double* とは重なりません。 int* (ただし char*void* は何にでも重なります)。

それらを使えば、CとFortranから同じスピードが得られるでしょう。しかし、その能力は restrict キーワードは、パフォーマンス上重要な関数にのみ使用することで、C(およびC++)のプログラムはより安全で書きやすくなります。例えば、無効なFortranのコードを考えてみましょう。 CALL TRANSFORM(A(1, 30), A(2, 31), A(3, 32), 30) これは、ほとんどのFortranコンパイラが何の警告も出さずに喜んでコンパイルしますが、一部のコンパイラ、一部のハードウェア、一部の最適化オプションでのみ表示されるバグを引き起こします。