Windowsマシン：各種環境でのPython本体の実行速度

# 狙い
EasyISTR（FrontISTR用のGUIツール）のWindows版は、メッシュ変換などが遅いです。
Windows版では、条件の悪いVirtualBoxのLinuxよりも2倍くらい時間がかかり、六面体2次要素が８万個くらいでいらいらする状態になります。
MSYS2のPython3自体の性能がLinux環境と比べて劣るのかを調査しました。
# 比較した条件
## 使用したコード
モジュールをインポートしないで（Numpyなどを使わない）、行列の行と列を入れ替えを行う。データは100000行×100列の乱数。Copilotを利用して、Pythonコードを作成。
```python:Modyfy_Matrix.py
#!/usr/bin/env python3
"""
乱数による行列生成および転置処理の速度比較用コード

・行数：1000行、列数：100列
・各要素は線形合同法による擬似乱数（0～1 の浮動小数点数）
・転置処理（行と列を入れ替え）を純粋な Python の二重ループで実施
・計測には __import__("time") を利用しており、トップレベルでの import 文は避けています
"""

def generate_matrix(n_rows, n_cols):
    """
    n_rows 行 n_cols 列の行列を生成する。
    各要素は線形合同法による疑似乱数で生成（0～1 の範囲）
    """
    # 線形合同法の定数（シンプルな例）
    seed = 1
    modulus = 0x7fffffff  # 2^31 - 1
    multiplier = 214013
    increment = 2531011
    matrix = []
    for i in range(n_rows):
        row = []
        for j in range(n_cols):
            seed = (multiplier * seed + increment) & modulus
            row.append(seed / modulus)  # 0～1に正規化
        matrix.append(row)
    return matrix

def transpose(matrix):
    """
    引数 matrix（リストのリスト）の転置を行い、新しい行列として返す。
    """
    n_rows = len(matrix)
    n_cols = len(matrix[0])
    # 転置後の行列は「列数 x 行数」の大きさとなる
    result = [[0] * n_rows for _ in range(n_cols)]
    for i in range(n_rows):
        for j in range(n_cols):
            result[j][i] = matrix[i][j]
    return result

def main():
    # 100000行 x 100列の行列を生成
    matrix = generate_matrix(100000, 100)
    
    # 組み込みの __import__ を利用して time モジュール（計測用）を動的に利用
    perf_counter = __import__("time").perf_counter

    # 転置前の時刻を記録
    start = perf_counter()
    transposed = transpose(matrix)
    # 転置処理終了後の時刻を記録
    end = perf_counter()
    
    print("転置処理にかかった時間: {:.6f} 秒".format(end - start))

if __name__ == '__main__':
    main()
```
## 比較した環境と結果
MSYS2自体は、Windows標準やLinuxの環境と比べて、速度が劣ることはありません。
仮想環境（VirtualBoxやHyper-V）ではホストOSとのインターフェースを介するため、実際のハードウェア上で動作するものに比べてオーバーヘッドが大きくなるようです。
### Windowsの標準 (Python3.13.2)
```
.\python
Python 3.13.2 (tags/v3.13.2:4f8bb39, Feb  4 2025, 15:23:48) [MSC v.1942 64 bit (AMD64)] on win32

.\python "C:\Users...\Modify_Matrix.py"
転置処理にかかった時間: 0.226710 秒
```
### MSYS2 (Python3.11.7, EasyISTR用の環境)
```
.\python3
Python 3.11.7 (main, Dec  7 2023, 09:09:57)  [GCC UCRT 13.2.0 64 bit (AMD64)] on win32

.\python3 "C:\...\Modify_Matrix.py"
転置処理にかかった時間: 0.187958 秒
```
### WSL2 / Debian 12のPython3
```
python3
Python 3.11.2 (main, Nov 30 2024, 21:22:50) [GCC 12.2.0] on linux

python3 Modify_Matrix.py
転置処理にかかった時間: 0.216160 秒
```
### VirtualBox / Ubuntu 22.04のpython3  
```
python3
Python 3.10.12 (main, Feb  4 2025, 14:57:36) [GCC 11.4.0] on linux

python3 Modify_Matrix.py
転置処理にかかった時間: 0.405191 秒
```
### Hyper-V / Ubuntu 22.04のpython3
```
python3
Python 3.10.12 (main, Feb  4 2025, 14:57:36) [GCC 11.4.0] on linux

python3 Modify_Matrix.py
転置処理にかかった時間: 0.291611 秒
```
## 考察
EasyISTRのコードをぱらっとみると、globやmultiprocessingなどWindows環境では、不利となるモジュールがありました。

:::note info
Copilotが解説してくれたので紹介します。
「WindowsとLinuxの両方で高速に動作させる」のは、手間がかかるようです。
:::

たとえば、multiprocessing モジュールでは、Linux は fork() システムコールを利用できるため、既存のプロセスの状態をそのまま複製することができます。一方、Windows ではプロセスの生成時に新しく Python インタプリタを読み込む必要があり、その結果、初期化オーバーヘッドが大きくなる傾向があります。これが、同じコードでもWindows側で処理開始時に不利になる一因です。
また、glob も内部でファイルシステムのパスを扱う際に、それぞれのOSのファイルシステム特性やエンコーディング、さらにはパターンの解釈方法の違いにより、処理速度に差が出る場合があります。これらのライブラリは基本的に高い汎用性を持ちますが、その反面、OSに最適化された実装になっていないこともあり、特定の環境下では意外なパフォーマンス低下を引き起こすことがあります。
このような状況で「WindowsとLinuxの両方で高速に動作させる」ためのアプローチとしては、以下のような方法が考えられます：
- OS毎の条件分岐を用いた最適化
sys.platform や platform.system() で実行環境を判定し、WindowsとLinuxで異なる実装を採用する。たとえば、Windowsでは multiprocessing を使う際に、プロセス生成の初期化を最小化するための工夫や、代替の並列処理モデル（たとえばスレッドや非同期I/O）の検討が挙げられます。
- ライブラリの選定・利用
高速なファイルアクセスや並列処理が求められる場合、状況に応じてサードパーティ製のライブラリ（例：pathlib や専用の並列処理ライブラリ、あるいはJITコンパイラを利用するなど）を検討してみるのも良いでしょう。これにより、OS固有の実装差をある程度吸収できます。
- プロファイリングとボトルネックの特定
どこに一番時間がかかっているのかプロファイリングして、プラットフォーム毎に最も負荷の高い処理を特定することが重要です。これにより、本当にどの部分が問題なのか、そしてそこをどう最適化すべきかの方向性が見えてきます。
- 環境に依存しない設計の見直し
最初からOS固有の挙動に依存しない形でのコード設計を模索することで、両環境でのパフォーマンスの差を小さくできる可能性もあります。たとえば、必要な場合のみ並列化する設計や、ある程度のオーバーヘッドが許容される処理では高速化よりもコードの可読性や保守性を優先する、といった判断が考えられます。


## マイナーなノウハウ
### VirtualBox
```
sudo apt install dkms # カーネルアップデート時に失敗するリスクを下げる
# 私は、ゲストOSにHWEカーネルは不要と考えていますので、
# Grub2から古いカーネルを起動してHWE関連は削除しています。
# アップデート時にカーネル関係の設定時間を短縮できます。

cat /etc/group # 共有フォルダを利用する場合。グループ名の確認。
sudo adduser username vboxsf # vboxsfのグループに加わる
# Debian系の場合は、このコマンドでOK
```
### Hyper-V
共有フォルダのファイルのコピー・移動・解凍などは、端末から操作する。

:::note warn
警告
nautilusからパスのコピーはできますが、コピーや移動の操作を行うとフリーズします。
:::