Mam kilka pytań dotyczących wydajności tego prostego skryptu Pythona:

import sys, urllib2, asyncore, socket, urlparse
from timeit import timeit

class HTTPClient(asyncore.dispatcher):
    def __init__(self, host, path):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect( (host, 80) )
        self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path
        self.data = ''
    def handle_connect(self):
        pass
    def handle_close(self):
        self.close()
    def handle_read(self):
        self.data += self.recv(8192)
    def writable(self):
        return (len(self.buffer) > 0)
    def handle_write(self):
        sent = self.send(self.buffer)
        self.buffer = self.buffer[sent:]

url = 'http://pacnet.karbownicki.com/api/categories/'

components = urlparse.urlparse(url)
host = components.hostname or ''
path = components.path

def fn1():
    try:
        response = urllib2.urlopen(url)
        try:
            return response.read()
        finally:
            response.close()
    except:
        pass

def fn2():
    client = HTTPClient(host, path)
    asyncore.loop()
    return client.data

if sys.argv[1:]:
    print 'fn1:', len(fn1())
    print 'fn2:', len(fn2())

time = timeit('fn1()', 'from __main__ import fn1', number=1)
print 'fn1: %.8f sec/pass' % (time)

time = timeit('fn2()', 'from __main__ import fn2', number=1)
print 'fn2: %.8f sec/pass' % (time)

Oto wyjście, które dostałem Linux:

$ python2 test_dl.py
fn1: 5.36162281 sec/pass
fn2: 0.27681994 sec/pass

$ python2 test_dl.py count
fn1: 11781
fn2: 11965
fn1: 0.30849886 sec/pass
fn2: 0.30597305 sec/pass

Dlaczego Urllib2 jest o wiele wolniejszy niż asyncore w pierwszym biegu?

I dlaczego rozbieżność wydaje się zniknąć na drugim biegu?

Edytuj : Znalazłem hackish rozwiązanie tego problemu tutaj: Force Python Mecheresh / Urllib2, aby używać tylko żądań?

Pięć drugi opóźnienie znika, jeśli monkey-poprawę moduł gniazda w następujący sposób:

_getaddrinfo = socket.getaddrinfo

def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0):
    return _getaddrinfo(host, port, socket.AF_INET, socktype, proto, flags)

socket.getaddrinfo = getaddrinfo
12
ekhumoro 7 październik 2011, 21:32

3 odpowiedzi

Najlepsza odpowiedź

Wreszcie znalazłem Dobre wyjaśnienie co powoduje Ten problem i dlaczego:

Jest to problem z Resolver DNS.

Ten problem nastąpi na dowolne żądanie DNS, które Resolver DNS nie obsługuje. Właściwe rozwiązanie jest naprawienie resolvera DNS.

Co się dzieje:

  • Program jest włączony IPv6.
  • Kiedy wyszukuje nazwę hosta, Getaddrinfo () pyta najpierw na rekord AAAA
  • Resolver DNS widzi prośbę o rekord AAAA, idzie "Uhmmm i nie dunno, co to jest, wyrzuć go"
  • Klient DNS (Getaddrinfo () w Libc) czeka na odpowiedź ..... ma na czas, ponieważ nie ma odpowiedzi. (To jest opóźnienie)
  • Brak jeszcze otrzymanych rekordów, więc GetAddrinfo () idzie na żądanie rekordu. To działa.
  • Program otrzymuje rekordy i używa tych.

Nie tylko wpływa na rekordy IPv6 (AAAA), wpływa również na wszelkie inne rekordy DNS, które Resolver nie obsługuje.

Dla mnie rozwiązanie było zainstalowanie DNSMasq (ale przypuszczam, że każdy inny DNS Resolver zrobiłby).

1
Michael Hampton 10 luty 2017, 16:58

Jest to prawdopodobnie w systemie operacyjnym: Jeśli twoje prośby DNS OS CHACHES, pierwsza prośba musi być odpowiedzona przez serwer DNS, kolejne żądania o tej samej nazwie są już pod ręką.

Edytuj: Jak pokazują komentarze, prawdopodobnie nie jest problemem DNS. Nadal utrzymuję, że jest to system operacyjny, a nie pytona. Przetestowałem kod zarówno w systemie Windows, jak i na FreeBSD i nie widziałem tego rodzaju różnicy, obie funkcje potrzebują mniej więcej w tym samym czasie.

Co dokładnie powinno być, nie powinno być znaczącej różnicy na jeden wniosek. I / O i opóźnienie sieci stanowią prawdopodobnie około 90% tych czasów.

0
knitti 7 październik 2011, 23:37

Czy próbowałeś robić odwrotnie? Najpierw za pomocą Syncore i Urllib?

przypadek 1: Najpierw próbujemy Urllib, a następnie z Ayncore.

fn1: 1.48460957 sec/pass
fn2: 0.91280798 sec/pass

Obserwacja: Ayncore zrobił to samo operacja w 0,57180159 sekund mniej

Pozwala go odwrócić.

Case 2: Spróbujemy teraz z Ayncore, a następnie Urllib.

fn2: 1.27898671 sec/pass
fn1: 0.95816954 sec/pass the same operation in 0.12081717

Obserwacja: Tym razem Urllib wziął 0,32081717 sekund niż asyncore

Dwa konkluzje tutaj:

  1. Urllib2 zawsze zajmowałby więcej czasu niż asyncore, a to dlatego, że Urllib2 definiuje typ rodziny gniazdo jako nieokreślony, podczas gdy asyncore pozwala go zdefiniować go iw tym przypadku zdefiniowaliśmy go jako protokół AF_INET IPv4.

  2. Jeśli dwie gniazda są wykonane w tym samym serwerze, niezależnie od ANCORE lub URLLIB, drugi gniazdo działałby lepiej. I to z powodu domyślnego zachowania pamięci podręcznej. Aby zrozumieć więcej tego, sprawdź to: https://stackoverflow.com/a/6928657/1060337

Referencje:

Chcesz ogólny przegląd tego, jak działa gniazdo?

http://www.cs.odu.edu/~mweigle/courses/cs45-f06/lectrures/2-1-ClientServer.pdf.

Chcesz napisać własne gniazdo w Pythonie?

http://www.ibm.com/developerworks/linux/tutors/l-pysocks/index.html.

Aby wiedzieć o rodzinach gniazd lub terminologii ogólnej Sprawdź tę wiki:

http://en.wikipedia.org/wiki/berkeley_sockets.

Uwaga: Ta odpowiedź została ostatnio zaktualizowana 05 kwietnia 2012, 2Am Ist

0
Community 23 maj 2017, 10:29