Cのコードは163行あるよ。そのうち、-O3では104行がアセンブリ出力に含まれてる。だから、Cコンパイラはさらに約36.2%の命令を削除できるんだ。特別なことはしてなくて、自動ベクトル化とかはしてないよ。今、プロファイルした結果はこうだ:
| instrs (aarch64) | time 100k (s) | conway samples (%) |
| -O0 | 606 | 19.10s | 78.50% |
| -O3 | 135 | 3.45 | 90.52% |
3.45秒ってのは驚きだね、前に測った4.09秒より速いから。もしかしたらPコアとEコアの違いかも。-O0の時、コンパイラが出力するマシンコードはこんな感じ:
0000000100002d6c ldr x8, [sp, #0x4a0]
0000000100002d70 ldr x9, [sp, #0x488]
0000000100002d74 orn x8, x8, x9
0000000100002d78 str x8, [sp, #0x470]
これ、めちゃくちゃひどいよね。例えば、-Ogで試すと、-O3と同じ逆アセンブルが得られる。-01でも同じ逆アセンブルになる。アセンブリ(-Og, -01, -03)はCのかなり直接的な翻訳に見える。良くなってるけど、特にすごいわけじゃない(自動ベクトル化はなし):
0000000100003744 orr x3, x3, x10
0000000100003748 orn x1, x1, x9
000000010000374c and x1, x3, x1
0000000100003750 orr x3, x8, x17
よく見ると、実はレジスタのスピリングが意外と少ないんだ。君が本当に聞きたいことは、私が書いたように:
「命令のレイテンシが1サイクルだと仮定すると、2,590 fpsを期待するべきだ。でも、実際にはほぼ10倍の数字を測ってる!どういうこと?」
これの一部は、逆アセンブルで命令を数えるのを間違えてるからだ。ブログ記事では349命令を使ったけど、実際には135なんだ。この新しい数字で計算し直すと、ビットあたり2.11命令、ステップあたり55.3万命令、3.70 gcycles/sで割ると6,690 fpsになる。これは2,590 fpsより良いけど、24,400にはまだ3.6倍遅い。でも、3.6倍は命令レベルの並列性に起因すると思う。これで君の質問に答えられたらいいな。君の文章が大好きだよ、Gwern。