退職した

前回までのあらすじ : http://w0.hatenablog.com/entry/20160921/1474454159

まあ挫折しました。2年11ヶ月です。

特にどうすればよかったかとか全然わからないし無力感がある。所詮雇われ会社員のエンジニアができる事なんてたかが知れてるよねみたいなことを考えがち。

次どうするかはまだ決まっていません。少なくとも9月中は法的にも立派な無職です。15年社会人やってきて、最初の転職の時にミスって2日無職になったのを除いて、はじめて書類上もきちんと無職になります。


いまだかつてないぐらい仕事する気が起きないね…なんかもうみんな好きにすれば的な…

ただ経験的に、こういう場合って休んでても状況が改善する場合はなくて、なんか次やることをはやく見つけたほうがいい。

どうするかな…まあ今の会社では色々好き勝手にやったので、貴重な経験はできた。せっかくだし経験を生かす仕事をしたほうがいいのかなやっぱり。でも(何回も言ってるが)全然違う仕事をしたい気持ちもある。といっても私にできる仕事他にあるのか…?

まあいいや…しばらくふにゃふにゃしよ…

SVEの自動ベクタライズ

ARM SVE というベクタ拡張があるんだけど、それって公開されてるコンパイラでどのぐらい対応されるんや?という話題を見かけたので、ちょっと試してみたら良い感じだったのでついでにもう少し調べてみた。

http://d.hatena.ne.jp/w_o/20150423#1429775436

個人的には自動ベクタライザというのは信用してないんだけど、それはそれとして、ループ変換ツールとしての最適化パスの処理はまあ面白いと思ってるので。


GCCは、ビルドしてもいいけど、

https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads

ここからビルド済みバイナリが取得できるので、そちらのほうが簡単だと思う。


このGCCに、-march=armv8-a+sve -O3 を渡すと、SVEを使って自動ベクタライズしてくれるようになる。


自動ベクタライザを信用しない理由として、上のリンク先で上げてる通り、どうしても命令数がかなり膨らんでしまうというのがありますね。これは性能上のデメリットもあるんだけど、出るasmの見た目が汚なくなるから嫌という気持ちの問題も大きい。


ところが、SVE向けに自動ベクトル化で出したコードは、この命令数の膨張が無くて、見ためがスッキリしてたので好きになった。

int f(int *__restrict__ a,
      int *__restrict__ b,
      int *__restrict__ c,
      int n)
{
    for (int i=0; i<n; i++) {
        a[i] += b[i] * c[i];
    }
}

これが、

f:
.LFB0:
	.cfi_startproc
	cmp	w3, 0
	ble	.L2
	mov	x4, 0
	sxtw	x3, w3
	ptrue	p1.s, all
	whilelo	p0.s, xzr, x3
	.p2align 3
.L3:
	ld1w	z1.s, p0/z, [x0, x4, lsl 2]
	ld1w	z2.s, p0/z, [x1, x4, lsl 2]
	ld1w	z0.s, p0/z, [x2, x4, lsl 2]
	mad	z0.s, p1/m, z2.s, z1.s
	st1w	z0.s, p0, [x0, x4, lsl 2]
	incw	x4
	whilelo	p0.s, x4, x3
	bne	.L3
.L2:
	ret

vectorizeしない場合は、

f:
.LFB0:
	.cfi_startproc
	cmp	w3, 0
	ble	.L2
	mov	x4, 0
	.p2align 3
.L3:
	ldr	w6, [x0, x4, lsl 2]
	ldr	w5, [x1, x4, lsl 2]
	ldr	w7, [x2, x4, lsl 2]
	madd	w5, w5, w7, w6
	str	w5, [x0, x4, lsl 2]
	add	x4, x4, 1
	cmp	w3, w4
	bgt	.L3
.L2:
	ret

ループ前の処理が3命令増えているが、ループ中の命令数は全く同じで、関数レベルで見ても12->15 の 3命令しか増えていない。neonの場合は、これが47命令の+35命令になって、ここに貼るのも嫌なレベルだ。


一番大きいのは、ループ回数がSIMD幅で割り切れない時の端数の処理に必要なマスクをwhilelo 命令一発で作れるところだろう。

http://d.hatena.ne.jp/w_o/20160901#1472719309 に昔説明を書いたけど、実際これだけ自動ベクタライザがスッキリした命令出すのを見てかなり感動しましたね。


自動ベクタライザが出すasmは汚いという時代はもう終わったんだ。


あと

int f(int *__restrict__ a,
      int *__restrict__ b,
      int *__restrict__ c,
      int n)
{
    for (int i=0; i<n; i++) {
        if (a[i]) {
            a[i] += b[i] * c[i];
        }
    }
}

こういう条件付きストアがある場合でも

f:
.LFB0:
	.cfi_startproc
	cmp	w3, 0
	ble	.L2
	mov	x4, 0
	sxtw	x3, w3
	ptrue	p2.s, all
	whilelo	p1.s, xzr, x3
	.p2align 3
.L3:
	ld1w	z1.s, p1/z, [x0, x4, lsl 2]
	cmpne	p0.s, p2/z, z1.s, #0
	and	p0.b, p0/z, p0.b, p1.b
	ld1w	z0.s, p0/z, [x1, x4, lsl 2]
	ld1w	z2.s, p0/z, [x2, x4, lsl 2]
	mad	z0.s, p2/m, z2.s, z1.s
	st1w	z0.s, p0, [x0, x4, lsl 2]
	incw	x4
	whilelo	p1.s, x4, x3
	bne	.L3
.L2:
	ret

スッキリしていて美しいですね。(でも条件付きストアはよく考えたらAVX512でも同じことになるのでSVE固有ではない)


で、まあそれはよくて、この whilelo みたいなほぼアーキ固有みたいな命令をGCCがどう扱ってるのか気になったので調べたくなった。


まあこれが富士通コンパイラなら、なんか色々追加したんだろうな、という気持ちになるけど、GCCだとあんまりアーキ固有ベッタリな変換パスがベクタライザに入ってるイメージがないので。


調べた結果としては、以下のとおりだった。

自分で調べる人は、 gcc-8.3で、-fdump-tree-all して、xx.c.161t.vect とかを見れば、色々調べられると思う。あとは -fopt-info-vec-missed の出力も参考になるかもしれない。

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/internal-fn.def;h=4080e1698ea82404a1102ef586cce64100033e76;hb=4ac50a4913ed81cc83a8baf865e49a2c62a5fe5d#l136

WHILE_ULT という whilelo と対応するtree nodeが生まれている。これはまんまwhileloで、ループ最大値とループカウンタを受け取って、それをもとにマスクを作る。


あとMASK_LOAD,MASK_STOREもある。

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/internal-fn.def;h=4080e1698ea82404a1102ef586cce64100033e76;hb=4ac50a4913ed81cc83a8baf865e49a2c62a5fe5d#l118

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/internal-fn.def;h=4080e1698ea82404a1102ef586cce64100033e76;hb=4ac50a4913ed81cc83a8baf865e49a2c62a5fe5d#l131

AVX512 用に自動ベクトル化した場合もMASK_LOAD、MASK_STOREを使うようだ。


https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/tree-vect-loop.c;h=c74a485cc2f638c26498ac6edb57faa221f5a46d;hb=4ac50a4913ed81cc83a8baf865e49a2c62a5fe5d#l1290

direct_internal_fn_supported_p で、バックエンドがWHILE_ULTをサポートしてるかどうか見てる。

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/config/aarch64/aarch64-sve.md;h=d88553405094853ab58ba1c55cd212092a14116c;hb=4ac50a4913ed81cc83a8baf865e49a2c62a5fe5d#l1254

direct_internal_fn_supported_p は多分このmdから生成したテーブルとかで見てるんではないかな…(未確認)

それで、LOOP_VINFO_FULLY_MASKED_P が決定されてその場合は端数処理を付けないようにしてると思う。


ベクタライズのアルゴリズム自体は…みんなの宿題だゾ!

PCIe M.2 変換

なんか違和感を感じるよな。変換とは。

NVMeはプロトコルはPCIeなんだから、プロトコルは変換してない。PCIeはスロットの形も定義してるから、PCIeスロット M.2 スロット変換とでも呼ぶべきか?

まあM.2はM.2で、「M.2とは何か」みたいなのが一言で言いづらい感じはあるよな。

あと定期的に書いてる気がするが、これ系で一番好きなのは、「USBシリアル変換」で、U シリアル B シリアル変換であって、お前USBはシリアルとちゃうんか?的な的な。

真面目に考えれば、USBはちゃんとバスとして働いているのに対して、RS232Cは本当にただのシリアルなので、シリアルバス to シリアル変換と考えれば別にそれほどおかしくない気もする。

おぷたん

(書きながら更新中)

[global]
ioengine=libaio
size=4g
direct=1
runtime=30
filename=/dev/nvme0n1

[Seq-Read]
iodepth=1
bs=1m
rw=read
stonewall

[rand-4k-q1]
iodepth=1
bs=4k
rw=randread
stonewall

[rand-4k-q32]
iodepth=32
bs=4k
rw=randread
stonewall

[rand-4k-q256]
iodepth=256
bs=4k
rw=randread
stonewall

http://int.main.jp/files/fio/ST3000DM001.log

ST3000DM001 とかいうヤバいHDD使ってるやつはおらんやろな?

http://int.main.jp/files/fio/sm960evo.log

申しわけないが、この960EVO、NTFSしか入れてないから書き込みは測ってない。/dev/nvme0n1 でとってる。

https://twitter.com/tanakmura/status/902902676236582912

Windows で測ったやつ。CrystalDiskMark はファイルで測るから、公平な比較ではない。けど、スループットはWinのほうがいいね。ランダムは、まあ公平ではないのでなんとも…


CPU Ryzen 7 1700X、960 EVO

  • アイドル49Wぐらい。
  • シーケンシャル 72W
  • rand 4k q32 78-80W

まあ現代のNVMeの速度なら、CPUへの影響はかなりあるので、ワットチェッカではCPUの消費電力とストレージの消費電力うまく切り離せないが…


http://int.main.jp/files/fio/optane16g.log

はい。

https://twitter.com/tanakmura/status/903698660549992448

twitterに書いてしまったのでそっち見て。

まあ一言で言うと、この性能では使うメリット無い。QD=1時の性能は確かに良いが、どこのデスクトップがQD=1時の性能を必要としてるんだ。

つまり、Optane 16G は高いだけで速度も容量もメリットは無く、実質的には、Intel謹製のNVMeキャッシュアプリケーションを買ってるだけと言っていい。

  • アイドル49Wぐらい。
  • seq read 68W
  • seq write 66W
  • rand 4k read q32 78-80W
  • rand 4k write q32 68


SPDKもやろうと思ったけど、VFIO group is not viable とか出てきて使えぬ…

PCIe 差す場所変えればワンチャンあるっぽいがNVMeは差す場所変えれないが…あとACSがなんとかかんとかって書いてあるが説明読む気力が無い。

PCIe M.2 変換みたいなやつ使ってスロット変えて試すか、i7 で試すか…



http://int.main.jp/files/fio/sm960evo-2.log

960EVO ディスク後ろ空けてwriteも試した。(一瞬間違えてデータ消しかけた)

read もえらい性能上がるな。2.5GB/sと3.5GB/sぐらい違う。まあ後ろ領域使ってないから、そういう違いだろうな。そうすると、何か書いてからテストしたほうが、実際の使い方に近いのでは。

また似たような話書くけど

foobarアーキテクチャが難しいとかいう人は、今のDirectXの初期化コードとか書いて世の中の名も無いゲームプログラマが全員その難しいアーキテクチャの上でプログラム書いてるというのを理解すべきではないか。

まあ大半のプログラマはUnityとか使っててDirectXレイヤなんて使わないというのはそうなんだが、一番下のアーキテクチャが難しいという条件は同じはずで、HPCが他の分野と比べて難しいというなら、最下層アーキテクチャが難しいというところとは別のところに問題があるはずと考えるべき。

例えば、テクスチャの転送が必要でそれ用にリソース管理がいる、シェーダを別に書かないといけない、とかは、HPCでアーキテクチャ難しいと言われるのと本質的には同じ問題で、世の中のゲームは、そういうものの上で動いてるのであって、HPCの人だけが不当に難しいプログラミングをしているわけではない。


この話題しょっちゅう書いてる気がするが、まあ正直言ってHPCの人アーキテクチャに文句言いすぎだと思う。結局本質的にはDRAMと通信が遅いのが問題でどうしようもないのだから、プロセッサアーキテクチャに文句言うのって筋違いじゃない?

私も富裕層になりた〜い!

別に金には困ってないけど。


富裕層かどうかというのが、大半が生まれた時の初期値で決まっているというのがやっぱ悔しいんだよな。もちろん、富裕層の生まれと言っても、本人もそれなりに努力したり悩んだりしてるんだろうから、富裕層に生まれたら勝ち組とは思わないけど。
それでもやっぱりとある個人が社会における存在感、発言力を持ってる主要因が、親から引き継いだコネ・資産だというような人間が一定数いることは、なんらかの社会の歪みみたいなのを感じさせて、理不尽感みたいなのはあるよな。


あと、貧困層と富裕層では、受けられる医療サービスや栄養状態の違いから、生命が危険にさらされるリスクに違いが発生するというのも、理不尽感があるよな。

それが本人の努力の違いならいいんだけど、現実問題として、親の状態の違いから発生していることはそれなりにあって、まあ親の努力が一回引き継がれる程度なら親の努力だからまだ許容範囲としても、それが、3代、4代と引き継がれるというのは、それでいいのか?という気はかなりするよな。


政治家とかもそうなんだけど、ああいう人達も別に人間なんだから、能力が平凡なのはまあ別にいいんだけど(良くないんだけど)能力が平凡、かつ、世襲議員みたいなのだと、「所詮生まれた時の初期値が全てか」、みたいな感は感じちゃうよね〜



そんなことでは良くないよ。一般的な社会正義と照らしあわせても、階層の固定化は社会に閉塞感を発生させて、個人の幸福度を下げるというのは十分考えられることだ。富裕層に生まれたら富裕層、貧困層に生まれたら貧困層、みたいなのは、できる限り是正されるべきだと思う。



じゃあ、さらに再分配を進めるべきか?というと、それも難しいと思う。公平、平等の問題は本質的に矛盾を含みがち。



階層の固定化をうまく是正するためには、個人個人が努力すべきだと思うんだよね。つまり、きちんと能力のある人間が富裕層に這い上がる努力をすべきだと。
そうして、富裕層に生まれたとしても平凡な努力して平凡な悩みを持つだけの平凡な人間は、いつかそれ相応の立場になってしまうと、そういう社会における緊張感を発生させていったほうがいいと思うんだよね。
たとえ初代では、成金と言われようと、きちんと、それを2代3代と引き継いで、由緒ある家系へと育てて、歴史ある名家に生まれたというだけで有利な立場に生まれた人間を叩き落していくべきだと思うね。


つまり、俺が富裕層になりたいと思うのは、社会正義の心の発露であり、つまり、その程度の心の持ちようで、つまり、「今の日本の一番クリティカルな点は、少子化、人口減少だから俺ももっと子供生むべきだ」と思ってるのと同じようなレベルの心の発露である。(つまり、実際は何の努力もしてない)