えっ、私のPython、遅すぎ？〜brewでインストールしたpython@3.8が何故か遅かった話〜

結論は表題の通りで、公式のものを入れたら速くなりました。**変数アクセスで数割程度。**
理由は分かっていません。（全然関係ない話では、以前dockerではspectre対策で処理が遅くなる、みたいな話もあったので、そのようなものも含むビルド時の何らかの最適化とかかなと思うのですが、不明です...）

以下、ストーリーをお送りします。

# ストーリー

ある晴れた日、"Literal"という記述がPython3.7でエラーを起こしたのを目にした私は、Python3.8で追加された機能のPEPを見ていました。

Literal types
https://www.python.org/dev/peps/pep-0586/
TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys
https://www.python.org/dev/peps/pep-0589/
Adding a final qualifier to typing
https://www.python.org/dev/peps/pep-0591/

へー、と思って調子に乗った私は、ついでに3.9で追加された機能もチェックしていました。
そうすると、面白い記述を見つけました。
https://docs.python.org/ja/3.9/whatsnew/3.9.html#optimizations
3.4〜3.9に至るまで、変数へのアクセススピードがどのように改善されてきたか、というデータがあったのです。

|Python version                      | 3.4|3.5|3.6|3.7|3.8|3.9|
|:--|--:|--:|--:|--:|--:|--:|--:|
|Variable and attribute read access:|||||||
|    read_local                      | 7.1|7.1|5.4|5.1|3.9|4.0|
|    read_nonlocal                   | 7.1|8.1|5.8|5.4|4.4|4.8|
|    read_global                     |15.5|19.0|14.3|13.6|7.6|7.7|
|    read_builtin                    |21.1|21.6|18.5|19.0|7.5|7.7|
|    read_classvar_from_class        |25.6|26.5|20.7|19.5|18.4|18.6|
|    read_classvar_from_instance     |22.8|23.5|18.8|17.1|16.4|20.1|
|    read_instancevar                |32.4|33.1|28.0|26.3|25.4|27.7|
|    read_instancevar_slots          |27.8|31.3|20.8|20.8|20.2|24.5|
|    read_namedtuple                 |73.8|57.5|45.0|46.8|18.4|23.2|
|    read_boundmethod                |37.6|37.9|29.6|26.9|27.7|45.9|
|Variable and attribute write access:|||||||
|    write_local                     | 8.7|9.3|5.5|5.3|4.3|4.2|
|    write_nonlocal                  |10.5|11.1|5.6|5.5|4.7|4.9|
|    write_global                    |19.7|21.2|18.0|18.0|15.8|17.2|
|    write_classvar                  |92.9|96.0|104.6|102.1|39.2|43.2|
|    write_instancevar               |44.6|45.8|40.0|38.9|35.5|40.7|
|    write_instancevar_slots         |35.6|36.1|27.3|26.6|25.7|27.7|
|Data structure read access:|||||||
|    read_list                       |24.2|24.5|20.8|20.8|19.0|21.1|
|    read_deque                      |24.7|25.5|20.2|20.6|19.8|21.6|
|    read_dict                       |24.3|25.7|22.3|23.0|21.0|22.5|
|    read_strdict                    |22.6|24.3|19.5|21.2|18.9|21.6|
|Data structure write access:|||||||
|    write_list                      |27.1|28.5|22.5|21.6|20.0|21.6|
|    write_deque                     |28.7|30.1|22.7|21.8|23.5|23.2|
|    write_dict                      |31.4|33.3|29.3|29.2|24.7|27.8|
|    write_strdict                   |28.4|29.9|27.5|25.2|23.1|29.8|
|Stack (or queue) operations:|||||||
|    list_append_pop                 |93.4|112.7|75.4|74.2|50.8|53.9|
|    deque_append_pop                |43.5|57.0|49.4|49.2|42.5|45.5|
|    deque_append_popleft            |43.7|57.3|49.7|49.7|42.8|45.5|
|Timing loop:|||||||
|    loop_overhead                   | 0.5|0.6|0.4|0.3|0.3|0.3|

> These results were generated from the variable access benchmark script at: Tools/scripts/var_access_benchmark.py. The benchmark script displays timings in nanoseconds. The benchmarks were measured on an Intel® Core™ i7-4960HQ processor running the macOS 64-bit builds found at python.org.

ということでしたが、[実際に3.8.3でベンチマークを取ることが流行った](https://qiitadon.com/@sharow/104275167590539388)ので、私も手元の環境でベンチマークを取ってみることにしました。
ベンチマークソース：
https://github.com/python/cpython/blob/master/Tools/scripts/var_access_benchmark.py
（コピペで動きます）

# 環境

<img width="316" alt="スクリーンショット 2020-06-03 1.02.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94255/d081053d-8642-45bb-9e5d-60bebd97d6d0.png">

# 3.7.3

（多分brewで入れた）3.7.3で測定した結果は次の通りでした。

```haskell
% nice -n 10 python3 var_access_benchmark.py 
Variable and attribute read access:
   4.3 ns	read_local
   4.9 ns	read_nonlocal
  13.7 ns	read_global
  21.8 ns	read_builtin
  17.6 ns	read_classvar_from_class
  16.3 ns	read_classvar_from_instance
  25.5 ns	read_instancevar
  22.9 ns	read_instancevar_slots
  45.7 ns	read_namedtuple
  26.0 ns	read_boundmethod

Variable and attribute write access:
   4.8 ns	write_local
   5.1 ns	write_nonlocal
  18.7 ns	write_global
  81.8 ns	write_classvar
  36.8 ns	write_instancevar
  26.8 ns	write_instancevar_slots

Data structure read access:
  19.7 ns	read_list
  19.4 ns	read_deque
  21.3 ns	read_dict
  19.3 ns	read_strdict

Data structure write access:
  20.0 ns	write_list
  21.7 ns	write_deque
  26.0 ns	write_dict
  24.5 ns	write_strdict

Stack (or queue) operations:
  70.3 ns	list_append_pop
  49.8 ns	deque_append_pop
  49.0 ns	deque_append_popleft

Timing loop overhead:
   0.3 ns	loop_overhead
```

2017のi5ですが、3.7のものと比較すると単一スレッドではi7より多少速い場合もありますね。

# 3.8.3(brew install python@3.8)

```haskell
% nice -n 10 /usr/local/opt/python@3.8/bin/python3.8 var_access_benchmark.py
Variable and attribute read access:
   5.8 ns	read_local
   5.8 ns	read_nonlocal
   7.8 ns	read_global
   7.7 ns	read_builtin
  19.8 ns	read_classvar_from_class
  20.1 ns	read_classvar_from_instance
  27.4 ns	read_instancevar
  25.9 ns	read_instancevar_slots
  21.2 ns	read_namedtuple
  31.6 ns	read_boundmethod

Variable and attribute write access:
   5.9 ns	write_local
   7.5 ns	write_nonlocal
  19.7 ns	write_global
  47.0 ns	write_classvar
  42.4 ns	write_instancevar
  31.3 ns	write_instancevar_slots

Data structure read access:
  23.4 ns	read_list
  24.8 ns	read_deque
  25.7 ns	read_dict
  22.8 ns	read_strdict

Data structure write access:
  24.9 ns	write_list
  29.1 ns	write_deque
  29.9 ns	write_dict
  27.8 ns	write_strdict

Stack (or queue) operations:
  62.0 ns	list_append_pop
  51.2 ns	deque_append_pop
  52.4 ns	deque_append_popleft

Timing loop overhead:
   0.4 ns	loop_overhead
```

ファッ！？
3.8.3の方がだいたい速くなるはずなのに...
でも、read_namedtupleの傾向などは、3.8の傾向を示してはいます。
（read_namedtupleは、バージョンアップで劇的に性能が改善しています）

# 3.8.3（公式）
公式から3.8.3を入れて、python3で3.8.3が呼ばれるようにしました。

```haskell
% nice -n 10 python3 var_access_benchmark.py 
Variable and attribute read access:
   3.9 ns	read_local
   4.4 ns	read_nonlocal
   7.2 ns	read_global
   7.1 ns	read_builtin
  17.1 ns	read_classvar_from_class
  16.2 ns	read_classvar_from_instance
  24.6 ns	read_instancevar
  20.8 ns	read_instancevar_slots
  18.0 ns	read_namedtuple
  28.1 ns	read_boundmethod

Variable and attribute write access:
   4.4 ns	write_local
   4.9 ns	write_nonlocal
  16.6 ns	write_global
  45.7 ns	write_classvar
  38.7 ns	write_instancevar
  25.8 ns	write_instancevar_slots

Data structure read access:
  19.9 ns	read_list
  20.4 ns	read_deque
  20.3 ns	read_dict
  18.5 ns	read_strdict

Data structure write access:
  20.8 ns	write_list
  22.8 ns	write_deque
  24.6 ns	write_dict
  23.3 ns	write_strdict

Stack (or queue) operations:
  50.1 ns	list_append_pop
  42.8 ns	deque_append_pop
  44.7 ns	deque_append_popleft

Timing loop overhead:
   0.3 ns	loop_overhead
```
無事、それっぽい結果になりました。

# 謎

何が違うのかはわかりませんでしたが、こういう事もあるんですね、ということで。
もし詳しい方が居たら教えてください。