Python 旋转大阵列,尽可能快

Python 旋转大阵列,尽可能快,python,arrays,opengl,numpy,vispy,Python,Arrays,Opengl,Numpy,Vispy,我对Python比较陌生,正在寻找用于旋转大型多维数组的最佳优化代码。在下面的代码中,我有一个16x60000032位浮点多维数组,根据计时器,在我的四核acer windows 8平板电脑上旋转内容大约需要30毫秒。如果可以减少旋转阵列所需的时间,我正在考虑使用一些Cython例程或类似的程序 最终,该代码将用于存储基于VisPy软件包的高速数据绘图图形的y轴值,32位浮点数组将传递给OpenGL例程。如果可能的话,我希望达到1ms以下 如有任何意见、建议或示例代码,将不胜感激 import

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

最终,该代码将用于存储基于VisPy软件包的高速数据绘图图形的y轴值,32位浮点数组将传递给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_())
更新 我想对于我到底想做什么有些困惑,所以我会尝试更好地解释

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

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

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

下面您可以看到我修改的图形更新例程。首先,它从队列中获取新数据并将其解压到一个临时数组中,然后移动主缓冲区数组的内容,最后将新数据复制到主数组的最后一个插槽中。一旦队列为空,它将调用更新函数,以便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()

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

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)
尽管从速度上看,这只对小型阵列有帮助。对于较大的,您的覆盖分配更快

我还建议使用转置,在
轴=0
上滚动。对于
order='C'
数组,行的值构成一个连续块


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

正如其他人所提到的,我认为您不想移动CPU上的数组。每次更新时移动数组中的所有元素总是很慢。我也不希望Cython在这方面有所帮助,因为Numpy已经是非常理想的了

相反,您需要注意顶点着色器中的移动。下面是一个我认为与您想要的类似的示例:


<>编辑:一种方法是考虑VBO为循环缓冲区。在“最旧”位置添加新值,并在顶点着色器中使用偏移统一将信号映射到正确位置。中的realtime-signals.py示例使用了类似的技术,它实现了一个环形缓冲区,可能有助于您:

它也很容易适应vispy(并将很快适应)。诀窍是使用类似Fortran的布局,这样更新16个信号会导致将16个浮点的连续块上载到GPU。如果您的16 x 600000数据适合GPU内存,则该示例应能处理该数据(仅以每秒250帧的速度测试16 x 100000)


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

是否确实需要旋转数据?也许你可以通过滑动窗口来查看数据。嗨,詹妮,你可能是对的,但我无法想象如何做到这一点。我已经测试了几个用于绘图的python包(pyqtgraph、galry、Vispy),发现Vispy是最有前途和最活跃的解决方案。因此,我的最终目标是能够以每通道1ms的采样率显示尽可能多通道的折线图。在我看来,获得最佳性能的唯一真正答案是使用这些基于openGL的包中的一个并在其基础上进行构建。目前,我有一个解决方案,使用此代码中旋转阵列的部分这是16x18000,基本上提供16个数据通道,采样率为1.0ms,相当于18秒的数据,并且