Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/353.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在numpy数组上的紧密双for循环中高效使用python生成器_Python_Performance_Numpy_Generator_Cython - Fatal编程技术网

在numpy数组上的紧密双for循环中高效使用python生成器

在numpy数组上的紧密双for循环中高效使用python生成器,python,performance,numpy,generator,cython,Python,Performance,Numpy,Generator,Cython,我的代码中的速度瓶颈是两个数组x和y的元素之间的紧密双for循环。提高性能的一个标准hpc技巧是分块执行循环,以便将缓存未命中降至最低。我试图使用python生成器来进行分块,但在外部for循环中不断重新创建已用过的生成器的需要正在扼杀我的运行时 问题: 有没有更智能的算法来构造适当的生成器来执行分块双for循环 具体说明: 我将创建两个虚拟数组x和y。为了便于说明,我会将它们保持简短,但实际上它们是包含~1e6个元素的numpy数组 x = np.array(['a', 'b', 'b', '

我的代码中的速度瓶颈是两个数组x和y的元素之间的紧密双for循环。提高性能的一个标准hpc技巧是分块执行循环,以便将缓存未命中降至最低。我试图使用python生成器来进行分块,但在外部for循环中不断重新创建已用过的生成器的需要正在扼杀我的运行时

问题:

有没有更智能的算法来构造适当的生成器来执行分块双for循环

具体说明:

我将创建两个虚拟数组x和y。为了便于说明,我会将它们保持简短,但实际上它们是包含~1e6个元素的numpy数组

x = np.array(['a', 'b', 'b', 'c', 'c', 'd'])
y = np.array(['e', 'f', 'f', 'g'])
简单的double for循环应该是:

for xletter in x:
    for yletter in y:
        # algebraic manipulations on x & y
现在,让我们使用生成器分块执行此循环:

chunk_size = 3
xchunk_gen = (x[i: i+chunk_size] for i in range(0, len(x), chunk_size))
for xchunk in xchunk_gen:
    ychunk_gen = (y[i: i+chunk_size] for i in range(0, len(y), chunk_size))
    for ychunk in ychunk_gen:
        for xletter in xchunk:
            for yletter in ychunk:
                # algebraic manipulations on x & y
请注意,为了实现此问题的生成器解决方案,我必须在外循环中不断重新创建ychunk_gen。由于y是一个大数组,这会扼杀我的运行时(对于~1e6个元素,在我的笔记本电脑上创建此生成器需要~20ms)

有没有一种方法可以让我更聪明地构造我的生成器来解决这个问题?还是有必要完全放弃发电机解决方案


(注意:在实践中,我使用cython来执行这个紧密循环,但不管怎样,以上所有内容都适用)

在我看来,生成器表达式的创建正在消耗您的运行时间,因为cython没有对其进行优化


一个更好的解决方案是使用。由于x和y的操作是代数的,它应该非常符合您的约束条件(numexpr可以做得更多)

您在xchunk循环中再次定义了
ychunk\u gen
。也许下面的速度会更快:

chunk_size = 3
xchunk_gen = (x[i: i+chunk_size] for i in xrange(0, len(x), chunk_size))

def ychunk_gen(some_dependency_on_outer_loop):
    # use some_dependency_on_outer_loop
    for i in xrange(0, len(y), chunk_size):
        yield y[i: i+chunk_size]

for xchunk in xchunk_gen:
    for ychunk in ychunk_gen(chunk_or_something_else):
        for xletter in xchunk:
            for yletter in ychunk:
                # algebraic manipulations on x & y
但也许还有更好的方法:

我假设
x
y
numpy
数组,因此您可以重塑数组的形状,然后在每一行中循环:

for xchunk in x.reshape((len(x)//chunk_size, chunk_size)):
    for ychunk in y.reshape((len(y)//chunk_size, chunk_size)):
        # the letter loops
在本文中,我了解到,如果您希望数据不被
重塑
复制,则应更改数据的
形状
-属性:

x.shape = len(x)//chunk_size, chunk_size 
y.shape = len(y)//chunk_size, chunk_size

itertools.tee
可以节省一定的时间:

y = np.arange(100)
def foo1(y):
   # create ygen each loop
   # py3 so range is a generator
   for j in range(100):
       ygen=(y[i:i+10] for i in range(0,1000,10))
       r = [x.sum() for x in ygen]
   return r

def foo3(y):
   # use tee to replicate the gen
   ygen=(y[i:i+10] for i in range(0,1000,10))
   ygens=itertools.tee(ygen,100)
   for g in ygens:
       r=[x.sum() for x in g]
   return r

In [1123]: timeit foo3(y)
10 loops, best of 3: 108 ms per loop
In [1125]: timeit foo1(y)
10 loops, best of 3: 144 ms per loop
但基于

自Cython 0.13以来,当可以将某些生成器表达式转换为内联循环并与内置循环结合使用时,就支持这些表达式,例如sum(x*2表示序列中的x)。从0.14开始,受支持的内置项为list()、set()、dict()、sum()、any()、all()、sorted()

我想知道cython对分块生成器表达式做了什么


在行上重新整形和迭代对时间没有多大帮助

def foo4(y):
   y2d=y.reshape(100,10)
   for _ in range(100):
       r=[x.sum() for x in y2d]
   return r

teed
生成器稍微慢一点。当然,这样的相对计时可能会随着数组大小而改变。

如果
x
y
是RAM中的列表,那么像您这样使用生成器根本没有任何好处。。。您也可以运行
counter=len(x)*len(y)
您是否尝试过列表理解而不是生成器表达式,并在外部循环之前创建两个块列表?此外,您能否告诉我们您在实际循环中实际在做什么,以及您的实际任务是什么?也许我们可以提供更好的帮助,如果我们看到全局。FWIW,在Python 2中,在创建大范围时,您应该使用
xrange()
而不是
range()
range()
必须构建一个列表,即使在生成器表达式中使用它
xrange()
不会,因为它本身实际上就是一个生成器。你不能用C风格的迭代直接模拟一个分块生成器吗?如果分块大小不除以数组长度,第二个numpy解决方案会起作用吗?看起来不是这样,因为重塑对数组长度的保留有很高的要求,但也许有什么我不明白的?你对形状的限制是正确的,但我认为这很容易解决?