<p>les requĂȘtes ont des performances mĂ©diocres en streaming de grandes rĂ©ponses binaires</p>

CrĂ©Ă© le 5 dĂ©c. 2014  Â·  40Commentaires  Â·  Source: psf/requests

https://github.com/alex/http-client-bench contient les benchmarks que j'ai utilisés.

Les résultats sont quelque chose comme :

| | requĂȘtes/http | prise |
| --- | --- | --- |
| CPython | 12 Mo/s | 200 Mo/s |
| PyPy | 80 Mo/s | 300 Mo/s |
| Aller | 150 Mo/s | n/a |

request impose un surcoût considérable par rapport à une socket, en particulier sur CPython.

Propose Close

Tous les 40 commentaires

Cette surcharge est Ă©tonnamment grande. Cependant, l'Ă©viter peut ĂȘtre dĂ©licat.

Le gros problĂšme est que nous effectuons beaucoup de traitement par morceau. C'est tout le long de la pile : requĂȘtes, urllib3 et httplib. Il serait extrĂȘmement intĂ©ressant de voir oĂč le temps est dĂ©pensĂ© pour dĂ©terminer qui est Ă  l'origine de l'inefficacitĂ©.

devinez qu'une prochaine Ă©tape serait d'essayer de profiler httplib / urllib3 pour voir le
performances lĂ -bas?

Kevin Burke
téléphone : 925.271.7005 | vingtmillisecondes.com

Le jeu. 4 dĂ©cembre 2014 Ă  17h01, Cory Benfield [email protected]
a Ă©crit:

Cette surcharge est Ă©tonnamment grande. Cependant, l'Ă©viter peut ĂȘtre dĂ©licat.

Le gros problĂšme est que nous effectuons beaucoup de traitement par morceau. C'est
tout en bas de la pile : requĂȘtes, urllib3 et httplib. Ce serait
extrĂȘmement intĂ©ressant de voir oĂč est passĂ© le temps pour dĂ©terminer qui
est à l'origine de l'inefficacité.

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment-65732050
.

Je viens d'exécuter des tests avec urllib3 :

PyPy : 120 Mo/s
CPython : 70 Mo/s

Et j'ai rĂ©-exĂ©cutĂ© CPython + requĂȘtes : 35 Mo/s

(Ma machine semble ressentir beaucoup de bruit dans les benchmarks, si quelqu'un a un systÚme plus silencieux sur lequel il peut les utiliser, ce serait génial)

J'ai essayé de les exécuter sur ma machine aprÚs avoir fermé tous les autres
fenĂȘtre d'application et de terminal et a Ă©galement eu pas mal de bruit - le
la référence du socket était de 30 Mo/s à 460 Mo/s.

Kevin Burke
téléphone : 925.271.7005 | vingtmillisecondes.com

Le jeu. 4 dĂ©cembre 2014 Ă  21:24, Alex Gaynor [email protected]
a Ă©crit:

Je viens d'exécuter des tests avec urllib3 :

PyPy : 120 Mo/s
CPython : 70 Mo/s

Et j'ai rĂ©-exĂ©cutĂ© CPython + requĂȘtes : 35 Mo/s

(Ma machine semble Ă©prouver un peu de bruit dans les repĂšres, si
tout le monde a un systÚme plus silencieux sur lequel ils peuvent les utiliser, ce serait génial)

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65748982
.

J'ai rendu les benchmarks plus faciles à exécuter maintenant, afin que d'autres personnes puissent, espérons-le, vérifier mes chiffres :

CPython :

BENCH SOCKET:
   8GiB 0:00:22 [ 360MiB/s] [======================================================>] 100%
BENCH HTTPLIB:
   8GiB 0:02:34 [53.1MiB/s] [======================================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:30 [90.2MiB/s] [======================================================>] 100%
BENCH REQUESTS
   8GiB 0:01:30 [90.7MiB/s] [======================================================>] 100%
BENCH GO HTTP
   8GiB 0:00:26 [ 305MiB/s] [======================================================>] 100%

PyPy :

BENCH SOCKET:
   8GiB 0:00:22 [ 357MiB/s] [======================================================>] 100%
BENCH HTTPLIB:
   8GiB 0:00:43 [ 189MiB/s] [======================================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:07 [ 121MiB/s] [======================================================>] 100%
BENCH REQUESTS
   8GiB 0:01:09 [ 117MiB/s] [======================================================>] 100%
BENCH GO HTTP
   8GiB 0:00:26 [ 307MiB/s] [======================================================>] 100%

Euh... ces chiffres sont bizarres. La httplib de CPython est plus lente que les requĂȘtes ou urllib3, mĂȘme si les deux bibliothĂšques utilisent httplib ? Cela ne peut pas ĂȘtre juste.

Ils se reproduisent de maniÚre cohérente pour moi - pouvez-vous essayer les références et voir si
pouvez-vous reproduire? En supposant que vous le puissiez, voyez-vous quelque chose de mal avec le
repĂšres ?

Le vendredi 05 dĂ©cembre 2014 Ă  11:16:45 Cory Benfield [email protected]
a Ă©crit:

Euh... ces chiffres sont bizarres. La httplib de CPython est plus lente que les requĂȘtes ou
urllib3, mĂȘme si les deux bibliothĂšques utilisent httplib? Cela ne peut pas ĂȘtre juste.

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65821989
.

J'attrape juste une machine connue et silencieuse maintenant. Cela devrait prendre quelques minutes pour devenir disponible car c'est une box physique qu'il faut installer (dieu j'adore MAAS).

CPython 2.7.8

BENCH SOCKET:
   8GiB 0:00:26 [ 309MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:02:24 [56.5MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:42 [79.7MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:45 [77.9MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:27 [ 297MiB/s] [================================>] 100%

Pour ce que ça vaut :

Ce patch , CPython 3.4.2 :

BENCH SOCKET:
   8GiB 0:00:27 [ 302MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:00:53 [ 151MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:00:54 [ 149MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:00:56 [ 144MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:31 [ 256MiB/s] [================================>] 100%

Vous devriez pouvoir obtenir le mĂȘme effet sur Python2 avec
env PYTHONUNBUFFERED= ou le drapeau -u .

Le vendredi 05 dĂ©cembre 2014 Ă  11:42:36 Corey Farwell [email protected]
a Ă©crit:

Pour ce que ça vaut :

Ce correctif https://gist.github.com/frewsxcv/1c0f3c81cda508e1bca9 , CPython
3.4.2 :

PRISE DE BANC :
8Gio 0:00:27 [ 302Mio/s] [================================>] 100%
BANC HTTPLIB :
8Gio 0:00:53 [ 151Mio/s] [================================>] 100%
BANC URLLIB3 :
8Gio 0:00:54 [ 149Mio/s] [================================>] 100%
DEMANDES DE BANC
8Gio 0:00:56 [ 144Mio/s] [================================>] 100%
BANC ALLER HTTP
8Gio 0:00:31 [ 256Mio/s] [================================>] 100%

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65826239
.

@alex Fait intĂ©ressant, ni env PYTHONUNBUFFERED= ni -u n'ont le mĂȘme effet sur Python 2. Les rĂ©sultats de ma machine arrivent.

D'accord, les données ci-dessous proviennent d'une machine qui ne fait rien d'autre que d'exécuter ces tests. Le dernier test a été exécuté avec le drapeau Python -u défini, et comme vous pouvez le voir, ce drapeau n'a aucun effet.

Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 500MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:32 [88.6MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:21 [ 100MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:21 [ 385MiB/s] [================================>] 100%
Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 503MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:33 [87.8MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:22 [99.3MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:20 [ 391MiB/s] [================================>] 100%
Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 506MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:31 [89.1MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:21 [ 389MiB/s] [================================>] 100%

Ces chiffres sont extrĂȘmement stables et prĂ©sentent les caractĂ©ristiques suivantes :

  1. Les lectures de sockets brutes sont rapides (duh).
  2. Go correspond Ă  environ 80% de la vitesse de lecture d'un socket brut.
  3. urllib3 correspond Ă  environ 20% de la vitesse de lecture d'un socket brut.
  4. request est légÚrement plus lent que urllib3, ce qui est logique car nous ajoutons quelques cadres de pile pour que les données passent.
  5. httplib est plus lent que request/urllib3. C'est tout simplement impossible, et je soupçonne que nous devons configurer httplib ou la bibliothÚque de sockets d'une maniÚre que httplib ne l'est pas.

FWIW, je viens de fusionner en ajoutant buffering=True de @kevinburke , faites vos courses
inclure ça?

Le ven 05 dĂ©c 2014 Ă  12:04:40 Cory Benfield [email protected]
a Ă©crit:

D'accord, les données ci-dessous proviennent d'une machine qui ne fait rien d'autre
mais en exécutant ces tests. Le dernier test a été exécuté avec le drapeau Python -u
défini, et comme vous pouvez le voir, ce drapeau n'a aucun effet.

Python 2.7.6
go version go1.2.1 linux/amd64
PRISE DE BANC :
8Gio 0:00:16 [ 500Mio/s] [================================>] 100%
BANC HTTPLIB :
8Gio 0:01:32 [88.6Mio/s] [================================>] 100%
BANC URLLIB3 :
8Gio 0:01:20 [101Mio/s] [================================>] 100%
DEMANDES DE BANC
8Gio 0:01:21 [ 100Mio/s] [================================>] 100%
BANC ALLER HTTP
8Gio 0:00:21 [ 385Mio/s] [================================>] 100%

Python 2.7.6
go version go1.2.1 linux/amd64
PRISE DE BANC :
8Gio 0:00:16 [ 503Mio/s] [================================>] 100%
BANC HTTPLIB :
8Gio 0:01:33 [87.8Mio/s] [================================>] 100%
BANC URLLIB3 :
8Gio 0:01:20 [101Mio/s] [================================>] 100%
DEMANDES DE BANC
8 Gio 0:01:22 [99,3 Mio/s] [================================>] 100 %
BANC ALLER HTTP
8Gio 0:00:20 [ 391Mio/s] [================================>] 100%

Python 2.7.6
go version go1.2.1 linux/amd64
PRISE DE BANC :
8Gio 0:00:16 [ 506Mio/s] [================================>] 100%
BANC HTTPLIB :
8Gio 0:01:31 [89,1 Mio/s] [================================>] 100%
BANC URLLIB3 :
8Gio 0:01:20 [101Mio/s] [================================>] 100%
DEMANDES DE BANC
8Gio 0:01:20 [101Mio/s] [================================>] 100%
BANC ALLER HTTP
8Gio 0:00:21 [ 389Mio/s] [================================>] 100%

Ces chiffres sont extrĂȘmement stables et prĂ©sentent les caractĂ©ristiques suivantes :

  1. Les lectures de sockets brutes sont rapides (duh).
  2. Go correspond Ă  environ 80% de la vitesse de lecture d'un socket brut.
  3. urllib3 correspond Ă  environ 20% de la vitesse de lecture d'un socket brut.
  4. request est légÚrement plus lent que urllib3, ce qui est logique car nous
    ajoutez quelques cadres de pile pour que les données passent à travers.
  5. httplib est plus lent que request/urllib3. C'est juste impossible,
    et je soupçonne que nous devons configurer httplib ou la bibliothÚque de sockets dans
    une maniĂšre que httplib n'est pas.

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment-65829335
.

Cory - voir la derniĂšre version du client de banc qui allume le
buffering=True dans httplib (comme le font les requĂȘtes/urllib3)

Kevin Burke
téléphone : 925.271.7005 | vingtmillisecondes.com

Le vendredi 5 dĂ©cembre 2014 Ă  10h04, Cory Benfield [email protected]
a Ă©crit:

D'accord, les données ci-dessous proviennent d'une machine qui ne fait rien d'autre
mais en exécutant ces tests. Le dernier test a été exécuté avec le drapeau Python -u
défini, et comme vous pouvez le voir, ce drapeau n'a aucun effet.

Python 2.7.6
go version go1.2.1 linux/amd64
PRISE DE BANC :
8Gio 0:00:16 [ 500Mio/s] [================================>] 100%
BANC HTTPLIB :
8Gio 0:01:32 [88.6Mio/s] [================================>] 100%
BANC URLLIB3 :
8Gio 0:01:20 [101Mio/s] [================================>] 100%
DEMANDES DE BANC
8Gio 0:01:21 [ 100Mio/s] [================================>] 100%
BANC ALLER HTTP
8Gio 0:00:21 [ 385Mio/s] [================================>] 100%

Python 2.7.6
go version go1.2.1 linux/amd64
PRISE DE BANC :
8Gio 0:00:16 [ 503Mio/s] [================================>] 100%
BANC HTTPLIB :
8Gio 0:01:33 [87.8Mio/s] [================================>] 100%
BANC URLLIB3 :
8Gio 0:01:20 [101Mio/s] [================================>] 100%
DEMANDES DE BANC
8 Gio 0:01:22 [99,3 Mio/s] [================================>] 100 %
BANC ALLER HTTP
8Gio 0:00:20 [ 391Mio/s] [================================>] 100%

Python 2.7.6
go version go1.2.1 linux/amd64
PRISE DE BANC :
8Gio 0:00:16 [ 506Mio/s] [================================>] 100%
BANC HTTPLIB :
8Gio 0:01:31 [89,1 Mio/s] [================================>] 100%
BANC URLLIB3 :
8Gio 0:01:20 [101Mio/s] [================================>] 100%
DEMANDES DE BANC
8Gio 0:01:20 [101Mio/s] [================================>] 100%
BANC ALLER HTTP
8Gio 0:00:21 [ 389Mio/s] [================================>] 100%

Ces chiffres sont extrĂȘmement stables et prĂ©sentent les caractĂ©ristiques suivantes :

  1. Les lectures de sockets brutes sont rapides (duh).
  2. Go correspond Ă  environ 80% de la vitesse de lecture d'un socket brut.
  3. urllib3 correspond Ă  environ 20% de la vitesse de lecture d'un socket brut.
  4. request est légÚrement plus lent que urllib3, ce qui est logique car nous
    ajoutez quelques cadres de pile pour que les données passent à travers.
  5. httplib est plus lent que request/urllib3. C'est juste impossible,
    et je soupçonne que nous devons configurer httplib ou la bibliothÚque de sockets dans
    une maniĂšre que httplib n'est pas.

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment-65829335
.

Oui, cela corrige le comportement des performances de httplib pour avoir beaucoup plus de sens.

Nouveaux résultats et conclusions :

Python 2.7.6
go version go1.2.1 linux/amd64
BENCH SOCKET:
   8GiB 0:00:16 [ 499MiB/s] [================================>] 100%
BENCH HTTPLIB:
   8GiB 0:01:12 [ 113MiB/s] [================================>] 100%
BENCH URLLIB3:
   8GiB 0:01:21 [ 100MiB/s] [================================>] 100%
BENCH REQUESTS
   8GiB 0:01:20 [ 101MiB/s] [================================>] 100%
BENCH GO HTTP
   8GiB 0:00:20 [ 391MiB/s] [================================>] 100%
  1. Les lectures de sockets brutes sont rapides (duh).
  2. Go correspond Ă  environ 80% de la vitesse de lecture d'un socket brut.
  3. httplib représente un peu moins de 25 % de la vitesse de lecture d'un socket brut.
  4. urllib3 correspond à environ 20 % de la vitesse de lecture d'un socket brut, ce qui ajoute un léger surcoût à httplib.
  5. request est légÚrement plus lent que urllib3, ce qui est logique car nous ajoutons quelques cadres de pile pour que les données passent.

Donc, sans doute le coût réel ici est httplib. Pour accélérer cela, il faut éliminer httplib.

Je suis intéressé de savoir quelle partie de httplib nous coûte. Je pense que le profilage de bench_httplib.py est une bonne prochaine étape.

J'ai exclu la conversion du socket en objet fichier via socket.makefile en ajoutant cette ligne au test bench_socket.py , cela ne le ralentit pas du tout. Bizarrement, cela semble le rendre plus rapide.

La réponse est presque certainement le transfert-encodage : la gestion des morceaux.
Voir : https://github.com/alex/http-client-bench/pull/6 , passer à
Content-Length sur le serveur produit des résultats inattendus.

Le ven 05 dĂ©c 2014 Ă  12:24:53 Cory Benfield [email protected]
a Ă©crit:

Donc, sans doute le coût réel ici est httplib. Accélérer cela nécessite
obtenir httplib Ă  l'Ă©cart.

Je suis intéressé de savoir quelle partie de httplib nous coûte. je
pensez que le profilage bench_httplib.py est une bonne prochaine Ă©tape.

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65831653
.

Intéressant.

La gestion des morceaux est presque certainement le problÚme, et je ne suis pas vraiment surpris que go le gÚre mieux, d'autant plus que les morceaux sont le mode HTTP par défaut pour go.

Cependant, des requĂȘtes plus rapides qu'un socket brut sont... inattendues !

Une chose Ă  noter : si le socket ne dĂ©codait pas l'encodage fragmentĂ© dans les tests prĂ©cĂ©dents, il bĂ©nĂ©ficiait d'un avantage injuste, car il lisait en fait moins de donnĂ©es que les autres mĂ©thodes ! Ils lisaient tous les en-tĂȘtes fragmentĂ©s ainsi que les 8 Go de donnĂ©es.

Cela conduit Ă  une question de suivi : pensons-nous toujours que toutes ces mĂ©thodes lisent rĂ©ellement la mĂȘme quantitĂ© de donnĂ©es ?

Oui, la couche socket trichait, elle ne décodait pas les métadonnées fragmentées,
et techniquement lire un peu moins. C'Ă©tait lĂ  comme une ligne de base pour "Ă  quelle vitesse
peut-on lire", pour ne rien prouver.

Le ven 05 dĂ©c 2014 Ă  12:33:10 Cory Benfield [email protected]
a Ă©crit:

Intéressant.

La manipulation en morceaux est presque certainement le problĂšme, et je ne suis pas vraiment
surpris que go le gÚre mieux, d'autant plus que le chunked est la valeur par défaut
Mode HTTP pour aller.

Cependant, des requĂȘtes plus rapides qu'un socket brut sont... inattendues !

Une chose à noter : si le socket ne décodait pas l'encodage fragmenté
dans les tests précédents, il a obtenu un avantage injuste, car il était en fait
lire moins de données que les autres méthodes ! Ils lisaient tous le
en-tĂȘtes fragmentĂ©s ainsi que les 8 Go de donnĂ©es.

Cela conduit à une question de suivi : pensons-nous toujours que toutes ces méthodes
lisent rĂ©ellement la mĂȘme quantitĂ© de donnĂ©es ?

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65833299
.

Je ne serais pas surpris si cela est lié à la taille du morceau que nous lisons sur le socket à la fois.

GĂąteau pour @alex pour ĂȘtre super utile :cake:

@nelhage a fait du traçage des différents exemples (dans le transfert
encodage : cas fragmenté) https://gist.github.com/nelhage/dd6490fbc5cfb815f762
sont les résultats. Il semble qu'il y ait un bogue dans httplib qui en résulte
ne lisant pas toujours un morceau complet de la prise.

Le lundi 08 dĂ©cembre 2014 Ă  9:05:14 Kenneth Reitz [email protected]
a Ă©crit:

Gùteau pour @alex https://github.com/alex pour avoir été super utile [image :
:gĂąteau:]

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -66147998
.

Donc ce que nous avons ici est un bogue dans une bibliothĂšque standard que personne ne maintient vraiment ? ( @Lukasa a au moins 2 ensembles de correctifs qui sont ouverts depuis > 1 an.) Peut-ĂȘtre que je vais Ă©lever une puanteur sur une liste quelque part ce soir

Quelqu'un (je pourrais y arriver, pas clair) a probablement besoin d'approfondir avec pdb
ou quelque chose et déterminez quel code exact génÚre ces 20 octets
lit afin que nous puissions préparer un bon rapport de bogue.

Le lundi 08 dĂ©cembre 2014 Ă  09:14:09 Ian Cordasco [email protected]
a Ă©crit:

Donc, ce que nous avons ici est un bogue dans une bibliothĂšque standard que personne n'est vraiment
maintenir? ( @Lukasa https://github.com/Lukasa a au moins 2 correctifs
ensembles ouverts depuis plus d'un an.) Peut-ĂȘtre que je vais Ă©lever une puanteur sur une liste
quelque part ce soir

-
RĂ©pondez directement Ă  cet e-mail ou consultez-le sur GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -66149522
.

J'essaierai de l'intégrer ce soir ou demain si personne d'autre n'y arrive.

Alors, des nouvelles sur la cause profonde ? Qu'est-ce qui génÚre ces courtes lectures et dans quelle mesure la situation s'améliore-t-elle sans elles ?

@kislyuk Pas à ma connaissance. J'espÚre avoir un peu de temps pour le chasser pendant les vacances de Noël.

Merci @Lukasa. Je suis confrontĂ© Ă  un problĂšme de performances oĂč la vitesse de tĂ©lĂ©chargement sur une rĂ©ponse fragmentĂ©e Ă  l'aide de urllib3/requests est beaucoup plus lente qu'avec curl et d'autres bibliothĂšques, et j'essaie de comprendre si c'est le coupable.

Je fouillais un peu avec ça. Les lectures courtes proviennent de la fonction _read_chunked dans httplib

https://fossies.org/linux/misc/Python-2.7.9.tgz/Python-2.7.9/Lib/httplib.py#l_585

Les lectures de 2 octets semblent provenir principalement de la ligne 622.

J'ai un motif de strace légÚrement différent de celui posté plus tÎt:
recvfrom(3, "400\r\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0"..., 8192, 0, NULL, NULL) = 8192
recvfrom(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 0\0\0\0\0\0\0\0\0\0"..., 54, 0, NULL, NULL) = 54
recvfrom(3, "\r\n", 2, 0, NULL, NULL) = 2

Ce modĂšle peut ĂȘtre expliquĂ© comme suit :

  • le self.fp.readline (ligne 591) dĂ©clenche une lecture tamponnĂ©e de 8192 octets (dans socket.readline)
  • chaque bloc consommĂ© est de 1031 octets (longueur de bloc de 5 octets ("400\r\n") + 1024 octets de donnĂ©es + 2 octets de terminateur)
  • nous pouvons consommer 7 de ces morceaux sur les 8192 octets mis en mĂ©moire tampon, ce qui nous laisse 975 octets
  • nous lisons ensuite la longueur du morceau suivant (5 octets) qui laisse 970 octets
  • nous n'avons plus que 970 octets ce qui est insuffisant pour remplir le morceau actuel (1024) donc nous retournons sur le rĂ©seau pour le manque de 54 octets
  • pour accomplir cela, httplib effectue un sock.read(54) sur les octets en attente. socket.read dans ce cas (avec une longueur explicite) choisira d'aller sur le rĂ©seau pour les 54 octets spĂ©cifiĂ©s (plutĂŽt que de mettre en mĂ©moire tampon un autre 8192)
  • nous arrivons ensuite Ă  lire le terminateur de morceau qui est de 2 octets et encore une fois c'est le mĂȘme scĂ©nario que ci-dessus

Le motif se répétera alors (retournez à l'étape 1)

FWIW, j'ai trouvĂ© qu'une accĂ©lĂ©ration modeste (20 % environ) pouvait ĂȘtre rĂ©alisĂ©e ici en roulant le terminateur de bloc de 2 octets lu dans le corps du bloc lu, c'est-Ă -dire plutĂŽt que ceci :

            value.append(self._safe_read(chunk_left)) 
            amt -= chunk_left

        self._safe_read(2)  # toss the CRLF at the end of the chunk

fais ceci Ă  la place :

            value.append(self._safe_read(chunk_left + 2)[:-2]) 
            amt -= chunk_left

Vraiment, cependant, ce serait probablement mieux si la lecture pour les 54 octets pouvait tamponner plus d'octets que 54 (c'est-à-dire 8192 octets), ce qui signifierait que le socket tamponné ne serait pas vide en ce qui concerne la lecture de 2 octets.

Suite à cela. Je ne suis pas sûr que les petites lectures soient le principal facteur de perte de débit (ou pas sur localhost). J'ai joué avec la taille du tampon de socket de telle sorte qu'elle soit un multiple de 1031 octets et malgré le fait que la strace n'ait plus de petites lectures, cela n'a pas eu beaucoup d'impact sur le débit.

Je pense que la perte de dĂ©bit est peut-ĂȘtre davantage liĂ©e Ă  la façon dont socket.py traite les petites lectures. Voici le code correspondant (de socket.read) :

https://fossies.org/linux/misc/Python-2.7.9.tgz/Python-2.7.9/Lib/socket.py#l_336

Lorsque vous passez une longueur explicite dans socket.read et qu'elle peut ĂȘtre remplie Ă  partir de donnĂ©es mises en mĂ©moire tampon existantes, voici le chemin du code :

        buf = self._rbuf
        buf.seek(0, 2)  # seek end

        #.....

        # Read until size bytes or EOF seen, whichever comes first
        buf_len = buf.tell()
        if buf_len >= size:
            # Already have size bytes in our buffer?  Extract and return.
            buf.seek(0)
            rv = buf.read(size)
            self._rbuf = StringIO()
            self._rbuf.write(buf.read())
            return rv

Le problĂšme que je perçois ici est que mĂȘme une lecture de 2 octets signifie copier le reste non lu dans un nouveau StringIO. Cela semble devenir trĂšs coĂ»teux pour beaucoup de petites lectures. Si un StringIO donnĂ© pouvait d'une maniĂšre ou d'une autre ĂȘtre drainĂ© Ă  chaque lecture plutĂŽt que le modĂšle actuel de copie du reste non lu dans un nouveau StringIO, je m'attends Ă  ce que cela puisse aider le dĂ©bit

@gardenia Je n'ai pas eu la chance d'absorber tout cela, mais merci beaucoup pour vos efforts et votre travail ici. @shazow peut-ĂȘtre @gardenia intĂ©ressantes.

:+1: merci @gardenia. Incidemment, mes propres recherches sur les performances dans mon cas d'utilisation ont révélé que dans mon cas, les réponses ne sont pas fragmentées, mais urllib3 s'exécute 20 % plus rapidement que les demandes, il y a donc une surcharge que je souhaite caractériser. Toujours en ligne avec le titre de ce problÚme, mais cause racine différente.

Fascinant, merci pour le partage ! :)

Cela semble Ă©galement ĂȘtre un excellent objectif pour

@alex - J'ai jouĂ© un peu avec le problĂšme de performance urllib3 vs requĂȘtes non fragmentĂ© que vous avez mentionnĂ©. Je pense que je vois une baisse similaire de 20% des demandes.

Dans les requĂȘtes, j'ai tentĂ© de remplacer l'appel Ă  self.raw.stream par l'implĂ©mentation en ligne de stream() (depuis urllib3). Cela a semblĂ© rapprocher beaucoup le dĂ©bit entre les requĂȘtes et urllib3, au moins sur ma machine :

--- requests.repo/requests/models.py    2015-03-06 16:05:52.072509869 +0000
+++ requests/models.py  2015-03-07 20:49:25.618007438 +0000
@@ -19,6 +19,7 @@
 from .packages.urllib3.fields import RequestField
 from .packages.urllib3.filepost import encode_multipart_formdata
 from .packages.urllib3.util import parse_url
+from .packages.urllib3.util.response import is_fp_closed
 from .packages.urllib3.exceptions import (
     DecodeError, ReadTimeoutError, ProtocolError, LocationParseError)
 from .exceptions import (
@@ -652,8 +654,12 @@
             try:
                 # Special case for urllib3.
                 try:
-                    for chunk in self.raw.stream(chunk_size, decode_content=True):
-                        yield chunk
+                    while not is_fp_closed(self.raw._fp):
+                        data = self.read(amt=chunk_size, decode_content=True)
+
+                        if data:
+                            yield data
+
                 except ProtocolError as e:
                     raise ChunkedEncodingError(e)
                 except DecodeError as e:

Vous pourriez peut-ĂȘtre essayer la mĂȘme chose sur votre machine pour voir si cela fait une diffĂ©rence pour vous aussi.

(Notez que oui, je sais que l'appel à is_fp_closed est un échec d'encapsulation, il ne s'agit pas d'un correctif sérieux, mais d'un point de données)

@shazow J'espĂšre que le BufferedSocket que l'hyper utilise devrait remĂ©dier Ă  une grande partie de cette inefficacitĂ©, en empĂȘchant essentiellement les petites lectures. Je me demande si httplib sur Py3 a ce problĂšme, car il utilise beaucoup io.BufferedReader , ce qui devrait fournir Ă  peu prĂšs le mĂȘme type d'avantage que le BufferedSocket .

Certes, cependant, lorsque hyper dĂ©veloppe suffisamment de fonctionnalitĂ©s HTTP/1.1 pour ĂȘtre utiles, nous devrions essayer de les comparer avec ces autres implĂ©mentations et faire des efforts pour rendre hyper aussi rapide que possible.

Inactif depuis presque un an. Fermeture.

Je constate des problÚmes similaires, 10 fois moins de débit en utilisant requests par rapport à urllib3 .

Je pense que le problÚme réside dans la classe HTTPResponse urllib3, lorsqu'il est lu comme un itérateur, son débit est vraiment mauvais. J'ai fait fonctionner mon code avec un hack trÚs moche : je renvoie l'objet souligné httplib.HTTPResponse utilisé par urllib3 et cela semble résoudre mon problÚme de débit.

Fait intéressant : la superclasse HTTPResponse urllib3 est io.IOBase . La superclasse httplib.HTTPResponse Python3 est io.BufferedIOBase . Je me demande si cela a quelque chose à voir avec ça.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

xsren picture xsren  Â·  3Commentaires

NoahCardoza picture NoahCardoza  Â·  4Commentaires

remram44 picture remram44  Â·  4Commentaires

avinassh picture avinassh  Â·  4Commentaires

jakul picture jakul  Â·  3Commentaires