【问题标题】:How to speed up for loop?如何加快for循环?
【发布时间】:2021-02-26 14:19:09
【问题描述】:

我正在尝试在 python 中加速这个 for 循环,其中 N 是 12000 左右的一个很大的数字。这个循环需要 20 多秒才能运行。计算 cossum 和 sinsum 的行似乎是这里的罪魁祸首。我该如何优化它以使其运行得更快?

(我对编码很陌生,因此非常感谢您提供简单的解释!谢谢)

for i in range(N): 

    cossum = 0
    sinsum = 0

    sq = k_array[i][0]**2 + k_array[i][1]**2 + k_array[i][2]**2
   
    if sq <= sqmax and sq>0:

        kvec_used = kvec_used + 1
        kfac[i] = np.exp(-sq/(4*beta**2))/(sq)
    
        for j in range(sites):
            cossum = cossum + q_array_initial[j]*np.cos(np.dot(k_array[i],r_cart_initial[j]))
            sinsum = sinsum + q_array_initial[j]*np.sin(np.dot(k_array[i],r_cart_initial[j]))

            cos_part = (abs(cossum))**2
            sin_part = (abs(sinsum))**2

【问题讨论】:

  • 作为建议,您可以尝试使用time 模块来确定循环的哪个部分平均花费的时间最长。然后,尝试优化这些块中的代码(例如,看看问题是否出在cossumsinsum。就目前而言,这个问题非常广泛。
  • 谢谢,我做了这个,发现大部分时间(大约 19 秒)都花在计算 cossum 和 sinsum 上

标签: python for-loop optimization


【解决方案1】:

您可以使用 Numpy 中的矢量化来删除代码中的所有 for 循环。我建议您在开始冒险时阅读Look Ma No For Loops

我以您的示例为例,并使用random 生成了一些垃圾数据来代替您的数组和变量。

import numpy as np
import random
import time

kvec_used = 0
k_array = []
sqmax = 10
beta = .5
kfac = []
sites = 3
q_array_initial = r_cart_initial = [.1, .2, .3]

for i in range(12000):
    temp = [random.random(), random.random(), random.random()]
    k_array.append(temp)
    kfac.append(random.random())

mytime = time.time()
for i in range(12000): 
    cossum = 0
    sinsum = 0

    sq = k_array[i][0]**2 + k_array[i][1]**2 + k_array[i][2]**2

    if sq <= sqmax and sq>0:

        kvec_used = kvec_used + 1
        kfac[i] = np.exp(-sq/(4*beta**2))/(sq)
    
        for j in range(sites):
            cossum = cossum + q_array_initial[j]*np.cos(np.dot(k_array[i],r_cart_initial[j]))
            sinsum = sinsum + q_array_initial[j]*np.sin(np.dot(k_array[i],r_cart_initial[j]))

            cos_part = (abs(cossum))**2
            sin_part = (abs(sinsum))**2

print("looped", time.time() - mytime)


mytime = time.time()

k_array = np.asarray(k_array)
sq = np.square(k_array[:, 0]) + np.square(k_array[:, 1]) + np.square(k_array[:, 2])
sq = sq[np.where(np.logical_and(sq[:] <= sqmax, sq[:] > 0))]
kvec = np.shape(sq)[0]
kfac = np.exp(-sq[:]/(4*beta**2))/(sq[:])

for j in range(sites):
    cossum = cossum + q_array_initial[j]*np.cos(np.dot(k_array[i],r_cart_initial[j]))
    sinsum = sinsum + q_array_initial[j]*np.sin(np.dot(k_array[i],r_cart_initial[j]))

    cos_part = (abs(cossum))**2
    sin_part = (abs(sinsum))**2

print("vectorized:", time.time() - mytime)

通过仅对第一个 for 循环进行矢量化,当我使用 time.time() 进行测量时,我的速度提高了约 100 倍。在我的机器上,这段代码的输出是:

looped 0.40552735328674316
vectorized: 0.004940986633300781

当你对第二个循环进行矢量化时,你应该能够加快速度。


编辑:我刚刚意识到您在原始问题中要求提供更多解释。我上面链接的文章详细解释了一些事情,并且是我第一次开始对大型数据集进行矢量化时使用的。但是,我可以提供一个超级简单的解释来帮助您了解这里:

当你有一个大数组时,在 Python 中运行for 循环实际上有点慢。 Numpy 使用了一些更高级的幕后代码,它们可以比在for 循环中顺序运行操作更快。对于新手学习这一点,您可以接受 Numpy 暂时是“神奇的”。这里最大的收获是,与其对数组的ith 元素进行操作,不如告诉Numpy 对整个事物进行操作,表示为myarray[:]。您可以将其扩展到多维数组。一个完整的二维数组是myarray[:, :]。如果(从电子表格的角度考虑)您只想对第 0 列和 2d 数组上的所有行进行操作,则变为 myarray[:, 0]

在这里,我使用了花哨的 Numpy 特定方法 wherelogical_and 来替换您的 if 条件。这是另一种向量化技术,它能够比迭代方法更快地处理条件语句。

我特意留下了第二个for 循环未翻译作为练习给你。没有人不吃苦就学!您在这里使用np.cosnp.sin 做得很好,但是您以迭代的方式使用它们。使用向量方法对结果向量的所有元素运行这些数学运算符。

【讨论】:

  • 很好的矢量化示例,有很好的参考支持。 +1 为你好先生
  • 谢谢-非常好的解释!我正在尝试第二个for 循环。我尝试将 [:] 用于k_array(在执行k_array = k_array[np.where...] 之后)和r_cart_initial,但它们的尺寸不同,所以它不起作用。然后我列出了k_arrayr_cart_initial 的索引组合列表,但是我对如何避免循环感到困惑。有什么提示吗?
  • k_array[np.where...] 步骤实际上删除了产生错误结果的行,这将改变数组的大小。也许尝试反转逻辑,以便将不正确的值替换为 n/a 而不是删除。另外,我假设q_array_initial 的大小与k_array 不同。在这种情况下,您将需要研究广播不同形状的数组的方法。
  • k_arrayq_array_initialr_cart_initial 的大小不同,广播是否允许我考虑k_array 和其他数组的所有组合? ——
  • 是的,但是您应该通过重复行在一个维度上创建一个等效大小的新数组来“作弊”。例如。对于像A = [[0, 1]]B = [[3, 4, 5]] 这样的一维数组,使用numpy repeat/tile 和朋友创建新数组,最终看起来像AA = [[0, 1, 0, 1, 0, 1]]BB = [[3, 4, 5, 3, 4, 5]]
【解决方案2】:

使用矢量化而不是 for 循环,除非你必须开发你的,否则你无法加快速度

【讨论】:

  • 你的句子看起来不完整。作为评论,它可能是可接受的边界,但肯定不能作为答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-29
相关资源
最近更新 更多