在Python中从浮点元组列表构建C数组的最快方法是什么?
背景:我的Python代码将二维顶点数组传递给OpenGL。
我测试了两种方法,一种是用ctypes,另一种是用struct,后者的速度快了两倍多。
from random import random
points = [(random(), random()) for _ in xrange(1000)]
from ctypes import c_float
def array_ctypes(points):
n = len(points)
return n, (c_float*(2*n))(*[u for point in points for u in point])
from struct import pack
def array_struct(points):
n = len(points)
return n, pack("f"*2*n, *[u for point in points for u in point])
还有其他的替代方案吗?有没有什么建议可以加速这段代码(没错,这确实是我代码中的一个瓶颈)?
5 个回答
我碰到一个新的想法。现在没时间去测试它,但如果有人有时间的话,可以试试:
# untested, but I'm fairly confident it runs
# using 'flattened points' list, i.e. a list of n*2 floats
points = [random() for _ in xrange(1000 * 2)]
c_array = c_float * len(points * 2)
c_array[:] = points
首先,我们创建一个ctypes数组,但不填充它。然后我们用切片的方式来填充它。比我聪明的人告诉我,这样给切片赋值可能会提高性能。这样做可以让我们直接在赋值的右边使用一个列表或可迭代对象,而不需要用*iterable的语法,这样会避免对可迭代对象进行一些中间处理。我猜这就是在创建pyglet的Batches时发生的事情。
你可以只创建一次c_array,然后每次points列表改变时,只需重新赋值(上面代码的最后一行)。
可能还有另一种写法,可以接受原始的points定义(一个包含(x,y)元组的列表)。像这样:
# very untested, likely contains errors
# using a list of n tuples of two floats
points = [(random(), random()) for _ in xrange(1000)]
c_array = c_float * len(points * 2)
c_array[:] = chain(p for p in points)
你可以把numpy数组直接传给PyOpenGL,而不会增加任何额外的负担。(numpy数组的data
属性其实是一个缓冲区,它指向底层的C数据结构,这个结构包含了和你正在构建的数组相同的信息)
import numpy as np
def array_numpy(points):
n = len(points)
return n, np.array(points, dtype=np.float32)
在我的电脑上,这种方法比基于struct
的方法快大约40%。
你可以试试Cython。对我来说,这样做的效果是:
function usec per loop:
Python Cython
array_ctypes 1370 1220
array_struct 384 249
array_numpy 336 339
在我的硬件上(一台运行Windows XP的老笔记本),Numpy的性能提升只有15%,而Cython的提升大约是35%(而且在你的分发代码中不需要额外的依赖)。
如果你可以放宽要求,不一定要每个点都是浮点数的元组,可以把'points'简单地做成一个扁平的浮点数列表:
def array_struct_flat(points):
n = len(points)
return pack(
"f"*n,
*[
coord
for coord in points
]
)
points = [random() for _ in xrange(1000 * 2)]
这样得到的结果是一样的,但运行时间会进一步减少:
function usec per loop:
Python Cython
array_struct_flat 157
Cython可能还可以做得更好,如果有比我更聪明的人愿意在代码中添加静态类型声明的话。(运行'cython -a test.pyx'是非常有用的,它会生成一个html文件,告诉你代码中最慢的部分(黄色)是哪些普通的Python代码,以及哪些部分已经转换成纯C代码(白色)。这就是我把上面的代码分成这么多行的原因,因为颜色是按行显示的,分开写可以帮助你更好地理解。)
完整的Cython使用说明在这里: http://docs.cython.org/src/quickstart/build.html
Cython可能会在你的整个代码库中带来类似的性能提升,在理想情况下,应用适当的静态类型后,速度可以提高十倍甚至一百倍。