2016-09-11 24 views
3

私は今日、さまざまなPython HTTPライブラリをテストしていました。ライブラリは、requestsよりはるかに高速に実行されているようです。なぜPython 3のhttp.clientはPythonリクエストよりもずっと高速ですか?

これをテストするには、次の2つのコードサンプルを実行します。

import http.client 

conn = http.client.HTTPConnection("localhost", port=8000) 
for i in range(1000): 
    conn.request("GET", "/") 
    r1 = conn.getresponse() 
    body = r1.read() 
    print(r1.status) 

conn.close() 

と、ここでのコードのpython-リクエストで同じことをやっている:

import requests 

with requests.Session() as session: 
    for i in range(1000): 
     r = session.get("http://localhost:8000") 
     print(r.status_code) 

私はSimpleHTTPServer起動した場合:

> python -m http.server 

とコードサンプルの上で実行を(私は、Python 3.5を使用しています2)。私は、次のような結果を得る:

http.client:

0.35user 0.10system 0:00.71elapsed 64%CPU 

のpython-要求:

1.76user 0.10system 0:02.17elapsed 85%CPU 

は正しい私の測定やテストはありますか?あなたもそれらを再現できますか?はいの場合、誰もが内部で何が起こっているのかを知っていますhttp.clientそれははるかに高速になりますか?処理時間に大きな違いがあるのはなぜですか?

答えて

5

両方のプロファイリングに基づいて、http.clientバージョンはそのようにしている間に、requestsバージョンがすべての要求に対してDNSルックアップを実行していることが主な違いです。

# http.client 
ncalls tottime percall cumtime percall filename:lineno(function) 
    1974 0.541 0.000 0.541 0.000 {method 'recv_into' of '_socket.socket' objects} 
    1000 0.020 0.000 0.045 0.000 feedparser.py:470(_parse_headers) 
    13000 0.015 0.000 0.563 0.000 {method 'readline' of '_io.BufferedReader' objects} 
... 

# requests 
ncalls tottime percall cumtime percall filename:lineno(function) 
    1481 0.827 0.001 0.827 0.001 {method 'recv_into' of '_socket.socket' objects} 
    1000 0.377 0.000 0.382 0.000 {built-in method _socket.gethostbyname} 
    1000 0.123 0.000 0.123 0.000 {built-in method _scproxy._get_proxy_settings} 
    1000 0.111 0.000 0.111 0.000 {built-in method _scproxy._get_proxies} 
    92000 0.068 0.000 0.284 0.000 _collections_abc.py:675(__iter__) 
... 

あなたは一度http.client.HTTPConnection()にホスト名を提供しているので、それはそれは一度gethostbynameを呼ぶような意味があります。 requests.Sessionはおそらくホスト名のルックアップをキャッシュできますが、明らかにそうではありません。

編集:今後のいくつかの調査の後、それはキャッシュの単純な問題ではありません。実際のリクエスト自体に関係なく、gethostbynameを呼び出すようになるプロキシをバイパスするかどうかを判断する機能があります。

+0

本当に面白いですね。私は両方のプロファイルに使用したツールを尋ねることができますか?私はPythonリクエストgithubでこれを報告すべきかどうか疑問に思います。 –

+0

これは 'cProfile'です –

1

@Lukasaからのコピー&ペーストの応答がhere投稿:

それはhttplibよりも実質的にないので、リクエストが遅い理由があります。 httplibは、スタックの最下位レイヤと考えることができます。ソケットの低レベルの論争を行います。リクエストはさらに2層上にあり、クッキー、接続プーリング、追加設定、その他の楽しいことのようなものが追加されます。これは必然的に物事を遅らせることになります。私たちは、単にhttplibよりも多くを計算しなければなりません。

これは、リクエストのcProfileの結果を見ることで確認できます。これは、httplibの場合よりも多くの結果があります。これは、高レベルのライブラリでは常に期待されます。さらに多くの作業をする必要があるため、オーバーヘッドが増えます。

目標とするパフォーマンスの向上を見ることができますが、すべてのケースでコールスタックの高さが著しく高くなると、パフォーマンスが著しく低下します。これは、「要求がhttplibよりも遅い」という苦情が常に真実になることを意味しています。「要求がソケットの下に注意深く作成されたrawバイトを送信するよりも遅い」という不満のようです。それは本当です、そして、それはいつも真実でしょう:それについて私たちができることは何もありません。

関連する問題