読者です 読者をやめる 読者になる 読者になる

ムーアの法則の終わり、そして最近の分岐予測について

ムーアの法則の終わり、そして最近の分岐予測について

僕らx86の大地の上に生きるものは、この10年Intelが告げるTick-Tockの鐘の音にあわせてムーアの法則の恩恵を享受してきた。*1 *2 しかし、Kaby Lakeの14nmプロセス採用つまり、2年おきのプロセスルール刷新を諦めたことを持って、ムーアの法則は終焉を迎えたとされる。が、この認識は本当に正しいのだろうか。

ムーアの1965年の論文では、後のムーアの法則を”The complexity for minimum component costs has increased at a rate of roughly a factor of two per year"と表現している。人々はこの”complexity"は単位面積あたりのトランジスタ数のことだとこの50年間理解してきた。もっと言えばこの10年はプロセス・ルールのことだったかもしれない。だが、CPUがその内に集積してきたものはトランジスタだけだったのだろうか。

前述の論文のタイトルは"Cramming more components onto integrated circuits”という。Intelは様々なものをCPUという集積回路に詰め込んできた。しかし、このタイトルとムーアの法則の記述をあわせれば、真にCPUが集積したものが何かは明らかだろう。それは「複雑さ」だ。多数のSIMD命令も、AVXの奇怪なVEXエンコーディングも、すべてこの世界の複雑さを小さな魔法の石に封じ込めた結果なのだ。

分岐予測の進化

さて、高度なCPUにはブラックボックスが多数含まれますが、分岐予測はその中の最たるものの一つでしょう。CPUの最適化情報豊富なIntel® 64 and IA-32 Architectures Optimization Reference Manualを見ても毎度"Improved branch predictor."としか書かれていません。

とはいえ、手元にCPUはあるわけで、実際に動かしてその実力を測ることは出来るので測ってみましょう。

計測に使ったのはSpeed of various interpreter dispatch techniques V2のthreading.tar.gzの./directです。現代においてこれが適切なベンチマークだとは思わないのですが、間接分岐については多少参考にはなるんじゃないかな……。

SandyBridge

naruse@chkbuild008:~/threading$ grep -m1 'model name' /proc/cpuinfo
model name  : Intel(R) Xeon(R) CPU E5-2630L 0 @ 2.00GHz

naruse@chkbuild008:~/threading$ perf stat ./direct-gcc48

 Performance counter stats for './direct':

        277.440861 task-clock (msec)         #    0.998 CPUs utilized
                 5 context-switches          #    0.018 K/sec
                 0 cpu-migrations            #    0.000 K/sec
               112 page-faults               #    0.404 K/sec
       650,780,115 cycles                    #    2.346 GHz                     [82.74%]
       365,836,274 stalled-cycles-frontend   #   56.22% frontend cycles idle    [82.72%]
       377,885,001 stalled-cycles-backend    #   58.07% backend  cycles idle    [67.70%]
       423,892,636 instructions              #    0.65  insns per cycle
                                             #    0.89  stalled cycles per insn [84.15%]
       208,884,768 branches                  #  752.898 M/sec                   [84.14%]
        12,251,769 branch-misses             #    5.87% of all branches         [82.71%]

       0.278114864 seconds time elapsed

naruse@chkbuild008:~/threading$ perf stat ../direct-gcc54

 Performance counter stats for '../direct-gcc54':

        155.542201 task-clock (msec)         #    0.996 CPUs utilized
                 3 context-switches          #    0.019 K/sec
                 0 cpu-migrations            #    0.000 K/sec
               113 page-faults               #    0.726 K/sec
       352,393,558 cycles                    #    2.266 GHz                     [82.03%]
       141,991,479 stalled-cycles-frontend   #   40.29% frontend cycles idle    [82.23%]
       160,691,365 stalled-cycles-backend    #   45.60% backend  cycles idle    [69.17%]
       379,813,966 instructions              #    1.08  insns per cycle
                                             #    0.42  stalled cycles per insn [84.58%]
       160,216,299 branches                  # 1030.050 M/sec                   [84.57%]
             3,933 branch-misses             #    0.00% of all branches         [82.03%]

       0.156168350 seconds time elapsed

Ivy Bridge

% grep -m1 'model name' /proc/cpuinfo
model name      : Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz

%  perf stat ../direct-gcc48

 Performance counter stats for '../direct-gcc48':

        238.146900      task-clock (msec)         #    0.982 CPUs utilized
                37      context-switches          #    0.155 K/sec
                 0      cpu-migrations            #    0.000 K/sec
                42      page-faults               #    0.176 K/sec
         689162737      cycles                    #    2.894 GHz
         403352479      stalled-cycles-frontend   #   58.53% frontend cycles idle
   <not supported>      stalled-cycles-backend
         430913321      instructions              #    0.63  insns per cycle
                                                  #    0.94  stalled cycles per insn
         210172241      branches                  #  882.532 M/sec
          14205738      branch-misses             #    6.76% of all branches

       0.242429416 seconds time elapsed

% perf stat ./direct-gcc54

 Performance counter stats for './direct-gcc54':

        120.812507      task-clock (msec)         #    0.996 CPUs utilized
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
                41      page-faults               #    0.339 K/sec
         355605784      cycles                    #    2.943 GHz
         170213145      stalled-cycles-frontend   #   47.87% frontend cycles idle
   <not supported>      stalled-cycles-backend
         380562594      instructions              #    1.07  insns per cycle
                                                  #    0.45  stalled cycles per insn
         160106383      branches                  # 1325.247 M/sec
              9808      branch-misses             #    0.01% of all branches

       0.121333298 seconds time elapsed

Haswell

% grep -m1 'model name' /proc/cpuinfo
model name      : Intel(R) Xeon(R) CPU E5-2660 v3 @ 2.60GHz
%  perf stat ./direct

 Performance counter stats for './direct':

        159.226456      task-clock:u (msec)       #    0.996 CPUs utilized
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
                35      page-faults:u             #    0.220 K/sec
         321780650      cycles:u                  #    2.021 GHz
         380085245      instructions:u            #    1.18  insn per cycle
         160015808      branches:u                # 1004.957 M/sec
              5206      branch-misses:u           #    0.00% of all branches

       0.159844405 seconds time elapsed

%  perf stat ../direct-gcc48

 Performance counter stats for '../direct-gcc48':

        205.179841      task-clock:u (msec)       #    0.995 CPUs utilized
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
                36      page-faults:u             #    0.175 K/sec
         448041978      cycles:u                  #    2.184 GHz
         430085324      instructions:u            #    0.96  insn per cycle
         210015836      branches:u                # 1023.570 M/sec
              4887      branch-misses:u           #    0.00% of all branches

       0.206141759 seconds time elapsed

%  perf stat ../direct-gcc54

 Performance counter stats for '../direct':

        158.532437      task-clock:u (msec)       #    0.995 CPUs utilized
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
                35      page-faults:u             #    0.221 K/sec
         315471369      cycles:u                  #    1.990 GHz
         380085288      instructions:u            #    1.20  insn per cycle
         160015810      branches:u                # 1009.357 M/sec
              6829      branch-misses:u           #    0.00% of all branches

       0.159275797 seconds time elapsed

Skylake

% grep -m1 'model name' /proc/cpuinfo
model name      : Intel(R) Xeon(R) CPU E3-1245 v5 @ 3.50GHz
% perf stat ./direct

 Performance counter stats for './direct':

         84.458565      task-clock (msec)         #    0.996 CPUs utilized
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
                38      page-faults               #    0.450 K/sec
         300759565      cycles                    #    3.561 GHz
         380503237      instructions              #    1.27  insn per cycle
         160093203      branches                  # 1895.524 M/sec
              9349      branch-misses             #    0.01% of all branches

       0.084794713 seconds time elapsed


% perf stat ../direct-gcc54

 Performance counter stats for '../direct':

        101.940704      task-clock (msec)         #    0.996 CPUs utilized
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
                37      page-faults               #    0.363 K/sec
         361064330      cycles                    #    3.542 GHz
         380540900      instructions              #    1.05  insn per cycle
         160100883      branches                  # 1570.530 M/sec
             18006      branch-misses             #    0.01% of all branches

       0.102305561 seconds time elapsed

訂正: 初出の記事ではSkylakeの値が-O3にしたものになっていました。-Oだと以上の通りです。

考察

所要時間はCPUの周波数も影響するのでその分は差し引いてみてくださいね。(本当はTurbo Boostオフにするべきなんだけど、手元のマシンじゃないんだゴメンな……)

考察と言ってもそこまで詳しいわけではないので実のあることは言えないのですが、今でもその進化が続いていることは見ればわかるでしょう。また、分岐予測に詳しい人ほど最近のIntel CPUの分岐予測的中率には驚かれるのではないかと思いました。

まとめ

あんまり賢い解説もないまま締めに入りますが、あんまり最近のIntel CPUの分岐予測について言及した記事って英語含めてもないと思うので、これで盛り上がって誰か解説してくれるといいなと思います!

追記

Sandy Bridgeが著しく悪かったのはgcc5.4以降が賢いからっぽい。

gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

000000000040052d <main>:
  40052d:       48 8b 35 2c 0b 20 00    mov    0x200b2c(%rip),%rsi        # 601060 <prog.1723>
  400534:       48 89 f2                mov    %rsi,%rdx
  400537:       b9 80 96 98 00          mov    $0x989680,%ecx
  40053c:       b8 68 10 60 00          mov    $0x601068,%eax
  400541:       ff e2                   jmpq   *%rdx
  400543:       48 8b 10                mov    (%rax),%rdx
  400546:       48 8d 40 08             lea    0x8(%rax),%rax
  40054a:       eb f5                   jmp    400541 <main+0x14>
  40054c:       48 8b 10                mov    (%rax),%rdx
  40054f:       48 8d 40 08             lea    0x8(%rax),%rax
  400553:       eb ec                   jmp    400541 <main+0x14>
  400555:       48 8b 10                mov    (%rax),%rdx
  400558:       48 8d 40 08             lea    0x8(%rax),%rax
  40055c:       eb e3                   jmp    400541 <main+0x14>
  40055e:       48 8b 10                mov    (%rax),%rdx
  400561:       48 8d 40 08             lea    0x8(%rax),%rax
  400565:       eb da                   jmp    400541 <main+0x14>
  400567:       48 8b 10                mov    (%rax),%rdx
  40056a:       48 8d 40 08             lea    0x8(%rax),%rax
  40056e:       eb d1                   jmp    400541 <main+0x14>
  400570:       85 c9                   test   %ecx,%ecx
  400572:       7e 0d                   jle    400581 <main+0x54>
  400574:       83 e9 01                sub    $0x1,%ecx
  400577:       48 89 f2                mov    %rsi,%rdx
  40057a:       b8 68 10 60 00          mov    $0x601068,%eax
  40057f:       eb c0                   jmp    400541 <main+0x14>
  400581:       48 83 ec 08             sub    $0x8,%rsp
  400585:       bf 00 00 00 00          mov    $0x0,%edi
  40058a:       e8 a1 fe ff ff          callq  400430 <exit@plt>
  40058f:       90                      nop
%  gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

0000000000400526 <main>:                                                                   [37/1887]
  400526:       48 8b 35 33 0b 20 00    mov    0x200b33(%rip),%rsi        # 601060 <prog.1832>
  40052d:       48 89 f2                mov    %rsi,%rdx
  400530:       b9 80 96 98 00          mov    $0x989680,%ecx
  400535:       b8 68 10 60 00          mov    $0x601068,%eax
  40053a:       eb 07                   jmp    400543 <main+0x1d>
  40053c:       48 8b 10                mov    (%rax),%rdx
  40053f:       48 8d 40 08             lea    0x8(%rax),%rax
  400543:       ff e2                   jmpq   *%rdx
  400545:       48 8b 10                mov    (%rax),%rdx
  400548:       48 8d 40 08             lea    0x8(%rax),%rax
  40054c:       eb f5                   jmp    400543 <main+0x1d>
  40054e:       48 8b 10                mov    (%rax),%rdx
  400551:       48 8d 40 08             lea    0x8(%rax),%rax
  400555:       eb ec                   jmp    400543 <main+0x1d>
  400557:       48 8b 10                mov    (%rax),%rdx
  40055a:       48 8d 40 08             lea    0x8(%rax),%rax
  40055e:       eb e3                   jmp    400543 <main+0x1d>
  400560:       48 8b 10                mov    (%rax),%rdx
  400563:       48 8d 40 08             lea    0x8(%rax),%rax
  400567:       eb da                   jmp    400543 <main+0x1d>
  400569:       85 c9                   test   %ecx,%ecx
  40056b:       7e 0d                   jle    40057a <main+0x54>
  40056d:       83 e9 01                sub    $0x1,%ecx
  400570:       48 89 f2                mov    %rsi,%rdx
  400573:       b8 68 10 60 00          mov    $0x601068,%eax
  400578:       eb c9                   jmp    400543 <main+0x1d>
  40057a:       48 83 ec 08             sub    $0x8,%rsp
  40057e:       bf 00 00 00 00          mov    $0x0,%edi
  400583:       e8 88 fe ff ff          callq  400410 <exit@plt>
  400588:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40058f:       00

追記2

かと思われたが、HaswellだとGCC4.8で作ったバイナリでも分岐予測ミスは0.00%だった。なお、GCC4.8バイナリをIvy Bridgeマシンで実行するとSandyと大差ない結果なので、分岐予測能力はSandyでもそれなりにすごくて、Haswellはもっとすごいってことかな。

追記3

なんか前にやろうとしてできなかった気がしたんだけど、今改めてGCCのマニュアル見たら-fprofile-generateつけて-fprofile-useつけるだけだった。

%  perf stat ./direct

 Performance counter stats for './direct':

        121.760309      task-clock (msec)         #    0.993 CPUs utilized
                 2      context-switches          #    0.016 K/sec
                 0      cpu-migrations            #    0.000 K/sec
                42      page-faults               #    0.345 K/sec
         353933863      cycles                    #    2.907 GHz
         167429077      stalled-cycles-frontend   #   47.31% frontend cycles idle
   <not supported>      stalled-cycles-backend
         380553084      instructions              #    1.08  insns per cycle
                                                  #    0.44  stalled cycles per insn
         160104472      branches                  # 1314.915 M/sec
              7542      branch-misses             #    0.00% of all branches

       0.122630111 seconds time elapsed

0000000000400526 <main>:
  400526:       48 8b 0d 33 0b 20 00    mov    0x200b33(%rip),%rcx        # 601060 <prog.1832>
  40052d:       48 89 ca                mov    %rcx,%rdx
  400530:       be 80 96 98 00          mov    $0x989680,%esi
  400535:       b8 68 10 60 00          mov    $0x601068,%eax
  40053a:       eb 07                   jmp    400543 <main+0x1d>
  40053c:       48 8b 10                mov    (%rax),%rdx
  40053f:       48 8d 40 08             lea    0x8(%rax),%rax
  400543:       ff e2                   jmpq   *%rdx
  400545:       48 8b 10                mov    (%rax),%rdx
  400548:       48 8d 40 08             lea    0x8(%rax),%rax
  40054c:       eb f5                   jmp    400543 <main+0x1d>
  40054e:       48 8b 10                mov    (%rax),%rdx
  400551:       48 8d 40 08             lea    0x8(%rax),%rax
  400555:       eb ec                   jmp    400543 <main+0x1d>
  400557:       48 8b 10                mov    (%rax),%rdx
  40055a:       48 8d 40 08             lea    0x8(%rax),%rax
  40055e:       eb e3                   jmp    400543 <main+0x1d>
  400560:       48 8b 10                mov    (%rax),%rdx
  400563:       48 8d 40 08             lea    0x8(%rax),%rax
  400567:       eb da                   jmp    400543 <main+0x1d>
  400569:       85 f6                   test   %esi,%esi
  40056b:       7e 0d                   jle    40057a <main+0x54>
  40056d:       83 ee 01                sub    $0x1,%esi
  400570:       48 89 ca                mov    %rcx,%rdx
  400573:       b8 68 10 60 00          mov    $0x601068,%eax
  400578:       eb c9                   jmp    400543 <main+0x1d>
  40057a:       48 83 ec 08             sub    $0x8,%rsp
  40057e:       bf 00 00 00 00          mov    $0x0,%edi
  400583:       e8 88 fe ff ff          callq  400410 <exit@plt>
  400588:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40058f:       00

あんまかわりませんね。

ちなみに-O3だと以下のような調子なので、最適化レベルを上げると意味がある模様。

%  gcc -O3 -fomit-frame-pointer direct.c -o direct
direct.c:4:1: warning: return type defaults to 'int' [-Wimplicit-int]
 main()
 ^
direct.c: In function 'main':
direct.c:32:3: warning: implicit declaration of function 'exit' [-Wimplicit-function-declaration]
   exit(0);
   ^
direct.c:32:3: warning: incompatible implicit declaration of built-in function 'exit'
direct.c:32:3: note: include '<stdlib.h>' or provide a declaration of 'exit'
# 1471360659 0:17:39 naruse@tk2-243-31075:~/threading
% perf stat ./direct

 Performance counter stats for './direct':

         46.157782      task-clock (msec)         #    0.980 CPUs utilized
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
                41      page-faults               #    0.888 K/sec
         135493298      cycles                    #    2.935 GHz
          49319786      stalled-cycles-frontend   #   36.40% frontend cycles idle
   <not supported>      stalled-cycles-backend
         320446132      instructions              #    2.37  insns per cycle
                                                  #    0.15  stalled cycles per insn
         110085385      branches                  # 2384.980 M/sec
              6551      branch-misses             #    0.01% of all branches

       0.047097047 seconds time elapsed

0000000000400430 <main>:
  400430:       48 8b 35 29 0c 20 00    mov    0x200c29(%rip),%rsi        # 601060 <prog.1832>
  400437:       b9 80 96 98 00          mov    $0x989680,%ecx
  40043c:       b8 68 10 60 00          mov    $0x601068,%eax
  400441:       ff e6                   jmpq   *%rsi
  400443:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  400448:       85 c9                   test   %ecx,%ecx
  40044a:       7e 64                   jle    4004b0 <main+0x80>
  40044c:       83 e9 01                sub    $0x1,%ecx
  40044f:       b8 68 10 60 00          mov    $0x601068,%eax
  400454:       ff e6                   jmpq   *%rsi
  400456:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40045d:       00 00 00
  400460:       48 8b 10                mov    (%rax),%rdx
  400463:       48 83 c0 08             add    $0x8,%rax
  400467:       ff e2                   jmpq   *%rdx
  400469:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  400470:       48 8b 10                mov    (%rax),%rdx
  400473:       48 83 c0 08             add    $0x8,%rax
  400477:       ff e2                   jmpq   *%rdx
  400479:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  400480:       48 8b 10                mov    (%rax),%rdx
  400483:       48 83 c0 08             add    $0x8,%rax
  400487:       ff e2                   jmpq   *%rdx
  400489:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  400490:       48 8b 10                mov    (%rax),%rdx
  400493:       48 83 c0 08             add    $0x8,%rax
  400497:       ff e2                   jmpq   *%rdx
  400499:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  4004a0:       48 8b 10                mov    (%rax),%rdx
  4004a3:       48 83 c0 08             add    $0x8,%rax
  4004a7:       ff e2                   jmpq   *%rdx
  4004a9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  4004b0:       48 83 ec 08             sub    $0x8,%rsp
  4004b4:       31 ff                   xor    %edi,%edi
  4004b6:       e8 55 ff ff ff          callq  400410 <exit@plt>
  4004bb:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
% gcc -O3 -fprofile-use -fomit-frame-pointer direct.c -o direct
direct.c:4:1: warning: return type defaults to 'int' [-Wimplicit-int]
 main()
 ^
direct.c: In function 'main':
direct.c:32:3: warning: implicit declaration of function 'exit' [-Wimplicit-function-declaration]
   exit(0);
   ^
direct.c:32:3: warning: incompatible implicit declaration of built-in function 'exit'
direct.c:32:3: note: include '<stdlib.h>' or provide a declaration of 'exit'
# 1471360636 0:17:16 naruse@tk2-243-31075:~/threading
%  perf stat ./direct

 Performance counter stats for './direct':

         46.108716      task-clock (msec)         #    0.981 CPUs utilized
                 0      context-switches          #    0.000 K/sec
                 0      cpu-migrations            #    0.000 K/sec
                41      page-faults               #    0.889 K/sec
         123812228      cycles                    #    2.685 GHz
          39859092      stalled-cycles-frontend   #   32.19% frontend cycles idle
   <not supported>      stalled-cycles-backend
         320466395      instructions              #    2.59  insns per cycle
                                                  #    0.12  stalled cycles per insn
         110090077      branches                  # 2387.620 M/sec
              6079      branch-misses             #    0.01% of all branches

       0.046992565 seconds time elapsed


0000000000400430 <main>:
  400430:       48 8b 0d 29 0c 20 00    mov    0x200c29(%rip),%rcx        # 601060 <prog.1832>
  400437:       be 80 96 98 00          mov    $0x989680,%esi
  40043c:       b8 68 10 60 00          mov    $0x601068,%eax
  400441:       ff e1                   jmpq   *%rcx
  400443:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  400448:       48 8b 10                mov    (%rax),%rdx
  40044b:       48 83 c0 08             add    $0x8,%rax
  40044f:       ff e2                   jmpq   *%rdx
  400451:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  400458:       4c 8b 18                mov    (%rax),%r11
  40045b:       48 83 c0 08             add    $0x8,%rax
  40045f:       41 ff e3                jmpq   *%r11
  400462:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  400468:       85 f6                   test   %esi,%esi
  40046a:       7e 3e                   jle    4004aa <main+0x7a>
  40046c:       83 ee 01                sub    $0x1,%esi
  40046f:       b8 68 10 60 00          mov    $0x601068,%eax
  400474:       ff e1                   jmpq   *%rcx
  400476:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40047d:       00 00 00
  400480:       4c 8b 00                mov    (%rax),%r8
  400483:       48 83 c0 08             add    $0x8,%rax
  400487:       41 ff e0                jmpq   *%r8
  40048a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  400490:       4c 8b 08                mov    (%rax),%r9
  400493:       48 83 c0 08             add    $0x8,%rax
  400497:       41 ff e1                jmpq   *%r9
  40049a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  4004a0:       4c 8b 10                mov    (%rax),%r10
  4004a3:       48 83 c0 08             add    $0x8,%rax
  4004a7:       41 ff e2                jmpq   *%r10
  4004aa:       50                      push   %rax
  4004ab:       31 ff                   xor    %edi,%edi
  4004ad:       e8 5e ff ff ff          callq  400410 <exit@plt>
  4004b2:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4004b9:       00 00 00
  4004bc:       0f 1f 40 00             nopl   0x0(%rax)

*1:ムーアの法則の初出は1965年だが、IntelがTick-Tockモデルを採用したのは2006年のCoreマイクロアーキテクチャからである。

*2:2002年の頃を思い出して欲しいのだが、21世紀になってまもなくフリーランチの時代は終わっていたのではなかったか?