質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1166閲覧

配列計算の処理速度向上検討

dream-20xx

総合スコア17

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/05/23 02:25

質問内容

Pythonで作成した配列計算の処理速度向上を検討しております。
色々試したところ、行きついたのがmultiprocessです。
multiprocessでは処理速度が2倍程度向上しましたが、もう少し速くしたいと考えております。
お手数ですがなにか案があればご回答頂けると幸いです。

試したこと

■ Cython・・・処理速度はほぼ変わりませんでした。
■ numba(jit)・・・処理速度はほぼ変わりませんでした。
■ multiprocess・・・約2倍程度向上。

サンプルコード

python

1import numpy as np 2import time as t 3 4start = t.time() 5np.random.seed(0) 6x = np.random.rand(100) 7y = np.random.rand(1000, 1000) 8z = np.random.rand(1000, 1000) 9 10a = 0 11b = np.zeros((y.shape[0], y.shape[1])) 12c = np.zeros((z.shape[0], z.shape[1])) 13 14for j in reversed(range(5)): 15 for i in reversed(range(5)): 16 17 p = x[a] * y ** i * z ** j 18 q = x[a + 25] * y ** i * z ** j 19 b = b + p 20 c = c + q 21 a = a + 1 22 23print('time : ' + str(round((t.time() - start),5)) + ' [sec]')

上記コードをマルチプロセス化

python

1import numpy as np 2import time as t 3from multiprocessing import Pool, Array 4 5def f(args): 6 7 x = args[0] 8 y = args[1] 9 z = args[2] 10 a = args[3] 11 i = args[4] 12 j = args[5] 13 14 p = x[a] * y ** i * z ** j 15 q = x[a + 25] * y ** i * z ** j 16 17 return p, q 18 19def main(): 20 21 start = t.time() 22 23 p = Pool(processes=8) 24 np.random.seed(0) 25 x = np.random.rand(100) 26 y = np.random.rand(1000, 1000) 27 z = np.random.rand(1000, 1000) 28 29 b = np.zeros((y.shape[0], y.shape[1])) 30 c = np.zeros((z.shape[0], z.shape[1])) 31 32 ii = [] 33 jj = [] 34 aa = np.arange(25) 35 for j in reversed(range(5)): 36 for i in reversed(range(5)): 37 ii.append(i) 38 jj.append(j) 39 40 args = [(x, y, z, aa[i], ii[i], jj[i]) for i in range(25)] 41 out = p.map(f, args) 42 43 for k in range(25): 44 b = b + out[k][0][:] 45 c = c + out[k][1][:] 46 47 print('time : ' + str(round((t.time() - start),5)) + ' [sec]') 48 49if __name__ == "__main__": 50 main()

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

python では * よりも ** の方が優先順位が高いため、

python

1p = x[a] * y ** i * z ** j

python

1p = x[a] * (y ** i) * (z ** j)

と同じです。 y や z のべき乗を複数回計算するのは無駄ですので、それを予め計算するだけでかなり速くなります。

python

1import numpy as np 2import time as t 3 4start = t.time() 5np.random.seed(0) 6x = np.random.rand(100) 7y = np.random.rand(1000, 1000) 8z = np.random.rand(1000, 1000) 9 10yi=[y**i for i in range(5)] 11zj=[z**j for j in range(5)] 12 13a = 0 14b = np.zeros((y.shape[0], y.shape[1])) 15c = np.zeros((z.shape[0], z.shape[1])) 16 17for j in reversed(range(5)): 18 for i in reversed(range(5)): 19 20 p = x[a] * yi[i] * zj[j] 21 q = x[a + 25] * yi[i] * zj[j] 22 b = b + p 23 c = c + q 24 a = a + 1 25 26print('time : ' + str(round((t.time() - start),5)) + ' [sec]')

とすると、手元の環境で 3.81263秒→ 0.70095秒と約5.4倍に速くなりました。

更に、べき乗をそれぞれ別々に求めるのも時間の無駄なので、

python

1yi=[y**i for i in range(5)] 2zj=[z**j for j in range(5)]

python

1yi=[np.ones_like(y)] 2for i in range (1,5): 3 yi.append(yi[-1]*y) 4zj=[np.ones_like(z)] 5for j in range (1,5): 6 zj.append(zj[-1]*z)

と修正したところ、0.40384秒にまで速くなりました。更に yi[i] と zj[j] の掛け算も2回行うのはもったいないため、

python

1 p = x[a] * yi[i] * zj[j] 2 q = x[a + 25] * yi[i] * zj[j]

python

1 yizj=yi[i]*zj[j] 2 p = x[a] * yizj 3 q = x[a + 25] * yizj

とすると、0.3625秒になりました。ただしこの変更は計算の順番が変わるため、二つの結果の間には若干の計算誤差が有るかもしれません。更に、

python

1 b = b + p 2 c = c + q

python

1 b += p 2 c += q

と書くと、0.33198秒になりました。

投稿2019/05/23 03:07

編集2019/05/23 03:19
hiro-k

総合スコア902

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dream-20xx

2019/05/23 04:36

早速のご回答ありがとうございます! ご提示のコードにて高速化されることを確認できました。 最終的なコードを示すのではなく、順序を追って説明をしてくださり大変感謝いたします。 ご指摘の内容は、高速化という観点で全て知らない内容でしたので、勉強になりました。 なお、ご教授頂いたソースを一部OpenCVとすることによりさらに速くなりました。 yi=[np.ones_like(y)] for i in range (1,5): yi.append(yi[-1]*y) zj=[np.ones_like(z)] for j in range (1,5): zj.append(zj[-1]*z) を yi=[cv2.pow(y, i) for i in range(5)] zj=[cv2.pow(z, j) for j in range(5)]
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問