Help us understand the problem. What is going on with this article?

Python で高速化したいなら Python を書いてはいけない

はじめに

職場で Python に関するライトニングトークをすることになったので、資料をかねて 5 分程度で読めるものを書いてみます。
トークテーマは「Python で高速化を試みる上での心得」です。
Pythonをディスる記事ではないです。

この記事の対象者

この記事は下記の人を対象としています。

  • Python 初学者
  • Python に興味あるけど、速度でないらしいしなぁ、と考えている人

早速結論

タイトルにあるとおり、Python で高速化したいなら(出来る限り)Python を書いてはいけません。
Python を書く量を減らして処理速度を上げようと言うのがこの記事の主旨です。

そもそも Python は遅い

さて、早速ですが C++や Java などと比べるとPython は遅い言語です。
Python の遅さに関しては以下のような様々な記事で言及されています。

まとめると主に下記の理由から Python は遅いとされています。

  • インタープリタ型(コンパイル型ではない)言語だから
  • 動的型付け言語だから

しかし、機械学習や画像処理などの、一般的に重いとされているタスクが、盛んに Python を使って行われています。
これらの処理はなぜ、実用に耐え得る速度で実行出来ているのでしょうか?
それは、処理の殆どが Python を使って書かれていないからです。

どういうことか

つまりPython の処理速度を上げたいのであれば、重い処理は Python で行うのではなく、C++などで書かれた標準関数や、ライブラリに任せてしまうのです。
下記の二つの関数を見てください。
fastCode()は速く実行出来るようにした関数。slowCode()は遅くなるようにした関数です。

import numpy as np

# 速いコード
def fastCode():
    # リストはリスト内記法で宣言
    list1 = [ i for i in range(0,10000)]
    list2 = [ i for i in range(-10000,0)]

    # 出来る限り、標準関数を使う
    listSum = sum(list1)

    # 大量の計算を行列式にして、numpyを用いる
    array1 = np.array(list1)
    array2 = np.array(list2)
    # array1とarray2の全ての要素を足し合わせる
    # [0, 1, 2, ...] + [-100000, -99999, -99998, ...]
    # >> [-100000, -99998, -99996, ...]
    array3 = array1 + array2
    # array1とarray2の全ての要素を掛け合わせる
    # [0, 1, 2, ...] * [-100000, -99999, -99998, ...]
    # >> [0, -99999, -199996, ...]
    array4 = array1 * array2

# 遅いコード
def slowCode():
    # appendを使ってリストを作成
    list1 = []
    for i in range(0,10000):
        list1.append(i)
    list2 = []
    for i in range(-10000,0):
        list2.append(i)

    # 標準関数を使わず、for文で回して合計を計算
    listSum = 0
    for value in list1:
        listSum += value

    # 大量の計算をfor文でゴリゴリやる
    list3 = []
    list4 = []
    for i , value in enumerate(list1):
        list3.append(value + list2[i])
        list4.append(value * list2[i])

fastCode()は標準関数やライブラリに処理を任して、短くコーディングできているのに対して、slowCode()は for 文を使って自力で処理を記述していることがお分かりいただけると思います。
実際の処理速度は、それぞれ次のようになります。

関数 コメントを除いた行数 実行速度(100 回実行した平均)
fastCode() 7 行 0.00207[sec]
slowCode() 14 行 0.00430[sec]

標準関数やライブラリはC/C++で書かれたコンパイル済のバイナリによって構成されており、それらに処理を任せることによって、重いタスクでも高速に処理出来るようになるのです。
「高速化」という観点で見れば、Python はいわば高速なライブラリ群を簡単に使用するための言語であると言えます。

まとめ

Python で処理を高速化したいのであれば、下記のようなテクニックを用いて Python で書く処理は最低限にしましょう。

  • 高速な C/C++で書かれた標準関数やライブラリを用いる
  • 大量の計算は行列式に落とし込んで、numpy で一気に処理する
  • どうしようもない場合は、C/C++を用いてライブラリを自作する

最後に

ここで紹介した以外でも、Python の高速化の方法はたくさんあります。
ただ、Python で高速化する上で、最初に意識するべきはこの考え方だと思います。
より速度を求める必要が出た時に、自作ライブラリ・関数の事前参照・Cython や Numba などに手を出していきましょう。

参考

なぜ Python はこんなにも遅いのか? | POSTD
Python の for 文は遅い? - atsuoishimoto's diary
Python, Java, C++の速度比較 - Qiita
【Python C API 入門】C/C++で拡張モジュール作って Python から呼ぶ -前編-
高速化のための Python Tips - のんびりしているエンジニアの日記

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした