如何生成用于查找具有 9 个前导零的散列的计数器

How to generate a counter for finding a hash with 9 leading zeroes

我正在尝试创建一个函数,该函数将使用带有 9 个前导零的 sha1 算法生成散列。哈希基于一些随机数据,就像在并发挖掘中一样,我只想将 1 添加到哈希函数中使用的字符串。

为了更快,我使用池 class 中的 map() 使其在我的所有核心上都 运行,但是如果我传递的块大于范围( 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)))    

我想创建类似于全局计数器的东西,以便在 运行ning 多线程时将其输入函数,但是如果我尝试 range(sys.maxsize) 我会得到一个 MemoryError (我知道,因为我没有足够的 RAM,很少有人有),但我想将 range() 生成的列表分成块。 这是可能的还是我应该尝试不同的方法?

嗨,Alin,欢迎来到 Whosebug。

首先,是的,全局计数器是可能的。例如,传递给工人的 multiprocessing.Queue or a multiprocessing.Value。但是,从全局计数器获取新数字将导致锁定(并可能等待)计数器。这可以而且应该避免,因为您需要进行大量的反查询。我在下面提出的解决方案通过安装多个本地计数器来避免全局计数器,这些本地计数器就像一个全局计数器一样一起工作。

关于您的代码的 RAM 消耗,我看到两个问题:

  1. computesha return 大多数时候都是 None 值。这进入由 map 创建的迭代器(即使您没有分配 map 的 return 值)。这意味着迭代器比需要的大很多。
  2. 一般来说,一个进程的内存在进程结束后被释放。您的进程启动了很多任务,这些任务都保留了自己的内存。一个可能的解决方案是 maxtasksperchild 选项(参见 multiprocessing.pool.Pool 的文档)。当您将此选项设置为 1000 时,它会在完成 1000 个任务后关闭进程并创建一个新任务,从而释放内存。

但是,我想提出一个解决这两个问题的不同解决方案,非常 memory-friendly 并且运行速度更快(在我看来,经过 N<10 次测试)作为 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循环的每次迭代中,hashnewHash的内存可以重复使用,循环比函数更便宜而且只有number_of_processes函数必须进行调用,而无趣的结果将被遗忘。

一个可能的缺点是,计数器是完全独立的,每个进程都将完全 1/number_of_processes 完成整个工作,即使某些进程比其他进程快。最终,该程序与最慢的进程一样快。我没有测量它,但我想这是一个相当理论上的问题。

希望对您有所帮助!