Python: как асинхронизировать цикл for - PullRequest
0 голосов
/ 25 мая 2018

Можно ли повторить объект generator в Python с asyncio?Я сделал простую функцию с именем hash_generator(), которая возвращает уникальный хеш.Теперь я решил сравнить цикл и получил около 8 секунд для итерации для печати 100 000 хешей.Могу ли я запустить это в асинхронном режиме, чтобы сократить время?Я прочитал документацию об этом, но я в замешательстве.Я хочу исследовать асинхронность и начать с этой проблемы.

import hashlib
import string
import random
import time


def hash_generator():
    """Return a unique hash"""
    prefix = int(time.time())
    suffix = (random.choice(string.ascii_letters) for i in range(10))
    key = ".".join([str(prefix), str("".join(suffix))])
    value = hashlib.blake2b(key.encode(), digest_size=6).hexdigest()
    return value.upper()


"""Iterating the hashes and printing the time it loaded"""
hashes = (hash_generator() for i in range(100000))
time_before = time.time()
[print(i) for i in hashes]
time_after = time.time()
difference = time_after - time_before
print('Loaded in {0:.2f}sec'.format(difference))
# 40503CBA2DAE
# ...
# A511068F4945
# Loaded in 8.81sec

РЕДАКТИРОВАНИЕ 1

Функция random.choice() является основной причиной, по которой программе потребовалось слишком много времени для запуска.Я воссоздаю функцию ниже со текущим временем и случайной строкой из os.urandom (низкое столкновение) в качестве значений.Я пробовал многопоточность, но вместо того, чтобы выполнять задачу так быстро, она занимает слишком медленно.Любые рекомендации по рефакторингу приведенного ниже кода всегда приветствуются.

import hashlib
import time
import os
import timeit


def hash_generator():
    """Return a unique hash"""
    prefix = str(time.time())
    suffix = str(os.urandom(10))
    key = "".join([prefix, suffix])
    value = hashlib.blake2b(key.encode(), digest_size=6).hexdigest()
    return value.upper()


"""Iterating the hashes and printing the time it loaded"""
print(timeit.timeit(hash_generator, number=100000), "sec")
# 0.497149389999322 sec

РЕДАКТИРОВАТЬ 2

С помощью Джека Тейлора и Stackoverflowers я могу увидеть разницу, используя multiprocessing в течение 1M итераций.Я тестирую код ниже.

import hashlib
import time
import os
import timeit
import multiprocessing


def hash_generator(_=None):
    """Return a unique hash"""
    prefix = str(time.time())
    suffix = str(os.urandom(10))
    key = "".join([prefix, suffix])
    value = hashlib.blake2b(key.encode(), digest_size=6).hexdigest()
    return value.upper()


# Allows for the safe importing of the main module
if __name__ == "__main__":
    start_time = time.time()
    number_processes = 4
    iteration = 10000000
    pool = multiprocessing.Pool(number_processes)
    results = pool.map(hash_generator, range(iteration))
    pool.close()
    pool.join()
    end_time = time.time()
    pool_runtime = end_time - start_time
    print('(Pool) Loaded in: {0:.5f} sec'.format(pool_runtime))

    ordinary_runtime = timeit.timeit(hash_generator, number=iteration)
    print('(Ordinary) Loaded in: {0:.5f} sec'.format(ordinary_runtime))

iteration = 10
(Pool) Loaded in: 1.20685 sec
(Ordinary) Loaded in: 0.00023 sec

iteration = 1000
(Pool) Loaded in: 0.72233 sec
(Ordinary) Loaded in: 0.01767 sec

iteration = 1000
(Pool) Loaded in: 0.99571 sec
(Ordinary) Loaded in: 0.01208 sec

iteration = 10,000
(Pool) Loaded in: 1.07876 sec
(Ordinary) Loaded in: 0.12652 sec

iteration = 100,000
(Pool) Loaded in: 1.57068 sec
(Ordinary) Loaded in: 1.23418 sec

iteration = 1,000,000
(Pool) Loaded in: 4.28724 sec
(Ordinary) Loaded in: 11.56332 sec

iteration = 10,000,000
(Pool) Loaded in: 27.26819 sec
(Ordinary) Loaded in: 132.68170 sec

1 Ответ

0 голосов
/ 25 мая 2018

Похоже, что вам лучше использовать последовательную версию.Общепринятым является то, что в Python с заданиями, связанными с вводом / выводом (чтение / запись файлов, работа в сети), вы можете получить ускорение с помощью цикла обработки событий или нескольких потоков, а также с заданиями, связанными с процессором (например, вычислительные хэши).) вы можете ускорить работу, используя несколько процессов.

Однако я взял вашу версию и переписал ее, используя concurrent.futures и пул процессов, и вместо того, чтобы ускорить ее, она замедлилась в 10 раз.

Вот код:

from concurrent import futures
import hashlib
import string
import random
import time

def hash_generator():
    """Return a unique hash"""
    prefix = int(time.time())
    suffix = (random.choice(string.ascii_letters) for i in range(10))
    key = ".".join([str(prefix), str("".join(suffix))])
    value = hashlib.blake2b(key.encode(), digest_size=6).hexdigest()
    return value.upper()

def main(workers = None):
    """Iterating the hashes and printing the time it loaded"""
    time_before = time.time()
    with futures.ProcessPoolExecutor(workers) as executor:
        worker_count = executor._max_workers
        jobs = (executor.submit(hash_generator) for i in range(100000))
        for future in futures.as_completed(jobs):
            print(future.result())
    time_after = time.time()
    difference = time_after - time_before
    print('Loaded in {0:.2f}sec with {1} workers'.format(difference, worker_count))

if __name__ == '__main__':
    main()

# 2BD6056CC0B4
# ...
# D0A6707225EB
# Loaded in 50.74sec with 4 workers

С несколькими процессами возникают некоторые издержки, связанные с запуском и остановкой различных процессов, а также с межпроцессным взаимодействием, что, вероятно, является причиной многопроцессорности.версия медленнее, чем последовательная, хотя она использует все ядра процессора.

Вы также можете попробовать использовать кластеризацию для разделения работы на несколько компьютеров и / или написать алгоритм на языке более низкого уровня.(Go ударил меня как хороший выбор).Но стоит ли это вашего времени, я не знаю.

...