Как создать счетчик для поиска хеша с 9 ведущими нулями - PullRequest
1 голос
/ 13 июня 2019

Я пытаюсь создать функцию, которая будет генерировать хеш, используя алгоритм sha1 с 9 ведущими нулями.Хэш основан на некоторых случайных данных, и, как и в параллельном майнинге, я просто хочу добавить 1 к строке, которая используется в хэш-функции.

Для того, чтобы это было быстрее, я использовал map () из класса Pool, чтобы он работал на всех моих ядрах, но у меня возникает проблема, если я передаю чанк, превышающий диапазон (99999999)

def computesha(counter):
        hash = 'somedata'+'otherdata'+str(counter)
        newHash = hashlib.sha1(hash.encode()).hexdigest()     
        if newHash[:9] == '000000000':       
            print(str(newHash))
            print(str(counter))
            return str(newHash), str(counter)   

if __name__ == '__main__':

    d1 = datetime.datetime.now()
    print("Start timestamp" + str(d1))

    manager = multiprocessing.Manager()
    return_dict = manager.dict()

    p = Pool()
    p.map(computesha, range(sys.maxsize) )
    print(return_dict)
    p.close()
    p.join()

    d2 = datetime.datetime.now()  
    print("End timestamp " + str(d2))
    print("Elapsed time: " + str((d2-d1)))    

Я хочу создать что-то похожее на глобальный счетчик, чтобы передать его в функцию, пока он работает многопоточным, но если я попробую диапазон (sys.maxsize), я получу MemoryError (я знаю, потому что я неу меня недостаточно оперативной памяти, мало у кого есть), но я хочу разбить список, сгенерированный range (), на куски.Возможно ли это или я должен попробовать другой подход?

1 Ответ

0 голосов
/ 14 июня 2019

Привет, Алин, и добро пожаловать в stackoverflow.

Во-первых, да, глобальный счетчик возможен .Например, с многопроцессорной обработкой . Queue или многопроцессорной обработкой . Значение , которое передается рабочим.Однако выбор нового номера из глобального счетчика приведет к блокировке (и, возможно, ожиданию) счетчика.Этого можно и нужно избегать, так как вам нужно сделать МНОГО встречных запросов.Мое предлагаемое ниже решение позволяет избежать глобального счетчика, установив несколько локальных счетчиков, которые работают вместе, как если бы они были одним глобальным счетчиком.

Что касается потребления ОЗУ вашего кода, я вижу две проблемы:

  1. computesha возвращает значение None большую часть времени.Это входит в итератор, который создается map (даже если вы не присваиваете возвращаемое значение map).Это означает, что итератор намного больше необходимого.
  2. Вообще говоря, ОЗУ процесса освобождается после его завершения.Ваши процессы запускают множество задач, которые все резервируют свою собственную память.Возможное решение - опция maxtasksperchild (см. Документацию multiprocessing.pool.Pool ).Когда вы устанавливаете эту опцию на 1000, она закрывает процесс после 1000 задач и создает новую, которая освобождает память.

Однако я хотел бы предложить другое решение, которое решает обе проблемы, очень дружественный к памяти и работает быстрее (как мне кажется после N <10 тестов) как решение с опцией <code>maxtasksperchild:

#!/usr/bin/env python3
import datetime
import multiprocessing
import hashlib
import sys

def computesha(process_number, number_of_processes, max_counter, results):
    counter = process_number # every process starts with a different counter
    data = 'somedata' + 'otherdata'

    while counter < max_counter: #stop after max_counter jobs have been started
        hash = "".join((data,str(counter)))
        newHash = hashlib.sha1(hash.encode()).hexdigest()
        if newHash[:9] == '000000000':
            print(str(newHash))
            print(str(counter))

            # return the results through a queue
            results.put((str(newHash), str(counter)))
        counter += number_of_processes # 'jump' to the next chunk

if __name__ == '__main__':
    # execute this file with two command line arguments:
    number_of_processes = int(sys.argv[1])
    max_counter = int(sys.argv[2])

    # this queue will be used to collect the results after the jobs finished
    results = multiprocessing.Queue()

    processes = []
    # start a number of processes...
    for i in range(number_of_processes):
        p = multiprocessing.Process(target=computesha, args=(i,
                                                             number_of_processes,
                                                             max_counter,
                                                             results))
        p.start()
        processes.append(p)

    # ... then wait for all processes to end
    for p in processes:
        p.join()

    # collect results
    while not results.empty():
        print(results.get())
    results.close()

Этот код порождает желаемый number_of_processes, который затемвызовите функцию computesha.Если number_of_processes=8, то первый процесс вычисляет хэш для значений счетчика [0,8,16,24,...], второй процесс для [1,9,17,25] и т. Д.

Преимущества этого подхода: в каждой итерации цикла whileпамять hash и newHash можно использовать повторно, циклы дешевле, чем функции, и необходимо выполнять только number_of_processes вызовы функций, а неинтересные результаты просто забываются.

Возможный недостаток состоит в том, чточто счетчики полностью независимы, и каждый процесс будет выполнять ровно 1/number_of_processes всей работы, даже если некоторые из них быстрее, чем другие.В конце концов, программа работает так же быстро, как самый медленный процесс.Я не измерял это, но я полагаю, что это довольно теоретическая проблема.

Надеюсь, это поможет!

...