1
Help us understand the problem. What are the problem?

posted at

Ubuntu 20.04 の apt 標準リポジトリで GFortran と Intel MKL を使用する

はじめに

Intel MKL (Math Kernel Library) は Intel が開発している数値演算ライブラリで、BLASLAPACK と同じインターフェースを持っており、高速化された BLAS や LAPACK としても知られています。

以前は Intel のコンパイラ製品に同梱の形でしか入手できなかったのですが、ライセンスが Intel Simplified Software License となって、取得や利用、再配布が容易になりました。そのため、各 Linux ディストリビューションでも Intel MKL が提供されるようになってきています。

この記事では、Ubuntu 20.04 の標準リポジトリから apt でインストールする、という前提で、Fortran と MKL を利用するための環境構築について紹介します。Ubuntu 20.04 は WSL2 でも提供されているので、Windows のユーザーにとっても参考にできるかと思います。

実際のところは、しばらく Fortran を使用してなかった私が久しぶりに環境構築しようとしたらいろいろと変わっていて戸惑ったので、そのとき調べた情報をまとめてみた、という話で、最近の Fortran の動向にはまったくついていけていないのですが、あまり手をかけないでサクッと Fortran + MKL の環境構築を行う、というところを目標にしています。

GFortran (GNU Fortran) と Intel MKL のインストール

apt install でのインストール

前提が apt からのフリーの環境構築なので、Fortran は GFortran を使います。GFortran は Free Software Foundation により開発されたフリーの Fortran で GCC (GNU Compiler Collection) の一部として配布されています。

GFortran, Intel MKL (以降、単に MKL と記載します) のいずれも、 Ubuntu 標準リポジトリから apt でインストールできるようになっていて、それぞれ gfortran, intel-mkl という名称で提供されています。

したがって、Ubuntu では単に apt install intel-mkl のようにしてインストールすることができます1

$ sudo apt update
$ sudo apt install gfortran
$ sudo apt install intel-mkl

intel-mkl のインストール (依存関係で多くのパッケージがインストールされます) 時には、下のように BLAS や LAPACK のデフォルトのライブラリとして MKL を使用するように設定するかどうかを訊かれます。配布するプログラムを作成するのでなければ Yes でよいでしょう。そうでなければライセンスを確認して選択してください。

intel-mkl.png

pkg-config によるコンパイルオプションの取得

一般に、外部のライブラリを使用する際には、コンパイルオプションで使用するライブラリを指定する必要があります。規模の大きいライブラリの場合では、しばしば指定は複雑で理解が難しものになりがちです。

apt で MKL をインストールした場合には、GFortran に指定するオプションを pkg-config で取得することができます。

例えば、MKL の LP64 インターフェースで並列処理を行う場合のオプションを取得するには、ライブラリ名を mkl-static-lp64-iomp として、次のように pkg-condfig --libs mkl-static-lp64-iomp を実行すると必要なオプション文字列を得ることができます。

$ pkg-config --libs mkl-static-lp64-iomp
-Wl,--start-group /usr/lib/x86_64-linux-gnu/libmkl_intel_lp64.a /usr/lib/x86_64-linux-gnu/libmkl_intel_thread.a /usr/lib/x86_64-linux-gnu/libmkl_core.a -Wl,--end-group -liomp5 -lpthread -lm -ldl

pkg-config で指定可能なライブラリ名の一覧は pkg-config --list-all で確認できます。

$ pkg-config --list-all
udev                   udev - udev
tbb                    Threading Building Blocks - Intel's parallelism library for C++
mkl-static-lp64-seq    mkl - compatible pkg-config
mkl-static-ilp64-seq   mkl - compatible pkg-config
xkeyboard-config       XKeyboardConfig - X Keyboard configuration data
mkl-dynamic-lp64-iomp  mkl-dynamic-lp64-iomp - compatible pkg-config
blas64                 mkl-sdl-ilp64 - Debian Specific pkg-config for MKL SDL
blas                   mkl-sdl-lp64 - Debian Specific pkg-config for MKL SDL
blas-netlib            BLAS - FORTRAN reference implementation of BLAS Basic Linear Algebra Subprograms
systemd                systemd - systemd System and Service Manager
iso-codes              iso-codes - ISO country, language, script and currency codes and translations
mkl-static-ilp64-iomp  mkl-static-ilp64-iomp - compatible pkg-config
mkl-dynamic-ilp64-seq  mkl-dynamic-ilp64-seq - compatible pkg-config
libdmmp                libdmmp - Device mapper multipath management library
mkl-dynamic-ilp64-iomp mkl-dynamic-ilp64-iomp - compatible pkg-config
mkl-sdl-ilp64          mkl-sdl-ilp64 - Debian Specific pkg-config for MKL SDL
libxcrypt              libxcrypt - Extended crypt library for DES, MD5, Blowfish and others
mkl-static-lp64-iomp   mkl - compatible pkg-config
mkl-sdl-lp64           mkl-sdl-lp64 - Debian Specific pkg-config for MKL SDL
lapack64               mkl-sdl-ilp64 - Debian Specific pkg-config for MKL SDL
shared-mime-info       shared-mime-info - Freedesktop common MIME database
mkl-dynamic-lp64-seq   mkl-dynamic-lp64-seq - compatible pkg-config
lapack                 mkl-sdl-lp64 - Debian Specific pkg-config for MKL SDL
bash-completion        bash-completion - programmable completion for the bash shell
lapack-netlib          LAPACK - FORTRAN reference implementation of LAPACK Linear Algebra PACKage
libcrypt               libxcrypt - Extended crypt library for DES, MD5, Blowfish and others

システム全体に登録されている一覧なので、MKL と無関係なものも含まれていますが、mkl を接頭辞として static/dynamic, ilp64/lp64, iomp/seq の組み合わせを選択するようになっています。

static/dynamic は、ライブラリを静的リンク (static) とするか動的リンク (dynamic) とするかを選択します。

ilp64/lp64 は、配列インデックスのサイズに関わる指定です。大規模な配列を使用する場合は ILP64 (ilp64) を指定します。従来と互換性があるのは LP64 (lp64) です。詳細は公式ドキュメント 「ILP64 インターフェイスと LP64 インターフェイスの使用」 を参照してください。

iomp/seq は、並列計算を行う (iomp) か、それとも逐次計算を行う (seq) かを選択します。

つまり、先の例 mkl-static-lp64-iomp は、静的リンクでLP64インターフェースを使用して並列計算を行う、という指定になります。

サンプルコードによる MKL の動作確認

MKL の動作確認を行うために、LAPACK のサンプルプログラムを用意する必要があります。自分で書けばよいという話もありますが、LAPACK ののサンプルプログラムを白紙から書き起こすのは慣れないと苦痛です (少なくとも私は苦痛です)。

幸いにして Intel による LAPACK 用のサンプルコードが Intel® oneAPI Math Kernel Library LAPACK Examples で提供されているので、このコードを使って動作確認を行うことができます。

例えば、倍精度で連立一次方程式の解を求めるライブラリ DGESV の場合であれば、DGESV Example から、下記のような FORTRAN77 用のサンプルコードを取得することができます。

DGESV のサンプルコード dgesv_ex.f (クリックするとコードが表示されます)
*  Copyright (C) 2009-2015 Intel Corporation. All Rights Reserved.
*  The information and material ("Material") provided below is owned by Intel
*  Corporation or its suppliers or licensors, and title to such Material remains
*  with Intel Corporation or its suppliers or licensors. The Material contains
*  proprietary information of Intel or its suppliers and licensors. The Material
*  is protected by worldwide copyright laws and treaty provisions. No part of
*  the Material may be copied, reproduced, published, uploaded, posted,
*  transmitted, or distributed in any way without Intel's prior express written
*  permission. No license under any patent, copyright or other intellectual
*  property rights in the Material is granted to or conferred upon you, either
*  expressly, by implication, inducement, estoppel or otherwise. Any license
*  under such intellectual property rights must be express and approved by Intel
*  in writing.
*  =============================================================================
*
*  DGESV Example.
*  ==============
*
*  The program computes the solution to the system of linear
*  equations with a square matrix A and multiple
*  right-hand sides B, where A is the coefficient matrix:
*
*    6.80  -6.05  -0.45   8.32  -9.67
*   -2.11  -3.30   2.58   2.71  -5.14
*    5.66   5.36  -2.70   4.35  -7.26
*    5.97  -4.44   0.27  -7.17   6.08
*    8.23   1.08   9.04   2.14  -6.87
*
*  and B is the right-hand side matrix:
*
*    4.02  -1.56   9.81
*    6.19   4.00  -4.09
*   -8.22  -8.67  -4.57
*   -7.57   1.75  -8.61
*   -3.03   2.86   8.99
*
*  Description.
*  ============
*
*  The routine solves for X the system of linear equations A*X = B,
*  where A is an n-by-n matrix, the columns of matrix B are individual
*  right-hand sides, and the columns of X are the corresponding
*  solutions.
*
*  The LU decomposition with partial pivoting and row interchanges is
*  used to factor A as A = P*L*U, where P is a permutation matrix, L
*  is unit lower triangular, and U is upper triangular. The factored
*  form of A is then used to solve the system of equations A*X = B.
*
*  Example Program Results.
*  ========================
*
* DGESV Example Program Results
*
* Solution
*  -0.80  -0.39   0.96
*  -0.70  -0.55   0.22
*   0.59   0.84   1.90
*   1.32  -0.10   5.36
*   0.57   0.11   4.04
*
* Details of LU factorization
*   8.23   1.08   9.04   2.14  -6.87
*   0.83  -6.94  -7.92   6.55  -3.99
*   0.69  -0.67 -14.18   7.24  -5.19
*   0.73   0.75   0.02 -13.82  14.19
*  -0.26   0.44  -0.59  -0.34  -3.43
*
* Pivot indices
*      5      5      3      4      5
*  =============================================================================
*
*     .. Parameters ..
      INTEGER          N, NRHS
      PARAMETER        ( N = 5, NRHS = 3 )
      INTEGER          LDA, LDB
      PARAMETER        ( LDA = N, LDB = N )
*
*     .. Local Scalars ..
      INTEGER          INFO
*
*     .. Local Arrays ..
      INTEGER          IPIV( N )
      DOUBLE PRECISION A( LDA, N ), B( LDB, NRHS )
      DATA             A/
     $  6.80,-2.11, 5.66, 5.97, 8.23,
     $ -6.05,-3.30, 5.36,-4.44, 1.08,
     $ -0.45, 2.58,-2.70, 0.27, 9.04,
     $  8.32, 2.71, 4.35,-7.17, 2.14,
     $ -9.67,-5.14,-7.26, 6.08,-6.87
     $                  /
      DATA             B/
     $  4.02, 6.19,-8.22,-7.57,-3.03,
     $ -1.56, 4.00,-8.67, 1.75, 2.86,
     $  9.81,-4.09,-4.57,-8.61, 8.99
     $                  /
*
*     .. External Subroutines ..
      EXTERNAL         DGESV
      EXTERNAL         PRINT_MATRIX, PRINT_INT_VECTOR
*
*     .. Executable Statements ..
      WRITE(*,*)'DGESV Example Program Results'
*
*     Solve the equations A*X = B.
*
      CALL DGESV( N, NRHS, A, LDA, IPIV, B, LDB, INFO )
*
*     Check for the exact singularity.
*
      IF( INFO.GT.0 ) THEN
         WRITE(*,*)'The diagonal element of the triangular factor of A,'
         WRITE(*,*)'U(',INFO,',',INFO,') is zero, so that'
         WRITE(*,*)'A is singular; the solution could not be computed.'
         STOP
      END IF
*
*     Print solution.
*
      CALL PRINT_MATRIX( 'Solution', N, NRHS, B, LDB )
*
*     Print details of LU factorization.
*
      CALL PRINT_MATRIX( 'Details of LU factorization', N, N, A, LDA )
*
*     Print pivot indices.
*
      CALL PRINT_INT_VECTOR( 'Pivot indices', N, IPIV )
      STOP
      END
*
*     End of DGESV Example.
*
*  =============================================================================
*
*     Auxiliary routine: printing a matrix.
*
      SUBROUTINE PRINT_MATRIX( DESC, M, N, A, LDA )
      CHARACTER*(*)    DESC
      INTEGER          M, N, LDA
      DOUBLE PRECISION A( LDA, * )
*
      INTEGER          I, J
*
      WRITE(*,*)
      WRITE(*,*) DESC
      DO I = 1, M
         WRITE(*,9998) ( A( I, J ), J = 1, N )
      END DO
*
 9998 FORMAT( 11(:,1X,F6.2) )
      RETURN
      END
*
*     Auxiliary routine: printing a vector of integers.
*
      SUBROUTINE PRINT_INT_VECTOR( DESC, N, A )
      CHARACTER*(*)    DESC
      INTEGER          N
      INTEGER          A( N )
*
      INTEGER          I
*
      WRITE(*,*)
      WRITE(*,*) DESC
      WRITE(*,9999) ( A( I ), I = 1, N )
*
 9999 FORMAT( 11(:,1X,I6) )
      RETURN
      END

残念ながら Intel の当該ページは HTML で記述されているので、テキストをコピーして適当なエディタにペーストし、dgesv_ex.f というファイル名で保存するようにしてください。

サンプルプログラム dgesv_ex.f が用意できたら、下記のように gfortran を実行すると MKL とリンクされた実行ファイル dgesv_ex が作成されます。バッククオートで括った部分は pkg-config --libs mkl-dynamic-lp64-iomp の結果を gfortran のオプション文字列として渡すための指定です。ライブラリ名に dynamic の文字列が含まれていることからもわかるように、ここでは動的リンクとなるように指定しました。

$ gfortran -o dgesv_ex dgesv_ex.f `pkg-config --libs mkl-dynamic-lp64-iomp`

実行ファイル dgesv_ex を実行すると、下記のような出力が得られるはずです。ソースコード dgesv_ex.f のコメントの部分に想定される出力が記述されているので見比べてください。同じ結果となっているでしょうか。

$ ./dgesv_ex
 DGESV Example Program Results

 Solution
  -0.80  -0.39   0.96
  -0.70  -0.55   0.22
   0.59   0.84   1.90
   1.32  -0.10   5.36
   0.57   0.11   4.04

 Details of LU factorization
   8.23   1.08   9.04   2.14  -6.87
   0.83  -6.94  -7.92   6.55  -3.99
   0.69  -0.67 -14.18   7.24  -5.19
   0.73   0.75   0.02 -13.82  14.19
  -0.26   0.44  -0.59  -0.34  -3.43

 Pivot indices
      5      5      3      4      5

出力結果が正しいだけでは MKL が使われているかどうか確認できません。Netlib の方の LAPACK ライブラリが使われたかもしれないからです。動的リンクされた実行ファイルであれば、コマンド ldd を使用してどのライブラリが使用されているかを簡単に確認することができます。

ldd dgesv_ex を実行すると、下記のように MKL のライブラリ libmkl_* がリンクされていることを確認できます。

$ ldd dgesv_ex
        linux-vdso.so.1 (0x00007ffc9b5fa000)
        libmkl_intel_lp64.so => /lib/x86_64-linux-gnu/libmkl_intel_lp64.so (0x00007efd5916e000)
        libmkl_intel_thread.so => /lib/x86_64-linux-gnu/libmkl_intel_thread.so (0x00007efd56c02000)
        libmkl_core.so => /lib/x86_64-linux-gnu/libmkl_core.so (0x00007efd528e2000)
        libomp.so.5 => /lib/x86_64-linux-gnu/libomp.so.5 (0x00007efd527f1000)
        libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007efd52529000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efd52337000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007efd5232f000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007efd5230c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007efd59cea000)
        libquadmath.so.0 => /lib/x86_64-linux-gnu/libquadmath.so.0 (0x00007efd522c2000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007efd52173000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007efd52158000)

Intel MKL インストールのまとめ

以上で、apt install による GFortran と MKL のインストール、そしてその動作確認についての説明は終わりです。ライブラリの指定の方法などで若干わかりにくいところもありますが、導入そのものは以前に比べると非常に容易にできるようになったと思います。

記事の後半は Lapack95 についての話です。使えるようにはなっていないので、記録として残していますが読む必要はありません。

MKL 同梱の Lapack95 インターフェースについて

Lapack95 について

本記事での Lapack95 は、Intel MKL で提供されている LAPACK に対する Fortran95 用のインターフェースを指します。Netlib にも同様の目的で LAPACK95 というものが提供されていますが、両者に互換性はないようです。

結論から言うと、apt でインストールされる MKL の Lapack95 を GFortran で使用することはできませんでした。MKL をビルドしなおせば可能2なようですが、記事の趣旨から外れるので見送りました。

以下、失敗の記録なので折りたたんでいます。

モジュールファイル lapack95.mod の作成

Lapack95 を使用するためには lapack95.mod などのファイルが必要になります。apt で intel-mkl をインストールした場合、lapack95.mod/usr/include/mkl/intel64/lp64, /usr/include/mkl/intel64/ilp64 などのディレクトリに設置されています。

しかし、Lapack95 を使用するコード lapacktest.f90 (use lapack95 を含むコード) を GFortran でコンパイルしようすると下記のようなエラーとなりました。lapack95.mod の読み込み自体に失敗しているようです。

$ gfortran -I/usr/include/mkl/intel64/lp64 lapacktest.f90 
f951: Fatal Error: Reading module ‘lapack95’ at line 1 column 2: Unexpected EOF
compilation terminated.

ここで使用した lapack95.mod は intel-mkl 同梱の lapack95.modです。つまり Intel Fortran 用として作成されたもので、GFortran とは互換性がなかったと考えられます。

Intel のドキュメント Fortran 95 Interface Conventions for LAPACK Routines によれば、MKL 同梱の lapack.f90 から lapack95.mod を作成すればよさそうです。lapack.f90/usr/include/mkl にありました。

$ gfortran -c /usr/include/mkl/lapack.f90
$ ls *.mod
f95_precision.mod  lapack95.mod

カレントディレクトリに lapack95.modf95_precision.mod が作成されました。他にも lapack.o が作成されましたが、こちらは使用しないはずです。

ライブラリ libmkl_lapack95_lp64.a のリンク

GFortran 用の lapack95.mod が用意できたので、再度 lapacktest.f90 のコンパイルを試みます。

lapack95.mod は読み込めたようですが、dsyev_f95 への参照が未定義であるとのエラーが出ました。

$ gfortran lapacktest.f90 `pkg-config --libs mkl-dynamic-lp64-iomp`
/usr/bin/ld: /tmp/ccaCroPn.o: in function `test_':
lapacktest.f90:(.text+0xbc8): undefined reference to `dsyev_f95_'
/usr/bin/ld: lapacktest.f90:(.text+0xc64): undefined reference to `dsyev_f95_'
/usr/bin/ld: lapacktest.f90:(.text+0x1b73): undefined reference to `zheev_f95_'
/usr/bin/ld: lapacktest.f90:(.text+0x1c0f): undefined reference to `zheev_f95_'
collect2: error: ld returned 1 exit status

dsyev_f95 は LAPACK のルーチン DSYEV へのラッパーであることが lapack.f90 のコードから推測できます。どうやら MKL のコアライブラリには dsyev_f95 が含まれていないようです。

コマンド nm を使用すると動的ライブラリ内で定義されているシンボルを確認することができます。dsyev_f95libmkl_lapack95_lp64.a, libmkl_lapack95_ilp64.a で定義されていました。

オプション -lmkl_lapack95_lp95 を追加してコンパイルを実行すると、dsyev_f95 は解決したようですが、ライブラリが PIE オブジェクトではないというエラーが発生しました。

$ gfortran lapacktest.f90 -lmkl_lapack95_lp64 `pkg-config --libs mkl-dynamic-lp64-iomp`
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libmkl_lapack95_lp64.a(dsyev.o): relocation R_X86_64_32 against `__STRLITPACK_2' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libmkl_lapack95_lp64.a(zheev.o): relocation R_X86_64_32 against `__STRLITPACK_2' can not be used when making a PIE object; recompile with -fPIE
collect2: error: ld returned 1 exit status

PIE とは Position Independent Executable (位置独立実行形式) を表します3。エラーはライブラリに含まれるオブジェクトの形式の違いによるもので、手っ取り早く回避するにはオプション -no-pic を指定します。

未定義関数 for_check_mult_overflow64, for_allocate

オプション -no-pie を追加してコンパイルを実行したところ、今度は for_check_mult_overflow64, for_allocate が未定義であるとのエラーが発生しました。

$ gfortran lapacktest.f90 -lmkl_lapack95_lp64 `pkg-config --libs mkl-dynamic-lp64-iomp`
 -no-pie
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libmkl_lapack95_lp64.a(dsyev.o): in function `dsyev_f95_':
dsyev.f90:(.text+0x83f): undefined reference to `for_check_mult_overflow64'
/usr/bin/ld: dsyev.f90:(.text+0x875): undefined reference to `for_allocate'
/usr/bin/ld: dsyev.f90:(.text+0x8cd): undefined reference to `for_check_mult_overflow64'
/usr/bin/ld: dsyev.f90:(.text+0x1318): undefined reference to `for_dealloc_allocatable'
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libmkl_lapack95_lp64.a(zheev.o): in function `zheev_f95_':
zheev.f90:(.text+0xad): undefined reference to `for_check_mult_overflow64'
/usr/bin/ld: zheev.f90:(.text+0xe3): undefined reference to `for_allocate'
/usr/bin/ld: zheev.f90:(.text+0x15f): undefined reference to `for_check_mult_overflow64'
/usr/bin/ld: zheev.f90:(.text+0x972): undefined reference to `for_dealloc_allocatable'
/usr/bin/ld: zheev.f90:(.text+0xa92): undefined reference to `for_dealloc_allocatable'
/usr/bin/ld: zheev.f90:(.text+0xada): undefined reference to `for_check_mult_overflow64'
/usr/bin/ld: zheev.f90:(.text+0xb13): undefined reference to `for_allocate'
/usr/bin/ld: zheev.f90:(.text+0xb7a): undefined reference to `for_check_mult_overflow64'
collect2: error: ld returned 1 exit status

先程と同様に for_check_mult_overflow64, for_allocate が定義されているライブラリをリンクすれば動くのではないかと考えられますが、MKL でインストールされているライブラリの中には見つけることはできませんでした。もしかすると Intel Fortran の中で定義されているのかもしれません。

Lapack95 インターフェース使用についてのまとめ

apt でインストールした MKL で Lapack95 インターフェースを利用しようと試みましたが、未定義シンボルが解決できず、実行ファイルを作成するところまで進めることはできませんでした。GFortran で MKL のビルドを行えば Lapack95 を利用できる可能性がありますが、手軽さは大きく損なわれてしまうので、別の機会に検討したいと思います。



  1. Qiita: 「Intel MKLの各Linuxへのインストール方法」 では他の Linux ディストリビューションの場合や、Intel 公式の apt リポジトリを使う方法が紹介されています。 

  2. Qiita: 「MKLのLapack95インターフェースを使ってFortranで行列を対角化しよう」では Lapack95 のビルドを行っているようです。残念なことに参照している公式ドキュメントのリンクが切れてしまっていて一部が確認できません。 

  3. Qiita: 「makeで "can not be used when making a PIE object" が出たら」にわかりやすい説明があります。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?