Greenlet Vs. Потоки

Я новичок в gevents и greenlets. Я нашел хорошую документацию о том, как работать с ними, но никто не дал мне обоснования того, как и когда я должен использовать зелья!

  • На что они действительно хороши?
  • Можно ли использовать их на прокси-сервере или нет?
  • Почему не потоки?

Что я не уверен в том, как они могут предоставить нам concurrency, если они в основном являются совместными программами.

Ответ 1

Зелень обеспечивает concurrency, но не parallelism. concurrency - это когда код может работать независимо от другого кода. Parallelism - одновременное выполнение одновременного кода. Parallelism особенно полезен, когда в пользовательском пространстве требуется много работы, и это типично тяжелый процессор. concurrency полезен для устранения проблем, позволяя планировать и настраивать различные детали параллельно.

Greenlets действительно сияют в сетевом программировании, где взаимодействие с одним сокетом может происходить независимо от взаимодействия с другими сокетами. Это классический пример concurrency. Поскольку каждая зелень работает в своем собственном контексте, вы можете продолжать использовать синхронные API без потоковой передачи. Это хорошо, потому что потоки очень дороги с точки зрения виртуальной памяти и издержек ядра, поэтому concurrency, которую вы можете достичь с помощью потоков, значительно меньше. Кроме того, потоки в Python являются более дорогими и более ограниченными, чем обычно, благодаря GIL. Альтернативами concurrency обычно являются проекты типа Twisted, libevent, libuv, node.js и т.д., Где весь ваш код имеет один и тот же контекст выполнения и регистрирует обработчики событий.

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

Зелень предоставляет concurrency по причинам, которые я дал ранее. concurrency не parallelism. Сокрывая регистрацию событий и выполняя планирование для вас по вызовам, которые обычно блокируют текущий поток, проекты, такие как gevent, раскрывают этот concurrency, не требуя изменения асинхронного API и значительно меньшую стоимость вашей системы.

Ответ 2

Принимая @Max ответ и добавляя некоторую значимость к нему для масштабирования, вы можете увидеть разницу. Я достиг этого, изменив URL-адреса для заполнения следующим образом:

URLS_base = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']
URLS = []
for _ in range(10000):
    for url in URLS_base:
        URLS.append(url)

Мне пришлось отказаться от версии с несколькими процессорами, поскольку она упала до того, как у меня было 500; но при 10000 итераций:

Using gevent it took: 3.756914
-----------
Using multi-threading it took: 15.797028

Итак, вы можете видеть, что в I/O есть существенная разница с использованием gevent

Ответ 3

Это достаточно интересно анализировать. Вот код для сравнения производительности зеленых и многопроцессорных пулов по сравнению с многопоточными:

import gevent
from gevent import socket as gsock
import socket as sock
from multiprocessing import Pool
from threading import Thread
from datetime import datetime

class IpGetter(Thread):
    def __init__(self, domain):
        Thread.__init__(self)
        self.domain = domain
    def run(self):
        self.ip = sock.gethostbyname(self.domain)

if __name__ == "__main__":
    URLS = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']
    t1 = datetime.now()
    jobs = [gevent.spawn(gsock.gethostbyname, url) for url in URLS]
    gevent.joinall(jobs, timeout=2)
    t2 = datetime.now()
    print "Using gevent it took: %s" % (t2-t1).total_seconds()
    print "-----------"
    t1 = datetime.now()
    pool = Pool(len(URLS))
    results = pool.map(sock.gethostbyname, URLS)
    t2 = datetime.now()
    pool.close()
    print "Using multiprocessing it took: %s" % (t2-t1).total_seconds()
    print "-----------"
    t1 = datetime.now()
    threads = []
    for url in URLS:
        t = IpGetter(url)
        t.start()
        threads.append(t)
    for t in threads:
        t.join()
    t2 = datetime.now()
    print "Using multi-threading it took: %s" % (t2-t1).total_seconds()

вот результаты:

Using gevent it took: 0.083758
-----------
Using multiprocessing it took: 0.023633
-----------
Using multi-threading it took: 0.008327

Я думаю, что greenlet утверждает, что он не связан GIL в отличие от библиотеки многопоточности. Более того, документ Greenlet говорит, что он предназначен для сетевых операций. Для интенсивной работы с сетью потоковая коммутация прекрасна, и вы можете видеть, что многопоточный подход выполняется довольно быстро. Также он всегда предпочтет использовать официальные библиотеки python; Я попытался установить greenlet на windows и столкнулся с проблемой зависимости dll, поэтому я провел этот тест на linux vm. Alway попытается написать код с надеждой, что он работает на любой машине.