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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

3851閲覧

Python3 処理高速化を実現したい

dream-20xx

総合スコア17

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2019/04/10 06:56

編集2019/04/10 09:04

前提・実現したいこと

Python3にてプログラムをつくっております。処理高速化を実現したいです。
numpyを使用していますが、ループ回数が膨大なので遅いです。
下記サンプルコードは、私の環境では0.45秒です。
これでも十分速いのかもしれませんが、あと10倍以上は速くしたいです。
良いアドバイスはありますでしょうか?

ソースコード例

python

1import numpy as np 2import time as t 3 4start = t.time() 5sl = np.random.rand(10000) 6ang = np.random.rand(10000) 7 8def main(): 9 10 n = sl.size 11 msk = np.ones(n) 12 angmax = ang[0] 13 14 for i in range(1, n): 15 #maxidx = np.argmax([angmax, ang[i]]) 16 #angmax = np.max([angmax, ang[i]]) 17 #if maxidx == 0: msk[i] = 1 18 if angmax > ang[i]: 19 msk[i] = 3 20 else: 21 angmax = ang[i] 22 23 csl = sl[0:i+1] 24 ply = np.where(csl > sl[i]) 25 kly = np.size(ply) 26 if kly > 0: msk[ply] = 2 27 28 print(msk) 29 print('time : ' + str(round((t.time() - start),3)) + ' [sec]') 30 31if __name__ == "__main__": 32 main()

試したこと

Cythonは試してみましたが殆ど速くなりませんでした(要検証)。
jitも試しましたが速くなりませんでした。
for文を除いてOpenCVで書ければ速くなりそうですが、
具体的な記述方法がわからない状況です。

Cython

1import cython 2import numpy as np 3cimport numpy as np 4 5sl = np.random.rand(10000) 6ang = np.random.rand(10000) 7 8cdef int n = sl.size 9cdef np.ndarray msk = np.ones(n) 10cdef float angmax = ang[0] 11 12for i in range(1, n): 13 if angmax > ang[i]: 14 msk[i] = 3 15 else: 16 angmax = ang[i] 17 18 csl = sl[0:i+1] 19 ply = np.where(csl > sl[i]) 20 kly = np.size(ply) 21 if kly > 0: msk[ply] = 2

補足情報

GPUマシンはありますので、例えばcupyを使用して速くなるのであれば試してみたいです。
もしcupyでの記述方法がわかればご教授頂けると幸いです。

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

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

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

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

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

tiitoi

2019/04/10 07:00

質問欄のコードの記述は markdown を使いましょう。
dream-20xx

2019/04/10 07:07

ありがとうございます。すぐに気づいたので修正しております。
KojiDoi

2019/04/10 07:21

angmax = np.max([angmax, ang[i]]) これは何のためにここにあるのでしょうか。必要なく最大値の計算を繰り返しているように見えるのですが。
dream-20xx

2019/04/10 07:28

ひとつ前の配列要素と大小比較しているだけですので、np.maxを使う必要はないですね。すみません、実は気づいていましたがnumpyは速い(はず)なのでそのまま修正せずにいました。修正して速度をみてみます。
tachikoma

2019/04/10 07:29

アルゴリズムの説明があるとなおいいですね。
tiitoi

2019/04/10 07:32

元のやりたい処理を数式もしくは言葉で書いていただかないと、コード化した段階ですでに冗長になっている可能性があるので、改善の余地がないかどうかわからないですね。
dream-20xx

2019/04/10 07:51

ありがとうございます。処理の説明ですが、実は、元は別の方がIDLで書かれているのをそのままPythonで実装しなおしただけですので、私も現在ソースと睨めっこ中です。IDLで実装した人もこれで正しいのかわからないという状況のようです。説明ができそうであれば、可能な範囲で、回答させて頂く予定です(業務機密にひっかからない程度に)。少し時間かかるかもしれません。なお、非常に簡単に説明すると、衛星画像の影やレイオーバのサンプルコードです。かなり加工し、かつコメントは全て消しているので、なんのコードかわからないですよね。。すみません。
tiitoi

2019/04/10 08:09 編集

処理の概要が記載できないのであれば、KojiDoi さんが指摘されているような 「argmax() と max() をしているのは、計算量2倍なので冗長」というような指摘しかできないですね。 > Cで書いても速くならないと考えられます。 そんなことはないと思いますよ。 コンパイラの最適化も効きますし、for 文を使うようなアルゴリズムであれば、numpy も中身は C とはいえいろいろオーバーヘッドがあるので、C/C++ でアルゴリズムを書いたほうが全然早いです。 きちんとコードを書けば、Python の10倍ぐらい早くすることは全然可能です。
dream-20xx

2019/04/10 08:40 編集

ご指摘いただいた「argmax() と max()」の部分を修正しました。 0.2秒速くなりました。ありがとうございます。
dream-20xx

2019/04/10 08:22

また、Cでも書いて速度確認してみます。最近定時退社が厳しいので明日回答いたします。
dream-20xx

2019/04/11 04:25

ロジックの変更およびCythonにて約1万倍の高速化を実現できました! 変更なし:0.65秒 部分ロジック変更(python):0.45秒 全般ロジック変更(python):0.06秒 全般ロジック変更(Cython):0.00006秒 本当にありがとうございます!みなさんにベストアンサー付けたいところですが申し訳ないです。
guest

回答1

0

ベストアンサー

「Cythonは試してみましたが殆ど速くなりませんでした。」と書いてありますが、以下のようにすべて型を定義すると約2倍早くなりました。1万回の一重ループで内側はnumpyで処理していたのでそれぐらいのものだと思います。Cythonで書けばCよりも2倍以上遅いということはないと思います。

Cython

1import cython 2import numpy as np 3cimport numpy as np 4 5ctypedef np.float64_t np_float_t 6ctypedef np.int32_t np_int_t 7 8@cython.boundscheck(False) 9@cython.wraparound(False) 10cpdef np.ndarray[np_int_t, ndim=1] func1( 11 np.ndarray[np_float_t, ndim=1] sl, 12 np.ndarray[np_float_t, ndim=1] ang): 13 14 cdef np.ndarray[np_int_t, ndim=1] msk 15 cdef int i, j 16 cdef int n = len(sl) 17 cdef double angmax = ang[0] 18 19 msk = np.ones(n, dtype='int32') 20 for i in range(1, n): 21 if angmax > ang[i]: 22 msk[i] = 3 23 else: 24 angmax = ang[i] 25 26 for j in range(i): 27 if sl[j] > sl[i]: 28 msk[j] = 2 29 return msk

python

1import numpy as np 2import time as t 3 4def main() 5 start = t.time() 6 sl = np.random.rand(10000) 7 ang = np.random.rand(10000) 8 msk = func1(s1, ang) 9 print(msk) 10 print('time : ' + str(round((t.time() - start),3)) + ' [sec]') 11 12if __name__ == "__main__": 13 main()

計算の最後の部分は、要約すると配列の各要素でその要素より後ろにそれより小さい値があれば2にするということなので、以下のように計算方法を変更すれば1000倍ぐらい速くなります。このロジックが実際に使えるかどうかはわかりませんが、いろいろ方法を試す前にロジックを考えた方がいいと思います。

import cython import numpy as np cimport numpy as np ctypedef np.float64_t np_float_t ctypedef np.int32_t np_int_t @cython.boundscheck(False) @cython.wraparound(False) cpdef np.ndarray[np_int_t, ndim=1] func2( np.ndarray[np_float_t, ndim=1] sl, np.ndarray[np_float_t, ndim=1] ang): cdef np.ndarray[np_int_t, ndim=1] msk cdef int i, j cdef int n = len(sl) cdef double angmax = ang[0] cdef double slmin msk = np.ones(n, dtype='int32') for i in range(1, n): if angmax > ang[i]: msk[i] = 3 else: angmax = ang[i] slmin = sl[n - 1] for j in range(n - 1, -1, -1): if sl[j] > slmin: msk[j] = 2 else: slmin = sl[j] return msk

投稿2019/04/10 08:27

編集2019/04/10 12:23
YasuhiroNiji

総合スコア584

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

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

dream-20xx

2019/04/10 09:02 編集

ありがとうございます。確かにCythonについては素人なのでやり方が間違っている可能性が高いです。 Cythonコードを追記しました。 また、msk[i]への代入については元のソースコードから改変する際に数値を違うものにしてしまっていました。正しい値にもどしました。
dream-20xx

2019/04/11 04:24

夜遅くに投稿恐縮です。 ロジックの変更およびCythonにて約1万倍の高速化を実現できました! 変更なし:0.65秒 部分ロジック変更(python):0.45秒 全般ロジック変更(python):0.06秒 全般ロジック変更(Cython):0.00006秒 本当にありがとうございます!師匠と呼びたいくらいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問