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

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

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

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python

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

Q&A

解決済

2回答

228閲覧

少しややこしい配列累積加算ループの最適化

dream-20xx

総合スコア17

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python

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

0グッド

0クリップ

投稿2019/05/24 04:31

編集2019/05/24 18:06

質問内容

ボトルネックとなっている配列累積加算ループの処理速度向上を検討しております。
ロジックをイジったり色々試行してみましたが、期待する速度向上はできませんでした。
なお、Cythonでは2倍程度の速度向上がみられましたが、これより速くする方法を見出しているところです。徹夜して奮闘していますが、なかなか名案が浮かばなく...
大変お手数ですが、何かアドバイス等ありましたらお願いいたします。

試したこと

■ Cython ・・・2倍程度速度向上しましたがこれ以上の速度向上を望んでいます。
■ numba(jit) ・・・殆ど変化しませんでした。

今後試そうと思っていること

■ マルチプロセス化・・・ソースが煩雑になり可読性が失われるため最終手段と考えております...

サンプルコード

Python

1import numpy as np 2import time as t 3 4np.random.seed(0) 5a = np.random.rand(2, 2, 10000) 6b = np.random.rand(10000, 10000) 7c = (np.array(list(range(10000)))).astype('i') 8#x = np.zeros(10000).astype('i') 9x = (np.random.rand(10000) * 1000).astype('i') 10y = np.full(10000, 1000).astype('i') 11 12start = t.time() 13 14num = 10000 15img = np.zeros((10000, 10000)) 16 17# add 18out = [a[:, :, i] * b[c[i], 0] for i in range(num)] 19 20#for i in range(num): 21# img[x[i] : x[i] + 2 , y[i] : y[i] + 2 ] += a[:, :, i] * b[c[i], 0] 22for i in range(num): 23 img[x[i] : x[i] + 2 , y[i] : y[i] + 2 ] += out[i][:][:] 24 25print('time : ' + str(round((t.time() - start),5)) + ' [sec]')

サンプルコード(Cython化)

以下を実行して頂くとコンパイルおよび処理が実行されます。
$ python setup.py build_ext --inplace ; python main_test_20190524.py

test_20190524.pyx

Cython

1import cython 2import numpy as np 3cimport numpy as np 4 5ctypedef np.int32_t np_int_t 6ctypedef np.float64_t np_float_t 7 8@cython.boundscheck(False) 9@cython.wraparound(False) 10 11cpdef np.ndarray[np_int_t, ndim=1] test_20190524( 12 np.ndarray[np_float_t, ndim=3] a, 13 np.ndarray[np_float_t, ndim=2] b, 14 np.ndarray[np_int_t, ndim=1] c, 15 np.ndarray[np_int_t, ndim=1] x, 16 np.ndarray[np_int_t, ndim=1] y): 17 18 cdef np.ndarray[np_float_t, ndim=2] img 19 cdef int i, num 20 21 num = 10000 22 img = np.zeros((10000, 10000)) 23 24  # add 25 out = [a[:, :, i] * b[c[i], 0] for i in range(num)] 26 27 #for i in range(num): 28 # img[x[i] : x[i] + 2 , y[i] : y[i] + 2 ] += a[:, :, i] * b[c[i], 0] 29 for i in range(num): 30 img[x[i] : x[i] + 2 , y[i] : y[i] + 2 ] += out[i][:][:] 31 32 return img

main_test_20190524.py

Python

1import test_20190524 as cy 2import numpy as np 3import time as t 4 5def main(): 6 np.random.seed(0) 7 a = np.random.rand(2, 2, 10000) 8 b = np.random.rand(10000, 10000) 9 c = (np.array(list(range(10000)))).astype('i') 10 x = (np.random.rand(10000) * 1000).astype('i') 11 y = np.full(10000, 1000).astype('i') 12 13 start = t.time() 14 test = cy.test_20190524(a, b, c, x, y) 15 print('time : ' + str(round((t.time() - start),5)) + ' [sec]') 16 17if __name__ == "__main__": 18 main()

setup.py

Python

1from distutils.core import setup 2from distutils.extension import Extension 3from Cython.Distutils import build_ext 4 5setup( 6 cmdclass = {'build_ext': build_ext}, 7 ext_modules = [Extension("test_20190524", ["test_20190524.pyx"])] 8)

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

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

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

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

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

MasashiKimura

2019/05/24 04:46

これだと条件がわからないので高速化しようがないとおもいます。 x[i], y[i] は全部ゼロなので、 img[0:2, 0:2] だけを計算すれば高速化します。という答えになってしまいます。
dream-20xx

2019/05/24 04:58

ご回答ありがとうございます! 失礼いたしました。サンプルプログラム作成ミスです。x[i]はゼロでないようにしました。y[i]は全て1000となります。
MasashiKimura

2019/05/24 05:01

計算結果のprint(img)が全部ゼロです。これで良いのでしょうか?
dream-20xx

2019/05/24 05:09

配列の多くの部分がゼロとなっているのですが、部分的には数値が代入されていると思います。 print(img[0]))で確認すると数値がでてきました。質問内容を勘違いしていたらすみません。
dream-20xx

2019/05/24 05:11

少々お待ちください。確認中です。勘違いしているかもしません。
dream-20xx

2019/05/24 05:18

やはり、ゼロが出る場合が多いですが、配列には値が入っていることを確認いたしました。ややこしい代入式で申し訳ございません。
hayataka2049

2019/05/24 05:40

いちおうcythonのも載せてみてください。
dream-20xx

2019/05/24 05:52

cythonコードを質問欄に追記いたしました。
dream-20xx

2019/05/24 05:54

すみません、元のサンプルコードと配列の要素数を合わせます...
dream-20xx

2019/05/24 06:03

cythonですが、元のサンプルコードと配列の要素数をあわせました。
ozwk

2019/05/24 08:02 編集

b[c[i], 0]ってありますが cが0,1,2,...ですし、bが2次元である意味もなさそうなので bを1次元にしてb[i]だと駄目なんですか?
dream-20xx

2019/05/24 08:13

説明不足でした。実は、このサンプルコードはさらにループされるため、本当はb[c[i], 0]はb[c[i], j]です。jは10000回程ループします。今回質問するにあたり、簡単にするためjのところを0にしており、上位のループは除いております。このサンプルコードを10000回ループするのでボトルネックとなっております。上位の10000回ループを無くすことは今のところ無理なので、今回のサンプルコードを速くして対処したい所存です。
dream-20xx

2019/05/24 08:21

一部内包表記したところわずかに速くなりましたので、コードを修正させて頂きました。
bsdfan

2019/05/24 08:49

outは内包表記ではなく out = a * b[c, 0] として、ループ内では += out[:, :, i] とすればいいと思います。 (編集のあと+=から=だけになってます)
dream-20xx

2019/05/24 18:10

返信遅くなり申し訳ありません。ご指摘ありがとうございます! +=の件は修正しました。 out = a * b[c, 0]と+= out[:, :, i]はやってみましたが私の実装の仕方が悪いのかエラーになります。下記回答で高速化して頂いている方もいるのでこちらと合わせて確認いたします。現在余裕がなく、明日の夜から確認させていただきます。ありがとうございます。
dream-20xx

2019/05/26 11:08

ご回答いただいた方のコードを実行したところ私の環境で以下でした。 元コード:0.13秒 jit  :0.003秒 Cython:0.002秒 Cythonで最適化された方をベストアンサーに選ばさせていただきました。
guest

回答2

0

numbaのjitでほとんど変化なしとのことですが、jitのコンパイルの時間も含めてということでしょうか?

コンパイルの時間をのぞくと、私の環境(Windows10, Anaconda)では一桁近く速くなりました。(0.043 [sec] → 0.005 [sec])

ご参考まで。

python

1import time as t 2import numpy as np 3import numba 4 5np.random.seed(0) 6a = np.random.rand(2, 2, 10000) 7b = np.random.rand(10000, 10000) 8c = (np.array(list(range(10000)))).astype('i') 9x = (np.random.rand(10000) * 1000).astype('i') 10y = np.full(10000, 1000).astype('i') 11 12@numba.jit((numba.int64, numba.float64[:,:], numba.float64[:,:,:], numba.int32[:], numba.int32[:]), nopython=True) 13def func(num, img, d, x, y): 14 for i in range(num): 15 img[x[i] : x[i] + 2 , y[i] : y[i] + 2] += d[:, :, i] 16 return img 17 18start = t.time() 19 20num = 10000 21img = np.zeros((10000, 10000)) 22d = a * b[c, 0] 23 24img = func(num, img, d, x, y) 25 26print('time : ' + str(round((t.time() - start),5)) + ' [sec]')

投稿2019/05/25 01:06

bsdfan

総合スコア4560

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

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

dream-20xx

2019/05/25 17:13 編集

ありがとうございます!@numba.jitのあとに型を指定することは知りませんでした。。。 処理速度が殆ど変わらなかったのはそのせいでした。コードを載せておくべきでした。大変失礼いたしました。勉強になりました。 確かに一桁ほど処理速度が向上したことを確認しました。素晴らしいです。ありがとうございます。Cython化とどちらが速いかを比べてみます。 なお、自宅ではCython実行環境を整えておらず、確認は少々お待ちください。(自宅ではPyCharmを使用しているのですが、Cython実行環境を整えるのがうまくいきません。なので、VirtualBoxにanacondaを入れて環境は整えましたが、今度は実行時にメモリが足りずエラー。なので、Windows10とUbuntuとのデュアルブート環境を一休みしてから整えます。長文すみません。)
dream-20xx

2019/05/26 11:08

環境構築に手間取りましたが、確認できました。 私の環境での処理速度の結果は以下のとおりでした。 元コード:0.13秒 jit  :0.003秒 Cython:0.002秒 十分な高速化ができました!ありがとうございました!! すみません、最速のCythonでの最適化された方をベストアンサーに選ばさせていただきます。 ただ、jitの方が実装が楽なのでCythonとの時間差が問題なければjitを採用する可能性大です。
guest

0

ベストアンサー

cython版で、怪しい部分を多少修正し、スライスでアクセスするのをやめてループに展開したら元のコード(cython版)に比べて5倍ほど速くなりましたので、報告します。

main_test_20190524.py

python

1import test_20190524 as cy 2import numpy as np 3import time as t 4 5def main(): 6 np.random.seed(0) 7 a = np.random.rand(2, 2, 10000) 8 b = np.random.rand(10000, 10000) 9 c = (np.array(list(range(10000)))).astype('i') 10 x = (np.random.rand(10000) * 1000).astype('i') 11 y = np.full(10000, 1000).astype('i') 12 13 start = t.time() 14 result1 = cy.test_20190524_origin(a, b, c, x, y) 15 print('time : ' + str(round((t.time() - start),5)) + ' [sec]') 16 17 start = t.time() 18 result2 = cy.test_20190524_changed(a, b, c, x, y) 19 print('time : ' + str(round((t.time() - start),5)) + ' [sec]') 20 21 start = t.time() 22 result3 = cy.test_20190524_changed2(a, b, c, x, y) 23 print('time : ' + str(round((t.time() - start),5)) + ' [sec]') 24 25 26 print((result1 == result2).all() and 27 (result2 == result3).all()) 28 29if __name__ == "__main__": 30 main() 31""" => 32time : 0.09572 [sec] 33time : 0.01765 [sec] 34time : 0.01645 [sec] 35True 36""" 37 38

test_20190524.pyx

cython

1import cython 2import numpy as np 3cimport numpy as np 4 5ctypedef np.int32_t np_int_t 6ctypedef np.float64_t np_float_t 7 8@cython.boundscheck(False) 9@cython.wraparound(False) 10cpdef np.ndarray[np_float_t, ndim=2] test_20190524_changed( 11 np.ndarray[np_float_t, ndim=3] a, 12 np.ndarray[np_float_t, ndim=2] b, 13 np.ndarray[np_int_t, ndim=1] c, 14 np.ndarray[np_int_t, ndim=1] x, 15 np.ndarray[np_int_t, ndim=1] y): 16 17 cdef np.ndarray[np_float_t, ndim=2] img 18 cdef int i, j, k, num 19 20 num = 10000 21 img = np.zeros((10000, 10000)) 22 23 for i in range(num): 24 for j in range(2): 25 for k in range(2): 26 img[x[i] + j , y[i] + k] += a[j, k, i] * b[c[i], 0] 27 return img 28 29@cython.boundscheck(False) 30@cython.wraparound(False) 31cpdef np.ndarray[np_float_t, ndim=2] test_20190524_changed2( 32 np.ndarray[np_float_t, ndim=3] a, 33 np.ndarray[np_float_t, ndim=2] b, 34 np.ndarray[np_int_t, ndim=1] c, 35 np.ndarray[np_int_t, ndim=1] x, 36 np.ndarray[np_int_t, ndim=1] y): 37 38 cdef np.ndarray[np_float_t, ndim=3] d 39 cdef np.ndarray[np_float_t, ndim=2] img 40 cdef int i, j, k, num 41 42 num = 10000 43 44 d = a * b[c, 0] 45 46 img = np.zeros((10000, 10000)) 47 48 for i in range(num): 49 for j in range(2): 50 for k in range(2): 51 img[x[i] + j , y[i] + k] += d[j, k, i] 52 return img 53 54@cython.boundscheck(False) 55@cython.wraparound(False) 56cpdef np.ndarray[np_int_t, ndim=1] test_20190524_origin( 57 np.ndarray[np_float_t, ndim=3] a, 58 np.ndarray[np_float_t, ndim=2] b, 59 np.ndarray[np_int_t, ndim=1] c, 60 np.ndarray[np_int_t, ndim=1] x, 61 np.ndarray[np_int_t, ndim=1] y): 62 63 cdef np.ndarray[np_float_t, ndim=2] img 64 cdef int i, num 65 66 num = 10000 67 img = np.zeros((10000, 10000)) 68 69 out = [a[:, :, i] * b[c[i], 0] for i in range(num)] 70 71 for i in range(num): 72 img[x[i] : x[i] + 2 , y[i] : y[i] + 2 ] += out[i][:][:] 73 74 return img 75

投稿2019/05/24 10:18

編集2019/05/25 17:36
hayataka2049

総合スコア30933

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

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

dream-20xx

2019/05/24 18:12 編集

ありがとうございます!ちょっと余裕がないため今日の夜に確認させていただきます!!取り急ぎ、お礼まで。改めてご返信させていただきます。
dream-20xx

2019/05/25 17:18 編集

確認が遅れております、申し訳ございません。自宅でのCython実行環境構築に戸惑っており少々お待ちください。少し寝てから作業します。(なお、自宅ではWindows環境でPyCharmを使用しているのですが、Cython実行環境を整えるのがうまくいきません。これまでのCythonの実行は仕事場(Linuxサーバ)で行っておりました。自宅ではWindows環境です。なので、VirtualBoxにanacondaを入れて環境は整えましたが、今度は実行時にメモリが足りずエラー。しょうがないので、Windows10とUbuntuとのデュアルブート環境を一休みしてから整えます。長文すみません。)
hayataka2049

2019/05/25 17:50

べつに月曜日でもいいんじゃないでしょうか。
dream-20xx

2019/05/26 02:28

疲れで10時間ほど寝てしましました。。そうですね。ちゃちゃっとできそうなら今日中にやってしまいますが、無理そうなら明日仕事場で確認します。ありがとうございます。
dream-20xx

2019/05/26 11:09

環境構築に手間取りましたが、確認できました。 私の環境での処理速度の結果は以下のとおりでした。 元コード:0.13秒 jit  :0.003秒 Cython:0.002秒 十分な高速化ができました!ありがとうございました!! Cythonで最適化されたものが最速でしたのでベストアンサーに選ばさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問