PBSProを普通に使う場合にはcpu coreの空き数、メモリ搭載量とかでジョブの配送先が決まる。
ただ最近ではGPUの空き枚数でジョブの配送が決まってほしい場合もあり、基本のままでは不十分になりつつある。
例えば64coreなマシンで4枚GPUを搭載しているノードにジョブを入れたい場合、
64coreすべてを使用するような形でジョブを投げる必要がある
もし16coreで投げると、そのマシンはまだまだジョブを受け付けられる状態にあり、その他のジョブがGPUを使うようなら
4枚のGPUはその計算リソースを供給され遅くなるか、最悪ジョブが停止されてしまう。

ここではGPUの枚数で計算ノードが決定され(resource定義)、かつそのGPUがそのジョブにしか使われないようにしてみる(hook定義)。
*openpbs-20.0.1を使っています
初期 †
一応「pbsnodes」には「ngpus」という欄がある. だた何もしなければ「0」のままで、
[illya@c ~]$ pbsnodes -aS
vnode state OS hardware host queue mem ncpus nmics ngpus comment
--------------- --------------- -------- -------- --------------- ---------- -------- ------- ------- ------- ---------
s free -- -- s -- 31gb 8 0 0 --
[illya@c ~]$
[illya@c ~]$ qsub -q workq -l select=1:ncpus=1:ngpus=0 -I
qsub: Unknown resource: ngpus
[illya@c ~]$
かつそれを使うことも出来ない. 表記はされているが使えない状態で、まずはこれを調整します
resource †
既定ではGPUを定義する項目がないので、その定義項目をジョブ管理ノードに追加します。
[root@openpbs ~]# qmgr
Qmgr: create resource ngpus type=long, flag=nh <--「create resource ngpus type=long, flag=nh」を実行
Qmgr: list resource <--「list resource」で確認
#
# Create resources and set their properties.
#
Resource ngpus
type = long
flag = hn
Qmgr: quit
[root@openpbs ~]#
その後、pds機能を一旦停止させ、新たなリソース名として「ngpus(数値型)」を追加
「sched_config」を修正してpbsを再起動
[root@openpbs ~]# systemctl stop pbs
[root@openpbs ~]# vi /var/lib/pbs/sched_priv/sched_config
- resources: "ncpus, mem, arch, host, vnode, netwins, aoe"
+ resources: "ncpus, mem, arch, host, vnode, netwins, aoe, ngpus" <--- 274行目あたり、「ngpus」を追加
[root@openpbs ~]# systemctl start pbs
この後に、計算ノードにGPUを定義します
[root@openpbs ~]# qmgr -c "set node s resources_available.ngpus=2"
(確認)
[root@openpbs ~]# qmgr -c "print node s"
#
# Create nodes and set their properties.
#
#
# Create and define node s
#
create node s Mom=s.sybyl.local
set node s state = free
set node s resources_available.arch = linux
set node s resources_available.host = s
set node s resources_available.mem = 32886284kb
set node s resources_available.ncpus = 8
set node s resources_available.ngpus = 2
set node s resources_available.vnode = s
set node s resv_enable = True
[root@openpbs ~]#
ここでqsubのインターラクティヴモードで GPU を1つ使うようにしてみる
[illya@client ~]$ qsub -q workq -l select=1:ncpus=1:ngpus=1 -I
qsub: waiting for job 7.openpbs to start
qsub: job 7.openpbs ready
[illya@s ~]$
nvidia-smiコマンドでgpuリソースを確認すると、、、
[illya@s ~]$ nvidia-smi -L
GPU 0: GeForce GT 710 (UUID: GPU-f0753e20-06f7-695a-f325-b5b6342393ba)
GPU 1: GeForce GT 710 (UUID: GPU-34c49ab7-b79d-e341-93e2-1659e9bd9e57)
[illya@s ~]$
と2つのGPUが列挙されてしまう。1つだけ使いたいのに、、っでそれを解消するのがhook
hook †
参照先: https://www.altair.com/resource/altair-pbs-professional-support-on-nvidia-dgx-systems
まずは下記を実行して「pbs_cgroups.json」を得ます。
[root@openpbs ~]# qmgr -c "export hook pbs_cgroups application/x-config default" > pbs_cgroups.json
取得した「pbs_cgroups.json」の中身に修正を加えます。
{
"cgroup_prefix" : "pbs_jobs",
"exclude_hosts" : [],
"exclude_vntypes" : ["no_cgroups"],
"run_only_on_hosts" : [],
"periodic_resc_update" : true,
"vnode_per_numa_node" : false,
"online_offlined_nodes" : true,
"use_hyperthreads" : false,
"ncpus_are_cores" : false,
"cgroup" : {
"cpuacct" : {
"enabled" : true,
"exclude_hosts" : [],
"exclude_vntypes" : []
},
"cpuset" : {
"enabled" : true,
"exclude_cpus" : [],
"exclude_hosts" : [],
"exclude_vntypes" : [],
"mem_fences" : true,
"mem_hardwall" : false,
"memory_spread_page" : false
},
"devices" : {
"enabled" : true, <--ここを「false」から「true」に変更
"exclude_hosts" : [],
"exclude_vntypes" : [],
"allow" : [ # allow部分を下記に全変換
"c 195:* m",
"c 136:* rwm",
["infiniband/rdma_cm","rwm"], # infinibandを持たないなら削除
["fuse","rwm"],
["net/tun","rwm"],
["tty","rwm"],
["ptmx","rwm"],
["console","rwm"],
["null","rwm"],
["zero","rwm"],
["full","rwm"],
["random","rwm"],
["urandom","rwm"],
["cpu/0/cpuid","rwm","*"],
["nvidia-modeset", "rwm"],
["nvidia-uvm", "rwm"],
["nvidia-uvm-tools", "rwm"],
["nvidiactl", "rwm"],
["loop0","rwm"] # singularityをhookで使うなら
]
},
"hugetlb" : {
"enabled" : false,
"exclude_hosts" : [],
"exclude_vntypes" : [],
"default" : "0MB",
"reserve_percent" : 0,
"reserve_amount" : "0MB"
},
"memory" : {
"enabled" : true, # メモリー確保とか面倒ならfalseへ
"exclude_hosts" : [],
"exclude_vntypes" : [],
"soft_limit" : false,
"default" : "256MB",
"reserve_percent" : 0,
"reserve_amount" : "64MB"
},
"memsw" : {
"enabled" : true, # メモリー確保とか面倒ならfalseへ
"exclude_hosts" : [],
"exclude_vntypes" : [],
"default" : "256MB",
"reserve_percent" : 0,
"reserve_amount" : "64MB"
}
}
}
修正した「pbs_cgroups.json」ファイルをシステムに戻して反映させる
[root@openpbs ~]# qmgr -c "import hook pbs_cgroups application/x-config default pbs_cgroups.json"
hook 'pbs_cgroups' contents overwritten
[root@openpbs ~]#
その後に「pbs_cgroup」を有効にする
(現状の確認)
[root@pbspro ~]# qmgr
Qmgr: list hook
Hook pbs_cgroups
type = site
enabled = false
event = execjob_begin,execjob_epilogue,execjob_end,execjob_launch,
execjob_attach,
execjob_resize,
exechost_periodic,
exechost_startup
user = pbsadmin
alarm = 90
freq = 120
order = 100
debug = false
fail_action = offline_vnodes
(hookを有効にする)
Qmgr: set hook pbs_cgroups enabled = true
(有効になったかの確認)
Qmgr: list hook
Hook pbs_cgroups
type = site
enabled = true <-- trueに代わる
event = execjob_begin,execjob_epilogue,execjob_end,execjob_launch,
execjob_attach,
execjob_resize,
exechost_periodic,
exechost_startup
user = pbsadmin
alarm = 90
freq = 120
order = 100
debug = false
fail_action = offline_vnodes
Qmgr:
[root@pbspro ~]#
あとはpbs_serverと対象の計算ノードのpbsサービスを再起動します
[root@openpbs ~]# systemctl restart pbs
[root@s ~]# systemctl restart pbs
テスト †
同じようにqsubのインターラクティヴモードでテスト
この際、「-l nodes=1:ppn=1:ngpus=2」な書式はダメみたいで「-l select=1:ncpus=1:ngpus=2」と「select」と「ncpus」でないと無理みたい
[illya@client ~]$ qsub -q workq -l select=1:ncpus=1:ngpus=1 -I
qsub: waiting for job 22.openpbs to start
qsub: job 22.openpbs ready
[illya@s ~]$ nvidia-smi -L
GPU 0: GeForce GT 710 (UUID: GPU-f0753e20-06f7-695a-f325-b5b6342393ba)
[illya@s ~]$ exit
[illya@client ~]$ qsub -q workq -l select=1:ncpus=1:ngpus=2 -I
qsub: waiting for job 23.openpbs to start
qsub: job 23.openpbs ready
[illya@s ~]$ nvidia-smi -L
GPU 0: GeForce GT 710 (UUID: GPU-f0753e20-06f7-695a-f325-b5b6342393ba)
GPU 1: GeForce GT 710 (UUID: GPU-34c49ab7-b79d-e341-93e2-1659e9bd9e57)
[illya@s ~]$ exit
[illya@client ~]$ qsub -q workq -l select=1:ncpus=1 -I
qsub: waiting for job 24.openpbs to start
qsub: job 24.openpbs ready
[illya@s ~]$ nvidia-smi -L
No devices found.
[illya@s ~]$
1個指定、2個指定も問題なく使えたみたい。また何も指定しないとデバイスすら見えないみたい
複数のclientからGPUを一つづつ貰い受けた時のGPU IDはそれぞれ0となります(それぞれ別々のGPUです)
なので、8GPUマシンで4GPU毎に使用してもGPUカードは被りません
[illya@client ~]$ qsub -l select=1:ncpus=1:ngpus=1:mem=1gb -I [illya@client ~]$ qsub -q workq -l select=1:ncpus=1:ngpus=1 -I
qsub: waiting for job 11.openpbs to start qsub: waiting for job 10.openpbs to start
qsub: job 11.openpbs ready qsub: job 10.openpbs ready
[illya@s ~]$ nvidia-smi -L [illya@s ~]$ nvidia-smi -L
GPU 0: GeForce GT 710 (UUID: GPU-34c49ab7-b79d-e341-93e2-1659e9bd9e57) GPU 0: GeForce GT 710 (UUID: GPU-f0753e20-06f7-695a-f325-b5b6342393ba)
[illya@s ~]$ [illya@s ~]$
留意
ngpus=2でジョブリソースを確保しても、ncpusが余っていればジョブの投入は可能です. 単にGPUリソースにアクセスできないだけ
一応これでGPUリソースの排他的制限は出来たけど、今度は逆にCPUリソースの排他的制限は可能なのかと疑問がでる...未調査です
qsubで1coreリソースを確保して、でも展開されるスクリプトには1core以上を使うアプリが仕込まれると厄介極まりない...
1計算機ノード1coreな世界なら心配要らない話だけど、昨今のハイブリッドMPIなプログラムだと考慮が必要なのかも
つーる †
「qload」と名付けてます
yum install perl-JSON
(ubuntu)
apt install libjson-perl
-
!
-
|
!
-
|
|
|
|
!
-
|
!
-
|
|
|
|
|
|
|
|
!
-
|
!
-
|
-
|
!
!
-
!
-
|
|
-
-
|
|
|
-
|
|
|
!
!
!
-
|
|
|
!
-
|
|
|
|
|
!
-
|
|
-
-
|
-
|
!
!
|
!
| use JSON;
use strict;
my %queue = (
"cpu"=>["em01","em02"],
"gpu"=>["g06","g07","g08","g09","g10","g11","g12","a01","a02","b01","b02","b03","b04","b05","t01"],
);
my @q0 = ("cpu","gpu");
my (%tot,%que,%run);
open(OUT, "qstat -Q 2>&1 | tail -n +3 |");
while(<OUT>){
my @line = split(/\s+/,$_);
$tot{$line[0]}=$line[2];
$que{$line[0]}=$line[5];
$run{$line[0]}=$line[6];
}
close(OUT);
my (%state,%total,%runing,%subpend,%mem,%ncpus,%nmics,%ngpus);
my $pbsnodes=`pbsnodes -aSj -F json`;
my $json=decode_json($pbsnodes);
while (my ($node) = each( %{ $json->{'nodes'} }) ){
$state{$node}=$json->{'nodes'}->{$node}->{'State'};
$total{$node}=$json->{'nodes'}->{$node}->{'Total Jobs'};
$runing{$node}=$json->{'nodes'}->{$node}->{'Running Jobs'};
$subpend{$node}=$json->{'nodes'}->{$node}->{'Suspended Jobs'};
$mem{$node}=$json->{'nodes'}->{$node}->{'mem f/t'};
$ncpus{$node}=$json->{'nodes'}->{$node}->{'ncpus f/t'};
$nmics{$node}=$json->{'nodes'}->{$node}->{'nmics f/t'};
$ngpus{$node}=$json->{'nodes'}->{$node}->{'ngpus f/t'};
}
my %down;
open(OUT, "pbsnodes -l |");
while(<OUT>){
chomp;
if ( $_ =~ /^(\S+)\s+(.*),(.*)$/ ){
$down{$1} = $2;
}
}
close(OUT);
printf("%5s%4s%5s%6s%5s%16s%13s\n","Queue","Run", "wait", "Host","CPU","usage","GPU");
for(my $i = 0; $i <= $#q0 ; $i++ ){
my $q = $q0[$i];
my $n; my $job; my $np;
foreach $n ( 0 .. $#{ $queue{$q} } ){
if ( $n == 0 ){
printf("%5s%4s%5s%6s%6s%22s%6s %s %s\n", $q, $run{$q}, $que{$q}, $queue{$q}[$n], &conv($ncpus{ $queue{$q}[$n] }),
&jobbar( &conv($ncpus{ $queue{$q}[$n] })), &conv($ngpus{$queue{$q}[$n]}),
&jobbarG( $ngpus{$queue{$q}[$n]} ), $down{ $queue{$q}[$n] } );
}else{
printf("%20s%6s%22s%6s %s %s\n", $queue{$q}[$n], &conv($ncpus{ $queue{$q}[$n] }),
&jobbar( &conv($ncpus{ $queue{$q}[$n] })), &conv($ngpus{$queue{$q}[$n]}),
&jobbarG( $ngpus{$queue{$q}[$n]} ), $down{ $queue{$q}[$n] } );
}
}
}
sub conv{
my @value = split(/\//, "@_");
my $v = $value[1] - $value[0];
return "$v/$value[1]";
}
sub jobbarG{
my @value = split(/\//, "@_");
if ( $value[1] == 0 ){ return;}
my $jobbar= "*" x ( $value[1] - $value[0] );
$jobbar = $jobbar . "-" x $value[0];
return $jobbar;
}
sub jobbar{
my @value = split(/\//, "@_");
my $jobbar="";
for ( my $i=0;$i<20;$i++){
if ( $i/20 < $value[0]/$value[1] ){
$jobbar=$jobbar."*";
}else{
$jobbar=$jobbar."-";
}
}
return $jobbar;
}
|
中身は「pbsnodes -aSj」と同じなんだけどね。複数のqueueがあった場合の対策として準備
だいたいこんな出力になります
Queue Run wait Host CPU usage GPU
cpu 1 0 em01 20/20 ******************** 0/0
em02 24/48 ***********--------- 0/0
gpu 2 0 g01 16/16 ******************** 2/4 **--
g02 16/16 ******************** 4/4 ****
|