fortran66のブログ

fortran について書きます。

【メモ帳】M1 Mac で Fortran

天皇陛下のお誕生日を祝して M1 mac 購入

M1 mac を購入したので、Fortran をインストールしてみました。私は mac は生まれて初めてで右も左も分からぬ状態なので、話半分で読んでください。買ったのは一番安い macbook air 8G/256G/7GPU ので色は給食のアルマイトの食器の様なねずみ色のやつです。名前は Air ですが、ずっしり重くてどちらかというと Heavy Metal という感じです。

約1.29 kg~

最新 Apple Mac mini Apple M1 Chip (8GB RAM, 512GB SSD)

最新 Apple Mac mini Apple M1 Chip (8GB RAM, 512GB SSD)

  • 発売日: 2020/11/17
  • メディア: Personal Computers

約634g~

三種の Fortran

Arm 用 gfortran, Rosetta エミュレータintel 用 gfortran および oneAPI の intel fortran の三種が動くことを確かめました。インストールはいずれもインストーラを動かすだけでできました。

簡単なプログラムで見たところ intel fortran の場合、エミュレーションで動いているにもかかわらず、第四世代 Haswell i7 の4倍くらいのスピードが出ています。同じプログラムでみると ARM 版 gfortran は x86_64 版 gfortran の 1.5 倍くらいの速さで、intel fortran と比較するとプログラムによって速かったり遅かったりします。

ARM ネイティブ gfortran が Rosetta のエミュレーションの intel fortran より遅いのは意外ですが、ネットを流れているベンチマークを見ると、ARM 版 gcc は clang より平均2倍くらい遅いようなので、 GNU コンパイラの問題なのかもしれません。また元々 gfortran は intel fortran より遅く5割増し位な感じなので、合わせるとそんなところかもしれません。

なお、Appleintel fortran は昔から coarray をサポートせず oneAPI 版でもサポートされません。

インストール・メモ

intel fortran

Jacob Williams さんのツイートで intel fortran が動くことが分かっていました。

以下のページから HPC Tool kit 用のオンライン版インストーラを取ってこれます。登録が必要ですが無料です。 fortran コンパイラだけでいいなら oneAPI base kit の方は要りません。ただ Intel MKL は base kit の方に入っているので、これも入れるならば base kit 用のインストーラも必要になります。

software.intel.com

Mac 版は MPI その他が無く(多分そのせいで coarray もない... MPI 上に実装されているので)、選択できるのは C++Fortranコンパイラだけです。Fortran コンパイラだけインストールしました。

コマンドラインXcode というものが必要になるのですが、ifort でコンパイルしようとすると警告が出て自動でインストールしてくれます。

メモ コンパイラにパスを通すスクリプトを先に動かす必要があります。

 . /opt/intel/oneapi/setvars.sh 

ただライブラリへのパスが通っておらず、ネットを検索すると手動でライブラリ位置を指定せよと出てきました。Xcode の前に intel fortran をインストールしたせいかもしれません。

ifort   -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib   xxxx.f90

めんどくさいので .zshrc に alias を書いてみました。

alias ifort='ifort   -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'

gfortran

奥村先生のページを参考にしました。 oku.edu.mie-u.ac.jp

Homebrew というドブロク造りをイメージしたパッケージマネージャーを x86_64 用と arm64 用それぞれに、ページ画面に表示されたコマンド1行をターミナルにコピペ&実行でインストールして、その後それぞれで brew install gfortran を行えば gfortran がインストールされます。

ARM 版を優先したい場合、実行パスの優先順序を工夫する必要があります。

visual studio code

insider という所に行くと ARM 版が落とせます。

code.visualstudio.com

よく分からない形式の圧縮ファイルをクリックすると、実行ファイルらしきものが出てくるので、ええいままよとそれをアプリケーションのフォルダーにコピーしてみました。これでいいのかよく分かりません。クリックすると vs code が起動します。

visual studio code 起動後は Modern FortranFortran IntelliSense の拡張機能を入れました。何か言われるままにクリックして、文法解釈用の fortls も別途インストールした気がします。漫然とクリックしたのでよく覚えていません。とりあえず問題なく動いています。

実行例

とりあえず昔のプログラムを動かしてみました。

fortran66.hatenablog.com

    module m_stamp
        ! use portlib ! MS-FPS4.0
        implicit none
        logical :: first = .true.
        real(kind(0.0d0)) :: t0, t1
    contains
        subroutine stamp(text) ! print time stamp
            character(len = *), intent(in) :: text
            character(len =  8) :: date
            character(len = 10) :: time
            if (first) then 
                first = .false.
                call cpu_time(t0) ! t0 = timef() ! MS-FPS4.0
            end if 
            call cpu_time(t1)   ! t1 = timef() ! MS-FPS4.0
            call date_and_time(date, time)
            print '(6a, 6a, a, g15.3, 2a)', &
              date(1:4), '-', date(5:6), '-', date(7:8), '  ', &
              time(1:2), ':', time(3:4), ':', time(5:8), '  ', &
              ' cpu_time =', t1 - t0, '(sec) ::', text 
        end subroutine stamp  
    end module m_stamp    
  
    module m_cg
        implicit none
        integer, parameter :: kd = kind(0.0d0) !double precision
    contains
        subroutine cg(a, b, x) ! conjugate gradient method
            real(kd), intent(in) :: a(:, :), b(:)
            real(kd), intent(in out) :: x(:)
            real(kd) :: alpha, beta, r2n, r2d, eb2, p(size(b)), q(size(b)), r(size(b))
            integer :: i
            r = b - matmul(a, x) 
            p = r
            r2n = dot_product(r, r)
            eb2 = epsilon(b) * dot_product(b, b)
            do i = 1, size(b)
                if ( r2n < eb2 ) exit
                r2d = r2n
                q = matmul(a, p)
                alpha = dot_product(p, r) / dot_product(p, q)
                x = x + alpha * p
                r = r - alpha * q
                r2n = dot_product(r, r)
                beta = r2n / r2d
                p = r + beta * p              
            end do
        end subroutine cg
    end module m_cg
  
    program cg_main
        use m_cg
        use m_stamp
        implicit none
        integer, parameter :: ns = 2000 
        real(kd) :: a(ns, ns), b(ns), x(ns)
        integer :: i, j
        call stamp('make matrix')
        do i = 1, ns
            b(i) = 1.0_kd
            do j = i, ns ! givens matrix,  x = (1, 0, 0, ... , 0)t
                a(i, j) = real( 2 * min(i, j) - 1, kd )  
                a(j, i) = a(i, j)
            end do
        end do
        call stamp('solve Ax = b')
  ! 
        call random_seed() 
        call random_number(x) ! starting vector
        call cg(a, b, x)     
        x = matmul(a, x) - b
        print *, 'difference (rms) =', sqrt( dot_product(x, x) )
        call stamp('normal end')
    end program cg_main

M1 Mac

intel fortran (Rosetta2)

gfortran (ARM64)

gfortran (Rosetta2)

[a] M1:~/fortran% ifort -O2 cg.f90                               
[a] M1:~/fortran% ./a.out         
2021-02-24  02:07:14.7   cpu_time =      0.500E-05(sec) ::make matrix
2021-02-24  02:07:14.7   cpu_time =      0.182E-01(sec) ::solve Ax = b
 difference (rms) =  4.684681968851512E-007
2021-02-24  02:07:15.6   cpu_time =      0.945    (sec) ::normal end

[a] M1:~/fortran% gfortran -O2 cg.f90                            
[a] M1:~/fortran% ./a.out            
2021-02-24  02:07:24.3   cpu_time =      0.700E-05(sec) ::make matrix
2021-02-24  02:07:24.3   cpu_time =      0.137E-01(sec) ::solve Ax = b
 difference (rms) =   6.6372870430592568E-007
2021-02-24  02:07:26.1   cpu_time =       1.80    (sec) ::normal end

[a] M1:~/fortran% arch -x86_64 /usr/local/bin/gfortran -O2 cg.f90
[a] M1:~/fortran% ./a.out                                        
2021-02-24  02:07:34.2   cpu_time =      0.120E-04(sec) ::make matrix
2021-02-24  02:07:34.2   cpu_time =      0.218E-01(sec) ::solve Ax = b
 difference (rms) =   4.3568034320423338E-007
2021-02-24  02:07:36.4   cpu_time =       2.26    (sec) ::normal end

i7 4770

Windows x64 intel fortran

2021-02-24  02:08:36.8   cpu_time =       0.00    (sec) ::make matrix
2021-02-24  02:08:36.9   cpu_time =      0.469E-01(sec) ::solve Ax = b
 difference (rms) =  4.077532388488979E-007
2021-02-24  02:08:40.8   cpu_time =       3.95    (sec) ::normal end
続行するには何かキーを押してください . . .

追記: i7 4770 の計算時間が昔に比べて長くなって計算スピードが遅くなっていますが、実は MKL 付属の linpack benchmark なども遅くなっています。多分 intel chip の specter とかの対策のせいではないかと疑っているのですが面倒なので確かめていません。気のせいかもしれません。