Fortranのサブルーチン引数のAliasing

出典: トータル・ディスクロージャ・サイト(事実をありのままに)

Fortranの仕様に於いては、サブルーチン/関数の引数は、互いの値に依存関係が無いものとして処理を行うことができる。たとえば、SUBROUTINE F(A,B)という宣言のサブルーチンが有ったとして、サブルーチン中でAにどんな変更を加えたとしても、Bに変更を加える処理が無ければBの値は同じものとして処理を行うことができる。一見当たり前のことを言っているように見えるが、このサブルーチンをCALL F(X,X)のようにして呼び出せば、サブルーチン中のAへの変更が呼び出し元のXの値の変化に直結し、それがサブルーチン中のBの値に影響を及ぼし得るという状況が発生する。

この状況を分かりやすく見せるために、サンプルのプログラムを用意した。

      PROGRAM ALIASTEST
      X=1.0
      CALL F(X,X)
      STOP
      END
      SUBROUTINE F(A,B)
      A=B+1.0
      WRITE(*,*)B
      RETURN
      END

これらの2つのソースコードをコンパイルして実行すると、gfortran (4.6.1)やPGI Fortran (11.4)では2.0を出力する(つまり、Aの変更をBに反映している)が、Intel Fortran (12.1.0/13.0.0)では1.0を出力する。規格に従って、Aへの変更を無視してBの値を出力するように最適化した結果である。

このような処理系に依存する書き方は避けるのが望ましいが、すでに世に出回っているアプリケーションにもこのような処理系依存の書き方をしたものは多い。このようなアプリケーションをIntel Fortranでコンパイルする際に2.0を出力するような挙動をさせたい場合、-no-ansi-aliasというコンパイルオプションを使えば望んだ挙動にできる。

なお、C言語の場合、このようなプログラムは逆に変更を反映するのが義務的な仕様となっている。

void f(float *, float *);

int
main()
{
  float x = 1.0f;
  f(&x, &x);
  return 0;
}
#include <stdio.h>

void
f(float *a, float *b)
{
  *a = *b + 1.0f;
  printf("%f\n", *b);
}

という、先ほどのプログラムをC言語に移植したプログラムは、Intel Cでも2.0を出力する。ただしこの仕様は、特に配列などの処理の並列化の妨げになるため、逆にFortranのような挙動にしたい場合も有る。C99以降(多くのCコンパイラはデフォルトで対応せず、-std=c99などのコンパイルオプションが必要なことが多い)のC言語で定義されているrestrict修飾子を使って、void f(float *restrict a, float *restrict b)のような書き方をすれば、1.0を返すようなプログラムとなる。


この記事へのコメントをお寄せください

  • サイトへの書き込みに差し支えございましたら トータルディスクロージャーサイトサポート係へメールをお送りください
  • トータル・ディスクロージャ・サイトに投稿された文章と画像は、すべてその著作権がHPCシステムズ株式会社に帰属し、HPCシステムズ株式会社が著作権を所有することに同意してください。
  • あなたの文章が他人によって自由に編集、配布されることを望まない場合は、投稿を控えてください。
  • コメントを書き込む場合は名前にひらがなを織り交ぜてください。
  • あなたの投稿する文章と画像はあなた自身によって書かれたものであるか、パブリック・ドメインかそれに類する自由なリソースからの複製であることを約束してください。あなたが著作権を保持していない作品を許諾なしに投稿してはいけません!

<comments hideform="false" />


Comments

ノート:Fortranのサブルーチン引数のAliasing

個人用ツール