【Google Cloud】Python3.12をソースから13分でビルドする（PGO+LTO有効化）

## はじめに
みずほリサーチ&テクノロジーズの@fujineです。

本記事では、Google Cloudのオンライン環境である[Cloud Shell](https://cloud.google.com/shell)に、2024/2/6にリリースされた**Python3.12.2をソースからインストール**する方法をご紹介します。コンパイラ最適化手法である**PGOとLTOを有効化した状態で、13分でインストール**できました。

:::note info
`Cloud Shell`にはPython3.9を含む様々な[ランタイム](https://cloud.google.com/shell/docs/how-cloud-shell-works?hl=ja#language_support)や[ツール](https://cloud.google.com/shell/docs/how-cloud-shell-works?hl=ja#tools)がプリインストールされており、`sudo apt install`等で追加パッケージのインストールも可能です。しかし、`Cloud Shell`ではセッションが切断すると、[`$HOME`以外の変更は全て初期化されてしまう](https://cloud.google.com/shell/docs/quotas-limits?hl=ja#custom_installed_software_packages_and_persistence)という制約があります。
再起動の都度インストールするのは手間なのと、必要なバージョンが`apt`リポジトリにあるとは限らないことから、今回はソースから`$HOME`へインストールすることにしました。
:::

## スクリプト
以下スクリプトにて、ソースファイルのダウンロードからインストールまでを一括で実行します。

:::note warn
異なるPythonバージョンやマシン環境で実行する場合は、以降の解説を参考に、変数やオプションの値を適時変更して下さい。
:::

```sh
#!/bin/sh
url=https://www.python.org/ftp/python/3.12.2/Python-3.12.2.tar.xz

curl -s $url | tar -xJ
cd Python-3.12.2

./configure --prefix="$HOME/.local" \
            --enable-optimizations \
            --with-lto \
            PROFILE_TASK="-m test --pgo --timeout=$(TESTTIMEOUT) -j8"

sudo make -j8
sudo make altinstall -j8
```

## 解説
```sh
url=https://www.python.org/ftp/python/3.12.2/Python-3.12.2.tar.xz
```
ソースファイルの入手先URLを`url`に指定します。URLは[公式ダウンロードサイト](https://www.python.org/downloads/release/python-3122/)のリンクをコピーして下さい。

```sh
curl -s $url | tar -xJ
cd Python-3.12.2
```
ソースファイルをダウンロードして展開し、ディレクトリに移動します。ダウンロードファイルをディスクに保存せずパイプで`tar`に渡すことで、ダウンロードファイルを後で削除する手間を減らしています。

```sh
./configure --prefix="$HOME/.local" \
            --enable-optimizations \
            --with-lto \
            PROFILE_TASK="-m test --pgo --timeout=$(TESTTIMEOUT) -j8"
```
`configure`を実行します。`--prefix`を除く3つのオプションがコンパイラ最適化に関係するものです。

- `--prefix` : [Pythonのインストール先を指定](https://docs.python.org/ja/3/using/configure.html#cmdoption-prefix)します。`$HOME/.local`は[永続ストレージ](https://cloud.google.com/shell/docs/quotas-limits?hl=ja#disk_storage)のため、セッションが切断されても削除されません。
- `--enable-optimizations` : [Profile Guided Optimization (PGO) を有効化](https://docs.python.org/ja/3/using/configure.html#cmdoption-enable-optimizations)します。PGOとは、1度ビルドしたバイナリに対して[PROFILE_TASK（プロファイルタスク）](https://docs.python.org/3/using/configure.html#envvar-PROFILE_TASK)を実行し、その結果を使用して再度ビルドすることで、プログラムのランタイムパフォーマンスを向上させるコンパイラ最適化技法です。
- `--with-lto` : [Link Time Optimization (LTO)を有効化](https://docs.python.org/ja/3/using/configure.html#cmdoption-with-lto)します。LTOはリンク時にモジュール間を跨った解析による最適化を行うことで、プログラムのランタイムパフォーマンスを向上させるコンパイラ最適化技法です。
- `PROFILE_TASK` : [PGO用プロファイルタスクの実行方法を指定](https://docs.python.org/ja/3/using/configure.html#envvar-PROFILE_TASK)します。デフォルトの実行コマンドに並列化オプションである`-j8`を追加することで、タスク実行時間を短縮します。

:::note info
本記事では取り上げませんが、Python3.12から[LLVM BOLTによる最適化も実験的にリリース](https://docs.python.org/ja/3/using/configure.html#cmdoption-enable-bolt)されています。
:::

```sh
sudo make -j8
sudo make altinstall -j8
```
ソースのコンパイルとインストールを実行します。それぞれに並列化オプションである`-j8`を指定します。

筆者の環境では、スクリプトの実行に13分ほどかかりました。成功すると、`$HOME/.local/bin`に以下バイナリが保存されます。
```sh
$ ls $HOME/.local/bin
2to3-3.12  idle3.12  pip3.12  pydoc3.12  python3.12  python3.12-config
```

# Python3.12を実行
インストールが成功したら、`$PATH`に`$HOME/.local/bin`を追加するコマンドを`.bashrc`に追記して反映します。
```sh
$ echo "PATH=$HOME/.local/bin/:$PATH" >> ~/.bashrc
$ source ~/.bashrc
```

Python3.12がインストールされたことを確認します。実行コマンドは`Python3.12`と、バージョン番号まで指定して下さい。
```sh
$ python3.12 -V
Python 3.12.2
```

ビルド時にコンパイラ最適化が有効化されたことを確認します。
```sh
$ python3.12
>>> from test.support import python_is_optimized
>>> python_is_optimized()
True
```

# まとめ
本記事では、Python3.12のソースビルド方法とコンパイラ最適化を紹介させていただきました。機会があれば、Python3.13などの[プレリリース版](https://www.python.org/download/pre-releases/)のビルドにもチャレンジしてみようと思います。