スパコンを使う #2 〜ジョブ実行編〜

# はじめに
これは「スパコンを使う #1 〜申し込みからアクセスまで〜」の続きです。話の流れ的にこちらをざっとみていただけるとすんなり入れると思います。

https://qiita.com/Hidariuma_432/items/aaf5d0aeb1bdf7f895ba

今回も、[「スパコン超入門」](https://www.cc.u-tokyo.ac.jp/events/lectures/175/)の講習会内容を主に参照しながら進めていきます。
目標は、スパコンに実際にコードを実行してもらい結果を手元のパソコンで見られるようにすることです。

## 目次
1. [サンプルコードを実行してみる](#1-サンプルコードを実行してみる)
    1.1. [ディレクトリ構造](#11-ディレクトリ構造)
    1.2. [環境構築(Environment module)](#12-環境構築environment-module)
    1.3. [コード準備](#13-コード準備)
    1.4. [コンパイル](#14-コンパイル)
    1.5. [ジョブ実行](#15-ジョブ実行)
    1.6. [sftpコマンドを使ったファイルの送受信](#16-sftpを使ったファイルの送受信)
2. [補足](#2-補足)
    

## 1. サンプルコードを実行してみる
この章ではスパコン超入門に従って、サンプルコードを実行できるまでになる。実行はC/C++, Fortran, Pythonで可能だが今回はC/C++でのやり方のみ書く。（余裕あったらPythonも書くかも）
#### 1.1. ディレクトリ構造
スパコン超入門の資料を引用しながらざっくりとした理解をすると、操作するPCとスパコンへのログインを管理するPCと計算をするPCが違って、色々設定する必要があるから所々気をつける必要があるみたい。
<img alt="スパコン#2_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1691898/938fa74d-f874-3b95-c5c8-d9a657f169b1.png" width="500">

資料の引用にある実行までの流れとしては、
1. SSHでログイン：（Putty、Cygwin）、SSH、鍵認証
2. プログラム作成：エディタ(emacs, vi, nano)、Linux 利用法
3. コンパイル　　：環境モジュール、コンパイルオプション
4. ジョブの投入　：ジョブスクリプトの作成、ジョブ管理
5. 結果の確認

となるが、プログラム作成、ジョブスクリプト作成などについては手元のパソコンで（VScodeなどを利用して）作成してから`sftp`コマンドを使って容易にデータ転送可能（後述）なので、エディタを覚える必要はなさそう（スパコン上でのちょっとしたプログラムの修正などでは使うかもしれない）。

ディレクトリ構造としては以下の図のようになっているらしい。使うのは多分`/home`ディレクトリと`/work`ディレクトリだけだと思う。
<img alt="スパコン#2_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1691898/db3b1708-7f5c-2681-3785-e7ab16b27e99.png" width="500">

自分の理解ではログインノードの中に`/home`と`/work`があり、計算ノードにつながっているのが`/work`という理解。LUSTREファイルシステムはよくわかんない。
ディレクトリ名は、ユーザーIDとプロジェクトコードによって分けられている。（そもそもそれ以外は使えないので間違えることはないけど）。ユーザーIDとプロジェクトコードは登録完了時にメールで送られてくる「Oakbridge-CX 利用登録のお知らせ」に記入してある（下図）。
<img alt="スパコン#2_3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1691898/85141aa8-6f41-7667-3c72-a55a5350723c.png" width="500">

#### 1.2. 環境構築(Environment module)
C/C++またはFortranの場合コードをバイナリファイルにコンパイルする必要がある。またPythonの場合、バージョン2or3の選択が必要になる。それらを設定するためのコマンドが`module`である。正直細かい話は必要ないと思うが、参考記事を載せておく。

https://qiita.com/Ag_smith/items/f268ad27165a60aecd35

コンパイラ等の選択
gnu コンパイラ, インテル製コンパイラ, python インタープリター...

ライブラリを利用した計算の高速化
離散フーリエ変換, 線形代数・連立１次方程式, …

```bash
$ module list
Currently Loaded Modulefiles:
  1) impi/2019.9.304    2) intel/2020.4.304
```
デフォルトだとこんな感じになっていると思う。サンプルコードはintelコンパイラを使っているのでとりあえずそのまま。（スパコン入門ではバージョンを変えているから変えても問題ない。）

##### module コマンドの引数一覧（module [引数]）
| 引数      　　　　　　　　|説明                     　  |
|:-------------|:--------------------------|
|avail         |利用可能な環境の一覧を表示　　　　　　   |
|list          |現在ロードしている環境一覧を表示　  | 
|load          |指定した環境のロード            |
|unload        |指定した環境のアンロード         |
|switch        |環境のロードとアンロードを同時に実行|
|purge         |環境を全てアンロード            |

またPythonの場合はライブラリのインストールにpipを使ったり仮想環境を構築したりするが詳細は省略する（スパコン超入門資料の「スパコンでの機械学習実行超入門」に書かれているので参考にしてほしい。）余裕があれば書くかも。


#### 1.3. コード準備
```bash
$ ssh 'userID'@obcx.cc.u-tokyo.ac.jp 
```
でログイン。
```bash
$ pwd
/home/'userID'
```
となっていると確認できる。
スパコン超入門ではまずフィボナッチ数列の計算をさせようということでコードの作成をしている。ディレクトリの作成とエディタ（今回は簡単そうだったemacsを使う）でコードを作成していく。ここから手元のパソコンで作成したい人は[1.5. sftpコマンドを使ったファイルの送受信](#15-sftpを使ったファイルの送受信)を参照してほしい。
```bash
$ mkdir fibonacci
$ cd fibonacci
$ emacs fibonacci.c
```
ちなみに後述するが計算する際にはこのコードを`/work/'groupname'/'userID'`で実行させるため、はじめから
```bash
$ cd /work/'groupname'/'userID'
$ mkdir fibonacci
$ cd fibonacci
$ emacs fibonacci.c
```
でも良さそう。（スパコン超入門では`/home`は容量に限りがあるから基本`/work`下で管理した方がいいと書いてあるけど、調べてないからわからない）
作成したコードが以下(fibonacci.c)。emacsの操作方法についてはリンクを参照した。

https://qiita.com/namn1125/items/5cd6a9cbbf17fb85c740

<details><summary>fibonacci.c</summary>

```c:fibonacci.c
#include <stdio.h>

int main(void){
  int i;
  long a, b, tmp;

  a = 1;
  b = 1;
  printf("%ld\n", a);

  for (i=2; i<=92; i++){
    tmp = b;
    b = a + b;
    a = tmp;
    printf("%ld\n", a);
  }
}
```
</details>

スパコンではこれに加えて**ジョブスクリプト**と呼ばれるファイルが必要。これは計算ノードに計算してもらうための指示書のようなものである。スパコンはジョブという形でコードを計算ノードに投げてそれを実行してもらうことで、計算をおこなっている。後でも説明するが**ログインノードでコードを実行することはないので注意**
ジョブスクリプトは`.sh`形式で以下のように書く。
```bash:fibonacci.sh
#!/bin/bash
#PJM -L rscgrp=lecture
#PJM -L node=1
#PJM -L elapse=0:01:00
#PJM -g gt00
#PJM -N fibonacci
#PJM -o stdout.txt　
#PJM -j

model purge
module load intel/2020.4.304
./fibonacci.out
```
以下で説明していく
```bash
#!/bin/bash            ← bashの宣言
#PJM -L rscgrp=lecture　　← 自分の「利用可能なリソースキュー」、お試しなら「lecuture」だと思う。
#PJM -L node=1　　　　　　　　　　　　　　　　　　← 使用するノード数（契約で決められた上限は上回れないはず）
#PJM -L elapse=0:01:00 ← 実行時間上限（契約で決められた上限は上回れないはず）
#PJM -g gt00           ← プロジェクトコード
#PJM -N fibonacci　　　　　　　　　　　　← ジョブの名前、実行中に確認した際表示される名前?
#PJM -o stdout.txt     ← 標準出力先のファイル名
#PJM -j                ← エラー出力を標準出力にマージ（他のオプションの設定は不明...）
```
これらについてはOakbridgeに特有な書き方である可能性があるので他のスパコン使う時は要確認。計算させる際のさまざまな設定を指定している。

```
module purge
module load intel/2020.4.304
./fibonacci.out
```
`module`コマンドでコンパイル、実行環境を整える。

`./fibonacci.out`は実行するコンパイルしたファイル名
#### 1.4. コンパイル
ここではCのコンパイルについて書く。コンパイルは`/home`ディレクトリでも`/work`ディレクトリでもどちらでも良い。

```bash
$ icc fibonacci.c -o fibonacci.out
```
インテルコンパイラなので`icc`、`-o fibonacci.out`でコンパイル後の実行ファイルの名前を設定。

:::note
ジョブスクリプトに記入する実行ファイル名とここの実行ファイル名は同じ名前に
:::


#### 1.5. ジョブ実行
:::note alert
（コンパイルした後）ログインノード上で直接プログラム実行することはしない！
ー 負荷がかかると、他のユーザーの同時作業の妨げとなる
:::

`/home`ディレクトリでコンパイルした場合はフォルダごと`/work`ディレクトリにコピーする。
```bash
[ /home/userID ] $ cp –r fibonacci /work/group/userID/
$ cd /work/group/userID/fibonacci
```
fibonacciフォルダをコピー。実際は実行ファイルとジョブスクリプトだけで良さそうだけど。

```bash
$ pjsub fibonacci.sh
```
`pjsub`でジョブを計算ノードに投げることができる。

##### ジョブの管理コマンド
| コマンド 　　　|説明         |例|
|:--------|:-----------|--|
|pjsub    |ジョブの投入　　　　 |`$ pjsub “script.sh”`|
|pjdel    |ジョブの削除   |`$ pjdel “job ID”`| 
|pjstat   |ジョブの確認   |`$ pjstat`|

##### pjstatのオプションコマンド
| オプション 　　　|説明         |
|:--------|:-----------|
|-H   |終了したジョブの確認　　 |
|--rsc -b   |各リソースグループの混雑具合を確認可能   |
|--rsc -x   |各リソースグループで要求可能なリソース量を出力  |
|--nodeuse   |OBCX全体での使用状況確認|


#### 1.6. sftpを使ったファイルの送受信
スパコン超入門のサンプルコードにはMPIを使ったコードもある。（pi_mpi.c）

<details><summary>pi_mpi.c.c</summary>

```c:pi_mpi.c
#include <stdlib.h>
#include <stdio.h>
#include <mpi.h>

int main(int argc, char* argv[]){
    int ndiv = 5600000000;
    int ierr, myrank, nprocs;
    int ndiv_local, i;
    double x, width, sum, total_sum;
    double t1, t2;

    width = 1.0 / (double)ndiv;

    ierr = MPI_Init(&argc, &argv);
    ierr = MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
    ierr = MPI_Comm_size(MPI_COMM_WORLD, &nprocs);

    t1 = MPI_Wtime();

    sum = 0.0;
    ndiv_local = ndiv / nprocs;
    for(i = myrank*ndiv_local;i<(myrank+1)*ndiv_local;i++){
        x = (i + 0.5)*width;
        sum += width*4.0/(1.0+x*x);
    }

    MPI_Reduce(&sum, &total_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

    t2 = MPI_Wtime();

    if(myrank==0){
        printf("PI(MPI) = %.18f\n", total_sum);
        printf("Number of cores utilized = %d\n", nprocs);
        printf("Exwcution time = %.8f (sec.)\n", t2 - t1);
    }

    ierr = MPI_Finalize();

    return 0;
}

```
</details>

この時エディタで作成するのは（慣れていないのもあるが）やや大変。なので手元のパソコンのVScodeでコードを書いてからそれをスパコンのディレクトリに送信するという形で行う。
使ったコマンドは`sftp`でSSH接続している人はこれでいける（他の方法でアクセスしている人はわかんないです、、、）

https://dezanari.com/ssh-sftp/#toc5

スパコンにログインしている人は、一旦`exit`でログアウト。
手元のパソコンで
```bash
$ sftp userID@obcx.cc.u-tokyo.ac.jp
```
と入力。SSH接続同様パスワードを求められるので入力すると
```bash
Connected to obcx.cc.u-tokyo.ac.jp.
sftp> 
```
となり、ファイルのやりとりが可能な状態でログインできる。
##### 1. 手元のパソコンからスパコンにファイルを送信

ログインした状態の現在地は`/home/userID`ディレクトリなのでそこにそのまま入れても良いし、`/work`ディレクトリに移動してからでも良い
```bash
(sftp> cd /work/group/userID)
sftp> put -r desktop/スパコン/calc_pi_mpi
```
`put`は送信するコマンドでオプションは他のファイル操作のコマンドと多分同じ
```bash
#フォルダを送信するとき
sftp> put -r フォルダのpath
#ファイルを送信するとき
sftp> put ファイルのpath
```

##### 2. スパコンからファイルを手元のパソコンに受信 
基本計算し終わったファイル（フォルダ）を手に入れたいと思うので移動してから`get`コマンドで受信する。
```bash
sftp> cd /work/group/userID
sftp> get -r fibonacci
```
```bash
#フォルダを送信するとき
sftp> get -r フォルダのpath
#ファイルを送信するとき
sftp> get ファイルのpath
```

これで実行してそのデータを手に入れるまでのことはできようになりました。
さらなる理解は講習会の資料などで学ぶと良いと思います。


# 2. 補足
ここからはAdditionalで自分が試したことを書いておく。研究で扱うコードはサンプルとは違うところがいくつかある。
- C++で書かれている
- ヘッダーファイルの存在
- 入力データ、数値が存在する
- OpenMPを使っている

この4つがあるので注意が必要。
まずコンパイラはgccを使うので
```bash
$ module purge
$ module load gcc/7.5.0
$ module list
Currently Loaded Modulefiles:
  1) impi/2019.5.281   2) gcc/7.5.0
```
にしておく。

編集中