13

現在、ウェブサイトからできるだけ早くデータを取得する方法を研究しています。より高速にするために、マルチスレッドの使用を検討しています。マルチスレッドとシンプルな投稿の違いをテストするために使用したコードを次に示します。

import threading
import time
import urllib
import urllib2


class Post:

    def __init__(self, website, data, mode):
        self.website = website
        self.data = data

        #mode is either "Simple"(Simple POST) or "Multiple"(Multi-thread POST)
        self.mode = mode

    def post(self):

        #post data
        req = urllib2.Request(self.website)
        open_url = urllib2.urlopen(req, self.data)

        if self.mode == "Multiple":
            time.sleep(0.001)

        #read HTMLData
        HTMLData = open_url.read()



        print "OK"

if __name__ == "__main__":

    current_post = Post("http://forum.xda-developers.com/login.php", "vb_login_username=test&vb_login_password&securitytoken=guest&do=login", \
                        "Simple")

    #save the time before post data
    origin_time = time.time()

    if(current_post.mode == "Multiple"):

        #multithreading POST

        for i in range(0, 10):
           thread = threading.Thread(target = current_post.post)
           thread.start()
           thread.join()

        #calculate the time interval
        time_interval = time.time() - origin_time

        print time_interval

    if(current_post.mode == "Simple"):

        #simple POST

        for i in range(0, 10):
            current_post.post()

        #calculate the time interval
        time_interval = time.time() - origin_time

        print time_interval

ご覧のとおり、これは非常に単純なコードです。最初にモードを「シンプル」に設定すると、時間間隔を取得できます: 50 秒(おそらく私の速度は少し遅い :(). 次に、モードを「マルチ」に設定すると、時間間隔: 35が取得されます。その結果、マルチスレッドは実際に速度を上げることができますが、結果は私が想像するほど良くありません. もっと高速にしたい.

デバッグから、プログラムが主に行でブロックすることがわかりました: open_url = urllib2.urlopen(req, self.data)、このコード行は、指定された Web サイトからのデータの投稿と受信に多くの時間を要します。time.sleep()関数内にマルチスレッドを追加して使用することで速度を上げることができると思いurlopenますが、Python独自の関数であるため、それを行うことはできません。

サーバーが投稿速度をブロックする可能性のある制限を考慮していない場合、より速い速度を得るために他に何ができますか? または私が変更できる他のコードはありますか?ありがとう!

4

4 に答える 4

16

あなたが間違っている最大のこと、つまりスループットを最も損なっているのは、あなたが呼び出している方法thread.start()thread.join():

for i in range(0, 10):
   thread = threading.Thread(target = current_post.post)
   thread.start()
   thread.join()

ループを通過するたびに、スレッドを作成して開始し、終了するのを待ってから次のスレッドに進みます。あなたは同時に何もしていません!

おそらく代わりにすべきことは次のとおりです。

threads = []

# start all of the threads
for i in range(0, 10):
   thread = threading.Thread(target = current_post.post)
   thread.start()
   threads.append(thread)

# now wait for them all to finish
for thread in threads:
   thread.join()
于 2012-04-14T15:44:44.250 に答える
12

多くの場合、Pythonのスレッド化は実行速度をあまり向上させません...時々、それはそれを悪化させます。詳細については、Global Interpreter Lock / Pycon2010GILスライドに関するDavidBeazleyのPyCon2010プレゼンテーションを参照してください。このプレゼンテーションは非常に有益です。スレッド化を検討している人には強くお勧めします...

David Beazleyの講演では、ネットワークトラフィックによってPythonスレッドモジュールのスケジューリングが改善されると説明されていますが、マルチプロセッシングモジュールを使用する必要があります。私はこれをオプションとしてあなたのコードに含めました(私の答えの下部を参照してください)。

私の古いマシン(Python 2.6.6)の1つでこれを実行します:

current_post.mode == "Process"  (multiprocessing)  --> 0.2609 seconds
current_post.mode == "Multiple" (threading)        --> 0.3947 seconds
current_post.mode == "Simple"   (serial execution) --> 1.650 seconds

私はTokenMacGuyのコメントに同意し、上記の数字に.join()は別のループへの移動が含まれています。ご覧のとおり、Pythonのマルチプロセッシングはスレッド化よりも大幅に高速です。


from multiprocessing import Process
import threading
import time
import urllib
import urllib2


class Post:

    def __init__(self, website, data, mode):
        self.website = website
        self.data = data

        #mode is either:
        #   "Simple"      (Simple POST)
        #   "Multiple"    (Multi-thread POST)
        #   "Process"     (Multiprocessing)
        self.mode = mode
        self.run_job()

    def post(self):

        #post data
        req = urllib2.Request(self.website)
        open_url = urllib2.urlopen(req, self.data)

        if self.mode == "Multiple":
            time.sleep(0.001)

        #read HTMLData
        HTMLData = open_url.read()

        #print "OK"

    def run_job(self):
        """This was refactored from the OP's code"""
        origin_time = time.time()
        if(self.mode == "Multiple"):

            #multithreading POST
            threads = list()
            for i in range(0, 10):
               thread = threading.Thread(target = self.post)
               thread.start()
               threads.append(thread)
            for thread in threads:
               thread.join()
            #calculate the time interval
            time_interval = time.time() - origin_time
            print "mode - {0}: {1}".format(method, time_interval)

        if(self.mode == "Process"):

            #multiprocessing POST
            processes = list()
            for i in range(0, 10):
               process = Process(target=self.post)
               process.start()
               processes.append(process)
            for process in processes:
               process.join()
            #calculate the time interval
            time_interval = time.time() - origin_time
            print "mode - {0}: {1}".format(method, time_interval)

        if(self.mode == "Simple"):

            #simple POST
            for i in range(0, 10):
                self.post()
            #calculate the time interval
            time_interval = time.time() - origin_time
            print "mode - {0}: {1}".format(method, time_interval)
        return time_interval

if __name__ == "__main__":

    for method in ["Process", "Multiple", "Simple"]:
        Post("http://forum.xda-developers.com/login.php", 
            "vb_login_username=test&vb_login_password&securitytoken=guest&do=login",
            method
            )
于 2012-04-16T15:56:11.253 に答える
2

Python でマルチスレッドが「速度を上げる」ことができる唯一のケースは、このようなI/O バウンドの多い操作がある場合であることに注意してください。それ以外の場合、マルチスレッドは複数の CPU で実行できないため、「速度」は向上しません (いいえ、複数のコアがあっても、Python はそのようには機能しません)。マルチスレッドは、2 つのことを同時に実行したい場合に使用する必要があります。2 つのことを並行して実行したい場合 (つまり、2 つのプロセスを別々に実行したい場合) ではありません。

さて、あなたが実際に行っていることは、単一の DNS ルックアップの速度を実際に向上させるわけではありませんが、他のいくつかの結果を待っている間に複数のリクエストを発射することを可能にします。または、応答時間を現在よりもさらに悪化させるだけです。

また、urllib2 の使用をやめて、リクエストを使用してください: http://docs.python-requests.org

于 2012-04-14T15:15:25.203 に答える
0

DNS ルックアップには時間がかかります。それについてあなたができることは何もありません。最初に複数のスレッドを使用する理由の 1 つは、長い待機時間です。複数のルックアップ広告サイトの GET/POST が並行して発生する可能性があります。

sleep() をダンプします - それは役に立ちません。

于 2012-04-14T15:01:36.997 に答える