2012-05-24 8 views
5

私は別の配列に追加したい "マスクされた配列"を持っています。つまり、私は3つの配列ABmaskを持っています。私の質問は、(実行時の点で)最も効率的なマスクを(論理配列として、1と0の実際の配列として)格納する方法です。マスクされた配列を別のFortranに追加する最も効率的な方法

EDIT

はここ(あなたはmpif77を持っている場合)あなたがで遊ぶことができますおもちゃのプログラムです:

program main 
    implicit None 
    include 'mpif.h' 
    integer, parameter :: ntry=10000 
    integer, parameter :: asize=1000000 
    real,dimension(asize) :: A,B,maskr 
    logical,dimension(asize) :: mask 
    real*8 :: dd,dt,dtave,dtbest 
    integer i 

    do i=1,asize 
    maskr(i)=mod(i,2) 
    mask(i)=.False. 
    if(mod(i,2).eq.0) mask(i)=.True. 
    enddo 

    A=1.0; B=1.0 
    dtbest=1d33 
    dtave=0.0 
    do i=1,ntry 
    dt=mpi_wtime() 
    call add_arrays_logical(asize,A,B,mask) 
    dt=mpi_wtime()-dt 
    dtbest=min(dt,dtbest) 
    dtave=dtave+dt 
    enddo 
    print*,"==== logical ===" 
    print*,"Average",dtave/ntry 
    print*,"Best",dtbest 

    A=1.0; B=1.0 
    dtbest=1d33 
    dtave=0.0 
    do i=1,ntry 
    dt=mpi_wtime() 
    call add_arrays_real(asize,A,B,maskr) 
    dt=mpi_wtime()-dt 
    dtbest=min(dt,dtbest) 
    dtave=dtave+dt 
    enddo 
    print*,"==== Real ===" 
    print*,"Average",dtave/ntry 
    print*,"Best",dtbest 

    A=1.0; B=1.0 
    dtbest=1d33 
    dtave=0.0 
    do i=1,ntry 
    dt=mpi_wtime() 
    where(mask) A=A+B 
    dt=mpi_wtime()-dt 
    dtbest=min(dt,dtbest) 
    dtave=dtave+dt 
    enddo 
    print*,"==== Where ====" 
    print*,"Average",dtave/ntry 
    print*,"Best",dtbest 

    end 

    subroutine add_arrays_logical(n,A,B,mask) 
    integer n 
    real A(n),B(n) 
    logical mask(n) 
    do i=1,n 
    if(mask(i))then 
     A(i)=A(i)+B(i) 
    endif 
    enddo 
    end 

    subroutine add_arrays_real(n,A,B,mask) 
    integer n 
    real A(n),B(n),mask(n) 
    do i=1,n 
    A(i)=A(i)+mask(i)*B(i) 
    enddo 

    end 

マイ結果:

(のgfortran -O2)

==== logical === 
Average 1.52590200901031483E-003 
Best 1.48987770080566406E-003 
==== Real === 
Average 1.78022863864898680E-003 
Best 1.74498558044433594E-003 
==== Where ==== 
Average 1.48216445446014400E-003 
Best 1.44505500793457031E-003 

(のgfortran -03 -ffast-数学を-funroll-ループ)

==== logical === 
Average 1.47997992038726811E-003 
Best 1.44982337951660156E-003 
==== Real === 
Average 1.40655457973480223E-003 
Best 1.37186050415039063E-003 
==== Where ==== 
Average 1.48403010368347165E-003 
Best 1.45006179809570313E-003 

(pfg90 -fast) -

==== logical === 
Average 5.4871437072753909E-003 
Best 5.4519176483154297E-003 
==== Real === 
Average 4.6096980571746831E-003 
Best 4.5847892761230469E-003 
==== Where ==== 
Average 5.3572671413421634E-003 
Best 5.3288936614990234E-003 

非常に古いマシン上(pfg90 -O2) - 非常に例えば、ループをベクトル化するコンパイラの能力 - - 古いマシン

もちろん
==== logical === 
Average 5.4929971456527714E-003 
Best 5.4569244384765625E-003 
==== Real === 
Average 5.5974062204360965E-003 
Best 5.5701732635498047E-003 
==== Where ==== 
Average 5.3811835527420044E-003 
Best 5.3341388702392578E-003 

、これに影響を与える可能性がいくつかありますので、このような何かを達成する方法についての経験則がありますか?

答えて

3

浮動小数点演算を意味するフロップでは、最初のオプションが明らかに優れています。この場合、ループ反復ごとに1つのフロップがあるため、mask(n)== .trueです。 。一方、2番目のオプションでは、mask(n)の値にかかわらずループ毎に2つのフロップがあります。

OTOH、この機能の実行に費やす時間を最小限に抑えることに興味があれば、データとテストの両方でどちらのバージョンを試してみてください。

また、あなたはA = A + maskr*Bについて

where(mask) A = A + B 
+0

私が扱っているコードはf77です(私の選択ではありません)。 – mgilson

+0

そして、はい、私は時間を関数の中で過ごしたという意味です。私は編集します。 – mgilson

+0

これは、私が本当に簡単にアクセスできる唯一のコンパイラであるため、 'gfortran'を使ってより速く答えることができます。しかし、このコードはあらゆる種類のコンパイラで実行されることを意図しています。この種のことのための経験則があります。親指のルールがない場合、恐らくそれについて心配する価値はありません... – mgilson

5

なぜ "どこ"を使用しないのですか?

where (mask) A = A + B 

おそらくマスクを使用するのが最も速いですが、確実に知るための唯一の方法は測定することです。

+0

私が扱っているコードはf77です(私の選択ではありません)。だから、私はその理由でどこに避けていますか? – mgilson

+0

なぜ 'mask'を使うのが' maskr'より速いと思いますか?私は 'mask 'が' maskr'と比較してより無秩序なメモリアクセスにつながると思っていたでしょう... – mgilson

+0

@mgilson:なぜそれは "無秩序"でしょうか?それでも、A、B、およびマスクを順番に繰り返しています。第1のケースでは、AとBのすべての要素に触れていないだけですが、それで何ですか? – janneb

0

方法を構築Fortranの90+を使用したバージョンをテストしたいのでしょうか?マスクの疎さに関係なく、3つのアレイすべてをスキャンするという代償を払って、条件付きの動作を完全に削除します。

1

コンパイラ、プラットフォーム、および問題に依存するため、これを事前に決定する絶対的な方法はありません。一例として、PolyhedronのWebサイトにあるさまざまなFortranコンパイラを使用したベンチマークを参照してください。

...インテルのベンチマークでは面倒なことがありますが(たとえば、数年前、インテルは司法省によって、コンパイラが他の何かに故意に/インテルのCPUよりも)。

はそう言って、いくつかの考え:他の人が高い「実行コスト」を持っていながら、

1)いくつかの戦略は、より大きな「セットアップ費用」を持っています。つまり、コンパイラ/コードは、CPU /マイクロ命令に正しいビットを送るのに時間がかかります。これは、異なる戦略で異なる時間がかかります。そのため、短い配列を追加する方が良いかもしれませんが、長い配列を使う方が良いかもしれません。

あなたのマスクが「疎」対「疎」(下記も参照)の場合には、匹敵する問題があるかもしれません。

2)ベ​​ンチマークパラメータが小さすぎます。ベンチマークの経験則の1つは、少なくとも数十秒のexeが必要であるということです。あなたのベンチマークでは、実行時間は非常に小さいので、時計などの小さな違いは大きな相対的効果を持つことができます。これを明確にするには、わずか数秒、1〜2秒、7〜10秒が必要な場合に標準のLinpackベンチマークを実行します。一般に、この例では1秒未満の結果は無意味です。

3)いくつかのコンストラクトでF90/95コンストラクト(ForAll、Whereなど)と比較してF77がより速く動作する可能性があります(少なくとも、最近はテストしていませんでした)。

コンパイラで配列セクションをインデックスとして使用することができる場合は、時には(マスクが「スパース」の場合など)、最初にTrueの配列インデックスを保持する整数配列を作成し、アレイセクション。たとえば、Integer iIndex(:)が.Trueを保持しているとします。論理を持つ索引を作成するコストが一般的な算術演算に比べて小さいため、A(iIndex( "bounds"))+ B(iIndex( "bounds"))のようなものは、だから、余分な "セットアップ"コストにもかかわらず、それが "まばらな"問題であれば、ほんの少数の追加などしか必要ないかもしれません。乗算と高い算術演算は、さらに「高価」なので、ばらつきがあるとさらに顕著になります。これは一種の「家で作った」Where/ForAllコンストラクトです。

ちなみに、(明らかに)同じコンパイラであっても、exeに違いがある可能性があります。例えば、単純にFortranは、GCC/gFortranを使用しています(Windowsの場合はPhotran/Eclipseと同じですが、GCC/gFortranを使用しています)。これは、MingWやCygWinなどの「インタリーブ」の方法が異なることが原因である可能性があります。また、特定のチップや命令セットなどのSimply Fortranの広範な最適化などが原因である可能性もあります。これらの問題のいくつかはLinux 「プラットフォーム」問題の例もあります。

関連する問題