9

私は最近、小さなツール (urllib2 を使用して (非公式)twitter-button-count-url (> 2000 URL) にリクエストを送信し、その結果を解析する) をマルチプロセッシング モジュール (およびそれのワーカープール)。ここで、マルチスレッド (標準の非スレッド バージョンと比較して全体が遅くなる) とマルチプロセッシングに関するいくつかの議論を読みましたが、(おそらく非常に単純な) 質問に対する答えを見つけることができませんでした。

マルチプロセッシングで URL 呼び出しを高速化できますか、それともネットワーク アダプターのようなボトルネックではありませんか? たとえば、urllib2-open-method のどの部分を並列化できるのか、どのように機能するのかわかりません...

編集:これは私がスピードアップしたいリクエストと現在のマルチプロセッシングセットアップです:

 urls=["www.foo.bar", "www.bar.foo",...]
 tw_url='http://urls.api.twitter.com/1/urls/count.json?url=%s'

 def getTweets(self,urls):
    for i in urls:
        try:
            self.tw_que=urllib2.urlopen(tw_url %(i))
            self.jsons=json.loads(self.tw_que.read())
            self.tweets.append({'url':i,'date':today,'tweets':self.jsons['count']})
        except ValueError:
            print ....
            continue
    return self.tweets 

 if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=4)            
    result = [pool.apply_async(getTweets(i,)) for i in urls]
    [i.get() for i in result]
4

5 に答える 5

7

ああ、ここで GIL についての別の議論が始まります。さて、これが問題です。urllib2 を使用したコンテンツのフェッチは、ほとんどがIO バウンドになります。タスクが IO バウンドの場合、ネイティブ スレッドとマルチプロセッシングの両方で同じパフォーマンスが得られます (スレッドが問題になるのは、CPU バウンドの場合のみです)。はい、スピードアップできます。Pythonスレッドと10個のダウンローダースレッドなどを使用して自分で実行しました。

基本的に、ダウンロードする URL を生成する 1 つのスレッド (またはプロセス)と、そのキューから消費してサーバーに要求を行うN 個のスレッド (またはプロセス)を持つプロデューサー/コンシューマー モデルを使用します。

ここにいくつかの擬似コードがあります:

# Make sure that the queue is thread-safe!!

def producer(self):
    # Only need one producer, although you could have multiple
    with fh = open('urllist.txt', 'r'):
        for line in fh:
            self.queue.enqueue(line.strip())

def consumer(self):
    # Fire up N of these babies for some speed
    while True:
        url = self.queue.dequeue()
        dh = urllib2.urlopen(url)
        with fh = open('/dev/null', 'w'): # gotta put it somewhere
            fh.write(dh.read())

非常に大きなデータ チャンク (数百 MB) をダウンロードしていて、単一の要求で帯域幅が完全に飽和してしまう場合は、複数のダウンロードを実行しても意味がありません。複数のダウンロードを (一般的に) 実行する理由は、要求が小さく、待ち時間やオーバーヘッドが比較的大きいためです。

于 2011-08-01T23:52:13.770 に答える
6

geventを見て、具体的にはこの例を見てみましょう: concurrent_download.py。マルチプロセッシングやマルチスレッドよりもかなり高速であり、何千もの接続を簡単に処理できます。

于 2011-08-02T18:19:55.960 に答える
3

場合によります!別のサーバーに接続していますか、転送されたファイルは小さいか大きいか、サーバーが応答するのを待っている時間の多くを失っていますか、またはデータを転送しています...

一般に、マルチプロセッシングにはいくらかのオーバーヘッドが伴うため、作業を並列化することによって得られる高速化がオーバーヘッド自体よりも大きいことを確認する必要があります。

もう 1 つのポイント: ネットワーク、つまり I/O にバインドされたアプリケーションは、スレッド化やマルチプロセッシングではなく、非同期 I/O とイベント駆動型アーキテクチャを使用することで、より適切に機能し、スケーリングします。あらゆる計算を行っています。

特定の問題については、 TwistedgeventTornado、またはスレッドを使用して接続を並列化しないその他のネットワーク フレームワークを使用して、解決策を実装しようとします。

于 2011-08-01T23:52:47.870 に答える
1

Web リクエストを複数のプロセスに分割するときに行うことは、ネットワークのレイテンシ (つまり、応答の待機) を並列化することです。したがって、ほとんどのプロセスはほとんどの時間、イベントを待機してスリープ状態になるため、通常は高速化が得られるはずです。

またはツイストを使用します。;)

于 2011-08-01T23:52:56.720 に答える
0

コードが壊れている場合は何も役に立ちません: f()(括弧付き) Python で関数をすぐに呼び出しますf。代わりにプールで実行されるように (括弧なし) だけを渡す必要があります。質問からのコード:

#XXX BROKEN, DO NOT USE
result = [pool.apply_async(getTweets(i,)) for i in urls]
[i.get() for i in result]

その後の括弧getTweetsは、すべてのコードがメイン スレッドでシリアルに実行されることを意味します。

代わりに、呼び出しをプールに委任します。

all_tweets = pool.map(getTweets, urls)

json.loads()また、あなたのケースで(CPUに関して)高価でない限り、ここで個別のプロセスは必要ありません。スレッドを使用できます:multiprocessing.Poolで置き換えますmultiprocessing.pool.ThreadPool-- 残りは同じです。GIL は CPython の IO 中に解放されるため、ほとんどの時間をurlopen().read().

完全なコード例を次に示します。

于 2015-12-11T18:53:55.830 に答える