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