【问题标题】:How to make huge loops in python faster on mac?如何在mac上更快地在python中制作巨大的循环?
【发布时间】:2016-10-07 19:01:22
【问题描述】:

我是一名计算机科学专业的学生,​​我做的一些事情需要我在配备双核 i5 的 Macbook 上运行巨大的循环。有些循环需要 5-6 个小时才能完成,但它们只使用了我 25% 的 CPU。有没有办法让这个过程更快?我无法更改循环,但有没有办法让它们运行得更快?

谢谢

Mac OS 10.11 Python 2.7(我必须使用 2.7)和 Anaconda 上的 IDLE 或 Spyder

这是一个需要 15 分钟的示例代码:

def test_false_pos():
    sumA = [0] * 1000
    for test in range(1000):
        counter = 0
        bf = BloomFilter(4095,10)
        for i in range(600):
            bf.rand_inserts()
        for x in range(10000):
            randS = str(rnd.randint(0,10**8))
            if bf.lookup(randS):
                counter += 1
        sumA[test] = counter/10000.0
    avg = np.mean(sumA)
    return avg

【问题讨论】:

  • 你有 4 个处理器吗?
  • @StevenG:(S)他说,“Macbook with dual core i5”。
  • @tTIKA 请从here了解更多关于IteratorsGenerators的信息
  • 双核超线程 - 4 核。这就是为什么 25%。但是,是的,为了加快速度,您可以尝试以下方法:multiprocessing 或使用numpy
  • 不,它不是超线程的。只有物理 2 个核心

标签: python performance python-2.7 loops cpu-usage


【解决方案1】:

当然:Python 2.7 必须生成巨大的列表并浪费大量内存每次您使用range(<a huge number>)

尝试改用xrange 函数。它不会立即创建那个巨大的列表,它会懒惰地生成一个序列的成员。


但如果您使用 Python 3(这是 Python 的现代版本和未来版本),您会发现 range 比 Python 2 中的 xrange 更酷、更快。

【讨论】:

  • xrange 节省内存,它不一定会加快执行速度。您仍然必须在每次迭代时调用next;该调用只是计算下一个值,而不是从列表中提取它。
  • @chepner,好吧,即使分配如此大量的内存并用大量数据填充它只是为了循环它(这就是 Python 2 的range 所做的)浪费很多时间。
  • 10000 个整数并不是“巨大”的内存量。问题是每个布隆过滤器操作相对于循环开销需要多少时间。与任何事情一样,进行基准测试以找出答案。
  • 我刚刚尝试了 15 分钟示例代码的循环(其中包含 pass)。用了不到一秒钟。你真的认为这很明显吗?
  • @StefanPochmann 如果没有我的布隆过滤器课程,您是如何尝试的?我虽然那个手术是昂贵的部分。
【解决方案2】:

你可以把它分成4个循环:

import multiprocessing

def test_false_pos(times, i, q):
    sumA = [0] * times
    for test in range(times):
        counter = 0
        bf = BloomFilter(4095,10)
        for i in range(600):
            bf.rand_inserts()
        for x in range(10000):
            randS = str(rnd.randint(0,10**8))
            if bf.lookup(randS):
                counter += 1
        sumA[test] = counter/10000.0
    q.put([i, list(sumA)])

def full_test(pieces):
    threads = []
    q = multiprocessing.Queue()
    steps = 1000 / pieces
    for i in range(pieces):
        threads.append(multiprocessing.Process(target=test_false_pos, args=(steps, i, q)))
    [thread.start() for thread in threads]
    results = [None] * pieces
    for i in range(pieces):
        i, result = q.get()
        results[i] = result
    # Flatten the array (`results` looks like this: [[...], [...], [...], [...]])
    # source: https://stackoverflow.com/a/952952/5244995
    sums = [value for result in results for val in result]
    return np.mean(np.array(sums))

if __name__ == '__main__':
    full_test(multiprocessing.cpu_count())

这将运行 n 个进程,每个进程执行 1/nth 的工作,其中 n 是您计算机上的处理器数量.

test_false_pos 函数已修改为采用三个参数:

  • times 是循环运行的次数。
  • i 被传递给结果。
  • q 是将结果添加到的队列。

该函数循环times 次,然后将isumA 放入队列中以供进一步处理。

主线程 (full_test) 等待每个线程完成,然后将结果放在 results 列表中的适当位置。列表完成后,将其展平,计算并返回平均值。

【讨论】:

  • 谢谢。这是我第一次看到这个。所以要调用函数,我是调用full_test还是test_false_pos?并有什么论据?
  • @tTIKA 你打电话给full_test,然后把你想要的独立进程的数量传递给它。
  • @Eliethesaiyan 我已经添加了解释。看起来怎么样?
【解决方案3】:

考虑研究 Numba 和 Jit(即时编译器)。它适用于基于 Numpy 的函数。它可以处理一些 python 例程,但主要用于加速数值计算,尤其是带有循环的计算(比如做 cholesky rank-1 up/downdates)。我认为它不适用于 BloomFilter,但了解它通常非常有帮助。

如果您必须在 numpy 的流程中使用其他包,请将繁重的 numpy 例程分离到它们自己的函数中,并在该函数之上添加一个 @jit 装饰器。然后将它们与普通的 python 东西一起放入您的流程中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-28
    • 2013-09-01
    • 2023-03-06
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    相关资源
    最近更新 更多