Fortran プログラミングガイド ホーム目次前ページへ次ページへ索引


第 7 章

移植

この章では、他の Fortran から Sun コンパイラへのプログラムの移植について説明します。VAX VMS Fortran プログラムであれば、大部分はそのまま Sun f77 でコンパイルできます。詳細は、『FORTRAN 77 言語リファレンス』の 付録 D『VMS 言語拡張』を参照してください。


注 - 移植上の問題のほとんどは Fortran 77 プログラムに関連します。Sun Fortran 95 コンパイラ f95 は、標準以外の拡張をほとんど組み込んでいません。これについては、『Fortran ユーザーズガイド』を参照してください。

時間と日付関数

時刻や CPU の経過時間を戻すライブラリ関数は、システムによって異なります。

次に示す時間関数は Sun Fortran ライブラリでは直接にはサポートされていません。ただし、これらの関数と同じ機能のサブルーチンをユーザーが作成できます。

次の表に、Sun Fortran ライブラリでサポートされる時間関数を示します。

表 7-1   Sun Fortran 時間関数
名前 機能 マニュアルページ
time 1970 年 1 月 1 日からの経過秒数を返す time(3F)
date 日付を文字列で返す date(3F)
fdate 現在の日付と時刻を文字列で返す fdate(3F)
idate 現在の月、日、年を整数配列で返す idate(3F)
itime 現在の時、分、秒を整数配列で返す itime(3F)
ctime time 関数の返した時間を文字列に
変換する
ctime(3F)
ltime time 関数の返した時間を現地時刻に変換する ltime(3F)
gmtime time 関数の返した時間をグリニッジ標準時に変換する gmtime(3F)
etime シングルプロセッサ: プログラムの実行で経過したユーザー時間とシステム時間を返す。 マルチプロセッサ: 実測時間を返す。 etime(3F)
dtime 最後に dtime を呼び出した時点から経過したユーザー時間とシステム時間を返す dtime(3F)
date_and_time 日付と時刻を文字と数値で返す date_and_time(3F)


詳細は、『Fortran ライブラリ・リファレンス』、またはそれぞれの関数のマニュアルページを参照してください。

次の表に示すルーチンは、VMS Fortran のシステムルーチン idatetime との互換機能を提供します。これらのルーチンを使用するときは、f77 のコマンド行で -lV77 オプションを指定しなければなりません。この場合、標準の f77 バージョンの代わりに VMS バージョンの方が使用されることになります。

表 7-2   非標準 VMS Fortran システムルーチンの要約
名前 定義 呼び出し手順 引数の型
idate 日、月、年 (d,m,y) 形式の日付 call idate(d, m, y) integer
time 時分秒 (hhmmss) 形式の現在時刻 call time(t) character*8



注 - date(3F) ルーチンおよび idate(3F) ルーチンの VMS バージョンは年を示す場合に 2 桁の値しか返さないので、「2000 年には無効」 になります。これらのルーチンから返される日付を差し引いて継続時間を計算するプログラムは、1999 年 12 月 31 日以降は正しく機能しなくなります。Fortran 95 のルーチン
date_and_time(3F) が FORTRAN 77 と Fortran 95 の両プログラムで使用できるので、これらのルーチンの代わりに date_and_time(3F) を使用してください。詳細は、『Fortran ライブラリ・リファレンス』を参照してください。

エラー条件のサブルーチンである errsns は完全に VMS オペレーティングシステムに特有のものであるため、この Fortran にはありません。

次に、これら時間関数を使用した簡単な例を示します (TestTim.f)。

    subroutine startclock 
    common / myclock / mytime 
    integer mytime, time 
    mytime = time() 
    return 
    end 
    function wallclock 
    integer wallclock 
    common / myclock / mytime 
    integer mytime, time, newtime 
    newtime = time() 
    wallclock = newtime - mytime 
    mytime = newtime 
    return 
    end 
    integer wallclock, elapsed 
    character*24 greeting 
    real dtime, timediff, timearray(2) 
c   見出しを出力
    call fdate( greeting ) 
    print*,  "	Hello, Time Now Is: ",  greeting 
    print*,	"See how long 'sleep 4' takes, in seconds" 
    call startclock 
    call system( 'sleep 4' ) 
    elapsed = wallclock() 
    print*, "Elapsed time for sleep 4 was: ", elapsed," seconds"
c   普通の計算に必要な CPU 時間をテストする
    timediff = dtime( timearray ) 
    q = 0.01 
    do 30 i = 1, 1000 
    q = atan( q ) 
30  continue 
    timediff = dtime( timearray ) 
    print*, "atan(q) 1000 times took: ", timediff ," seconds"
    end 

このプログラムを実行すると、次のような結果になります。

demo% TimeTest
 Hello, Time Now Is: Mon Feb 12 11:53:54 1996
 See how long 'sleep 4' takes, in seconds
 Elapsed time for sleep 4 was:   5 seconds
 atan(q) 1000 times took:     2.26550E-03 seconds
demo%

書式

f77 および f95 の書式編集記述子は、他のシステム上では動作が異なる場合があります。ここでは、f77 と他の実装では取り扱いの異なる編集記述子を示します。

キャリッジ制御

Fortran のキャリッジ制御は、Fortran が最初に開発されていたときに使用されていた装置の機能から発達したものです。同じ歴史上の理由のため、UNIX オペレーティングシステムから派生したオペレーティングシステムには、Fortran のキャリッジ制御がありません。しかし、次の 2 つの方法でシミュレートできます。

このようにして開いたファイルを、lp(1) コマンドを使用して出力できます。

ファイルを扱う

Fortran の初期のシステムは名前付きファイルを使用せず、実際のファイル名と内部装置番号を対応させるコマンド行機構を提供していました。この機能は、標準の UNIX のリダイレクトなど、いくつかの方法でエミュレートできます。

例 : stdinredir.data からリダイレクトします。csh(1) を使用した例です。

demo% cat redir.data	データファイル
 9 9.9 

 
demo% cat redir.f	ソースファイル
    read(*,*) i, z	プログラムは標準入力を読み取る
    print *, i, z 
    stop 
    end 

 
demo% f77 -silent -o redir redir.f	コンパイル
demo% redir < redir.data	リダイレクトを伴う実行でデータファイルを
	                        読み取る
  9 9.90000
demo%

科学技術計算用メインフレームから移植する

アプリケーションコードが本来、CRAY や CDC などの 64 ビット (または 60 ビット) メインフレーム用に開発されていた場合、UltraSPARC-II プラットフォームへポーティングしているときに、これらのコードを次のオプションを付けてコンパイルしたい場合があります。

-fast -xarch=v9a -xchip=ultra2 \ -xtypemap=real:64,double:64,integer:64

このオプションは、自動的にすべてのデフォルトの REAL 変数および定数を REAL*8 に、デフォルトの COMPLEXCOMPLEX*16 に昇格させます。宣言されていない変数または単に REALCOMPLEX であると宣言されている変数だけを昇格させ、明示的に宣言された変数 (REAL*4 など) は昇格させません。単精度の REAL 定数もすべて REAL*8 に昇格されます (ターゲットプラットフォームに対して -xarch-xchip を設定します)。デフォルトの DOUBLE PRECISION データも REAL*16 に昇格させるには、-xtypemap 例で double:64double:128 に変更します。

-xtypemap オプションは、-dbl、-r8、-i2 に優先します。詳細については、『Fortran ユーザーズガイド』、 f77(1) および f95(1) のマニュアルページを参照してください。

より忠実にオリジナルのメインフレーム環境を再現したい場合は、オーバーフロー、ゼロ除算、無効演算時には停止するほうがよいかもしれません。主プログラムを
-ftrap=common を付けてコンパイルすると、そのようになります。

データ表現

Fortran におけるデータオブジェクトのハードウェア表現に関する詳細は、『FORTRAN 77 言語リファレンス』、『Fortran ユーザーズガイド』、『数値計算ガイド』を参照してください。通常、システムやハードウェアプラットフォーム間でデータ表現が異なると、移植性に関して重大な問題が生じます。

次のことに注意してください。

ホレリスデータ

古い Fortran アプリケーションの多くは、ホレリス ASCII データを数値データオブジェクトに格納します。1977 Fortran 規格 (および Fortran 95) において、CHARACTER データ型はこの目的のために提供され、その使用が推奨されています。現在でも古い Fortran のホレリス (nH) 機能を使用して変数を初期化できますが、標準的な使い方ではありません。次の表に、データ型に適合する文字の最大数を示します。この表では、太字のデータ型は、f77 コマンド行フラグ、-dbl、-r8、-xtypemap= のいずれかによって昇格させられるデフォルトの型を示します。

表 7-3   データ型の最大文字数 (f77)  

標準 ASCII 文字の最大文字数
データ型 -i2、-i4、-r8、-db1 以外 -i2 -i4 -r8 -db1
BYTE 1 1 1 1 1
COMPLEX 8 8 8 16 16
COMPLEX*16 16 16 16 16 16
COMPLEX*32 32 32 32 32 32
DOUBLE COMPLEX 16 16 16 32 32
DOUBLE PRECISION 8 8 8 16 16
INTEGER 4 2 4 4 8
INTEGER*2 2 2 2 2 2
INTEGER*4 4 4 4 4 4
INTEGER*8 8 8 8 8 8
LOGICAL 4 2 4 4 8
LOGICAL*1 1 1 1 1 1
LOGICAL*2 2 2 2 2 2
LOGICAL*4 4 4 4 4 4
LOGICAL*8 8 8 8 8 8
REAL 4 4 4 8 8
REAL*4 4 4 4 4 4
REAL*8 8 8 8 8 8
REAL*16 16 16 16 16 16


通常の Fortran における標準 ASCII 文字の格納に関しては、次のことが言えます。

つまり、いずれのオプションの場合でも領域は用意されますが、通常の Fortran で -r8 の場合は領域は利用できません。

オプション -i2、-r8、-dbl は現在では使用しないので、代わりに -xtypemap オプションを使用してください。

例 : ホレリスを使用して変数を初期化します。

demo% cat FourA8.f
    double complex x(2) 
    data x /16Habcdefghijklmnop, 16Hqrstuvwxyz012345/ 
    write( 6, '(4A8, "!")' ) x 
    end 

 
demo% f77 -silent -o FourA8 FourA8.f
demo% FourA8
abcdefghijklmnopqrstuvwxyz012345!
demo% 

ホレリス定数を引数として渡したり、式や比較の中で使用しようとすると、ホレリス定数は文字型の式として解釈されます。

そのデータの型で使用しなければならない場合は、ホレリス定数によってデータ項目を初期化し、それを他のルーチンへ引き渡してください。

例 :

        program respond
        integer yes, no
        integer ask
        data yes, no / 3hyes, 2hno /

 
        if ( ask() .eq. yes ) then
            print *, 'You may proceed!'
        else
            print *, 'Request Rejected!'
        endif
        end

 
        integer function ask()
        double precision solaris, response
        integer yes, no
        data yes, no / 3hyes, 2hno /
        data solaris/ 7hSOLARIS/
10      format( "What system? ", $ )
20      format( a8 )

 
        write( 6, 10 )
        read ( 5, 20 ) response
        ask = no
        if ( response .eq. solaris ) ask = yes
        return
        end

非標準コーディングの手順

一般的に、アプリケーションプログラムをあるシステムのコンパイラから別のシステムのコンパイラに移植するとき、非標準のコーティングを削除すれば、移植は簡単になります。あるシステムで成功した最適化や回避策が、他のシステムでは曖昧であり、コンパイラを混乱させることもあります。特に、特定のアーキテクチャ用に最適化された手作業によるチューニングは、他の場所ではパフォーマンスを低下させる原因となる可能性もあります。パフォーマンスとチューニングに関しては、第 8 章第 9 章で述べます。しかし、次の話題は、移植に際して、一般的に考慮すべきことです。

初期化されない変数

局所変数や COMMON 変数を自動的にゼロに初期化するシステムもあれば、「非数値」(NaN) に初期化するシステムもあります。しかし、標準的な取り決めはありません。したがって、プログラムは変数の初期値に関して仮定を行うべきではありません。移植性を最大限保証するために、プログラムはすべての変数を初期化すべきです。

呼び出し間での別名での参照

別名での参照は、同じ記憶領域アドレスが複数の名前で参照されるときに発生します。これは、副プログラムへの実引数が、それら実引数間で、あるいは副プログラム内の COMMON 変数間でオーバーラップしている場合に起こります。たとえば、引数 XZ は同じ記憶領域の位置を参照します。BH も同様です。

  COMMON /INS/B(100)
  REAL S(100), T(100)
  ...
  CALL SUB(S,T,S,B,100)
  ...
  SUBROUTINE SUB(X,Y,Z,H,N)
  REAL X(N),Y(N),Z(N),H(N)
  COMMON /INS/B(100)
  ...

このような別名での参照は、すべての移植可能なコードの中で避けるべきです。いくつかのシステム上で、高い最適化レベルを使用すると、予測できない結果になることがあります。

あいまいな最適化

古いコードには、古いベクトル化コンパイラに特定のアーキテクチャに最適なコードを生成させるための、通常の計算の DO ループを再構成しているソースコードが含まれていることがあります。ほとんどの場合、この再構成は必要がないもので、しかもプログラムの移植性を下げます。よく使用される再構成は、"strip-mining" (ストリップマイニング) とループの展開の 2 つです。

ループセクショニング (strip-mining)

いくつかのアーキテクチャ上では、固定長のベクトルレジスタのために、プログラマは手作業でループ内の配列計算について、セグメントの中にループセクショニングをしなければなりませんでした。

  REAL TX(0:63)
  ...
  DO IOUTER = 1,NX,64
     DO IINNER = 0,63
        TX(IINNER) = AX(IOUTER+IINNER) * BX(IOUTER+IINNER)/2.
        QX(IOUTER+IINNER) = TX(IINNER)**2
     END DO
  END DO

ループセクショニングは最近のコンパイラには適切でありません。このループは、次のようにより明瞭に書くことができます。

  DO IX = 1,N
    TX = AX(I)*BX(I)/2.
    QX(I) = TX**2
  END DO

ループの展開

以前、手作業によるループの展開はソースコード最適化のための典型的なテクニックでした。しかし、現在はコンパイラがこの再構成を自動的に行います。

  DO       K = 1, N-5, 6
     DO    J = 1, N
        DO I = 1,N
           A(I,J) = A(I,J) + B(I,K  ) * C(K  ,J)
 *                         + B(I,K+1) * C(K+1,J)
 *                         + B(I,K+2) * C(K+2,J)
 *                         + B(I,K+3) * C(K+3,J)
 *                         + B(I,K+4) * C(K+4,J)
 *                         + B(I,K+5) * C(K+5,J)
        END DO
     END DO
  END DO
  DO       KK = K,N
     DO    J =1,N
        DO I =1,N
           A(I,J) = A(I,J) + B(I,KK) * C(KK,J)
        END DO
     END DO
  END DO

上記ループは、本来意図していたとおり、次のように書き換えるべきです。

  DO       K = 1,N
     DO    J = 1,N
        DO I = 1,N
           A(I,J) = A(I,J) + B(I,K) * C(K,J)
        END DO
     END DO
  END DO

問題の解決方法

ここでは、Sun Fortran に移植したプログラムが予想どおりに動かないときに何をすればいいのかを提案します。

結果が近いけれども正確ではない場合

次の内容を試みてください。

VAX の数学演算は IEEE の数学演算と同じではなく、異なった IEEE プロセッサでさえも結果が違ってきます。特に、これは三角関数を多く含んでいる場合に顕著です。複雑な要因がからんでおり、また、標準仕様が厳密に定義するのは基本的な算術関数だけであるため、IEEE マシンの間にさえ微妙に差異があります。第 6 章「浮動小数点演算」を参照してください。

出力結果は次のようになります。

99,999,990 x 10^29 = 0.99999993E+37 = 7cf0bdc1
99,999,996 x 10^29 = 0.99999993E+37 = 7cf0bdc1

この例では、差は 6 x 1029 です。このような大きな差異が生じる理由は、IEEE の単精度で保証されているのは 10 進 - 2 進変換に対して 10 進の 6 桁だけだからです。7 桁や 8 桁を正しく変換できる場合もありますが、これは値によって異なります。

警告なしにプログラムが異常終了する

警告なしにプログラムが異常終了する場合で、実行のたびに異常が発生するまでの時間が異なる場合は、次のように対処してください。


サン・マイクロシステムズ株式会社
Copyright information. All rights reserved.
ホーム   |   目次   |   前ページへ   |   次ページへ   |   索引