Für kleine Dateien ist Hashing nur in Ordnung, aber bei großen kann man leicht feststellen, dass md5sum
die CPU gebunden ist. Gibt es einen Hashing-Algorithmus, der auf mehrere Kerne skaliert werden kann? Problemumgehungen? Ideen? Etwas? :) :)
Für kleine Dateien ist Hashing nur in Ordnung, aber bei großen kann man leicht feststellen, dass md5sum
die CPU gebunden ist. Gibt es einen Hashing-Algorithmus, der auf mehrere Kerne skaliert werden kann? Problemumgehungen? Ideen? Etwas? :) :)
Antworten:
Meine derzeit beste Lösung ist:
parallel --block=512M --pipepart -a …HUGEFILE… --progress --recend '' \
-k -j …NUMofProcessesSay4… md5sum | md5sum
- Es sollte angemerkt werden, dass:
pipe
als Eingabe keine Datei verwendenparallel
‚s , --pipepart
wie ich herausgefunden habe keine Unterstützung für FestplattenpartitionenAlso würde ich gerne auch andere Wege hören.
Leider ist MD5 ein linearer Prozess, bei dem sein Zustand von allen vorherigen Eingaben abhängt. Mit anderen Worten, Sie können es nicht wirklich parallelisieren. Außerdem sind mir keine echten Hash-Algen bekannt, die nicht auf diese Weise funktionieren.
Was Sie tun können (und basierend auf Ihrer Antwort, die Sie tun), ist, die Quelldateien zu teilen und gleichzeitig die md5sum jedes Chunks zu berechnen.
Wenn Sie das nicht können / wollen, mussten Sie eine schnellere Hash-Funktion wie xxHash , CityHash oder SpookyHash verwenden
Andere Idee (möglicherweise gilt dies für Ihre beabsichtigte Verwendung): Wenn Sie etwas schnelleres als MD5 benötigen (wenn auch mit einem Thread), können Sie CRC32 (das durch neuere CPUs hardwarebeschleunigt wird) für einen ersten schnellen Durchgang verwenden und auf MD5 zurückgreifen / SHA1 für einen zweiten Durchgang an scheinbar identischen Dateien.
Es gibt so gut wie kein Umgehen bei der Verarbeitung der gesamten Datei. MD4 oder CRC32 sind wahrscheinlich die besten Wetten für einen weit verbreiteten und schnellen Algorithmus (obwohl CRC32 weitaus weniger effektiv sein wird als MD4).
Das Testen verschiedener Implementierungen des Algorithmus Ihrer Wahl hilft dabei. Wenn Sie eine gut getestete asm-Implementierung finden, wird dies wahrscheinlich die Leistung der C / C ++ - Cousins verbessern.
Wenn Sie sich nicht wirklich für die Interoperabilität interessieren, können Sie das Hashing über mehrere Kerne hinweg problemlos durchführen, indem Sie die Datei in Blöcke aufteilen (muss nicht auf der Festplatte erfolgen, sondern nur von bestimmten Offsets lesen) und jeden Block separat verarbeiten (Dies führt jedoch zu einem ernsthaften Festplatten-Thrashing, was die Leistung beeinträchtigt, insbesondere bei mechanischen Festplatten.) Am Ende erhalten Sie separate Hashes für jeden Block (obwohl dies andere Vorteile hat, z. B. das Zeigen auf den kaputten Block), aber Sie können sie immer zusammen für einen endgültigen Wert hashen.
Dieser Kern könnte ein guter Anfang für etwas in Python sein.
split
kommt einem zwar in den Sinn, aber leider ist es keine Option, wenn wir über riesige Dateien sprechen (wie wir es tun).
Die meisten Antworten hier haben sich mit der linearen Natur der meisten Hashing-Algorithmen befasst. Obwohl ich sicher bin, dass es einige wirklich skalierbare Hashing-Algorithmen gibt, besteht eine einfachere Lösung darin, die Daten einfach in kleinere Teile aufzuteilen und jeweils einzeln zu hashen.
Betrachten Sie den BitTorrent-Ansatz: Wenn ein Torrent erstellt wird, werden alle Dateien in 'Blöcke' aufgeteilt, jeder Block einzeln gehasht und jeder dieser Hashes in der .torrent-Datei aufgezeichnet. Auf diese Weise kann ein Peer eingehende Daten schrittweise überprüfen, ohne warten zu müssen, bis die gesamte Datei zuerst heruntergeladen wurde. Fehler können auch blockweise korrigiert werden, anstatt dass die gesamte Datei erneut übertragen werden muss. Abgesehen von den logistischen Vorteilen ermöglicht dieser Ansatz auch das Skalieren von Hashing über mehrere Kerne hinweg. Wenn 8 Kerne verfügbar sind, können 8 Blöcke gleichzeitig gehasht werden.
Wenn Sie Ihren Überprüfungsprozess so gestalten, dass er mit einer Teilmenge der Daten arbeitet, z. B. mit Blöcken fester Größe, können Sie jeden Block auf einem separaten Kern hashen, wodurch eine große Verzögerung in der Pipeline vermieden wird. Offensichtlich hat dieser Ansatz einen kleinen Kompromiss zwischen Zeit und Speicher: Mit jeder zusätzlichen Hashing-Instanz ist ein gewisser Overhead verbunden, hauptsächlich in Form von Speicher, obwohl dies minimal ist, es sei denn, Sie führen Hunderte von Instanzen aus.
Ich arbeite an einem Tree-Hashing-Projekt, das genau für dieses Problem entwickelt wurde: paralleles Hashing großer Dateien von der Stange. Es funktioniert jetzt, obwohl es noch nicht überprüft wurde, und es besteht eine gute Chance, dass Änderungen gegenüber der Überprüfung zu Änderungen am endgültigen Digest führen. Das heißt, es ist sehr schnell: https://github.com/oconnor663/bao
Sie können hierfür md5deep und für andere Hashes Hashdeep verwenden. Es unterstützt Multithreading mit dem -j
Flag. Standardmäßig wird für jeden Kern ein Hashing-Thread erstellt. Es hat auch ein Flag, um Dateien vor dem Hashing in Teile zu zerlegen, verwendet jedoch nicht mehrere Threads für eine einzelne Datei. Ich habe dies verwendet, um sha256 von einer halben Million Dateien zu erhalten, und es hat großartig funktioniert. Es hat auch einen rekursiven Flash, der die Handhabung großer Verzeichnisbäume erleichtert.
Hier ist die Manpage dafür http://md5deep.sourceforge.net/md5deep.html und Git Repo https://github.com/jessek/hashdeep
Der Paketname in Ubuntu und Debian lautet md5deep und enthält Hashdeep.
-j
, dass Thread für jede angegebene Datei unterstützt wird, nicht für ihre Teile.
Es ist einfach, einen Hashing-Algorithmus zu entwerfen, der über mehrere Kerne skalierbar ist. Die bekanntesten Hashing-Algorithmen wurden nur speziell entwickelt, um dies zu verhindern, damit Aufgaben wie das Auffinden von Hash-Kollisionen so langsam wie möglich ausgeführt werden.
Hashing-Funktionen, die keine serielle Verarbeitung erzwingen, passen möglicherweise zu Ihnen, aber das hängt davon ab, welche Eigenschaften Sie von Ihrer Hashing-Funktion erwarten. Daher glaube ich nicht, dass Sie genug Informationen gegeben haben, um eine gute Empfehlung abzugeben.
Wie andere vorgeschlagen haben, können Sie eine Hashing-Funktion als Hash der verketteten Hashes jedes der Blöcke einer bestimmten Größe im Original erstellen. Solange die Blockgröße groß genug ist, um das Umkehren der Hashes einzelner Blöcke zu erschweren, funktioniert dies für die meisten Zwecke wahrscheinlich gut genug. Wie groß das sein sollte, hängt davon ab, wie vorhersehbar der Inhalt dieser Blöcke ist. Wenn Sie in der Lage sind, die Entropie zu schätzen und eine Blockgröße so zu wählen, dass Sie mehr als 128 Entropiebits pro Block erhalten, sollte dies für die meisten Zwecke ausreichen (und für viele, bei denen die Sicherheit nicht das Hauptanliegen ist, ein Overkill sein).
Aus Sicherheitsgründen sind Sie besorgt über den Entropiegrad auf Blockebene, da andernfalls das Auffinden einer Kollision für einen einzelnen Block ausreicht, um es einem böswilligen Akteur zu ermöglichen, einen Teil des Inhalts zu ersetzen und denselben endgültigen Hash zu erhalten.
Es ist vielleicht erwähnenswert, dass eine feste Blockgröße bedeutet, dass die Hauptschwäche von MD5s irrelevant ist - der Hacker kann keine zusätzlichen Daten an den Block anhängen.
Wenn es bei Ihren Anforderungen darum geht, natürlich vorkommende Hash-Kollisionen im Gegensatz zu böswilligen zu verhindern, können Sie es sich zweifellos leisten, eine viel schnellere Prüfsummenfunktion zu verwenden. Kryptografisch sichere Hashes sind in der Regel langsam zu berechnen.
Eine Funktion aus der Strangfunktionsgruppe, die den optionalen Hash-Baum-Modus verwendet, passt möglicherweise zu Ihnen. Andererseits könnte CRC32 alles sein, was Sie brauchen.