【问题标题】:How can I significantly improve the speed of nested loops?如何显着提高嵌套循环的速度?
【发布时间】:2021-09-06 17:36:56
【问题描述】:

我尝试过在线搜索,但没有发现任何有用的东西

我目前正在尝试遍历多个嵌套循环以查找值:

for i in range(10):
    val = 0
    for j in range(10):
        for k in range(10):
            for l in range(10):
                for m in range(10):
                    for n in range(10):
                        if i != j:
                            val += x[k, l, m, n] * r[k, i] * r[l, i] * r[m, j] * r[n, j]
    values.append(val)

其中x(10, 10, 10, 10) 数组,r(10, 10) 数组。

我觉得应该有一个比遍历它们更快的解决方案。

如何加快这些嵌套循环的速度?

编辑:

我正在寻找一种方法来加快嵌套循环或重写脚本以提高速度。

【问题讨论】:

  • 如果你唯一的条件是if i != j:,你不应该在for j循环中做if i == j: continue吗?
  • 这似乎是应该用einsum 表达的东西,但我真的不知道如何实际使用该功能,哈哈。 numpy.org/doc/stable/reference/generated/numpy.einsum.html 这到底是在计算什么?使用itertools.product 可以直观地简化代码的一种简单方法,但这不会对性能产生很大影响。
  • 如果您能解释一下您的代码的作用,将会很有帮助。可以概括为矩阵运算吗?
  • @AKX 这是真的。好点
  • @user16573587 如果您不接受其中一些可能是通用操作的想法,并且它必须是一个 6 层嵌套循环,那么明确的答案是“你注定要失败”。我猜你可以尝试用numba 编译它,但我真的建议你先重新考虑这个方法

标签: python performance numpy


【解决方案1】:

计算示例:

In [76]: x = np.random.rand( 10, 10, 10, 10); r=np.random.rand(10, 10 )
In [77]: values=[]
    ...: for i in range(10):
    ...:     val = 0
    ...:     for j in range(10):
    ...:         for k in range(10):
    ...:             for l in range(10):
    ...:                 for m in range(10):
    ...:                     for n in range(10):
    ...:                         if i != j:
    ...:                             val += x[k, l, m, n] * r[k, i] * r[l, i] * r[m, j] * r[n, j]
    ...:     values.append(val)
    ...: 

我没有计时,但有明显的滞后。

In [78]: values
Out[78]: 
[2405.069869674815,
 1906.3583547360959,
 1934.09600942877,
 1583.4551649741236,
 2441.8509094338756,
 2018.3416769619837,
 2508.333484796075,
 1664.3574504516193,
 2889.949142416882,
 1914.4720528808432]

用广播数组替换内部 4 个循环:

In [79]: values=[]
    ...: for i in range(10):
    ...:     val = 0
    ...:     for j in range(10):
    ...:        if i != j:
    ...:           temp = x *  r[:, i,None,None,None] * r[:, i,None,None] * r[:, j,None] * r[:, j]
    ...:           val += temp.sum()
    ...:     values.append(val)
    ...: 
    ...: 
In [80]: values
Out[80]: 
[2405.0698696748414,
 1906.3583547360881,
 1934.0960094287668,
 1583.4551649741336,
 2441.8509094339124,
 2018.3416769619737,
 2508.3334847960673,
 1664.3574504516007,
 2889.9491424168923,
 1914.4720528808484]

相同的值,而且肯定更快。

再想一想,我可能可以替换 ij 循环,但 i!=j 步骤使这有点困难。但这是很好的第一步。

%%timeit 循环:

1.22 s ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
10.8 ms ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

如果我定义一个效用函数:

def foo(x,i,j):
    temp =  x *  r[:, i,None,None,None] * r[:, i,None,None] * r[:, j,None] * r[:, j]
    return temp.sum()

然后

[sum([foo(x,i,j) for j in range(10)])-foo(x,i,i) for i in range(10)]

在没有i!=j 测试的情况下生成值。它有点慢,但我可以调整foo 来处理j 的数组值,从而消除j 循环。但稍后会详细介绍。

【讨论】:

【解决方案2】:

您必须注意的第一点是通过间接访问来访问数组中的成员。 例如,在第三个嵌套循环中添加 rki = r[k, i] 并在总和中直接使用 Rki。

【讨论】:

  • 你有没有讨论通过间接引用来提高速度的参考?
  • r[k, i] 已经是间接引用。您访问二维数组的元素。您的起点是数组的头部。但是这个值已经在第三个循环中设置了。如果您执行 rki=r[k, i] 并使用 rki,您将能够更有效地访问内存(或者 python 解释器正在为您优化)。还有一些这样的优化可能性。在每一步对 x 数组进行切片也可能会有所帮助。
【解决方案3】:

减少间接访问的次数会有所帮助。 例如,在第三个嵌套循环中添加 rki = r[k, i] 并在 sum 中直接使用 Rki...等等。

【讨论】:

    猜你喜欢
    • 2018-12-29
    • 2021-07-31
    • 2020-11-19
    • 2014-01-17
    • 1970-01-01
    • 2020-07-20
    • 2017-10-06
    • 2014-01-20
    • 2015-03-24
    相关资源
    最近更新 更多