狙い
EasyISTR(FrontISTR用のGUIツール)のWindows版は、メッシュ変換などが遅いです。
Windows版では、条件の悪いVirtualBoxのLinuxよりも2倍くらい時間がかかり、六面体2次要素が8万個くらいでいらいらする状態になります。
MSYS2のPython3自体の性能がLinux環境と比べて劣るのかを調査しました。
比較した条件
使用したコード
モジュールをインポートしないで(Numpyなどを使わない)、行列の行と列を入れ替えを行う。データは100000行×100列の乱数。Copilotを利用して、Pythonコードを作成。
#!/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環境では、不利となるモジュールがありました。
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
共有フォルダのファイルのコピー・移動・解凍などは、端末から操作する。
警告
nautilusからパスのコピーはできますが、コピーや移動の操作を行うとフリーズします。