Fortranバイナリデータを書いて、CとPython(numpy)で読む

Ubuntu-14.04 64bit上のgcc-4.8.4でテスト。

test.F90

program test
  implicit none

  integer :: i, j, count
  real(kind=8) :: data(4, 3)

  print '("Number of bits used for data:", i5)', storage_size(data)
  
  count = 1
  do i = 1, 4
     do j = 1, 3
        data(i, j) = count
        write(*, '(F15.8)', advance='no') data(i, j)
        count = count + 1
     end do
     print *, ''
  end do
  open(unit=11, file="data.bin", status="REPLACE", access="STREAM")
  write(11) data
  close(unit=11)

end program test
% gfortran test.F90
% ./a.out
Number of bits used for data:   64
     1.00000000     2.00000000     3.00000000
     4.00000000     5.00000000     6.00000000
     7.00000000     8.00000000     9.00000000
    10.00000000    11.00000000    12.00000000

test.c

#include<stdio.h>

int main(void) {
  FILE *fp;
  int i, j;
  double x;
  
  printf("Number of bits used for data: %d\n", (int)(sizeof(double) * 8));

  fp = fopen("data.bin", "rb");
  if (!fp) {
    printf("File open failed.\n");
    return 1;
  }

  for (j = 0; j < 3; j++) {
    for (i = 0; i < 4; i++) {
      fread(&x, sizeof(double), 1, fp);
      printf("%f ", x);
    }
    printf("\n");
  }

  return 0;
}
% gcc test.c
% ./a.out
Number of bits used for data: 64
1.000000 4.000000 7.000000 10.000000
2.000000 5.000000 8.000000 11.000000
3.000000 6.000000 9.000000 12.000000

test.py

#!/usr/bin/python

import numpy as np

shape = (3, 4)
dt = np.dtype(('double', shape))
print("Number data type is %s." % dt)
data = np.fromfile("data.bin", dtype=dt)[0]
print(data)
% ./test.py
Number data type is ('<f8', (3, 4)).
[[  1.   4.   7.  10.]
 [  2.   5.   8.  11.]
 [  3.   6.   9.  12.]]

Fortranはじめました

Fortranでプログラムを書くことになった。
まず他人が書いたコードを読み、修正するところがスタートである。

Fortranは古い書き方を許せば書き方に自由度がありすぎるようだ。
なんとなくは読めるけど、細かいところで言語仕様がわからずつまずく。
そのときネットで情報を探すがなかなかみつからない。
どういう書き方が「今」推奨されているのかという情報がなかなか見つからない。
とりあえず、なんとか見つかった情報をコピペしておく。
Fortran Best Practices — Fortran90 1.0 documentation
Fortran Wiki

VASP-GPU (Tesla K80)

Ubuntu 14.04の場合、CUDAのインストールは簡単。
Installing CUDA Toolkit 7.5 on Ubuntu 14.04 Linux | R Tutorial

仕事で使っているGPUマシンにはTesla K80が一枚(2GPU)と、GeForce GT 610が一枚刺さっている。
GeForceの方もCUDAから見えてしまうのでTesla K80だけがGPU計算で見えるようにしたい。そのための環境変数がある。

export CUDA_VISIBLE_DEVICES="0,2"

ただ、どのカードがどのIDを持っているのかがわからないので困った。試行錯誤で決定した。
sampleのdeviceQueryをmakeして、CUDA_VISIBLE_DEVICESと共にどのGPUが見えているかチェックするのが良いだろう。
GPU-UUIDを使うというのが正解らしい(
https://docs.nvidia.com/deploy/pdf/CUDA_Multi_Process_Service_Overview.pdf

nvidia-smi -q

GPU-UUIDが確認できる。

次に、CUDA-aware Open MPIを作る。--with-cuda付でコンパイルすれば良い。
それを使ってVASP-GPUコンパイルした。
VASP-GPUのウエブサイトはここ。GPU port of VASP - Vaspwiki

パフォーマンステストは次の設定で行った。
POSCAR

 S Cu Ga
   1.0
    10.5481205800000009    0.0000000000000000    0.0000000000000000
     0.0000000000000000   10.5481205800000009    0.0000000000000000
     0.0000000000000000    0.0000000000000000   10.4684162900000004
 S Cu Ga
  32  16  16
Direct
   0.3739084011856084  0.3750000000000000  0.6250000000000000
   0.8729603650000000  0.3750000000000000  0.6250000000000000
   0.3729603650000000  0.8750000000000000  0.6250000000000000
   0.8729603650000000  0.8750000000000000  0.6250000000000000
   0.1250000000000000  0.3729603650000000  0.3750000000000000
   0.6250000000000001  0.3729603650000000  0.3750000000000000
   0.1250000000000000  0.8729603650000000  0.3750000000000000
   0.6250000000000001  0.8729603650000000  0.3750000000000000
   0.1270396350000000  0.1250000000000000  0.6250000000000000
   0.6270396350000000  0.1250000000000000  0.6250000000000000
   0.1270396350000000  0.6250000000000001  0.6250000000000000
   0.6270396350000000  0.6250000000000001  0.6250000000000000
   0.3750000000000000  0.1270396350000000  0.3750000000000000
   0.8750000000000000  0.1270396350000000  0.3750000000000000
   0.3750000000000000  0.6270396350000000  0.3750000000000000
   0.8750000000000000  0.6270396350000000  0.3750000000000000
   0.1229603650000000  0.1250000000000000  0.1250000000000000
   0.6229603650000000  0.1250000000000000  0.1250000000000000
   0.1229603650000000  0.6250000000000001  0.1250000000000000
   0.6229603650000000  0.6250000000000001  0.1250000000000000
   0.3750000000000000  0.1229603650000000  0.8750000000000001
   0.8750000000000000  0.1229603650000000  0.8750000000000001
   0.3750000000000000  0.6229603650000000  0.8750000000000001
   0.8750000000000000  0.6229603650000000  0.8750000000000001
   0.3770396350000000  0.3750000000000000  0.1250000000000000
   0.8770396350000003  0.3750000000000000  0.1250000000000000
   0.3770396350000000  0.8750000000000000  0.1250000000000000
   0.8770396350000003  0.8750000000000000  0.1250000000000000
   0.1250000000000000  0.3770396350000000  0.8750000000000001
   0.6250000000000001  0.3770396350000000  0.8750000000000001
   0.1250000000000000  0.8770396350000003  0.8750000000000001
   0.6250000000000001  0.8770396350000003  0.8750000000000001
   0.2500000000000000  0.2500000000000000  0.5000000000000000
   0.7500000000000000  0.2500000000000000  0.5000000000000000
   0.2500000000000000  0.7500000000000000  0.5000000000000000
   0.7500000000000000  0.7500000000000000  0.5000000000000000
   0.0000000000000000  0.2500000000000000  0.2500000000000000
   0.5000000000000000  0.2500000000000000  0.2500000000000000
   0.0000000000000000  0.7500000000000000  0.2500000000000000
   0.5000000000000000  0.7500000000000000  0.2500000000000000
   0.0000000000000000  0.0000000000000000  0.0000000000000000

INCAR

      PREC = Accurate
       GGA = PS
    IBRION = -1
    NELMIN = 5
     ENCUT = 479.242400
     EDIFF = 1.000000e-08
    ISMEAR = 0
     SIGMA = 1.000000e-02
     IALGO = 38
     LREAL = .TRUE.
   ADDGRID = .TRUE.
     LWAVE = .FALSE.
    LCHARG = .FALSE.

KPOINTS

Automatic mesh
0
Gamma
     2     2     2
 0.000 0.000 0.000

PAW datasetはS,Cu_pv,Ga_d (PBE).

結果は次の通り。
CPUがXeon E5-2643 v3 @ 3.40GHzの場合
12コアCPUのみでおよそ1800秒。
2コア2GPUでおよそ750秒。
4コア2GPUでおよそ700秒。
6コア2GPUでおよそ680秒。
8コア2GPUでおよそ1100秒。
12コア2GPUでおよそ1050秒。
4コア2GPU(MPS)でおよそ640秒。
6コア2GPU(MPS)でおよそ580秒。
4コア1GPUでおよそ1190秒。

CPUがXeon E5-2603 v3 @ 1.60GHzの場合
12コアCPUのみでおよそ3300秒。ほぼクロック通り。
2コア2GPUでおよそ920秒。CPUの能力も無視できない。
4コア2GPUでおよそ890秒。
6コア2GPUでおよそ820秒。
8コア2GPUでおよそ1500秒。
12コア2GPUでおよそ1410秒。
4コア2GPU(MPS)でおよそ830秒。
6コア2GPU(MPS)でおよそ820秒。MPSの効果はあまりなし。
4コア1GPUでおよそ1360秒。CPU差はだいぶ縮まる。

Tesla K80カードが2枚のマシンで4 GPUを使うと半分程度のパフォーマンスしか出せなかった。
Tesla K80カード2枚の計算機で同時に同じ計算を二つ実行するとそれぞれ、4コア2GPUでおよそ1080秒。

NSCのPeter Larssonのブログ(Running VASP on Nvidia GPUs - Peter Larsson
)では、
CUDA Multi Process Service (MPS)を使うべしと書いてある。

export CUDA_VISIBLE_DEVICES="0,2"
mkdir /tmp/nvidia-mps
export CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps 
mkdir /tmp/nvidia-log
export CUDA_MPS_LOG_DIRECTORY=/tmp/nvidia-log 
nvidia-cuda-mps-control -d

clientsがserverに接続できたかは、ログを見ればわかる。
https://devtalk.nvidia.com/default/topic/887822/fail-to-launch-cuda-mps/

Larssonの書いているように、もっと原子数が多い系でGPUがより効果的であると思うが、
Gamma-onlyバージョンはまだサポートされていないので、100から200原子くらいの対称性の低い系がスイートポイントかなあ。