#26. バイトコードの最適化

バイトコードの最適化は、コード オブジェクトの実行準備が整う前の最後のクリーンアップと改良の段階です。

コンパイラの初期段階で、プログラムの意味が決定され、命令が発行されます。最適化では、Python のセマンティクスを維持しながら、これらの命令をより小さく、よりシンプルに、またはより高速にしようとします。

例えば:python id="u88pli" x = 1 + 2 書かれているかのようにコンパイルできます:```python id="k8v2ef" x = 3


しかし、これは:```python id="lbwhpl"
x = a + b
```折りたためないので、`a`そして`b`は実行時の値です。それらの型は、任意の動作を定義する可能性があります。`+`

## 26.1 コンパイルパイプラインでの位置

最適化は、最適化の種類に応じて、バイトコード生成後または生成中に行われます。```text id="olp5um"
source
    
tokenization
    
parsing
    
AST
    
symbol table
    
instruction generation
    
optimization
    
assembly
    
code object
```一部の最適化は AST で行われます。一部は制御フロー グラフまたは命令ストリームで発生します。一部は、特殊化によって実行時に後で発生します。

「最適化」を 1 回のパスとして扱わないでください。 CPython は、さまざまな層でいくつかのメカニズムを使用します。

## 26.2 最適化ではセマンティクスを保持する必要がある

Python は非常に動的です。

これにより、CPython が安全に最適化できる内容が制限されます。

例:```python id="wozly6"
x = 1 + 2
```折りたたんでも安全です。

例:```python id="i4bt05"
x = "a" + "b"
```折りたたんでも安全です。

例:```python id="ue1h3y"
x = a + b
```折りたたむと危険です。

操作では以下を呼び出す場合があります。```python id="fk1geq"
a.__add__(b)
```または:```python id="h80anr"
b.__radd__(a)
```これらのメソッドは任意の Python コードを実行できます。

最適化ルール:```text id="o2q4tx"
Only remove or precompute work when the observable behavior remains the same.
```観察可能な動作には次のものが含まれます。```text id="g5n65q"
return values
exceptions
side effects
evaluation order
name lookup behavior
attribute lookup behavior
object identity when relevant
traceback positions
debugging behavior where specified
```## 26.3 定数フォールディング

定数の折りたたみでは、定数のみから作られた式が事前計算されます。

:```python id="wpyjbl"
1 + 2
2 * 3
"py" + "thon"
(1, 2) + (3, 4)
```これらは定数になる可能性があります。

例:```python id="8yvbyh"
def f():
    return 1 + 2
```考えられるバイトコード:```text id="gjktcx"
LOAD_CONST 3
RETURN_VALUE
```それよりも:```text id="rs6njs"
LOAD_CONST 1
LOAD_CONST 2
BINARY_OP +
RETURN_VALUE
```コンパイラは折り畳まれた値を次の場所に保存します。`co_consts`

## 26.4 保守的な折り畳み

CPython は、コンパイル時に大きすぎる、またはコストがかかりすぎる可能性のある式の折りたたみを回避します。

例:```python id="lwt9ja"
x = "a" * 10
```折り畳まれる可能性があります。

ただし、コンパイル中に膨大な繰り返しによって膨大な定数が作成されるべきではありません。```python id="k9mjrp"
x = "a" * 10_000_000_000
```これをやみくもに折りたたむコンパイラは、プログラムが実行を開始する前に膨大なメモリを消費することになります。

最適化には制限を設ける必要があります。

CPython のオプティマイザーは実用的です。これにより、コンパイルが予期しないリソース攻撃に変わることが回避されます。

## 26.5 タプルとコンテナの折りたたみ

定数のみを含むタプルは折りたたむことができます。

例:```python id="i0us1v"
x = (1, 2, 3)
```単一のタプル定数をロードできます。

ただし、リストとセットは変更可能であるため、通常は定数になることはできません。```python id="g3apua"
x = [1, 2, 3]
```実行時に新しいリストを作成する必要があります。

なぜ?

実行ごとに新しい可変オブジェクトが必要です。

この関数は呼び出すたびに異なるリスト オブジェクトを返す必要があります。```python id="ku6rt2"
def f():
    return [1, 2, 3]
```コンパイラが 1 つの共有リスト定数を保存した場合、呼び出し間で突然変異がリークする可能性があります。

## 26.6 メンバーシップテストの最適化

メンバーシップ テストでは、変更可能なリテラルが不変の定数に置き換えられることがあります。

例:```python id="yku3da"
def is_small(x):
    return x in {1, 2, 3}
```セット リテラルは、メンバーシップのテストにのみ使用されます。 CPython ではこれを次のように置き換えることができます。`frozenset`絶え間ない。

概念的には:```text id="nibldo"
LOAD_FAST x
LOAD_CONST frozenset({1, 2, 3})
CONTAINS_OP
RETURN_VALUE
```これにより、関数が実行されるたびにセットを再構築することがなくなります。

設定されたオブジェクト自体はユーザー コードに公開されないため、最適化は有効です。

## 26.7 無条件終了後のデッドコード

無条件終了後、一部のコードは到達不能になります。

例:```python id="oss0kd"
def f():
    return 1
    x = 2
```割り当てを実行できません。

コンパイラは、到達不能な命令シーケンスを削除またはスキップする場合があります。

無条件終了には次のものが含まれます。```text id="u7yxa5"
return
raise
break
continue
```適切な制御フロー コンテキスト内で。

コンパイラは、必要に応じて行情報と診断を保存する必要があります。最適化によってデバッグや構文関連の動作が目に見える形で変更される場合、すべてを自由に消去することはできません。

## 26.8 ジャンプの最適化

制御フローは、多くの場合、ジャンプからジャンプを作成します。

形状の例:```text id="941xx7"
JUMP_FORWARD label_a

label_a:
JUMP_FORWARD label_b

label_b:
...
```オプティマイザは最初のジャンプを直接リダイレクトできます。`label_b````text id="np02i8"
JUMP_FORWARD label_b

label_b:
...
```これにより命令数が減り、不必要なディスパッチが回避されます。

ジャンプの最適化は、同じ制御フローの宛先を保持するため、通常は安全です。

## 26.9 基本的なブロックのクリーンアップ

コンパイラは内部で基本ブロックを構築または使用します。

基本ブロックは、1 つの入り口と 1 つの出口を持つ直線の命令シーケンスです。

最適化により、空のブロックを削除したり、隣接するブロックを結合したり、エッジを単純化したりすることができます。

例:```text id="o49sgv"
block A:
    jump block B

block B:
    jump block C

block C:
    useful work
```になる可能性があります:```text id="ke9fl3"
block A:
    jump block C

block C:
    useful work
```このクリーンアップは、ラベルがまだシンボルである最終アセンブリの前に行われます。

## 26.10 スタック効果の保存

すべての最適化ではスタックの正確性を維持する必要があります。

例:```text id="3fooh0"
LOAD_CONST 1
POP_TOP
```ロードされた定数に副作用がなく、その値が破棄される場合、このペアは削除される可能性があります。

しかし、これは:```text id="s5gd6m"
LOAD_NAME x
POP_TOP
```一般的には削除できません。

読み込み中`x`上げることができる`NameError`

同じく:```text id="hng0do"
LOAD_ATTR attr
POP_TOP
```動的属性検索を呼び出して例外を発生させることができます。

オプティマイザは、どの操作を削除しても安全であるかを認識する必要があります。

## 26.11 評価順序の制約

Python の評価順序は観察可能です。

例:```python id="bp4w5f"
f() + g()

f()前に実行する必要がありますg()

たとえその結果がf()変換された形式では使用されていないように見える場合、呼び出しをスキップしたり並べ替えたりすることはできません。

例:```python id="7e0ozh" items[key()] = value()


最適化では、Python 式を純粋な数式のように扱うことはできません。

## 26.12 名前検索の制約

名前の検索は動的です。

例:```python id="pipmww"
len([1, 2, 3])
```CPython では通常、これを次のものに置き換えることはできません`3`

なぜ?

モジュールレベルでは、`len`リバウンドする可能性があります:```python id="sksxhh"
len = lambda x: 999
```関数内では、`len`組み込みの前にグローバルを通じて解決される場合があります。

コンパイラは、組み込みの`len`は変わっていない。

これは、Python の最適化と静的にリンクされた言語での最適化の重要な違いです。

## 26.13 属性検索の制約

属性の検索は動的です。

例:```python id="o09etj"
obj.x
```これにより、以下が呼び出されます。```text id="jvlw4h"
__getattribute__
__getattr__
descriptors
properties
module-level __getattr__
custom metaclass logic
```コンパイラは、動作が変わっていないことを証明できない限り、繰り返される属性アクセスをキャッシュされた値に置き換えることはできません。

実行時の特殊化により、一般的なケースが投機的に最適化される可能性がありますが、それは実行時チェックによって保護されます。コンパイル時の最適化は依然として保守的です。

##26.14`assert`最適化`assert`ステートメントは最適化モードの影響を受けます。

例:```python id="ixz6ew"
assert x > 0, "x must be positive"
```通常、おおよそ次のようにコンパイルされます。```text id="96ihbr"
if not (x > 0):
    raise AssertionError("x must be positive")
```最適化を有効にして Python を実行すると、次のようになります。`python -O`assert ステートメントは削除されます。

これにより、意図的に動作が変化します。これは、Python の文書化された最適化モードの一部です。

実際的なルール:```text id="w3wpdm"
Do not use assert for required runtime validation.
```常に実行する必要があるチェックには、明示的な例外を使用します。

##26.15`__debug__`特別な定数`__debug__`最適化モードに関連付けられています。

通常:```python id="erm4nr"
__debug__ == True
````python -O`:

```python id="my7j8j"
__debug__ == False
```コンパイラは、次の条件に応じて分岐を最適化できます。`__debug__`

例:```python id="vd8gwm"
if __debug__:
    check()
```最適化モードでは、この分岐は省略できます。`__debug__`特別です。一般の名前はこの扱いを受けません。

## 26.16 文書文字列の処理

最適化レベルは docstring に影響を与える可能性があります。

例:```python id="prb94t"
def f():
    """Function docs."""
    return 1
```通常、関数本体の最初の文字列リテラルは関数 docstring になります。

より高度な最適化を行うと、`python -OO`docstring は削除される可能性があります。

これは以下に影響します。```python id="abqsw1"
f.__doc__
```コンパイラとランタイムは、docstring を特別に扱います。これは、docstring が通常の式ステートメントのように実行されるのではなく、オブジェクトのメタデータとして保存されるためです。

## 26.17 のぞき穴スタイルの最適化

CPython の古い説明では、ピープホール オプティマイザーについてよく言及されています。

ピープホール オプティマイザーは、命令の小さなウィンドウを調べて、非効率なパターンを置き換えます。

アイデアの例:```text id="gqvx1f"
LOAD_CONST 1
LOAD_CONST 2
BUILD_TUPLE 2
```になる可能性があります:```text id="ipc5mv"
LOAD_CONST (1, 2)
```最新の CPython では、単純なピープホール パスのみに依存するのではなく、最適化の多くが AST および制御フロー/命令段階に移行しています。

この概念は依然として有用です。一部の最適化は、小さな命令パターンに対するローカルな書き換えです。

## 26.18 AST レベルの最適化

一部の最適化は、バイトコードが存在する前に行う方が簡単です。

例:```python id="qsilq6"
x = 1 + 2
```AST には次のものが含まれます。```text id="nvue0h"
BinOp(Constant(1), Add, Constant(2))
```AST オプティマイザーは、そのノードを次のものに置き換えることができます。```text id="iz4wys"
Constant(3)
```その後、バイトコード生成がロードされるだけです`3`

AST レベルの最適化では高レベルの構造が考慮されるため、バイトコードから意味を再構築するよりも簡単です。

## 26.19 命令レベルの最適化

その他の最適化は、命令生成後の方が簡単です。

:```text id="qz307a"
remove unreachable blocks
redirect jumps
merge blocks
compute final stack sizes
shorten control flow
```命令レベルの最適化では、実際の制御フローとスタックの影響が確認されます。

これは最終的なバイトコード レイアウトに近づきます。

## 26.20 ランタイムの特殊化は別個です

最新の CPython にはランタイムの特化もあります。

これはコンパイル時の最適化とは異なります。

コンパイラは汎用バイトコードを生成します。 CPython は実行中に、頻繁に使用される命令を特殊化する場合があります。

操作例:```text id="4c4nxd"
LOAD_ATTR
LOAD_GLOBAL
BINARY_OP
CALL
STORE_ATTR
```CPython が安定した実行時の動作を観察した後は、汎用属性アクセスがより高速な特殊なパスになる可能性があります。

コンパイル時の最適化:```text id="p7xt6t"
happens before execution
must be safe without runtime knowledge
```ランタイムの特化:```text id="qo5hr8"
happens during execution
uses observed runtime types and cache guards
can fall back if assumptions fail
```この分離により、CPython はコンパイル時に危険な想定を行うことなく動的コードを最適化できます。

## 26.21 インラインキャッシュ

ランタイム特化では、バイトコード命令の近くでインライン キャッシュを使用します。

概念的には:```text id="yc3mav"
LOAD_ATTR name
CACHE
CACHE
```キャッシュには、次のような実行時情報が保存されます。```text id="ymdvl9"
observed type
dictionary version
descriptor information
resolved offset
specialized handler state
```コンパイラはキャッシュ用のスペースを準備します。インタプリタは後でそれらを埋めて使用します。

これが、逆アセンブリが要求されたときにキャッシュ エントリを表示する可能性がある理由です。

## 26.22 最適化とトレースバック

最適化では、有用なソース位置を保持する必要があります。

例:```python id="ett31m"
x = 1 / 0
```周囲のコードが最適化されていても、`ZeroDivisionError`意味のあるソースの場所を指す必要があります。

トレースバックは、バイトコードからソースへのマッピングに依存します。

オプティマイザは以下を更新または保存する必要があります。```text id="wtwgmh"
line information
column information
exception table ranges
instruction offsets
```メタデータが壊れていると、デバッグ動作が不良になります。

## 26.23 最適化と例外

一部の式は単純に見えますが、例外が発生する可能性があります。

例:```python id="k3r2bn"
1 / 0
```コンパイラーは、通常のコンパイル中にこれをコンパイル時例外に組み込むべきではありません。

例外は、コードのコンパイル時ではなく、コードの実行時に発生する必要があります。

例:```python id="9o0rsd"
def f():
    return 1 / 0
```定義する`f`成功するはずです。電話をかける`f()`上げるべきだ`ZeroDivisionError`

したがって、定数の折りたたみでは、例外を実行時からコンパイル時に移動させる変換を回避する必要があります。

## 26.24 最適化とオブジェクトのアイデンティティ

オブジェクトのアイデンティティは次の方法で観察できます。`is`

例:```python id="q46djy"
x is y
```コンパイラーは、ID に依存する動作を変更する変換を回避する必要があります。

不変定数の場合、CPython は実装動作で許可される方法でオブジェクト、インターン文字列、または定数を折り畳むことを再利用できます。ただし、コードはすべての一定の ID の詳細に依存すべきではありません。

例:```python id="7izgy0"
(1, 2) is (1, 2)
```結果はコンパイルと実装の選択によって異なる場合があります。

最適化では言語のセマンティクスを保持する必要がありますが、定数の一部の ID 動作は実装固有のままです。

## 26.25 最適化と変更可能なデフォルト

関数のデフォルト値は関数定義時に評価されます。

例:```python id="er8lwd"
def f(xs=[]):
    xs.append(1)
    return xs
```リストのデフォルトは、関数オブジェクトに格納されているランタイム オブジェクトです。

コンパイラは、セマンティクスが変更されるため、呼び出しごとにこれを新しいリストに置き換えることはできません。

これは最適化のバグではありません。これは Python の関数定義の動作です。

最適化では、オブジェクトがいつ作成されるかを考慮する必要があります。

## 26.26 条件定数の最適化

一定の条件により制御フローを簡素化できます。

例:```python id="epgm4i"
if False:
    x = 1
else:
    x = 2
```コンパイラは、到達不能なブランチを省略する場合があります。

同様に:```python id="c85x72"
while False:
    work()
```本体は実行できません。

ただし、オプティマイザはソース メタデータと、スコープや構文に影響を与える構成要素に注意する必要があります。

例:```python id="xt3ful"
if False:
    import never_loaded
```インポートは実行されませんが、最適化の前にシンボル テーブルの分析が行われるため、到達不能なブロックに代入が存在すると、ローカル変数の分類に影響を与える可能性があります。

## 26.27 最適化の前にスコープが決定される

シンボル テーブル分析では、最適化によって到達不能なコードが削除される前に、AST 全体が確認されます。

例:```python id="udvaey"
x = 1

def f():
    if False:
        x = 2
    return x
```これならまだ作れる`x`地元の`f`、次の場合にバインドされていないローカル動作が発生します。`return x`実行します。

オプティマイザは、単純に割り当てを消去してから再分類することはできません。`x`コンパイル モデル全体がそのようなセマンティック変更を許可しない限り、グローバルとして扱われます。

スコープ分類は Python セマンティクスの一部であり、多くの最適化の前に行われます。

## 26.28 最適化レベル

CPython は最適化レベルをサポートしています。

共通モード:

|モード |意味 |
| ------ | ---------------------------------------------------- |
|通常 |デフォルトのコンパイル |
|`-O`|アサートステートメントを削除し、設定します`__debug__``False` |
| `-OO`| docstring も削除します。

Python レベルでは:```python id="nqc269"
import sys

print(sys.flags.optimize)
```コンパイル API は最適化設定を受け取ることもできます。

例:```python id="ea0hid"
compile("assert False", "<input>", "exec", optimize=1)
```最適化レベルはコンパイル動作の一部です。

## 26.29 コンパイル時の最適化の制限

CPython は、コンパイル時の積極的な最適化を意図的に回避します。

通常は次のことは実行されません。```text id="g0a8os"
function inlining
global constant propagation
type-specialized compilation
loop unrolling
escape analysis
whole-program optimization
automatic vectorization
```理由:```text id="kgi5lw"
dynamic name lookup
dynamic attribute access
monkey patching
import-time side effects
debuggability
compile-time cost
implementation simplicity
semantic risk
```CPython は代わりに以下に依存します。```text id="nam80y"
simple safe compile-time optimizations
specialized C implementations of built-in types
runtime adaptive specialization
extension modules
external JIT implementations in other runtimes
```## 26.30 重要な CPython ソース領域

重要なソース ファイルには次のものが含まれます。```text id="9n21ql"
Python/ast_opt.c
Python/compile.c
Python/flowgraph.c
Python/assemble.c
Python/bytecodes.c
Lib/dis.py
Lib/test/test_peepholer.py
Lib/test/test_compile.py
```概念的な役割:

|エリア |役割 |
| ------------- | -------------------------------------- |
|`ast_opt.c`| AST レベルの最適化 |
|`compile.c`| AST から命令への生成 |
|`flowgraph.c`|制御フローグラフの作業 |
|`assemble.c`|最終的なコードアセンブリ |
|`bytecodes.c`|オペコード定義とランタイムのサポート |
|`dis.py`|バイトコード検査 |
|テスト |オプティマイザーの動作を保護する |

## 26.31 最小限のメンタルモデル

このモデルを使用します。```text id="dz2gpc"
CPython optimization is conservative.
It folds safe constants.
It simplifies some containers and membership tests.
It removes or redirects some unreachable or redundant control flow.
It respects evaluation order, exceptions, dynamic lookup, scope rules, and source positions.
Compile-time optimization differs from runtime specialization.
Runtime specialization can use observed types and guarded inline caches.
```バイトコードの最適化により、Python の動的セマンティクス内に留まりながら、生成されたコードが改善されます。