Разница в производительности между urllib2 и asyncore

У меня есть некоторые вопросы о производительности этого простого скрипта python:

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) 

Вот результат, который я получаю от 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 

Почему urllib2 намного медленнее, чем асинкор в первом запуске?

И почему расхождение, похоже, исчезает во втором прогоне?

EDIT : нашел хакерское решение этой проблемы: Force python mechanize / urllib2 использовать только запросы A?

Пятисекундная задержка исчезает, если я обезвредил модуль сокета следующим образом:

 _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 

3 Solutions collect form web for “Разница в производительности между urllib2 и asyncore”

Наконец, нашел хорошее объяснение причин этой проблемы и почему:

Это проблема с DNS-резольвером.

Эта проблема возникнет для любого DNS-запроса, который не поддерживает DNS-преобразователь. Правильное решение – исправить DNS-резольвер.

Что происходит:

  • Программа поддерживает IPv6.
  • Когда он ищет имя хоста, getaddrinfo () сначала запрашивает запись AAAA
  • DNS-распознаватель видит запрос на запись AAAA, идет «ммм, я не знаю, что это такое, давайте выбросить его»
  • DNS-клиент (getaddrinfo () в libc) ждет ответа ….. должен тайм-аут, так как ответа нет. (ЭТО ЗАДЕРЖКА)
  • Еще не получено записей, поэтому getaddrinfo () отправляется на запрос записи A. Это работает.
  • Программа получает записи A и использует их.

Это не только влияет на записи IPv6 (AAAA), но также влияет на любую другую запись DNS, которую не поддерживает распознаватель.

Для меня решение было установить dnsmasq (но я полагаю, что любой другой DNS-реверсор будет делать).

Вероятно, это в вашей ОС: если ваша ОС кэширует DNS-запросы, на первый запрос должен быть дан ответ DNS-сервер, последующие запросы с тем же именем уже под рукой.

EDIT: как показывают комментарии, это, вероятно, не проблема DNS. Я все еще утверждаю, что это ОС, а не питон. Я тестировал код как на Windows, так и на FreeBSD и не видел такого различия, обе функции нуждаются примерно в том же времени.

Именно так и должно быть, не должно быть существенной разницы для одного запроса. Задержка ввода-вывода и сети составляет, вероятно, около 90% этих таймингов.

Вы пытались сделать обратное? т.е. сначала через syncore и urllib?

Случай 1: Сначала мы попробуем с urllib, а затем с ayncore.

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

Наблюдение: Ayncore выполнил ту же операцию в 0,57180159 сек меньше

Давайте отменим его.

Случай 2: Теперь мы попытаемся использовать ayncore, а затем urllib.

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

Наблюдение: На этот раз Урлиб взял 0.32081717 секунд, чем асинкор

Два вывода здесь:

  1. urllib2 всегда занимает больше времени, чем asyncore, и это связано с тем, что urllib2 определяет тип семейства сокетов как неуказанный, а асинхронный позволяет пользователю определить его, и в этом случае мы определили его как протокол AF_INET IPv4.

  2. Если два сокета сделаны на одном сервере независимо от ayncore или urllib, второй сокет будет работать лучше. И это из-за поведения кэша по умолчанию. Чтобы это понять, проверьте это: https://stackoverflow.com/a/6928657/1060337

Рекомендации:

Хотите общий обзор работы сокета?

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

Хотите написать свой собственный сокет в python?

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

Чтобы узнать о семействах сокетов или общей терминологии, проверьте эту вики:

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

Примечание: Этот вопрос был последний раз обновлен 05 апреля 2012 г., 2AM IST

  • Запрос POST HTTPS Python
  • urllib2 возвращает 404 для веб-сайта, который отлично отображает браузеры
  • Отправка данных с использованием POST в Python для PHP
  • Почему эта ошибка от urllib?
  • Как я могу использовать прокси SOCKS 4/5 с urllib2?
  • Установите Host-заголовок при использовании Python и urllib2
  • ValueError: неизвестный тип URL
  • отладка веб-службы python
  • как бороться с ® в url для urllib2.urlopen?
  • Получите данные json через url и используйте в python (simplejson)
  • Python: urllib / urllib2 / httplib путаница
  •  
    Interesting Posts for Van-Lav

    Нарезка массива numpy с другим массивом

    SWIG: объект 'module' не имеет атрибута 'Decklist'

    Ошибка установки Pymssql

    Вставить двоичный файл в базу данных SQLite с помощью Python

    Ошибки при установке автозапуска python

    Поезд scikit svm один за другим (онлайн или стохастическое обучение)

    Вычисление площади под кривой, заданной набором координат, не зная функции

    ValueError: общий размер нового массива должен быть неизменным

    Использование iter () с часовым, чтобы заменить while

    Как динамически строить объект JSON с помощью Python?

    Формат номерного номера с определенной маской regex python

    Как получить строку isotformat datetime, включая часовой пояс по умолчанию?

    Написание прокси-сервера тонких объектов путем реализации дескриптора __get__

    Есть ли способ вывода строк по очереди из исполняемой в настоящее время программы python?

    Python Flask: отслеживание пользовательских сеансов? Как получить идентификатор сеанса cookie?

    Python - лучший язык программирования в мире.