LoginSignup
12
11

More than 5 years have passed since last update.

コンピュータ言語を遅く使ってしまう方法

Last updated at Posted at 2015-07-03

どの言語でも遅く使ってしまう方法はあるものです。
その遅く使ってしまう方法を使ってしまっているのに、
「○○という言語、ライブラリは遅いね。」などと言ってしまうことがありがちです。
そこで初心者がしがちなことを述べます。

・リストの項目を追加するために、+演算子でリストに連結した結果をリストを代入してしまう。

ex.py
a = []
for i in range(1000):
    a = a + [i]

・リストにリストを追記する効果的なメソッドが用意されているのにもかかわらず
 個々の要素をappendすることを繰り返してしまう。
 (pythonの場合にはextendメソッドがあります。)

・目的に応じたデータ構造の適切な使い分けをできていない場合
 辞書や集合で十分な処理にリストを用いてしまう。
 
 リストの中に値が含まれているかを確かめるのはO(N)
 if x in listData:
 辞書のキーに含まれいているかどうかはO(1)
 if dictData.has_key(x):
 集合setに含まれているかどうかはO(1)
 if x in setData:

・標準のライブラリに入っている機能を使えば効果的に実行できることを気づかずに
 自作のライブラリで遅い実装を作ってしまう。

 集合というデータ形式が用意されているライブラリでは、集合の合併や共通部分、差分を求める
 メソッドが用意されています。
Pythonのset型の場合
s.union(t)
s.intersection(t)
s.difference(t)

 それらを用いると、大概の場合、自作ライブラリよりも高速に動作する簡潔なプログラムになりま
す。

・不必要なオブジェクトのコピーを作ってしまう。
例:部分配列が簡潔に記述できてコピーを生成することなしに引数に渡せる言語・ライブラリである
のにもかかわらず、コピーを作って渡してしまう。
 C++でOpenCVのcv::Mat型は、部分画像を関数の引数に渡すことができます。
PythonのNumpyのarray型も、部分配列を関数の引数に渡すことができます。
 

例:C++では参照渡しができるのに値渡しをしてしまい、
 不必要なオブジェクトのコピーを作ってしまう。
(不十分なテキスト・カリキュラムではC++で、値渡しとポインタ渡しだけ教えて十分としてしまっている。)

・行列計算を効果的に行うようにライブラリが作られているのにもかかわらず
そのようなライブラリで行列演算をfor文のループで処理してしまう。
(MATLABやpythonのnumpyなど)

ex2.py
# -*- coding: utf-8 -*-
def func1():
    b=['d', 'e', 'f', 'g', 'h', 'i', 'j',  'k', 'l',  'm', 'n', 'o', 'p', 'q', 'r', 's']
    for i in range(10000):
        a=['a', 'b', 'c']

        for x in b:
            a= a+[x]

#        print a
def func2():
    b=['d', 'e', 'f', 'g', 'h', 'i', 'j',  'k', 'l',  'm', 'n', 'o', 'p', 'q', 'r', 's']
    for i in range(10000):
        a=['a', 'b', 'c']

        for x in b:
            a.append(x)

#        print a
def func3():
    b=['d', 'e', 'f', 'g', 'h', 'i', 'j',  'k', 'l',  'm', 'n', 'o', 'p', 'q', 'r', 's']
    for i in range(10000):
        a=['a', 'b', 'c']
        a = a+b

#        print a
def func4():
    b=['d', 'e', 'f', 'g', 'h', 'i', 'j',  'k', 'l',  'm', 'n', 'o', 'p', 'q', 'r', 's']
    for i in range(10000):
        a=['a', 'b', 'c']

        a.extend(b)

 #       print a

if __name__=='__main__':
    func1()
    func2()
    func3()
    func4()

Pythonの場合、Spyder統合環境で[Run][Profile]を利用することで、スクリプトを書き換えることなしに、プロファイルをとることができます。

まとめ:
 リストにデータを追加するときにはappend、extendメソッドを使う。+演算子を用いて代入を繰り返すことは得策ではない。
 リスト、辞書、集合などのデータ構造を使い分ける。
 標準的なライブラリにあるデータ構造のメソッドを使い、同等な機能を自作することのないようにする。
 データの不要なコピーを作らない。
 配列演算でforループなしで実行できることをforループを用いない。

追記:
これらの内容について、さらに知りたい方は、
「ハイパフォーマンスPython」
を読むといいでしょう。
この記事を書いた時点では、この日本語訳書は出版されていませんでした。

参考情報
Python リスト追加についての速度比較(append,内包表記など)
http://nonbiri-tereka.hatenablog.com/entry/2014/10/20/110304

12
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11