2013-11-21 90 views
0

我已經爲此搜尋了很多東西,但無法找到可以抓住我的頭的東西。我正在尋找的是如何使這個算法平行。它的並行方式無關緊要,比如多線程或多處理甚至分佈式計算,但是究竟是如何將節點或內核之間的工作分開?平行蠻力生成

def bruteforce(charset, maxlength): 
    return (''.join(candidate) 
     for candidate in itertools.chain.from_iterable(itertools.product(charset, repeat=i) 
     for i in range(1, maxlength + 1))) 
+1

開始在幾條指令分裂。 – njzk2

+0

結果產生的順序是否重要?你對len(charset)和maxlength有什麼樣的想法?你想讓它在並行化後運行*更快嗎?或者這只是好奇心? (這不是一個棘手的問題 - 例如,使用CPython下的線程,CPU綁定的任務運行速度較慢* –

+0

生成的結果順序並不重要。範圍可以變化。是的,並行化的原因是有點加快速度。如果你有一個關於如何用gpu來計算的建議,我不會介意它:)謝謝。 – TimCPogue

回答

1

你必須做的第一件事就是不要理會1-liner疾病;-)也就是說,並行處理增加了它自己的許多複雜性,所以你想保持你的代碼儘可能簡單和透明。這是一種推廣@BasSwinckels建議的方法。這並不短!但它是非常有效的:無論您擁有多少核心,它都會將您的CPU計量器釘在牆上。

CHARSET = "abcdefghijklmnopqrstuvwxyx" 
MAX_LENGTH = 6 # generate all strings from CHARSET with length 1 thru MAX_LENGTH 
NUM_PROCESSES = None # defaults to all available cores 

from itertools import product 

# string_gen is what the workers run. Everything else 
# runs in the main program. 
def string_gen(prefix, suffix_len, length): 
    # Generate all strings of length `length` starting with `prefix`. 
    # If length > suffix_len, only the last suffix_len characters 
    # need to be generated. 
    num_done = 0 
    if length <= suffix_len: 
     assert prefix == "" 
     for t in product(CHARSET, repeat=length): 
      result = "".join(t) 
      # do something with result 
      num_done += 1 
    else: 
     assert len(prefix) + suffix_len == length 
     for t in product(CHARSET, repeat=suffix_len): 
      result = prefix + "".join(t) 
      # do something with result 
      num_done += 1 
    return num_done 

def record_done(num): 
    global num_done 
    num_done += num 
    print num_done, "done so far" 

def do_work(pool, strings_per_chunk=1000000): 
    # What's the most chars we can cycle through without 
    # exceeding strings_per_chunk? Could do with this 
    # logs, but I'm over-reacting to 1-liner disease ;-) 
    N = len(CHARSET) 
    suffix_len = 1 
    while N**suffix_len <= strings_per_chunk: 
     suffix_len += 1 
    suffix_len -= 1 
    print "workers will cycle through the last", suffix_len, "chars" 

    # There's no point to splitting up very short strings. 
    max_short_len = min(suffix_len, MAX_LENGTH) 
    for length in range(1, max_short_len + 1): 
     pool.apply_async(string_gen, args=("", suffix_len, length), 
         callback=record_done) 
    # And now the longer strings. 
    for length in range(max_short_len + 1, MAX_LENGTH + 1): 
     for t in product(CHARSET, repeat=length-suffix_len): 
      prefix = "".join(t) 
      pool.apply_async(string_gen, args=(prefix, suffix_len, length), 
          callback=record_done) 

if __name__ == "__main__": 
    import multiprocessing 
    pool = multiprocessing.Pool(NUM_PROCESSES) 
    num_done = 0 
    do_work(pool) 
    pool.close() 
    pool.join() 
    expected = sum(len(CHARSET)**i 
        for i in range(1, MAX_LENGTH + 1)) 
    assert num_done == expected, (num_done, expected) 

有很多件,因爲你想要的是「塊」:各種大小的字符串。並行噱頭通常更容易使問題結構更加完全一致。但是可以處理塊狀 - 它只需要更多的代碼。

請注意assert聲明以及num_done的「無用」計算。並行代碼增加了複雜性的全新維度,所以從一開始就爲自己做一個好處和代碼防禦。你會嘗試許多根本無法工作的事情 - 這發生在每個人身上。

請注意,即使沒有多核心,打破1-liner疾病也提供了一個更有效的方法:計算並加入prefix只需一次更長的字符串將在長時間運行過程中節省數以億計的冗餘連接。

玩得開心:-)

+0

非常感謝你幫助我完成這一切。這非常有幫助。我將花一點時間來真正理解這一點,但它使事情變得更容易學習。感謝您的幫助。 編輯:我也爲單線疾病道歉;)我喜歡有時寫快速代碼。 – TimCPogue

+0

你能看看我寫的另一個關於你寫的腳本的問題嗎? http://stackoverflow.com/questions/20198348/python-parallel-hash-bruteforce – TimCPogue

2

如果我理解你的生成器表達式正確,你正試圖從1生成所有長度的話.. maxlength給予一定的字母表。

我不知道你想如何拆分你的問題(你有N工人嗎?),但一個明顯的方法是將第一個字母分開單詞列表,將它們交給各種並行工作者,然後所有人都必須將字母的所有可能組合加到0 .. maxlength - 1之後。

+0

這不是我以前想過的,但它似乎可以工作。我基本上只是假設我有多少工人分裂字符集。例如:[[「a」,「b」,「c」],[「d」,「e」,「f」]],但這不允許工作人員生成諸如「adc」之類的字符串。你澄清。 – TimCPogue

+0

他們仍然能夠生成該組合。你只分解第一個字母的可能字符,其餘的字仍然應該從完整的字母表中選擇。你只需要把函數簽名改成'do_work(first_letters,alphabet)'。 –

+0

每個工作人員的第一個字母都會有所不同,還是會通過字母表遞增? – TimCPogue