-1
# test.py

import threading
import time
import random
from itertools import count

def fib(n):
  """fibonacci sequence
  """
  if n < 2:
    return n
  else:
    return fib(n - 1) + fib(n - 2)

if __name__ == '__main__':
    counter = count(1)
    start_time = time.time()
    def thread_worker():
        while True:
            try:
                # To simulate downloading
                time.sleep(random.randint(5, 10))
                # To simulate doing some process, will take about 0.14 ~ 0.63 second
                fib(n=random.randint(28, 31))
            finally:
                finished_number = counter.next()
                print 'Has finished %d, the average speed is %f per second.' % (finished_number, finished_number/(time.time() - start_time))

    threads = [threading.Thread(target=thread_worker) for i in range(100)]
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()

以上是我的测试脚本。thread_worker 函数运行一次最多需要 10.63 秒。我启动了 100 个线程,预计结果是每秒 10 次左右。但实际结果令人沮丧如下:

...
Has finished 839, the average speed is 1.385970 per second.
Has finished 840, the average speed is 1.386356 per second.
Has finished 841, the average speed is 1.387525 per second.
...

如果我将“fib(n=random.randint(28, 31))”注释掉,结果是预期的:

...
Has finished 1026, the average speed is 12.982740 per second.
Has finished 1027, the average speed is 12.995230 per second.
Has finished 1028, the average speed is 13.007719 per second.
...

已完成1029次,平均速度为每秒12.860571次。

我的问题是为什么它这么慢?我预计每秒约 10 个。如何让它更快?fib() 函数只是为了模拟一些过程。例如从大 html 中提取数据。

4

4 回答 4

7

如果我让你烤一个蛋糕,这需要你一个半小时,面团需要 30 分钟,烤箱需要 60 分钟,按照你的逻辑,我预计 2 个蛋糕需要的时间完全相同。但是,您缺少一些东西。首先,如果我不告诉你一开始烤两个蛋糕,你必须做两次面团,现在是 2 次 30 分钟。现在它实际上需要你两个小时(一旦第一个蛋糕在烤箱里,你就可以自由地做第二个蛋糕了)。

现在假设我让你烤四个蛋糕,我不允许你一次做面团然后把它分成四个蛋糕,但你必须每次都做。我们现在期望的时间是 4*30 分钟 + 1 小时,用于烘烤第一个蛋糕。现在举个例子,假设你的妻子帮助你,这意味着你可以同时做两个蛋糕的面团。现在预计的时间是两个小时,因为每个人都要烤两个蛋糕。但是,您的烤箱一次只能放 2 个蛋糕。现在制作第一个面团的时间变成 30 分钟,烘烤它需要 1 小时,同时制作第二个面团,在前两个蛋糕完成后,您将接下来的两个蛋糕放入烤箱。这需要一个小时。如果你把时间加起来,你会发现现在花了你 2 个半小时。

如果你更进一步,我向你要一千块蛋糕,那将花费你 500 个半小时。

这与线程有什么关系?

将面团制作为创建 100% cpu 负载的初始计算。你的妻子是双核中的第二个核心。烤箱是一种资源,您的程序会为此产生 50% 的负载。

在真正的线程中,你有一些开销来启动线程(我告诉过你要烤蛋糕,你必须向你的妻子寻求帮助,这需要时间),你争夺资源(即内存访问)(你和你的妻子不能同时使用混合器)。即使线程数小于内核数,加速也是亚线性的。

此外,智能程序在主线程中下载他们的代码一次(一次制作面团),然后将其复制到线程中,无需复制计算。它不会因为您计算两次而使其更快。

于 2013-09-03T06:45:24.677 回答
4
于 2013-09-03T07:10:36.897 回答
0
于 2013-09-03T06:37:55.313 回答
0

您正在寻找更快的解决方案。记忆结果会有所帮助。

import collections
import functools


class Memoized(object):
    """Decorator. Caches a function's return value each time it is called.
    If called later with the same arguments, the cached value is returned
    (not reevaluated).
    """

    def __init__(self, func):
        self.func = func
        self.cache = {}

    def __call__(self, *args):
        if not isinstance(args, collections.Hashable):
            # uncacheable. a list, for instance.
            # better to not cache than blow up.
            return self.func(*args)
        if args in self.cache:
            return self.cache[args]
        else:
            value = self.func(*args)
            self.cache[args] = value
            return value

    def __repr__(self):
        """Return the function's docstring."""
        return self.func.__doc__

    def __get__(self, obj, objtype):
        """Support instance methods."""
        return functools.partial(self.__call__, obj)


if __name__ == '__main__':
    @Memoized
    def fibonacci(n):
        """Return the nth fibonacci number

        :param n: value
        """
        if n in (0, 1):
            return n
        return fibonacci(n - 1) + fibonacci(n - 2)

    print(fibonacci(35))

尝试使用和不使用 @Memoized 装饰器运行它。

食谱取自http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize

于 2013-09-03T09:20:39.220 回答