-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 関数は、1 回の実行に最大 10.63 秒かかります。100 スレッドを開始したところ、結果は 1 秒あたり最大 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 を終了しました。平均速度は 1 秒あたり 12.860571 です。

私の質問は、なぜそんなに遅いのですか?私は毎秒〜10を期待していました。速くする方法は?fib() 関数は、何らかのプロセスの実行をシミュレートするためのものです。たとえば、大きな html からデータを抽出します。

4

4 に答える 4

7

ケーキを焼くように頼んだら、生地を焼くのに 1 時間半、生地を焼くのに 30 分、オーブンに入れるのに 60 分かかります。ただし、欠けているものもいくつかあります。最初に2つのケーキを焼くように言わなかった場合、生地を2回作る必要がありますが、現在は2回30分です. 今では実際には2時間かかります(最初のケーキがオーブンに入ったら、2番目のケーキに自由に取り組むことができます).

ここで、4 つのケーキを焼くように依頼したとします。生地を 1 回作って 4 つのケーキに分割することは許可しませんが、毎回作成する必要があります。現在予想される時間は、アルスト ケーキを焼くのに 4*30 分 + 1 時間です。例として、妻が手伝ってくれると仮定します。つまり、2 つのケーキの生地を並行して作ることができます。1 人につき 2 つのケーキを焼かなければならないので、現在予想される時間は 2 時間です。しかし、あなたが持っているオーブンは、一度に2つのケーキしか入れられません. 時間は、最初の生地を作るのに 30 分、焼くのに 1 時間、2 番目の生地を作るのに 1 時間、最初の 2 つのケーキが完成したら、次の 2 つのケーキをオーブンに入れます。さらに1時間かかります。時間を合計すると、2 時間半かかったことがわかります。

これをさらに進めて、1000 個のケーキをお願いすると、500 時間半かかることになります。

これはスレッドと何の関係がありますか?

生地の作成は、100% の CPU 負荷を生み出す初期計算と考えてください。あなたの妻は、デュアル コアの 2 番目のコアです。オーブンは、プログラムが 50% の負荷を生成するリソースです。

実際のスレッディングでは、スレッドを開始するためにいくらかのオーバーヘッドがあり (ケーキを焼くと言いましたが、妻に助けを求める必要があり、時間がかかります)、リソース (つまり、メモリ アクセス) を競合します (あなたとあなたの妻はできません)。同時にミキサーを使用します) スレッド数がコア数よりも少ない場合でも、高速化はサブリニアです。

さらに、スマート プログラムはメイン スレッドでコードを 1 回ダウンロード (生地を 1 回作成) し、それをスレッドに複製するので、計算を複製する必要はありません。2回計算したからといって速くなるわけではありません。

于 2013-09-03T06:45:24.677 に答える
4

マノジの答えは正しいですが、もっと説明が必要だと思います。Python GILは cpython で使用されるミューテックスであり、基本的に Python コードの並列実行を無効にします。スレッド化されたコードが遅くなることはなく、OS がすべてのコアで同時に Python スレッドをスケジュールすることを実際に妨げることもありません。同時に 1 つのスレッドだけが Python バイトコードを実行できるようにするだけです。

これはあなたにとって何を意味しますか?基本的に次の 2 つのことを行います。

  1. スリープ: この関数を実行している間、Python コードは実行されません。5 ~ 10 秒間何もしません。それまでの間、他のスレッドはまったく同じことを行うことができます。呼び出しのオーバーヘッドがごくわずかであることを考えると、time.sleep何千ものスレッドが存在する可能性があり、予想どおりに線形にスケーリングされる可能性があります。これが、行をコメントアウトするとすぐにすべてが機能する理由ですfib。あなたの平均睡眠時間は7.5s、1 秒あたり 15 回の計算が予想されるためです。
  2. フィボナッチ数列の計算: これが問題です。実際に Python コードを実行しています。計算ごとに約 0.5 秒かかるとしましょう。これで、スレッドの数に関係なく、一度に 1 つの計算しか実行できないことがわかりました。それを考えると、1 秒あたり 2 回の計算しかできません。

現在、主にオーバーヘッドが関係しているため、15およびよりも低くなっています。2まず、データを画面に出力しますが、これはほとんどの場合、驚くほど遅い操作です。次に、100 個のスレッドを使用しています。つまり、100 個のスレッド スタックを (たとえスリープ状態であっても) 常に切り替えていることを意味し、これは軽量な操作ではありません。

ただし、スレッド化は依然として非常に有用であることに注意してください。たとえば、Python 自体ではなく他のリソースによって実行が行われる呼び出しをブロックする場合などです。これは、ソケットの結果、例のようなスリープ、または python 自体の外部で行われる計算 (たとえば、多くの numpy 計算) を待っている可能性があります。

于 2013-09-03T07:10:36.897 に答える
0
于 2013-09-03T06:37:55.313 に答える