Узнайте, как создавать стили LaTeX? [закрыто]

27
задан Parker 16 August 2010 в 23:28
поделиться

5 ответов

РЕДАКТИРОВАТЬ : я расширяю ответ, чтобы включить более совершенный пример. В этом посте я обнаружил много враждебности и дезинформации относительно threading v.s. асинхронный ввод-вывод. Поэтому я также добавляю дополнительные аргументы, чтобы опровергнуть некоторую недействительную претензию. Я надеюсь, что это поможет людям выбрать правильный инструмент для правильной работы.

Это дублированный вопрос 3 дня назад.

Python urllib2.open работает медленно, нужен лучший способ чтения нескольких URL-адресов - qaru Python urllib2.urlopen () работает медленно, нужен лучший способ чтения нескольких URL-адресов

Я полирую код, чтобы показать, как получать несколько веб-страниц параллельно с использованием потоков.

import time
import threading
import Queue

# utility - spawn a thread to execute target for each args
def run_parallel_in_threads(target, args_list):
    result = Queue.Queue()
    # wrapper to collect return value in a Queue
    def task_wrapper(*args):
        result.put(target(*args))
    threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    return result

def dummy_task(n):
    for i in xrange(n):
        time.sleep(0.1)
    return n

# below is the application code
urls = [
    ('http://www.google.com/',),
    ('http://www.lycos.com/',),
    ('http://www.bing.com/',),
    ('http://www.altavista.com/',),
    ('http://achewood.com/',),
]

def fetch(url):
    return urllib2.urlopen(url).read()

run_parallel_in_threads(fetch, urls)

Как видите, конкретный код приложения состоит всего из 3 строк, которые можно свернуть в 1 строку, если вы агрессивны. Я не думаю, что кто-то может оправдать свое заявление о том, что это сложно и недостижимо.

К сожалению, в большинстве других размещенных здесь потоковых кодов есть некоторые недостатки. Многие из них проводят активный опрос, чтобы дождаться завершения кода. join () - лучший способ синхронизировать код. Я думаю, что этот код улучшил все примеры потоковой передачи.

keep-alive соединение

Предложение WoLpH об использовании keep-alive соединения может быть очень полезным, если все ваши URL-адреса указывают на один и тот же сервер.

twisted

Аарон Галлахер - поклонник twisted фреймворка и враждебно настроен ко всем, кто предлагает нить. К сожалению, многие его утверждения являются дезинформацией. Например, он сказал «-1 для предложения потоков. Это связано с вводом-выводом; потоки здесь бесполезны.«Это противоречит доказательствам, поскольку и Ник Т., и я продемонстрировали прирост скорости от используемого потока. На самом деле приложение, привязанное к вводу-выводу, больше всего выигрывает от использования потока Python (по сравнению с отсутствием прироста в приложении, привязанном к ЦП). Ошибочная критика Аарона в отношении thread показывает, что он довольно смущен параллельным программированием в целом.

Правильный инструмент для правильной работы

Я хорошо осведомлен о проблемах, связанных с параллельным программированием с использованием потоков, python, асинхронного ввода-вывода и т. д. У этого инструмента есть свои плюсы и минусы. Для каждой ситуации есть подходящий инструмент. Я не против скрученных (хотя сам не использовал их). Но я не верю, что мы можем категорически сказать, что резьба - ПЛОХО, а скрученная - ХОРОШО во всех ситуациях.

Например, если требование OP состоит в том, чтобы получить 10 000 веб-сайтов параллельно, предпочтительным будет асинхронный ввод-вывод. Распределение потоков не будет применимо (если, возможно, с Python без стека).

Противодействие Аарона к темам - в основном обобщения. Он не понимает, что это тривиальная задача распараллеливания. Каждая задача независима и не разделяет ресурсы. Так что большая часть его атаки не применима.

Учитывая, что мой код не имеет внешней зависимости, я назову его подходящим инструментом для правильной работы.

Производительность

Я думаю, что большинство людей согласятся с тем, что производительность этой задачи во многом зависит от сетевого кода и внешнего сервера, где производительность кода платформы должна иметь незначительное влияние. Однако тест Aaron показывает прирост скорости на 50% по сравнению с многопоточным кодом. Я думаю, что необходимо реагировать на этот очевидный прирост скорости.

В коде Ника есть очевидная ошибка, которая привела к неэффективности. Но как вы объясните увеличение скорости на 233 мс по сравнению с моим кодом? Я думаю, что даже фанаты скрученного звука воздержатся от поспешных выводов, чтобы отнести это к эффективности скрученного. В конце концов, существует огромное количество переменных вне системного кода, таких как производительность удаленного сервера, сеть, кеширование и реализация различий между urllib2 и скрученным веб-клиентом и так далее.

Чтобы убедиться, что многопоточность Python не приведет к большой неэффективности, я провожу быстрый тест для создания 5 потоков, а затем 500 потоков. Мне вполне комфортно сказать, что накладные расходы на порождение 5 потоков незначительны и не могут объяснить разницу в скорости в 233 мс.

In [274]: %time run_parallel_in_threads(dummy_task, [(0,)]*5)
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
Wall time: 0.00 s
Out[275]: <Queue.Queue instance at 0x038B2878>

In [276]: %time run_parallel_in_threads(dummy_task, [(0,)]*500)
CPU times: user 0.16 s, sys: 0.00 s, total: 0.16 s
Wall time: 0.16 s

In [278]: %time run_parallel_in_threads(dummy_task, [(10,)]*500)
CPU times: user 1.13 s, sys: 0.00 s, total: 1.13 s
Wall time: 1.13 s       <<<<<<<< This means 0.13s of overhead

Дальнейшее тестирование моей параллельной выборки показало огромную изменчивость времени отклика за 17 прогонов. (К сожалению, мне не пришлось проверять код Аарона).

0.75 s
0.38 s
0.59 s
0.38 s
0.62 s
1.50 s
0.49 s
0.36 s
0.95 s
0.43 s
0.61 s
0.81 s
0.46 s
1.21 s
2.87 s
1.04 s
1.72 s

Мое тестирование не подтверждает вывод Аарона о том, что многопоточность постоянно медленнее, чем асинхронный ввод-вывод, но с измеримой величиной. Учитывая количество задействованных переменных, я должен сказать, что это недопустимый тест для измерения систематической разницы в производительности между асинхронным вводом-выводом и потоковой передачей.

29
ответ дан 28 November 2019 в 04:46
поделиться

Используйте витой ! Это делает такие вещи до абсурда проще, чем, скажем, с использованием потоков.

from twisted.internet import defer, reactor
from twisted.web.client import getPage
import time

def processPage(page, url):
    # do somewthing here.
    return url, len(page)

def printResults(result):
    for success, value in result:
        if success:
            print 'Success:', value
        else:
            print 'Failure:', value.getErrorMessage()

def printDelta(_, start):
    delta = time.time() - start
    print 'ran in %0.3fs' % (delta,)
    return delta

urls = [
    'http://www.google.com/',
    'http://www.lycos.com/',
    'http://www.bing.com/',
    'http://www.altavista.com/',
    'http://achewood.com/',
]

def fetchURLs():
    callbacks = []
    for url in urls:
        d = getPage(url)
        d.addCallback(processPage, url)
        callbacks.append(d)

    callbacks = defer.DeferredList(callbacks)
    callbacks.addCallback(printResults)
    return callbacks

@defer.inlineCallbacks
def main():
    times = []
    for x in xrange(5):
        d = fetchURLs()
        d.addCallback(printDelta, time.time())
        times.append((yield d))
    print 'avg time: %0.3fs' % (sum(times) / len(times),)

reactor.callWhenRunning(main)
reactor.run()

Этот код также работает лучше, чем любое из других опубликованных решений (отредактировано после того, как я закрыл некоторые вещи, которые использовали большую пропускную способность):

Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 29996)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.518s
Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 30349)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.461s
Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 30033)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.435s
Success: ('http://www.google.com/', 8117)
Success: ('http://www.lycos.com/', 30349)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.449s
Success: ('http://www.google.com/', 8135)
Success: ('http://www.lycos.com/', 30349)
Success: ('http://www.bing.com/', 28611)
Success: ('http://www.altavista.com/', 8378)
Success: ('http://achewood.com/', 15043)
ran in 0.547s
avg time: 0.482s

И с использованием кода Ника Т, настроенного так, чтобы также дать среднее значение пять и лучше показать результат:

Starting threaded reads:
...took 1.921520 seconds ([8117, 30070, 15043, 8386, 28611])
Starting threaded reads:
...took 1.779461 seconds ([8135, 15043, 8386, 30349, 28611])
Starting threaded reads:
...took 1.756968 seconds ([8135, 8386, 15043, 30349, 28611])
Starting threaded reads:
...took 1.762956 seconds ([8386, 8135, 15043, 29996, 28611])
Starting threaded reads:
...took 1.654377 seconds ([8117, 30349, 15043, 8386, 28611])
avg time: 1.775s

Starting sequential reads:
...took 1.389803 seconds ([8135, 30147, 28611, 8386, 15043])
Starting sequential reads:
...took 1.457451 seconds ([8135, 30051, 28611, 8386, 15043])
Starting sequential reads:
...took 1.432214 seconds ([8135, 29996, 28611, 8386, 15043])
Starting sequential reads:
...took 1.447866 seconds ([8117, 30028, 28611, 8386, 15043])
Starting sequential reads:
...took 1.468946 seconds ([8153, 30051, 28611, 8386, 15043])
avg time: 1.439s

И с использованием кода Вай Ип Тунга:

Fetched 8117 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30051 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.704s
Fetched 8117 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30114 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.845s
Fetched 8153 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30070 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.689s
Fetched 8117 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30114 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.647s
Fetched 8135 from http://www.google.com/
Fetched 28611 from http://www.bing.com/
Fetched 8386 from http://www.altavista.com/
Fetched 30349 from http://www.lycos.com/
Fetched 15043 from http://achewood.com/
done in 0.693s
avg time: 0.715s

Должен сказать, мне нравится, что последовательные выборки выполнялись лучше для меня.

19
ответ дан 28 November 2019 в 04:46
поделиться

Вот пример использования python Threads . Другие примеры потоков здесь запускают поток для каждого URL-адреса, что является не очень дружелюбным поведением, если оно вызывает слишком много обращений для обработки сервером (например, для пауков часто бывает много URL-адресов на одном и том же хосте)

from threading import Thread
from urllib2 import urlopen
from time import time, sleep

WORKERS=1
urls = ['http://docs.python.org/library/threading.html',
        'http://docs.python.org/library/thread.html',
        'http://docs.python.org/library/multiprocessing.html',
        'http://docs.python.org/howto/urllib2.html']*10
results = []

class Worker(Thread):
    def run(self):
        while urls:
            url = urls.pop()
            results.append((url, urlopen(url).read()))

start = time()
threads = [Worker() for i in range(WORKERS)]
any(t.start() for t in threads)

while len(results)<40:
    sleep(0.1)
print time()-start

Примечание: Время, указанное здесь, предназначено для 40 URL-адресов и во многом будет зависеть от скорости вашего интернет-соединения и задержки на сервере. Находясь в Австралии, мой пинг> 300 мс

При WORKERS = 1 для запуска потребовалось 86 секунд
При WORKERS = 4 выполнение
заняло 23 секунды. с WORKERS = 10 потребовалось 10 секунд для запуска

, поэтому загрузка 10 потоков в 8,6 раза быстрее, чем одиночный поток.

Вот обновленная версия, в которой используется очередь. Есть как минимум пара преимуществ.
1. URL-адреса запрашиваются в том порядке, в котором они появляются в списке
. 2. Можно использовать q.join () , чтобы определить, когда все запросы выполнены
3.Результаты сохраняются в том же порядке, что и список URL-адресов

from threading import Thread
from urllib2 import urlopen
from time import time, sleep
from Queue import Queue

WORKERS=10
urls = ['http://docs.python.org/library/threading.html',
        'http://docs.python.org/library/thread.html',
        'http://docs.python.org/library/multiprocessing.html',
        'http://docs.python.org/howto/urllib2.html']*10
results = [None]*len(urls)

def worker():
    while True:
        i, url = q.get()
        # print "requesting ", i, url       # if you want to see what's going on
        results[i]=urlopen(url).read()
        q.task_done()

start = time()
q = Queue()
for i in range(WORKERS):
    t=Thread(target=worker)
    t.daemon = True
    t.start()

for i,url in enumerate(urls):
    q.put((i,url))
q.join()
print time()-start
5
ответ дан 28 November 2019 в 04:46
поделиться

Очевидно, что получение веб-страниц займет некоторое время, поскольку вы не получаете доступа ни к чему локальному. Если у вас есть несколько для доступа, вы можете использовать модуль threading , чтобы запустить пару одновременно.

Вот очень грубый пример

import threading
import urllib2
import time

urls = ['http://docs.python.org/library/threading.html',
        'http://docs.python.org/library/thread.html',
        'http://docs.python.org/library/multiprocessing.html',
        'http://docs.python.org/howto/urllib2.html']
data1 = []
data2 = []

class PageFetch(threading.Thread):
    def __init__(self, url, datadump):
        self.url = url
        self.datadump = datadump
        threading.Thread.__init__(self)
    def run(self):
        page = urllib2.urlopen(self.url)
        self.datadump.append(page.read()) # don't do it like this.

print "Starting threaded reads:"
start = time.clock()
for url in urls:
    PageFetch(url, data2).start()
while len(data2) < len(urls): pass # don't do this either.
print "...took %f seconds" % (time.clock() - start)

print "Starting sequential reads:"
start = time.clock()
for url in urls:
    page = urllib2.urlopen(url)
    data1.append(page.read())
print "...took %f seconds" % (time.clock() - start)

for i,x in enumerate(data1):
    print len(data1[i]), len(data2[i])

Это был результат, когда я его запустил:

Starting threaded reads:
...took 2.035579 seconds
Starting sequential reads:
...took 4.307102 seconds
73127 19923
19923 59366
361483 73127
59366 361483

Захват данных из потока путем добавления в список, вероятно, не рекомендуется (лучше использовать очередь), но он показывает, что там это разница.

0
ответ дан 28 November 2019 в 04:46
поделиться

Фактическое ожидание, вероятно, находится не в urllib2 , а в сервере и / или в вашем сетевом подключении к серверу.

Есть 2 способа ускорить это.

  1. Поддерживайте соединение (см. Этот вопрос о том, как это сделать: Python urllib2 с keep alive )
  2. Используйте множественные соединения, вы можете использовать потоки или асинхронный подход, как предложил Аарон Галлахер. Для этого просто используйте любой пример потоковой передачи, и у вас все будет хорошо :) Вы также можете использовать библиотеку multiprocessing , чтобы упростить задачу.
2
ответ дан 28 November 2019 в 04:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: