2

私は Python に比較的慣れていないので、大規模な多次元配列を回転させるための最適化されたコードを探しています。次のコードでは、16X600000 32 ビットの浮動小数点多次元配列があり、タイマーによると、クアッド コアの acer Windows 8 タブレットでコンテンツを回転させるのに約 30 ミリ秒かかります。配列の回転に必要な時間を短縮できる場合は、いくつかの Cython ルーチンまたは同様のものを使用することを検討していました。

最終的に、このコードは、VisPy パッケージに基づく高速データ プロット グラフの y 軸値を格納するために使用され、32 ビット float 配列が OpenGL ルーチンに渡されます。できれば1ms以下を実現したいです。

コメント、推奨事項、またはサンプル コードをいただければ幸いです。

import sys, timeit
from threading import Thread
from PyQt4 import  QtGui
import numpy as np

m = 16              # Number of signals.
n = 600000          # Number of samples per signal.
y = 0.0 * np.random.randn(m, n).astype(np.float32) 
Running = False

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.button = QtGui.QPushButton('Start', self)
        self.button.clicked.connect(self.handleButton)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.button)

    def handleButton(self):
        global Running, thread, thrTest
        if Running == True:
            Running = False
            self.button.setText('Start')
            thrTest.isRunning = False
            print ('stop')
        else:
            Running = True
            self.button.setText('Stop')
            thrTest = testThread()
            thread = Thread(target=thrTest.run, daemon=True )
            thread.start()
            print ("Start")   

class testThread(Thread):
    def __init__(self):
        self.isRunning = True
    def run(self):
        print('Test: Thread Started')
        while self.isRunning == True:
            start_time = timeit.default_timer()
            y[:, :-1] = y[:, 1:]
            elapsed = timeit.default_timer() - start_time
            print ('Time (s)= ' + str(elapsed))
        print('Test: Closed Thread')


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

アップデート

私がやろうとしていることについて正確に混乱していると思うので、もう少し詳しく説明しようと思います.

最終的な目標は、信号値を表すグラフに線を引く、高速なリアルタイム データ ロギング デバイスを用意することです。複数のチャネルと、少なくとも 1 ミリ秒のサンプリング レートと、可能な限り多くの録音時間が存在します。この VisPy の例から始めました。新しいデータを配列に書き込み、それを OpenGL に送信する例のコードは、一番下のOn_Timer関数にあります。このコードを少し変更して、OpenGL キャンバスを Qt gui に統合し、イーサネット ソケットを介して Arduino Mega からデータを取得するコードを追加しました。

現在、サンプリングレート約1ms、フレームレート約30Hz、記録時間約14秒で16ラインのリアルタイムグラフを作成できます。チャネル数または記録の長さをこれ以上増やそうとすると、イーサネット ポートから 1 ミリ秒で入ってくるデータの流れに追いつけないため、プログラムが動作を停止します。

y[:, :-1] = y[:, 1:]これについて私が見つけることができる最大の原因は、ルーチンを使用してデータ バッファー シフトを完了するのにかかる時間です。もともと私は、誰かが同じことをより効率的な方法で行う方法を知っていることを期待して、この関数が時間を計っているベンチマーク コードを提出しました。この行の目的は、配列全体を 1 インデックス左にシフトし、次のコード行で右側の最初のスロットに新しいデータを書き込むことです。

以下に、変更したグラフ更新ルーチンを示します。最初にキューから新しいデータを取得して一時配列にアンパックし、次にメイン バッファー配列の内容をシフトし、最後に新しいデータをメイン配列の最後のスロットにコピーします。キューが空になると、OpenGL が表示を更新するように update 関数が呼び出されます。

def on_timer(self, event):
    """Add some data at the end of each signal (real-time signals)."""
    k=1
    s = struct.Struct('>16H')
    AdrArray =  0.0 * np.random.randn(16,1).astype(np.float32)
    if not q.qsize() == 0:
        while q.qsize() > 0:
            print (q.qsize())
            print ('iin ' + str(datetime.datetime.now()))
            AdrArray[:,0]= s.unpack_from(q.get(), offset=4)
            y[:, :-1] = y[:, 1:]
            y[:, -1:] = .002*AdrArray 
            print ('out ' + str(datetime.datetime.now()))
        self.program['a_position'].set_data(y.ravel().astype(np.float32))
        self.update()
4

3 に答える 3

1

他の人が言及しているように、CPU 上の配列をシフトしたいとは思わない。更新ごとに配列内のすべての要素を移動すると、常に遅くなります。ここでも Cython が役立つとは思いません。Numpy は既に最適化されているからです。

代わりに、頂点シェーダーでシフトを処理する必要があります。あなたが望むものに似ていると私が思うものの例はここにあります: http://vispy.org/examples/demo/gloo/realtime_signals.html

編集: 1 つのアプローチは、VBO を循環バッファーと見なすことです。「最も古い」位置に新しい値を追加し、頂点シェーダーでオフセット ユニフォームを使用して信号を正しい位置にマッピングします。同様の手法がhttp://vispy.org/examples/demo/gloo/spacy.htmlで使用されています

于 2015-01-08T09:22:04.960 に答える
1

glumpyの realtime-signals.py の例は、リング バッファーを実装しており、次のような場合に役立ちます。

https://github.com/glumpy/glumpy/blob/master/examples/realtime-signals.py

Vispy にも簡単に適用できます (すぐに適用される予定です)。秘訣は、16 個の信号を更新すると、16 個の float の連続したブロックが GPU にアップロードされるように、Fortran のようなレイアウトを使用することです。この例では、GPU メモリに収まる場合、16 x 600,000 データを処理する必要があります (250 fps で 16 x 100,000 のみをテスト)。

水平方向の画面解像度が限られているため、データ全体が表示されない可能性があることに注意してください (画面の幅が 1600 ピクセルの場合、最終的には信号ごとに 1600 個のデータしか表示されません)。

于 2015-01-19T17:04:28.863 に答える