パフォーマンスの最適化

1. 学習内容



2. 主要な概念

(1) ベクトル化

PYTHON
import numpy as np
import time

n = 10_000_000
a = np.random.rand(n)
b = np.random.rand(n)

# Slow: Python loop
t0 = time.time()
c = np.empty(n)
for i in range(n):
    c[i] = a[i] + b[i]
t1 = time.time()

# Fast: vectorized
t2 = time.time()
d = a + b
t3 = time.time()

print(f"Loop: {t1-t0:.3f}s, Vectorized: {t3-t2:.6f}s")
print(f"Speedup: {(t1-t0)/(t3-t2):.0f}x")

(2) インプレース操作

PYTHON
# Avoid copies with out= parameter
a = np.random.rand(1000000)
b = np.random.rand(1000000)

# Copy (allocates new memory)
c = a + b

# In-place (no allocation)
np.add(a, b, out=a)

(1) ▶ サンプル:ベクトル化による処理速度の比較(難易度 ⭐⭐)

PYTHON
import numpy as np
import time

n = 1_000_000
a = np.random.rand(n)
b = np.random.rand(n)

# Slow: Python loop
t0 = time.time()
c = np.empty(n)
for i in range(n):
    c[i] = a[i] + b[i]
t1 = time.time()

# Fast: vectorized
t2 = time.time()
d = a + b
t3 = time.time()

loop_time = t1 - t0
vec_time = t3 - t2
print(f"Loop: {loop_time:.3f}s, Vectorized: {vec_time:.6f}s")
print(f"Speedup: {loop_time/vec_time:.0f}x")

出力:

TEXT
ループ実行時間:0.312秒、ベクトル化実行時間:0.002秒
処理速度の向上:156倍

(2) ▶ サンプル:out= を使用したインプレース演算(難易度 ⭐⭐)

PYTHON
import numpy as np

a = np.array([10, 20, 30, 40, 50])
b = np.array([1, 2, 3, 4, 5])

# Without out: creates new array
c = np.add(a, b)
print("Without out:", c)

# With out: reuses existing memory
result = np.empty(5)
np.add(a, b, out=result)
print("With out:", result)

# Chain operations in-place
np.multiply(result, 2, out=result)
np.add(result, 1, out=result)
print("Chained:", result)

出力:

TEXT
除外:[11 22 33 44 55]
除外:[11 22 33 44 55]
チェーン:[23 45 67 89 111]

(3) ▶ サンプル:メモリ配置の影響(難易度 ⭐)

PYTHON
import numpy as np

# C-contiguous (row-major) — default
c = np.array([[1, 2, 3], [4, 5, 6]])
print("C-contiguous:", c.flags.c_contiguous)
print("Fortran-contiguous:", c.flags.f_contiguous)

# Convert to Fortran-contiguous (column-major)
f = np.asfortranarray(c)
print("Fortran-contiguous:", f.flags.f_contiguous)

# Shape and strides
print("C strides:", c.strides)
print("F strides:", f.strides)

出力:

TEXT
C-連続: True
Fortran-contiguous: False
Fortran-contiguous: True
Cのストライド:(24, 8)
Fの歩数:(8, 24)


❓ よくある質問

Q パフォーマンスに関して最も重要なルールは何か?
A Pythonのループは避けること。ループ(for i in range(n))を書くたびに、「これをベクトル化できるか?」と自問してください。もし可能なら、NumPyを使えば10~100倍速くなります。
Q C順とFortran順の違いは何ですか?
A C順(行優先)では、行が連続して格納されます。Fortran順(列優先)では、列が連続して格納されます。キャッシュのパフォーマンスを最大限に高めるには、アクセスパターンに合った順序を使用してください。
Q out= パラメータはどのような場合に使用すべきですか?
A 同じ大きな配列に対して多数の演算を行う場合、out= を使用することで、演算ごとに新しいメモリを割り当てることを回避できます。ループやパイプライン内で使用してください。

📖 まとめ



📝 練習問題

  1. 初心者向け(難易度 ⭐):1,000万個の要素に対して a + b を計算する時間を、ループを使用した場合とベクトル化を使用した場合で比較してください。速度向上率を記録してください。

  2. 中級(難易度 ⭐⭐)a + b を使用した場合と使用しない場合で、a + b の実行時間を比較してください。割り当てのオーバーヘッドの差を測定してください。

  3. 上級 (難易度 ⭐⭐⭐): 大きな配列を作成し、それを転置してから、ある軸に沿って合計を求めます。転置した配列と連続したコピーとのパフォーマンスを比較し、その違いを説明してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%