https://github.com/alex/http-client-bench contém os benchmarks que usei.
Os resultados são algo como:
| | solicitações / http | socket |
| --- | --- | --- |
| CPython | 12MB / s | 200 MB / s |
| PyPy | 80 MB / s | 300 MB / s |
| Go | 150 MB / s | n / a |
as solicitações impõem uma sobrecarga considerável em comparação com um soquete, particularmente no CPython.
Essa sobrecarga é inesperadamente grande. No entanto, evitá-lo pode ser complicado.
O grande problema é que fazemos muito processamento por bloco. Isso é todo o caminho para baixo na pilha: solicitações, urllib3 e httplib. Seria extremamente interessante ver onde o tempo está sendo gasto para descobrir quem está causando a ineficiência.
acho que a próxima etapa seria tentar criar o perfil de httplib / urllib3 para ver o
desempenho lá?
Kevin burke
telefone: 925.271.7005 | twentymilliseconds.com
Na quinta-feira, 4 de dezembro de 2014 às 17h01, Cory Benfield [email protected]
escreveu:
Essa sobrecarga é inesperadamente grande. No entanto, evitá-lo pode ser complicado.
O grande problema é que fazemos muito processamento por bloco. Isso é
todo o caminho para baixo na pilha: solicitações, urllib3 e httplib. Seria
extremamente interessante ver onde o tempo está sendo gasto para descobrir quem
está causando a ineficiência.-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65732050
.
Acabei de executar benchmarks com urllib3:
PyPy: 120 MB / s
CPython: 70 MB / s
E eu executei novamente as solicitações CPython +: 35 MB / s
(Minha máquina parece estar experimentando um pouco de ruído nos benchmarks, se alguém tiver um sistema mais silencioso que possa ligá-los, seria incrível)
Tentei executá-los na minha máquina depois de desligar todos os outros
janela do aplicativo e do terminal e também gerou bastante ruído - o
benchmark de soquete estava em qualquer lugar de 30 MB / s a 460 MB / s.
Kevin burke
telefone: 925.271.7005 | twentymilliseconds.com
Na quinta-feira, 4 de dezembro de 2014 às 21h24, Alex Gaynor [email protected]
escreveu:
Acabei de executar benchmarks com urllib3:
PyPy: 120 MB / s
CPython: 70 MB / sE eu executei novamente as solicitações CPython +: 35 MB / s
(Minha máquina parece estar experimentando um pouco de ruído nos benchmarks, se
qualquer um tem um sistema mais silencioso para poder usá-los, isso seria incrível)-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65748982
.
Tornei os benchmarks mais fáceis de executar agora, para que outras pessoas possam verificar meus números:
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%
Uh ... esses números são estranhos. Ohttplib de CPython é mais lento do que as solicitações ou urllib3, embora ambas as bibliotecas usem httplib? Isso simplesmente não pode estar certo.
Eles se reproduzem de forma consistente para mim - você pode experimentar os benchmarks e ver se
você pode reproduzir? Supondo que você possa, você vê algo de errado com o
benchmarks?
Na sexta-feira, 05 de dezembro de 2014 às 11:16:45 Cory Benfield [email protected]
escreveu:
Uh ... esses números são estranhos. Httplib do CPython é mais lento do que as solicitações ou
urllib3, mesmo que ambas as bibliotecas usem httplib? Isso simplesmente não pode estar certo.-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65821989
.
Estou apenas pegando uma máquina silenciosa conhecida agora. Deve demorar alguns minutos para ficar disponível porque é uma caixa física que tem que ser instalada (deus eu amo o 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%
Por que vale a pena:
Este 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%
Você deve ser capaz de obter o mesmo efeito no Python2 com
env PYTHONUNBUFFERED=
ou a bandeira -u
.
Na sexta-feira, 5 de dezembro de 2014 às 11h42min36s, Corey Farwell [email protected]
escreveu:
Por que vale a pena:
Este patch https://gist.github.com/frewsxcv/1c0f3c81cda508e1bca9 , CPython
3.4.2:TOMADA DE BANCADA:
8GiB 0:00:27 [302MiB / s] [====================================>] 100%
BENCH HTTPLIB:
8GiB 0:00:53 [151 MiB / s] [====================================>] 100%
BENCH URLLIB3:
8GiB 0:00:54 [149MiB / s] [====================================>] 100%
PEDIDOS DE BANCO
8GiB 0:00:56 [144 MiB / s] [====================================>] 100%
BENCH GO HTTP
8GiB 0:00:31 [256MiB / s] [====================================>] 100%-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65826239
.
@alex Curiosamente, nem env PYTHONUNBUFFERED=
nem -u
tem o mesmo efeito no Python 2. Resultados da entrada da minha máquina.
Tudo bem, os dados abaixo vêm de uma máquina que não está fazendo nada além de executar esses testes. O último teste foi executado com o sinalizador Python -u
definido e, como você pode ver, esse sinalizador não tem efeito.
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%
Esses números são extremamente estáveis e mostram os seguintes recursos:
FWIW, acabei de adicionar buffering=True
de @kevinburke , faça suas corridas
incluir isso?
Na sexta-feira, 05 de dezembro de 2014 às 12h04h40, Cory Benfield [email protected]
escreveu:
Tudo bem, os dados abaixo vêm de uma máquina que não está fazendo mais nada
mas executando esses testes. O último teste foi executado com a sinalização -u do Python
definido, e como você pode ver, esse sinalizador não tem efeito.Python 2.7.6
go versão go1.2.1 linux / amd64
TOMADA DE BANCADA:
8GiB 0:00:16 [500MiB / s] [====================================>] 100%
BENCH HTTPLIB:
8GiB 0:01:32 [88,6 MiB / s] [====================================>] 100%
BENCH URLLIB3:
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
PEDIDOS DE BANCO
8GiB 0:01:21 [100MiB / s] [====================================>] 100%
BENCH GO HTTP
8GiB 0:00:21 [385MiB / s] [====================================>] 100%Python 2.7.6
go versão go1.2.1 linux / amd64
TOMADA DE BANCADA:
8GiB 0:00:16 [503MiB / s] [====================================>] 100%
BENCH HTTPLIB:
8GiB 0:01:33 [87,8 MiB / s] [====================================>] 100%
BENCH URLLIB3:
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
PEDIDOS DE BANCO
8GiB 0:01:22 [99,3 MiB / s] [====================================>] 100%
BENCH GO HTTP
8GiB 0:00:20 [391 MiB / s] [====================================>] 100%Python 2.7.6
go versão go1.2.1 linux / amd64
TOMADA DE BANCADA:
8GiB 0:00:16 [506MiB / s] [====================================>] 100%
BENCH HTTPLIB:
8GiB 0:01:31 [89,1 MiB / s] [====================================>] 100%
BENCH URLLIB3:
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
PEDIDOS DE BANCO
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
BENCH GO HTTP
8GiB 0:00:21 [389MiB / s] [====================================>] 100%Esses números são extremamente estáveis e mostram os seguintes recursos:
- Leituras brutas de soquete são rápidas (duh).
- Go tem cerca de 80% da velocidade de uma leitura de soquete bruto.
- urllib3 tem cerca de 20% da velocidade de uma leitura de soquete bruto.
- solicitações é um pouco mais lento do que urllib3, o que faz sentido, pois
adicione alguns frames de pilha para os dados passarem.- httpplib é mais lento do que as solicitações / urllib3. Isso é simplesmente impossível,
e eu suspeito que devemos configurar o httplib ou a biblioteca de sockets em
uma maneira que o httplib não é.-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65829335
.
Cory - veja a última versão do cliente de bancada que liga o
buffering = True in httplib (como as solicitações / urllib3 fazem)
Kevin burke
telefone: 925.271.7005 | twentymilliseconds.com
Na sexta-feira, 5 de dezembro de 2014 às 10h04, Cory Benfield [email protected]
escreveu:
Tudo bem, os dados abaixo vêm de uma máquina que não está fazendo mais nada
mas executando esses testes. O último teste foi executado com a sinalização -u do Python
definido, e como você pode ver, esse sinalizador não tem efeito.Python 2.7.6
go versão go1.2.1 linux / amd64
TOMADA DE BANCADA:
8GiB 0:00:16 [500MiB / s] [====================================>] 100%
BENCH HTTPLIB:
8GiB 0:01:32 [88,6 MiB / s] [====================================>] 100%
BENCH URLLIB3:
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
PEDIDOS DE BANCO
8GiB 0:01:21 [100MiB / s] [====================================>] 100%
BENCH GO HTTP
8GiB 0:00:21 [385MiB / s] [====================================>] 100%Python 2.7.6
go versão go1.2.1 linux / amd64
TOMADA DE BANCADA:
8GiB 0:00:16 [503MiB / s] [====================================>] 100%
BENCH HTTPLIB:
8GiB 0:01:33 [87,8 MiB / s] [====================================>] 100%
BENCH URLLIB3:
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
PEDIDOS DE BANCO
8GiB 0:01:22 [99,3 MiB / s] [====================================>] 100%
BENCH GO HTTP
8GiB 0:00:20 [391 MiB / s] [====================================>] 100%Python 2.7.6
go versão go1.2.1 linux / amd64
TOMADA DE BANCADA:
8GiB 0:00:16 [506MiB / s] [====================================>] 100%
BENCH HTTPLIB:
8GiB 0:01:31 [89,1 MiB / s] [====================================>] 100%
BENCH URLLIB3:
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
PEDIDOS DE BANCO
8GiB 0:01:20 [101 MiB / s] [====================================>] 100%
BENCH GO HTTP
8GiB 0:00:21 [389MiB / s] [====================================>] 100%Esses números são extremamente estáveis e mostram os seguintes recursos:
- Leituras brutas de soquete são rápidas (duh).
- Go tem cerca de 80% da velocidade de uma leitura de soquete bruto.
- urllib3 tem cerca de 20% da velocidade de uma leitura de soquete bruto.
- solicitações é um pouco mais lento do que urllib3, o que faz sentido, pois
adicione alguns frames de pilha para os dados passarem.- httpplib é mais lento do que as solicitações / urllib3. Isso é simplesmente impossível,
e eu suspeito que devemos configurar o httplib ou a biblioteca de sockets em
uma maneira que o httplib não é.-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65829335
.
Sim, isso corrige o comportamento de desempenho do httplib para fazer muito mais sentido.
Novos resultados e conclusões:
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%
Portanto, indiscutivelmente o custo real aqui é httplib. Acelerar isso exige que o httplib saia do caminho.
Estou interessado em descobrir que parte do httplib está nos custando. Eu acho que o perfil de bench_httplib.py
é um bom próximo passo.
Eu descartei a conversão do soquete em um objeto de arquivo por meio de socket.makefile
adicionando essa linha ao teste bench_socket.py
, que não diminui a velocidade. Estranhamente, parece torná-lo mais rápido.
A resposta é quase certamente a codificação de transferência: manipulação em partes.
Veja: https://github.com/alex/http-client-bench/pull/6 , mudando para
O comprimento do conteúdo no servidor produz alguns resultados inesperados.
Na sexta-feira, 05 de dezembro de 2014 às 12h24min53, Cory Benfield [email protected]
escreveu:
Portanto, indiscutivelmente o custo real aqui é httplib. Acelerar isso requer
tirando o httplib do caminho.Estou interessado em descobrir que parte do httplib está nos custando. eu
acho que a criação de perfil bench_httplib.py é uma boa próxima etapa.-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65831653
.
Interessante.
O manuseio em partes é quase com certeza o problema, e não estou realmente surpreso que go lide melhor com isso, especialmente porque em partes é o modo HTTP padrão para go.
No entanto, as solicitações sendo mais rápidas do que um soquete bruto é ... inesperado!
Uma coisa que vale a pena notar: se o soquete não estava decodificando a codificação fragmentada nos testes anteriores, ele obteve uma vantagem injusta, pois estava lendo menos dados do que os outros métodos! Todos eles liam os cabeçalhos em partes, bem como os 8 GB de dados.
Isso leva a uma pergunta que se segue: ainda achamos que todos esses métodos estão realmente lendo a mesma quantidade de dados?
Sim, a camada de soquete estava trapaceando, ela não decodificou os metadados fragmentados,
e tecnicamente leia um pouco menos. Estava lá como uma linha de base para "quão rápido
podemos ler ", para não provar nada.
Na sexta-feira, 05 de dezembro de 2014 às 12h33min10s, Cory Benfield [email protected]
escreveu:
Interessante.
O manuseio em pedaços é quase com certeza o problema, e eu realmente não
surpreso que go lida com isso melhor, especialmente porque chunked é o padrão
Modo HTTP para ir.No entanto, as solicitações sendo mais rápidas do que um soquete bruto é ... inesperado!
Uma coisa que vale a pena notar: se o soquete não estava decodificando a codificação em partes
nos testes anteriores, ele obteve uma vantagem injusta, pois na verdade era
lendo menos dados do que os outros métodos! Todos eles estavam lendo o
cabeçalhos fragmentados, bem como 8 GB de dados.Isso leva a uma pergunta que se segue: ainda achamos que todos esses métodos
estão realmente lendo a mesma quantidade de dados?-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -65833299
.
Eu não ficaria surpreso se isso estiver relacionado ao tamanho do bloco que estamos lendo no soquete de cada vez.
Bolo para @alex por ser super útil: cake:
@nelhage fez alguns stracing dos vários exemplos (na transferência
codificação: caso fragmentado) https://gist.github.com/nelhage/dd6490fbc5cfb815f762
são os resultados. Parece que há um bug no httplib que resulta nele
nem sempre lendo um pedaço completo do soquete.
Na segunda-feira, 8 de dezembro de 2014 às 9h05min14s Kenneth Reitz [email protected]
escreveu:
Bolo para @alex https://github.com/alex por ser super útil [imagem:
:bolo:]-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -66147998
.
Então, o que temos aqui é um bug em uma biblioteca padrão que ninguém está realmente mantendo? ( @Lukasa tem pelo menos 2 conjuntos de patches que estão abertos há> 1 ano.)
Alguém (posso chegar lá, não está claro) provavelmente precisa fazer uma busca detalhada com PDB
ou algo assim e descobrir qual código exato está gerando aqueles 20 bytes
lê para que possamos montar um bom relatório de bug.
Em Seg, 08 de dezembro de 2014 às 9:14:09 Ian Cordasco [email protected]
escreveu:
Então, o que temos aqui é um bug em uma biblioteca padrão que ninguém está realmente
mantendo? ( @Lukasa https://github.com/Lukasa tem pelo menos 2 patch
conjuntos que estão abertos há> 1 ano.) Talvez eu fale mal de uma lista
em algum lugar esta noite-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/kennethreitz/requests/issues/2371#issuecomment -66149522
.
Vou tentar encaixar isso hoje à noite ou amanhã, se ninguém mais fizer isso.
Então, alguma notícia sobre a causa raiz? O que está gerando essas leituras curtas e quanto a situação melhora sem elas?
@kislyuk Não que eu saiba. Espero ter algum tempo para persegui-lo neste feriado de Natal.
Obrigado @Lukasa. Estou lidando com um problema de desempenho em que a velocidade de download em uma resposta fragmentada usando urllib3 / requests é muito mais lenta do que com curl e outras bibliotecas e estou tentando entender se esse é o culpado.
Eu estava mexendo um pouco com isso. As leituras curtas vêm da função _read_chunked em httplib
https://fossies.org/linux/misc/Python-2.7.9.tgz/Python-2.7.9/Lib/httplib.py#l_585
As leituras de 2 bytes parecem vir principalmente da linha 622.
Obtive um padrão de strace ligeiramente diferente do postado anteriormente:
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 \ 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 \ 0 \ 0 "..., 54, 0, NULL, NULL) = 54
recvfrom (3, "\ r \ n", 2, 0, NULL, NULL) = 2
Esse padrão pode ser explicado da seguinte forma:
O padrão então se repetirá (volte para a etapa 1)
FWIW, descobri que uma modesta (20% ou mais) aceleração poderia ser feita aqui rolando o terminador do bloco de 2 bytes lido no corpo do bloco lido, ou seja, em vez disso:
value.append(self._safe_read(chunk_left))
amt -= chunk_left
self._safe_read(2) # toss the CRLF at the end of the chunk
faça isso em vez disso:
value.append(self._safe_read(chunk_left + 2)[:-2])
amt -= chunk_left
Porém, provavelmente seria melhor se a leitura dos 54 bytes pudesse armazenar mais bytes do que 54 (ou seja, 8192 bytes), o que significaria que o soquete do buffer não estaria vazio quando se trata da leitura de 2 bytes.
Além disso. Não tenho certeza se as pequenas leituras são o principal fator na perda de taxa de transferência (ou não no localhost). Eu brinquei com o tamanho do buffer de soquete de forma que fosse um múltiplo de 1031 bytes e, apesar do strace não ter mais leituras pequenas, ele não teve muito impacto no rendimento.
Acho que a perda de taxa de transferência pode ter mais a ver com a forma como o socket.py lida com pequenas leituras. Aqui está o código relevante (de socket.read):
https://fossies.org/linux/misc/Python-2.7.9.tgz/Python-2.7.9/Lib/socket.py#l_336
Quando você passa um comprimento explícito para socket.read e pode ser preenchido a partir de dados existentes em buffer, este é o caminho do código:
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
O problema que percebo aqui é que mesmo uma leitura de 2 bytes significa copiar o restante não lido em um novo StringIO. Parece que ficará muito caro para muitas leituras pequenas. Se um determinado StringIO pudesse de alguma forma ser drenado em cada leitura, em vez do padrão atual de copiar o restante não lido em um novo StringIO, então espero que isso possa ajudar no rendimento
@gardenia Não tive a chance de absorver tudo isso, mas muito obrigado pelo seu esforço e trabalho aqui. @shazow talvez você ache a pesquisa de @gardenia interessante.
: +1: obrigado @gardenia. A propósito, minha própria pesquisa sobre desempenho em meu caso de uso descobriu que, no meu caso, as respostas não são fragmentadas, mas o urllib3 executa 20% mais rápido do que as solicitações, portanto, há alguma sobrecarga sendo introduzida que desejo caracterizar. Ainda de acordo com o título deste problema, mas a causa raiz diferente.
Fascinante, obrigado por compartilhar! :)
Parece ser um grande objetivo para o Hyper da @Lukasa abordar também.
@alex - Eu brinquei um pouco com o problema de desempenho não fragmentado urllib3 vs solicitações que você mencionou. Acho que vejo uma queda semelhante de 20% nas solicitações.
Em solicitações, tentei especulativamente substituir a chamada para self.raw.stream pela implementação embutida de stream () (de urllib3). Pareceu aproximar muito a taxa de transferência entre as solicitações e o urllib3, pelo menos na minha máquina:
--- 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:
Talvez você possa tentar o mesmo em sua máquina para ver se faz diferença para você também.
(Observe que sim, eu sei que a chamada para is_fp_closed está acabando com o encapsulamento, não é um patch sério, apenas um ponto de dados)
@shazow É minha esperança que o BufferedSocket
que o hyper usa deve resolver muito dessa ineficiência, essencialmente evitando pequenas leituras. Eu me pergunto se httplib
em Py3 tem esse problema, porque usa io.BufferedReader
extensivamente, o que deve fornecer aproximadamente o mesmo tipo de benefício que BufferedSocket
.
Certamente, entretanto, quando hyper
aumenta a funcionalidade HTTP / 1.1 suficiente para ser útil, devemos tentar compará-la ao lado dessas outras implementações e fazer esforços para tornar hyper
mais rápido possível.
Inativo há quase um ano. Fechando.
Estou vendo problemas semelhantes, 10x menos taxa de transferência usando requests
em comparação com urllib3
.
Acho que o problema reside na classe HTTPResponse
do urllib3, quando ele é lido como um iterador, seu rendimento é muito ruim. Meu código está funcionando com um hack muito feio: eu retorno o objeto httplib.HTTPResponse
sublinhado usado por urllib3 e isso parece corrigir meu problema de taxa de transferência.
Fato interessante: a superclasse HTTPResponse
urllib3 é io.IOBase
. A superclasse httplib.HTTPResponse
Python3 é io.BufferedIOBase
. Eu me pergunto se isso tem alguma coisa a ver com isso.