C++でrestrictと同様の機能を実現する方法
ループのベクトル化や、その他の最適化を促進させることを目的として、C言語のrestrict修飾子を使うことがよくある。
restrict修飾子を指定したポインタは、同じ関数の他のポインタが指すメモリアドレスを指さない。 複数のポインタが同じメモリアドレスを指すと、メモリ依存関係によって、ベクトル化ができなくなることが多い。 restrictを使うことで、コンパイラが「メモリ依存が無い」と判定できるようになって、ベクトル化が可能になることがある。
以下のようにするとベクトル化されない。
コンパイラはruntimeチェックを入れることで、restrictが無いプログラムもベクトル化してしまうので、-mllvm -pragma-vectorize-memory-check-threshold=0
を指定している。
norestrict.c
void test(double *a, double *b, int len) { for (int i=1; i<len; i++) { a[i] *= b[i] + i; } }
$ clang -O2 -S norestrict.c -fno-unroll-loops -Rpass=loop-vectorize -mavx512f -mllvm -pragma-vectorize-memory-check-threshold=0
以下のようにrestrictを付けるとベクトル化されることがメッセージからわかる。
restrict.c
void test(double *restrict a, double *restrict b, int len) { for (int i=1; i<len; i++) { a[i] *= b[i] + i; } }
$ clang -O2 -S restrict.c -fno-unroll-loops -Rpass=loop-vectorize -mavx512f -mllvm -pragma-vectorize-memory-check-threshold=0 restrict.c:2:3: remark: vectorized loop (vectorization width: 8, interleaved count: 1) [-Rpass=loop-vectorize] for (int i=1; i<len; i++) { ^
一方、C++の場合は、以下のように構造体のメンバとすることでベクトル化できるようになる。 これは、「別のタグ名を持つ構造体への2つのポインタは別名ではない」というルールを使っている。
a.cpp
struct A { double v; }; struct B { double v; }; void test(struct A *as, struct B *bs, int len) { for (int i=1; i<len; i++) { as[i].v *= bs[i].v + i; } }
$ clang++ -O2 -S a.cpp -fno-unroll-loops -Rpass=loop-vectorize -mavx512f -mllvm -pragma-vectorize-memory-check-threshold=0 a.cpp:10:3: remark: vectorized loop (vectorization width: 8, interleaved count: 1) [-Rpass=loop-vectorize] for (int i=1; i<len; i++) { ^
上は、-fno-strict-aliasing
を指定すると、ベクトル化されなくなる。
$ clang++ -O2 -S a.cpp -fno-unroll-loops -Rpass=loop-vectorize -mavx512f -mllvm -pragma-vectorize-memory-check-threshold=0 -fno-strict-aliasing
また、C++では、コンパイラの独自拡張である__restrict__
や__restrict
が使える様子。
restrict.cpp
void test(double *__restrict__ a, double *__restrict__ b, int len) { for (int i=1; i<len; i++) { a[i] *= b[i] + i; } }
clang++ -O2 -S restrict.cpp -fno-unroll-loops -Rpass=loop-vectorize -mavx512f -mllvm -pragma-vectorize-memory-check-threshold=0 restrict.cpp:2:3: remark: vectorized loop (vectorization width: 8, interleaved count: 1) [-Rpass=loop-vectorize] for (int i=1; i<len; i++) { ^
低レベルプログラミング | Igor Zhirkov, 吉川 邦夫, 吉川 邦夫 |本 | 通販 | Amazonのp.351 「14.6 厳密な別名のルール」を参考にした。
Linux無線LAN設定
Ubuntu Server 20.04.1 LTS で無線LANの設定を行いました。以下、メモです。
マザーボードに無線LANユニットが無かったので、 IODATA WN-G300UA をUSB2.0ポートに接続し、以下の手順で無線LANの設定をしました。
まず、以下のように設定ファイルを編集します。
/etc/netplan/50-clowd-init.yaml
network: version: 2 renderer: NetworkManager ethernets: enp5s0: addresses: - 192.168.3.10/24 dhcp4: false gateway4: 192.168.3.1 nameservers: addresses: - 8.8.4.4 - 8.8.8.8 search: - domain.local wifis: wlx3476c5f2c85f: addresses: - 192.168.3.10/24 dhcp4: false gateway4: 192.168.3.1 access-points: SSIDXXXXX: password: "PASSXXXXX" nameservers: addresses: - 8.8.4.4 - 8.8.8.8 search: - domain.local
無線LANの設定は、wifis
から下です。SSIDXXXXX
やPASSXXXXX
には、適切なSSIDとパスワードを設定します。
enp5s0
やwlx3476c5f2c85f
は私のアダプタの名前なので適切な名前を設定します。
次に以下のコマンドを実行します。
設定コマンド
$ sudo apt install network-manager $ sudo netplan apply
接続先のSSIDなどは、以下のように確認します。
$ nmcli dev wifi list
浮動小数点数とビット表現の変換
浮動小数点数をビット表現に変換したり、ビット表現を浮動小数点数に変換したりすることが多いので、コンバータを作ってみました。
bfloat16以外の浮動小数点数なら、pythonでも以下のように変換できます。
$ python3 Python 3.8.2 (default, Jul 16 2020, 14:00:26) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import struct >>> hex(struct.unpack('Q', struct.pack('d', 1.0))[0]) '0x3ff0000000000000' >>> hex(struct.unpack('I', struct.pack('f', 1.0))[0]) '0x3f800000' >>> hex(struct.unpack('H', struct.pack('e', 1.0))[0]) '0x3c00' >>> struct.unpack('d', struct.pack('Q', 0x3ff0000000000000))[0] 1.0 >>> struct.unpack('f', struct.pack('I', 0x3f800000))[0] 1.0 >>> struct.unpack('e', struct.pack('H', 0x3c00))[0] 1.0
上から順番に
- fp64->hex
- fp32->hex
- fp16->hex
- hex->fp64
- hex->fp32
- hex->fp16
です。
ハイティング代数の勉強
J. ヨスト著, 清水勇二訳 現代数学の基本概念 上 を読んでいて、p.35 の練習問題が難しかったので、この問題について書いておく。
p.35の練習問題とは、ハイティング代数において以下を示すことだ。
ハイティング代数は0と1を持つ束で冪対象を持つ。
冪対象の記法で書くと示したいことは以下になる。
を任意の元として、次の①と②, ②と③は一対一に対応する。ここで
は
を表す。
①
②
③
また、を任意の元として、次の④と⑤は一対一対応する。
④
⑤
もし①でを
とした射があるならば、③の
に
を代入して、射
が存在する。すなわち以下が成り立つ。
一方、もし④でを
とした射があるならば、⑤の
に
を代入して、射
が存在する。すなわち以下が成り立つ。
したがって、,
, 反対称律より
となる。
あとは、と
を示せばよい。
まず、は、
から成り立つことがわかる。
次に、は、次のように示す。
Kleisli圏の単位律と結合律
西郷 甲矢人 (著), 能美 十三 (著) 圏論の道案内 ~矢印でえがく数学の世界~ (数学への招待シリーズ) の「第9章モナド」で、モナドから随伴を作るときにKleisli圏という圏が登場した。
この記事では、Kleisli圏の単位律と結合律についてまとめてみる。
まず、圏上のモナド
とは、組
であり、以下の図式を可換にするものである。
ここで、は、関手
, 自然変換
, 自然変換
。
Kleisli圏はモナドに関連して考えられる圏である。
モナドに関するKleisli圏
の対象
は、圏
の対象
と同じであり、圏
の射
は、圏
の射
に対応する。
対象の恒等射
は、圏
の射
に対応する。
と
の合成
は、圏
の射
に対応する。
ここで、このように定義した恒等射が単位律を満たすこと、それから、このように定義した射および射の合成が結合律を満たすことを示したい。
まずは、恒等射が単位律を満たすこと、すなわち、任意のに対して、
が成り立つことを示す。
圏において、
が成り立つことを示せばよい。
の自然性を表す図式とモナド
の定義に使った2番目の可換図式を組み合わせた以下の図式が可換であることから、上の式が成り立つことがわかる。
よって、恒等射が単位律を満たすことが示せた。
次に、射および射の合成が結合律を満たすこと、すなわち
に対して、
が成り立つことを示す。
圏において、
が成り立つことを示せばよい。
ここで、モナドの定義の1番目の可換図式から、以下の図式が可換であることがわかる。
よって、上の式は以下になる。
の自然性を表す以下の可換図式が書けるので、
上の式は以下になる。
射および射の合成が結合律を満たすことが示せた。
図式を書くのに使ったTexソース
\documentclass[12pt]{ujarticle} \usepackage{amsmath,amsfonts,amsthm,amssymb,amscd} \usepackage[all]{xy} \def\objectstyle{\displaystyle} \begin{document} \[ \xymatrix{ TTT \ar@{=>}[r]^{T\mu} \ar@{=>}[d]_{\mu T} & TT \ar@{=>}[d]^{\mu} \\ TT \ar@{=>}[r]_{\mu} & T } \] \[ \xymatrix{ T \ar@{=>}[r]^{T\eta} \ar@{=>}[rd]_{1_T} & TT \ar@{=>}[d]^{\mu} & T \ar@{=>}[l]_{\eta T} \ar@{=>}[ld]^{1_T} \\ & T & } \] \[ \xymatrix{ X \ar[r]^{f} \ar[d]_{\eta_X} & T(Y) \ar[d]^{T(\eta_Y)} \ar[rd]^{1_{T(Y)}} \\ T(X) \ar[r]_{T(f)} & TT(Y) \ar[r]_{\mu_Y} & T(Y) } \] \[ \xymatrix{ TTT(W) \ar[r]^{T(\mu_W)} \ar[d]_{\mu_{T(W)}} & TT(W) \ar[d]^{\mu_W} \\ TT(W) \ar[r]_{\mu_W} & T(W) } \] \[ \xymatrix{ TT(Z) \ar[r]^{TT(h)} \ar[d]_{\mu_Z} & TTT(W) \ar[d]^{\mu_{T(W)}} \\ T(Z) \ar[r]_{T(h)} & TT(W) } \] \end{document}
C言語で浮動小数点数をビットパターンに変換する方法
1.0の64bit浮動小数点数、32bit浮動小数点数、16bit浮動小数点数でのビットパターンを求めます。
a.c
#include <stdio.h> #include <stdint.h> int main() { union A { double d; float f; _Float16 f16; uint64_t i64; }; union A a; a.d = 1.0; printf("64bit 0x%lx\n", a.i64 & 0xFFFFFFFFFFFFFFFF); a.f = 1.0; printf("32bit 0x%lx\n", a.i64 & 0xFFFFFFFF); a.f16 = 1.0; printf("16bit 0x%lx\n", a.i64 & 0xFFFF); }
コンパイルして、実行すると以下になります。
$ clang a.c $ ./a.out 64bit 0x3ff0000000000000 32bit 0x3f800000 16bit 0x3c00
Pythonで浮動小数点数をビットパターンに変換する方法
64bit浮動小数点数(1.0の例)
>>> import struct >>> hex(struct.unpack('<Q', struct.pack('<d', 1.0))[0]) '0x3ff0000000000000'
32bit浮動小数点数(1.0の例)
>>> import struct >>> hex(struct.unpack('<I', struct.pack('<f', 1.0))[0]) '0x3f800000'
16bit浮動小数点数(1.0の例) Python3.6以上で動く
>>> import struct >>> hex(struct.unpack('<H', struct.pack('<e', 1.0))[0]) '0x3c00'