ich hatte neulich folgenden "Problemstellung": gegeben ist ein SHA384 Hash-Wert, welcher sich aus einer bestimmten Geokoordinate berechnet. Per "Brute Force" soll nun aus einem Bereich von Koordinaten (in dem die Zielkoordinaten auf jeden Fall liegen die Zielkoordinaten bestimmt werden, in dem man für jede mögliche Koordinate den SHA384 Hash berechnet und mit dem Zielhash vergleich.
Simpel linear runter geschrieben sieht das so aus:
Code: Alles auswählen
import hashlib
import sys
import time
# Sollwert für die Zielkoordinaten
HashSoll = '6b80992d3cd3c975d1b14fa87a7f91bdbd6a13ed10692463965b61dd51bc6b5fc86e5c72cbbc62537ced4f9d035740f7'
# Struktur der Zielkoords : N54° AA.XXX' E013° BB.XXX'
# Zu testende Wertebereiche :
# AA: 21 – 26
# BB: 28 – 35
# XXX: 000 - 999
start = time.perf_counter()
for i in range(21,27,1):
for j in range (28,36,1):
for i_min in range (0,1000,1):
for j_min in range (0,1000,1):
HashTest = "N54° {}.{:0>3}' E013° {}.{:0>3}'".format(i, i_min, j, j_min)
HashIst = hashlib.sha384(HashTest.encode('utf-8')).hexdigest()
if HashIst == HashSoll:
print("Match für Wert <{}> bei HashWert".format(HashTest))
print(HashIst)
ende = time.perf_counter()
print("Laufzeit {:4.2f} Sekunden".format(ende - start))
sys.exit()
ende = time.perf_counter()
print("Laufzeit {:4.2f} Sekunden".format(ende-start))
print ("Kein Match, mit Soll = <{}>)".format(HashSoll))
Python-Version: 3.5.2 (falls es für jemanden wichtig ist) auf Ubuntu 16.04.
(Anm.: äquivalenter Code in Go braucht ca. 60 Sekunden bis zum Ergebnis, als 10 Sekunden weniger)
Jetzt kann man das ganze ja parallelisieren, indem man die Koordinatenbereich aufteilt und parallel berechnen lässt:
Code: Alles auswählen
import hashlib
import time
import concurrent.futures
RANGES = (((21, 22), (28, 31)),
((21, 22), (32, 35)),
((23, 24), (28, 31)),
((23, 24), (32, 35)),
((25, 26), (28, 31)),
((25, 26), (32, 35)))
def workhorse(args):
# Struktur der Zielkoords : N54° AA.XXX' E013° BB.XXX'
# Zu testende Wertebereiche :
# AA: 21 – 26
# BB: 28 – 35
# XXX: 000 - 999
target_hash = '6b80992d3cd3c975d1b14fa87a7f91bdbd6a13ed10692463965b61dd51bc6b5fc86e5c72cbbc62537ced4f9d035740f7'
for i in range(args[0][0], args[0][1]+1):
for j in range (args[1][0], args[1][1]+1):
for i_min in range (0,1000,1):
for j_min in range (0,1000,1):
coords = "N54° {}.{:0>3}' E013° {}.{:0>3}'".format(i, i_min, j, j_min)
coords_hash = hashlib.sha384(coords.encode('utf-8')).hexdigest()
if coords_hash == target_hash:
return coords
start = time.perf_counter()
pool = concurrent.futures.ProcessPoolExecutor(max_workers=4)
futures = [pool.submit(workhorse, ranges) for ranges in RANGES]
if concurrent.futures.wait(futures,
return_when=concurrent.futures.FIRST_COMPLETED):
found = time.perf_counter()
print('found it, shuting down remaining workers...')
pool.shutdown()
print('finished')
for future in futures:
res = future.result()
if res:
print(res)
finished = time.perf_counter()
print(found-start, finished-start)
Jedenfalls ist das ca. 15 Sekunden schneller als der nicht-parallele Ansatz.
Wenn ich jetzt den ThreadPoolExecutor nutze, dauert das ganze _deutlich_ länger, nämlich bei 4 Workern ca. 115 Sekunden Und damit ca. 45 Sekunden länger als der Nicht-lineare Ansatz und doppelt so lange wie der ProcessPoolExecutor.
Frage: woran liegt das? Meine Vermutung ist, dass die Prozesse auf die verfügbaren Kerne verteilt werden, die Threads aber ggf. nur auf einem Kern laufen und sich dadurch ausbremsen?
Sonstige Kommentare sind natürlich auch willkommen, besonders wenn jemand eine Idee für eine bessere Performance hat.
Gruß, noisefloor