forループの使用

forループはPythonで最もよく使われるループ文であり、その構文は以下のとおりです。

for i in range(10):
    print(i)

forループに関する問題点

forループの問題点は、実行が順次行われることです。つまり、ループ内のコードの実行が完了してからでないと、次のループの実行が開始されません。そのため、forループ内のコードの実行に時間がかかると、forループ全体の実行時間も長くなってしまいます。

ウェブスクレイピングを行う際、インデックスパラメータを変更してAPIに複数のリクエストを送信し、その後データを連結して分析する必要がある状況によく遭遇します。

import requests

url_template = 'https://www.example.com/api?index={index}'

def get_data(index):
    url = url_template.format(index=index)
    response = requests.get(url)
    return response.json()

indexes = [1, 2, 3]
data = []
for index in indexes:
    data.append(get_data(index))

しかし、ネットワークアクセスは常にプログラムの実行速度のボトルネックとなっていました。データ取得にforループを使用すると、多くの時間を無駄にしてしまいます。

マルチスレッドを有効にする

幸いなことに、Pythonはマルチスレッドをサポートしているため、複数のスレッドを起動し、複数のURLに同時にアクセスしてデータを取得できるため、大幅な時間短縮が可能です。以下の構文は、forループを完全に置き換えることができます。

from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=10) as executor:
    results = executor.map(get_data, indexes)
    data = list(results)

マルチスレッド高速化の原則と注意事項

この例でマルチスレッド処理によって実行効率が向上する理由は、コンピュータの性能を限界まで引き出しているからではなく、CPUがデータのダウンロード中に実際にはほとんどアイドル状態になり、次のループの反復処理(次のダウンロードの開始)を開始する前にダウンロードタスクの完了を待っているためです。つまり、パフォーマンスを低下させているのはCPUではなくネットワークなのです。したがって、タスクが主にローカル計算を伴う場合は、マルチスレッド処理を使用しても必ずしも実行速度が向上するとは限りません。

さらに、同じAPIに対して同時に複数のリクエストを行うことは推奨されません。ネットワークサーバーへの過負荷を避けるため、スレッドの最大数を制御することができます。