大型阵列,旋转速度最快

2024-10-01 05:03:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我对Python比较陌生,正在寻找用于旋转大型多维数组的最佳优化代码。在下面的代码中,我有一个16X600000个32位浮点多维数组,根据计时器,在四核AcerWindows8平板电脑上旋转内容大约需要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和尽可能多的记录时间。我从this VisPy example开始。示例中将新数据写入数组并将其发送到OpenGL的代码位于底部附近的On_Timer函数中。我稍微修改了这段代码,将OpenGL画布集成到Qt gui中,并添加了一些代码,通过以太网套接字从Arduino Mega获取数据。在

目前,我可以制作一个16行的实时图形,采样率约为1ms,帧速率约为30Hz,记录时间约为14秒。如果我再尝试增加通道数或记录长度,程序将停止工作,因为它无法跟上通过以太网端口以1ms速度传入的数据流

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

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

^{pr2}$

Tags: 数据代码importselftrue图形defbutton
3条回答

你真的想要这个吗?它将值左移,填充最后一列

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左边有一个不同的填充

^{pr2}$

就其价值而言,roll的核心是:

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

你的特殊“卷”可以用类似的“take”复制

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'数组,行的值构成一个连续的块。

最大的时间消耗者是您必须(几乎)将阵列的所有内容从一个位置复制到另一个位置,或者复制到新阵列中,或者复制到它本身。如果数据在某种环形缓冲区中,您只需更改指针,而不必复制任何数据。

正如其他人所提到的,我不认为你想改变CPU上的数组。每次更新时移动数组中的所有元素总是很慢的。我也不指望Cython能帮上忙,因为Numpy已经是最好的了。

相反,您需要处理顶点着色器中的移动。 我认为与您想要的类似的例子如下:http://vispy.org/examples/demo/gloo/realtime_signals.html

编辑:一种方法是将VBO视为一个循环缓冲区。你可以在最旧的位置添加一个新的值。在http://vispy.org/examples/demo/gloo/spacy.html中使用了类似的技术

实时-信号.pyglumpy中的示例实现了一个环形缓冲区,可能有助于您:

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

它很容易适应(而且很快就会适应)vispy。诀窍是使用类似Fortran的布局,这样更新16个信号就可以将一个连续的16个浮点块上传到GPU。这个例子应该处理你的16×600000数据,如果它适合GPU内存(只测试了16x100000的250fps)。

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

相关问题 更多 >