2

我对 Python 比较陌生,正在寻找用于旋转大型多维数组的最佳优化代码。在下面的代码中,我有一个 16X600000 32 位浮点多维数组,根据计时器,旋转我的四核 acer windows 8 平板电脑上的内容大约需要 30 毫秒。如果可以减少旋转阵列所需的时间,我正在考虑使用一些 Cython 例程或类似的东西。

最终,代码将用于存储基于 VisPy 包的高速数据绘图图的 y 轴值,并将 32 位浮点数组传递给 OpenGL 例程。如果可能的话,我希望达到不到 1 毫秒。

任何评论、建议或示例代码将不胜感激。

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_())

更新

我想我到底要做什么有些困惑,所以我会尝试更好地解释一下。

最终目标是拥有一个快速的实时数据记录设备,该设备在表示信号值的图表上画线。将有多个通道和至少 1ms 的采样率和尽可能多的记录时间。我从这个 VisPy 示例开始。示例中将新数据写入数组并将其发送到 OpenGL 的On_Timer代码位于底部附近的函数中。我稍微修改了这段代码以将 OpenGL 画布集成到 Qt gui 中,并添加了一些代码以通过以太网套接字从 Arduino Mega 获取数据。

目前,我可以生成 16 条线的实时图表,采样率约为 1 毫秒,帧率约为 30 赫兹,记录时间约为 14 秒。如果我尝试再增加通道数或记录长度,程序将停止工作,因为它无法跟上 1ms 通过以太网端口进入的数据流。

我能找到的最大罪魁祸首是使用y[:, :-1] = y[:, 1:]例程完成数据缓冲区移位所需的时间。最初我提交了这个函数正在计时的基准代码,希望有人知道以更有效的方式做同样事情的方法。这一行的目的是将整个数组向左移动一个索引,然后在我的下一行代码中,我将新数据写入右侧的第一个槽。

您可以在下面看到我修改后的图形更新例程。首先它从队列中取出新数据并解包到一个临时数组中,然后移动主缓冲区数组的内容,最后将新数据复制到主数组的最后一个槽中。一旦队列为空,它就会调用更新函数,以便 OpenGL 更新显示。

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

编辑:一种方法是将您的 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 (并且很快就会适应)。诀窍是使用类似 Fortran 的布局,这样更新 16 个信号会导致将 16 个浮点的连续块上传到 GPU。如果该示例适合 GPU 内存,则该示例应处理您的 16 x 600,000 数据(仅在 250 fps 时测试了 16 x 100,000)。

请注意,由于您的水平屏幕分辨率有限,这可能会阻止您查看整个数据(如果您的屏幕为 1600 像素宽,最终每个信号将只显示 1600 个数据)。

于 2015-01-19T17:04:28.863 回答
1

你真的想要这个“卷”吗?它向左移动值,填充最后一列

In [179]: y = np.arange(15).reshape(3,5)
In [180]: y[:,:-1]=y[:,1:]
In [181]: y
Out[181]: 
array([[ 1,  2,  3,  4,  4],
       [ 6,  7,  8,  9,  9],
       [11, 12, 13, 14, 14]])

对于像这样的非标准“滚动”,不太可能有更快的速度。

np.roll左边有不同的填充

In [190]: np.roll(y,-1,1)
Out[190]: 
array([[ 1,  2,  3,  4,  0],
       [ 6,  7,  8,  9,  5],
       [11, 12, 13, 14, 10]])

对于它的价值,核心roll是:

indexes = concatenate((arange(n - shift, n), arange(n - shift)))
res = a.take(indexes, axis)

您的特定“滚动”可以用类似的“拍摄”复制

In [204]: indexes=np.concatenate([np.arange(1,y.shape[1]),[y.shape[1]-1]])
In [205]: y.take(indexes,1)

y[:,:-1]...比它更快,roll因为它没有创建新数组;相反,它只是覆盖现有的一部分。

take接受一个out参数,所以这是可能的:

y.take(indexes,1,y)

尽管在速度方面这仅对小型阵列有所帮助。对于大型的,您的覆盖分配更快。

我还建议查看使用转置,并在axis=0. 对于order='C'数组,一行的值形成一个连续的块。

最重要的消费者是您必须将(几乎)所有数组从一个位置复制到另一个位置,无论是在新数组中还是在其自身上。如果数据在某种环形缓冲区中,您可以只更改指针,而无需复制任何数据。

于 2015-01-07T22:00:17.767 回答