Warum klappt Binary bei mir nicht
-
@Th69
Das kann viele Ursachen haben. Falls nicht das gesamte Datenset in deinen File-Cache passt kann es z.B. schonmal daran liegen. Beim Kompilieren fliegen ein paar Teile wieder aus dem File-Cache - die müssen beim 1. Durchlauf dann wieder geladen werden. Der 2. Durchlauf kann daher dann mehr aus dem Cache bekommen -> schneller.
-
Ich hab mir mal den Spass gemacht die Vergleiche in einen Hilfs-Thread auszuladern. Damit bleibt quasi nur mehr die Zeit zum Lesen übrig, die Vergleiche laufen dann Asynchron im 2. Thread:
Alles sehr Q&D und ich kann nicht garantieren dass kein Bug drinnen ist:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading; namespace AllFilesInDir // Alle Dateien eines Ordners (Kommandozeile) lesen { internal class Program { public static bool MitRead = true; // Read abschalten TEST TEST TEST public static bool MitComp = true; // Vergleich abschalten TEST TEST TEST public enum Method { SequenceEqual, MemCmp, ForNxt, Unsafe, RtlCompareMemory, } public static Method VergleichsMethode = Method.RtlCompareMemory; public static Int64 GesBytes = 0; // Summe aller gelesenen Bytes public static int Datei2Fehlt = 0; // Anzahl fehlender Dateien in Directory 2 public static int Pfad2Fehlt = 0; // Anzahl fehlender Unterpfade in Directory 2 public static int LenDiff = 0; // Anzahl Dateien mit unterschiedlicher Länge public static int ByteDiff = 0; // Anzahl Dateien mit inhaltlichen Differenzen public static int AnzDir = 0; // Anzahl aller Directories incl. Start-Dir public static int AnzFiles = 0; // Anzahl aller gelesenen Dateien public static int AnzPuff = 0; // Anzahl aller gelesenen Puffer public static BufferPairPool Pool = new BufferPairPool(); public static AsyncComparer Comparer; public const int MaxPuffL = 128 * 1024; [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] static extern int memcmp(byte[] b1, byte[] b2, IntPtr count); [DllImport("ntdll.dll", EntryPoint = "RtlCompareMemory", SetLastError = false)] private static extern IntPtr RtlCompareMemory(byte[] Source1, byte[] Source2, IntPtr length); static void Main(string[] args) { using (var comp = new AsyncComparer(Pool)) { Comparer = comp; Main2(args); Comparer = null; } } static void Main2(string[] args) { DateTime dtStart = DateTime.Now; // Start-Zeitpunkt merken Console.WriteLine("Start = " + dtStart.ToString()); if (MitComp) Console.WriteLine("Vergleichsmethode = " + VergleichsMethode.ToString()); Console.WriteLine("MitRead=" + MitRead + ", MitComp=" + MitComp); Console.WriteLine(MaxPuffL.ToString("###,###,###,###,##0") + " Bytes = Puffergröße"); //string StartPfad1 = args[0]; // Pfad 1 aus Kommandozeile holen //string StartPfad2 = args[1]; // Pfad 2 aus Kommandozeile holen string StartPfad1 = "C:\\temp\\data"; string StartPfad2 = "C:\\temp\\data_copy"; PfadVerarbeiten(StartPfad1, StartPfad2); Console.WriteLine(GesBytes.ToString("###,###,###,###,##0") + " Bytes eingelesen"); Console.WriteLine(AnzDir.ToString("###,###,###,###,##0") + " Directories"); Console.WriteLine(AnzFiles.ToString("###,###,###,###,##0") + " Dateien"); Console.WriteLine(AnzPuff.ToString("###,###,###,###,##0") + " Puffer"); Console.WriteLine(Datei2Fehlt.ToString("###,###,###,###,##0") + " fehlende Dateien"); Console.WriteLine(Pfad2Fehlt.ToString("###,###,###,###,##0") + " fehlende Unterpfade"); Console.WriteLine(LenDiff.ToString("###,###,###,###,##0") + " Dateien mit unterschiedlicher Länge"); Console.WriteLine(ByteDiff.ToString("###,###,###,###,##0") + " ungleiche Dateien"); DateTime dtEnde = DateTime.Now; // Ende-Zeitpunkt merken System.TimeSpan Dauer = dtEnde - dtStart; Console.WriteLine("Ende = " + dtEnde.ToString() + ", Dauer = " + Dauer.ToString().Substring(0, 10)); Console.ReadLine(); // Warten auf ENTER } static void PfadVerarbeiten(string pPfad1, string pPfad2) // wird rekursiv aufgerufen { AnzDir++; // Anzahl verglichene DIR gesamt DirectoryInfo di1 = new DirectoryInfo(pPfad1); // Aktuelles Directory FileInfo[] fi1 = di1.GetFiles("*.*"); // Dateien im Directory int FilesAnz1 = fi1.Count(); // Anzahl Dateien im Directory DirectoryInfo[] ui1 = di1.GetDirectories("*.*"); // Unterordner im Directory int DirAnz1 = ui1.Count(); // Anzahl Unterordner im Directory DirectoryInfo di2 = new DirectoryInfo(pPfad2); // Aktuelles Directory FileInfo[] fi2 = di2.GetFiles("*.*"); // Dateien im Directory int FilesAnz2 = fi2.Count(); // Anzahl Dateien im Directory DirectoryInfo[] ui2 = di2.GetDirectories("*.*"); // Unterordner im Directory int DirAnz2 = ui2.Count(); // Anzahl Unterordner im Directory bool gefunden = false; string SuchName = ""; for (int i = 0; i < FilesAnz1; i++) // zu allen Dateien aus Pfad1 zugehörige Datei in Pfad2 suchen { gefunden = false; SuchName = fi1[i].Name.ToLower(); // ohne Lw:\Pfad for (int j = 0; j < FilesAnz2 && !gefunden; j++) { if (SuchName.CompareTo(fi2[j].Name.ToLower()) == 0) { gefunden = true; if (fi1[i].Length == fi2[j].Length) { LeseDateien(fi1[i].FullName, fi2[j].FullName, fi1[i].Length); } else { LenDiff++; } } } if (!gefunden) { Datei2Fehlt++; } } for (int i = 0; i < DirAnz1; i++) // Alle Unterordner verarbeiten { gefunden = false; SuchName = ui1[i].Name.ToLower(); // ohne Lw:\Pfad for (int j = 0; j < DirAnz2 && !gefunden; j++) { if (SuchName.CompareTo(ui2[j].Name.ToLower()) == 0) { gefunden = true; PfadVerarbeiten(ui1[i].FullName, ui2[j].FullName); // Rekursiver Aufruf } } if (!gefunden) { Pfad2Fehlt++; } } } static void LeseDateien(string pDsn1, string pDsn2, Int64 lenFile) { Comparer.Reset(); AnzFiles++; // Anzahl Dateien gesamt Int64 RestLen = lenFile; // Dateilänge beider Dateien using (var fs1 = new FileStream(pDsn1, FileMode.Open, FileAccess.Read)) using (var fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read)) { Int64 LeseLen = Math.Min(RestLen, MaxPuffL); while (LeseLen > 0) { if (Comparer.PeekDifferences() > 0) break; if (MitRead) { GesBytes += LeseLen; var p = Pool.Get(); p.Length = (int)LeseLen; fs1.Read(p.Buffer1, 0, (int)LeseLen); fs2.Read(p.Buffer2, 0, (int)LeseLen); if (MitComp) { AnzPuff++; Comparer.Enqueue(p); } } RestLen -= LeseLen; LeseLen = Math.Min(RestLen, MaxPuffL); } } if (Comparer.GetDifferences() > 0) ByteDiff++; } static unsafe bool MemCmp2(byte[] a1, byte[] a2, int length) { if (a1 == null || a2 == null || a1.Length < length || a2.Length < length) return false; fixed (byte* p1 = a1, p2 = a2) { long* x1 = (long*)p1, x2 = (long*)p2; long* x0 = x1 + length / sizeof(long); for (; x1 < x0; x1++, x2++) if (*x1 != *x2) return false; byte* b1 = (byte*)x1, b2 = (byte*)x2; const int SizeInt = sizeof(int); if ((length & SizeInt) != 0) { if (*(int*)b1 != *(int*)b2) return false; b1 += SizeInt; b2 += SizeInt; } const int SizeShort = sizeof(short); if ((length & SizeShort) != 0) { if (*(short*)b1 != *(short*)b2) return false; b1 += SizeShort; b2 += SizeShort; } if ((length & 1) != 0) return *b1 == *b2; return true; } } public class BufferPair { public int Length; public byte[] Buffer1 = new byte[MaxPuffL]; public byte[] Buffer2 = new byte[MaxPuffL]; } public class BufferPairPool { public BufferPairPool() { } public BufferPair Get() { lock (m_lock) { if (m_pool.Count == 0) return new BufferPair(); var p = m_pool[m_pool.Count - 1]; m_pool.RemoveAt(m_pool.Count - 1); return p; } } public void Return(BufferPair p) { lock (m_lock) m_pool.Add(p); } readonly object m_lock = new object(); readonly List<BufferPair> m_pool = new List<BufferPair>(); } public class AsyncComparer : IDisposable { public AsyncComparer(BufferPairPool pool) { m_pool = pool; m_comparerThread = new Thread(ThreadFn); m_comparerThread.IsBackground = true; m_comparerThread.Start(); } public void Dispose() { lock (m_lock) { m_stop = true; Monitor.PulseAll(m_lock); } m_comparerThread?.Join(); m_comparerThread = null; } public void Enqueue(BufferPair p) { lock (m_lock) { if (m_stop) throw new ObjectDisposedException(nameof(AsyncComparer)); while (m_queue.Count > 1) Monitor.Wait(m_lock); m_queue.Enqueue(p); Monitor.PulseAll(m_lock); } } public int PeekDifferences() { lock (m_lock) { if (m_stop) throw new ObjectDisposedException(nameof(AsyncComparer)); return m_differences; } } public int GetDifferences() { lock (m_lock) { if (m_stop) throw new ObjectDisposedException(nameof(AsyncComparer)); while (m_queue.Count > 0) Monitor.Wait(m_lock); return m_differences; } } public void Reset() { lock (m_lock) { if (m_stop) throw new ObjectDisposedException(nameof(AsyncComparer)); while (m_queue.Count > 0) Monitor.Wait(m_lock); m_differences = 0; } } void ThreadFn() { ThreadFn0(); lock (m_lock) { m_queue.Clear(); Monitor.PulseAll(m_lock); } } void ThreadFn0() { while (true) { BufferPair p = null; lock (m_lock) { while (m_queue.Count == 0 && !m_stop) Monitor.Wait(m_lock); if (m_stop) return; p = m_queue.Peek(); } bool result = Compare(p, VergleichsMethode); m_pool.Return(p); lock (m_lock) { if (m_stop) return; m_queue.Dequeue(); if (!result) m_differences++; Monitor.PulseAll(m_lock); } } } static bool Compare(BufferPair p, Method method) { switch (method) { case Method.SequenceEqual: return p.Buffer1.Take(p.Length).SequenceEqual(p.Buffer2.Take(p.Length)); case Method.MemCmp: return memcmp(p.Buffer1, p.Buffer2, (IntPtr)p.Length) == 0; case Method.ForNxt: int l = p.Length; var b1 = p.Buffer1; var b2 = p.Buffer2; if (l > b1.Length) throw new IndexOutOfRangeException(); if (l > b2.Length) throw new IndexOutOfRangeException(); for (int b = 0; b < l; b++) { if (b1[b] != b2[b]) return false; } return true; case Method.Unsafe: return MemCmp2(p.Buffer1, p.Buffer2, p.Length); case Method.RtlCompareMemory: var eqCount = RtlCompareMemory(p.Buffer1, p.Buffer2, (IntPtr)p.Length); return eqCount == (IntPtr)p.Length; default: throw new ArgumentException(nameof(method)); } } Thread m_comparerThread; bool m_stop; readonly BufferPairPool m_pool; readonly object m_lock = new object(); readonly Queue<BufferPair> m_queue = new Queue<BufferPair>(); int m_differences; } } }
-
ps: Puffer-Grösse hab ich runtergesetzt weil kleinere Puffer bei mir schneller sind. Vermutlich auch wieder ne Caching-Sache (CPU Caches).
-
@Th69
ich habe beide Routinen jeweils zweimal laufen lassen, dabei gibt es kaum einen Unterschied~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs (09.12.2022) - Start = 10.12.2022 14:02:37 Vergleichsmethode = unsafe MemCmp von Th69 MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße Pfad1=N:\VideosN\, Pfad2=N:\VideosN\ 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 10.12.2022 14:36:56, Dauer = 00:34:18.8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs (09.12.2022) - Start = 10.12.2022 14:53:03 Vergleichsmethode = unsafe MemCmp von Th69 MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße Pfad1=N:\VideosN\, Pfad2=N:\VideosN\ 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 10.12.2022 15:25:58, Dauer = 00:32:55.3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs (09.12.2022) - Start = 10.12.2022 15:29:33 Vergleichsmethode = CompareByteArray (C++ ASM DLL von HK) MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße Pfad1=N:\VideosN\, Pfad2=N:\VideosN\ 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 10.12.2022 16:02:58, Dauer = 00:33:24.8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs (09.12.2022) - Start = 10.12.2022 16:04:11 Vergleichsmethode = CompareByteArray (C++ ASM DLL von HK) MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße Pfad1=N:\VideosN\, Pfad2=N:\VideosN\ 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 10.12.2022 16:37:22, Dauer = 00:33:10.8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
@hustbaer
ich habe Dein bearbeitetes Programm mit den gleichen Daten laufen lassen - das Ergebnis sieht gut aus~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Start = 10.12.2022 17:02:50 Vergleichsmethode = RtlCompareMemory MitRead=True, MitComp=True 131.072 Bytes = Puffergröße 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 1.589.437 Puffer 0 fehlende Dateien 0 fehlende Unterpfade 0 Dateien mit unterschiedlicher Länge 0 ungleiche Dateien Ende = 10.12.2022 17:28:33, Dauer = 00:25:43.1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
@hustbaer
Ich habe Deine Routine in mein Compareprogramm eingebaut und bekomme eigentlich die gleichen Ergebnisse.
Mit der kleineren Puffergröße läuft es deutlich schneller, auch die Vergleichsroutine von Th69.
Hier noch ein paar Ergebnisse~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs (09.12.2022) - Start = 10.12.2022 18:22:47 Vergleichsmethode = RtlCompareMemory [DllImport] kernel32.dll MitRead=True, MitComp=True 131.072 Bytes = Puffergröße Pfad1=N:\VideosN\, Pfad2=N:\VideosN\ 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 1.589.437 Puffer 2.446 identische Dateien Ende = 10.12.2022 18:48:31, Dauer = 00:25:44.5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs (09.12.2022) - Start = 10.12.2022 18:50:50 Vergleichsmethode = RtlCompareMemory [DllImport] kernel32.dll MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße Pfad1=N:\VideosN\, Pfad2=N:\VideosN\ 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 10.12.2022 19:23:14, Dauer = 00:32:24.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs (09.12.2022) - Start = 10.12.2022 19:26:05 Vergleichsmethode = unsafe MemCmp von Th69 MitRead=True, MitComp=True 131.072 Bytes = Puffergröße Pfad1=N:\VideosN\, Pfad2=N:\VideosN\ 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 1.589.437 Puffer 2.446 identische Dateien Ende = 10.12.2022 19:52:05, Dauer = 00:25:59.9 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jetzt habe ich das auch in meinem Delphi-Programm getestet, das ist kein bemerkenswerter Laufzeitunterschied bei veränderter Puffergröße festzustellen
Start 09:12:13 Ende 09:38:37 Dauer 00:26:24 - 16.777.216 Bytes = Puffergröße
Start 15:19:19 Ende 15:45:34 Dauer 00:26:15 - 131.072 Bytes = PuffergrößeDas muss mit C# zusammenhängen.
-
@hkdd
Du könntest auch versuchen beim ÖffnenFileOptions.SequentialScan
mitzugeben. Mit dem Asynchronen Vergleich wird es vermutlich nichts bringen. Wenn du synchron vergleichst u.U. schon.Jetzt habe ich das auch in meinem Delphi-Programm getestet, das ist kein bemerkenswerter Laufzeitunterschied bei veränderter Puffergröße festzustellen
Start 09:12:13 Ende 09:38:37 Dauer 00:26:24 - 16.777.216 Bytes = Puffergröße
Start 15:19:19 Ende 15:45:34 Dauer 00:26:15 - 131.072 Bytes = PuffergrößeDas muss mit C# zusammenhängen.
Hm. OK. Dann weiss ich nicht woran es liegt
-
@hustbaer
mit meiner Delphi-Aussage muss ich mich revidieren. Die Puffer-Größe hat doch eine entscheidende Auswirkung.
Hier ein paar meiner Mess-Ergebnisse.Puffergröße Laufzeit in hex Min:Sek 0x010000 2:04 0x020000 1:14 <= 128 * 1024 0x030000 1:00 0x040000 0:56 0x050000 0:51 0x060000 0:47 0x070000 0:43 0x080000 0:41 0x090000 0:44 0x0A0000 0:43 0x0B0000 0:40 0x0C0000 0:42 0x0D0000 0:40 0x0E0000 0:40 0x0F0000 0:40 0x100000 0:37 0x110000 0:39 0x120000 0:38 0x130000 0:41 0x140000 0:37 0x150000 0:37 0x160000 0:38 0x170000 0:28 0x180000 0:25 = 1536 * 1024 0x200000 0:38 0x280000 0:35 0x300000 0:33
Ich habe mich jetzt für 0x180000 Bytes entschieden.
Das ist gut in Delphi und in C#.Evtl. hängt das aber auch von der Hardware ab (CPU, RAM, GDD, SSD, m.2 u.a.m.).
@hustbaer sagte in Warum klappt Binary bei mir nicht:
Mit dem Asynchronen Vergleich wird es vermutlich nichts bringen. Wenn du synchron vergleichst u.U. schon.
Was meinst Du damit ?
Ich lese Datei1 in Puffer1 und Datei2 in Puffer , danach Vergleich Puffer 1 mit 2, und danach wieder Datei1 in Puffer1 usw.Hier die Ergebnisse der C#- und der Delphi-Version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompHKw - Dateien Vergleichen - (C) 12.12.2022 Hartmut aus Dresden ------------------------------------------------------------------- Parameter: "N:\VideosN" "N:\VideosN" "/U" "/VN" "/F" "/M:200" "/A" ------------------------------------------------------------------- Vergleich: N:\VideosN\ D E L P H I - V E R S I O N mit: N:\VideosN\ ~~~~~~~~~~~~~~~~~~~~~~~ 2.446.Datei: N:\VideosN\Youtube-HK\...den 1971\zz09 So scheid ich denn (Finale).mp4 ================================================================= 82 Unter-Pfade gefunden - identisch. 2.446 Dateien gefunden - und verglichen... 2.446 Dateien verglichen - identisch. Es wurden 208.144.569.612 Bytes = 203.266.181 K-Bytes verglichen Start 18:51:31 Ende 19:18:00 Dauer 00:26:29 ========================================================<Ende>=== Linke MausTaste / ESC = Schließen Rechte MausTaste / F1 = Info ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs - Dateien vergleichen - (C) 12.12.2022 Hartmut aus Dresden --------------------------------------------------------------- Parameter: N:\VideosN N:\VideosN --------------------------------------------------------------- Vergleich: N:\VideosN\ C # - V E R S I O N mit: N:\VideosN\ Th69 Vergleichs-Methode ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ============================================================ 82 Unterordner gefunden 2.446 Dateien gefunden - davon 2.446 identische Dateien verglichen. Es wurden 208.144.569.612 Bytes = 203.266.181 K-Bytes verglichen. 133.902 Puffer gelesen, Pufferlänge: 1.572.864 Bytes. Start 19:24:40 Ende 19:50:57 Dauer 00:26:17.5 ===================================================<Ende>=== ESC = Programm beenden ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
@hkdd sagte in Warum klappt Binary bei mir nicht:
@hustbaer
mit meiner Delphi-Aussage muss ich mich revidieren. Die Puffer-Größe hat doch eine entscheidende Auswirkung.
Hier ein paar meiner Mess-Ergebnisse.Puffergröße Laufzeit in hex Min:Sek 0x010000 2:04 0x020000 1:14 <= 128 * 1024 0x030000 1:00 0x040000 0:56 0x050000 0:51 0x060000 0:47 0x070000 0:43 0x080000 0:41 0x090000 0:44 0x0A0000 0:43 0x0B0000 0:40 0x0C0000 0:42 0x0D0000 0:40 0x0E0000 0:40 0x0F0000 0:40 0x100000 0:37 0x110000 0:39 0x120000 0:38 0x130000 0:41 0x140000 0:37 0x150000 0:37 0x160000 0:38 0x170000 0:28 0x180000 0:25 = 1536 * 1024 0x200000 0:38 0x280000 0:35 0x300000 0:33
Ich habe mich jetzt für 0x180000 Bytes entschieden.
Das ist gut in Delphi und in C#.Evtl. hängt das aber auch von der Hardware ab (CPU, RAM, GDD, SSD, m.2 u.a.m.).
Ja, vermutlich. Was auch mit reinspielt ist ob du zwei Verzeichnisse auf dem selben Datenträger vergleichst oder zwei Verzeichnisse auf verschiedenen Datenträgern. Speziell wenn beide Verzeichnisse auf der selben HDD steht werden sehr grosse Puffer vermutlich viel bringen. Wenn es dagegen verschiedene Datenträger sind oder die Daten von einer SSD kommen sollte eine Puffergrösse zwischen 64k und 1 MB ausreichend sein.
@hustbaer sagte in Warum klappt Binary bei mir nicht:
Mit dem Asynchronen Vergleich wird es vermutlich nichts bringen. Wenn du synchron vergleichst u.U. schon.
Was meinst Du damit ?
Ich lese Datei1 in Puffer1 und Datei2 in Puffer , danach Vergleich Puffer 1 mit 2, und danach wieder Datei1 in Puffer1 usw.Mit
FileOptions.SequentialScan
teilst du dem OS mit dass du vor hast die Datei linear zu lesen. Das OS sollte dann dafür sorgen dass die nächsten Stücke der Datei bereits im Hintergrund gelesen werden, bevor dein Programm sie anfordert ("read ahead"). Wenn du wie in der von mir veränderten Version den Vergleich asynchron in einem eigenen Thread machst, dann wird das nicht viel bringen. Weil ja der Haupt-Thread sofort das nächste Datenstück anfordert. Wenn du dagegen den Vergleich im selben Thread machst wie das Lesen kann das viel bringen. Weil das OS dann eben während dein Programm den Vergleich ausführt bereits das nächste Datenstück von der HDD/SSD anfordert.Allerdings sollte das OS auch schlau genug sein zu erkennen dass du die Datei linear liest, ohne dass du
FileOptions.SequentialScan
verwendest. Das kann aber wiederrum abhängig von der Puffergrösse sein. Auf Linux z.B. bekommst du AFAIK read-ahead per Default nur mit Puffergrössen bis max. 128 kB.
-
Wenn die Verzeichnisse auf unterschiedlichen Datenträgern liegen könnte folgende Änderung nochmal einiges bringen:
using (var fs1 = new FileStream(pDsn1, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) using (var fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) { // ... // ... p.Length = (int)LeseLen; var r1 = fs1.ReadAsync(p.Buffer1, 0, (int)LeseLen); fs2.Read(p.Buffer2, 0, (int)LeseLen); r1.Wait(); if (MitComp) // ...
Mit kleinen Puffern macht das die Sache bei mir langsamer, aber mit ~1MB Puffern bringt es bei mir nochmal ne deutliche Beschleunigung.
-
Jetzt müsste man nur noch eine Funktion finden, welche automatisch für die lokale Gegebenheit die beste Puffergröße ermittelt.
-
@hustbaer sagte in Warum klappt Binary bei mir nicht:
using (var fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan))
Wenn ich diese Parameter hinzufüge ( 4 und 5)
FileStream fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read, 4096, FileOptions.SequentialScan);
dann wird für (4) = 4096 angezeigt: CS1503: Konvertierung von int in System.IO.FileShare nicht möglich.
und für (5) = FileOptions.SequentialScan wird angezeigt:
CS1503: Konvertierung von System.IO.FileOptions in int nicht möglich.Mit ALT+Eingabe wird das Statement folgendermaßen korrigiert
FileStream fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read, (FileShare)4096, (int)FileOptions.SequentialScan);
Beim Ausführen erhalte ich allerdings einen Fehler:
System.ArgumentOutOfRangeException: "Der Enumerationswert lag außerhalb des gültigen Bereichs. Parametername: share"Ich benutze allerdings kein USING.
-
@hkdd
Du hastFileShare.Read
unterschlagen.
-
@hustbaer
JA, das war es - trotz meiner neuen Linsen in den Augen habe ich das übersehen.
Was bedeuten diese 4096, ist das der Lesepuffer oder nimmt Read dann die angegebene Anzahl Bytes zum lesen ?
-
Was bedeuten diese 4096, ist das der Lesepuffer oder nimmt Read dann die angegebene Anzahl Bytes zum lesen ?
Das ist die Grösse für den internen Puffer der File-Streams. Wenn kleinere Lese bzw. Schreib-Operationen gemacht werden, dann gehen die über den Puffer. Oft liest bzw. schreibt man Dateien ja in kleinen Stücken, da bringt das dann einiges.
Lese bzw. Schreib-Operationen die grösser als der interne Puffer sind werden allerdings direkt an die OS Funktionen weitergeleitet. Für dein Programm ist der Puffer also irrelevant. Alle Konstruktoren wo man
FileOptions
angeben kann erfordern allerdings dass man die Puffergrösse angibt. Also hab ich einfach den Default-Wert von 4096 genommen.
-
@hkdd sagte in Warum klappt Binary bei mir nicht:
Ich benutze allerdings kein USING.
Macht in dem Fall keinen Unterschied. Bloss: wieso verwendest du es nicht?
-
@hustbaer
Ein wenig schneller scheint es zu sein. Das erste Ergebnis ist von gestern und das zweite von heute~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ============================================================ 82 Unterordner gefunden 2.446 Dateien gefunden - davon 2.446 identische Dateien verglichen. Es wurden 208.144.569.612 Bytes = 203.266.181 K-Bytes verglichen. 133.902 Puffer gelesen, Pufferlänge: 1.572.864 Bytes. Start 19:24:40 Ende 19:50:57 Dauer 00:26:17.5 ===================================================<Ende>=== ESC = Programm beenden ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompCs - Dateien vergleichen - (C) 12.12.2022 Hartmut aus Dresden --------------------------------------------------------------- Parameter: N:\VideosN N:\VideosN --------------------------------------------------------------- Vergleich: N:\VideosN\ mit: N:\VideosN\ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ============================================================ 82 Unterordner gefunden 2.446 Dateien gefunden - davon 2.446 identische Dateien verglichen. Es wurden 208.144.569.612 Bytes = 203.266.181 K-Bytes verglichen. 133.902 Puffer gelesen, Pufferlänge: 1.572.864 Bytes. Start 16:08:58 Ende 16:34:20 Dauer 00:25:22.0 ===================================================<Ende>=== ESC = Programm beenden ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ich habe versucht bei den beiden FileStream Anweisungen USING davor zu schreiben und nach dem zweiten Using den Rest mit {...} einzuklammern - das klappt aber nicht. Ohne USING - alles ist OK.
//================================================= // Datei 1 und 2 lesen und vergleichen //================================================= private bool VergleicheDateien(string pDsn1, string pDsn2, Int64 lenFile, string pPfad) { bool rc; Int64 RestLen = lenFile; // Dateilänge beider Dateien Int64 LeseLen = Math.Min(RestLen, MaxPuffL); FileStream fs1 = new FileStream(pDsn1, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan); FileStream fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan); while (LeseLen > 0) { Application.DoEvents(); if (Abbruch > 0) { return false; } GesBytes += LeseLen; // Anzahl gelesene Bytes gesamt this.Text = "CompHk " + GesBytes.ToString("###,###,###,###,##0") + " Bytes " + pPfad; fs1.Read(ByPu1, 0, (int)LeseLen); fs2.Read(ByPu2, 0, (int)LeseLen); AnzPuff++; // Anzahl verglichene Puffer if (LeseLen < 9) { for (int b = 0; b < LeseLen; b++) { if (ByPu1[b] != ByPu2[b]) { goto VerglErr; // Vergleich abbrechen } } break; } switch (VergleichsMethode) // Welche Art des Vergleiches { case compSequenceEqual: // C# interne Funktion if (!(ByPu1.Take((int)LeseLen).SequenceEqual(ByPu2.Take((int)LeseLen)))) { goto VerglErr; // Vergleich abbrechen } break; case compMemCmp: // [DllImport] msvcrt.dll if (memcmp(ByPu1, ByPu2, LeseLen) != 0) { goto VerglErr; // Vergleich abbrechen } break; case compForNxt: // C# interner Byte-für-Byte-Vergleich for (int b = 0; b < LeseLen; b++) { if (ByPu1[b] != ByPu2[b]) { goto VerglErr; // Vergleich abbrechen } } break; case compUnsafe: // unsafe MemCmp if (!MemCmp(ByPu1, ByPu2, (int)LeseLen)) { goto VerglErr; // Vergleich abbrechen } break; case compRtlCompareMemory: // [DllImport] kernel32.dll RtlCompareMemory IntPtr len = (IntPtr)LeseLen; //IntPtr rc = RtlCompareMemory(ByPu1, ByPu2, len); var rCod = RtlCompareMemory(ByPu1, ByPu2, len); if (rCod != len) { goto VerglErr; // Vergleich abbrechen } break; case compCompareByteArray: // [DllImport] CompareByteArray.dll von HK if (CompareByteArray(ByPu1, ByPu2, (int)LeseLen) != 0) { goto VerglErr; // Vergleich abbrechen } break; } // switch (VergleichsMethode) RestLen -= LeseLen; LeseLen = Math.Min(RestLen, MaxPuffL); } // while (LeseLen > 0) AnzFilesOK++; rc = true; goto UpEnde; // Vergleich abbrechen VerglErr: ByteDiff++; // es gibt Differenzen rc = false; UpEnde: fs1.Close(); fs2.Close(); return rc; } // static bool VergleicheDateien(string pDsn1, string pDsn2, Int64 lenFile)
-
@hkdd
Hab's nicht ausprobiert, aber ich nehme an der Grund für die Fehler ist dass dugoto
s verwendest. Ich würde empfehlen aufgoto
zu verzichten. Weiters wäre es praktisch die eigentliche Vergleichs-Funktion vom Rest des Programms zu entkoppeln. z.B. von der Aktualisierung des User-Interface und dem Test ob abgebrochen werden soll. Könnte dann z.B. so aussehen:private bool VergleicheDateien(string pDsn1, string pDsn2, Int64 lenFile, string pPfad) { int res = VergleicheDateien0(pDsn1, pDsn2, lenFile, leseLen => { Application.DoEvents(); if (Abbruch > 0) { return false; } GesBytes += leseLen; // Anzahl gelesene Bytes gesamt AnzPuff++; // Anzahl verglichene Puffer this.Text = "CompHk " + GesBytes.ToString("###,###,###,###,##0") + " Bytes " + pPfad; return true; }); switch (res) { case 0: ByteDiff++; return false; case 1: AnzFilesOK++; return true; default: return false; } } private int VergleicheDateien0(string pDsn1, string pDsn2, Int64 lenFile, Func<long, bool> fortschrittsCallback) { Int64 RestLen = lenFile; // Dateilänge beider Dateien Int64 LeseLen = Math.Min(RestLen, MaxPuffL); using (FileStream fs1 = new FileStream(pDsn1, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) using (FileStream fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) { while (LeseLen > 0) { if (!fortschrittsCallback(LeseLen)) { return -1; } fs1.Read(ByPu1, 0, (int)LeseLen); fs2.Read(ByPu2, 0, (int)LeseLen); if (LeseLen < 9) { for (int b = 0; b < LeseLen; b++) { if (ByPu1[b] != ByPu2[b]) { return 0; } } break; } switch (VergleichsMethode) // Welche Art des Vergleiches { case compSequenceEqual: // C# interne Funktion if (!(ByPu1.Take((int)LeseLen).SequenceEqual(ByPu2.Take((int)LeseLen)))) { return 0; } break; case compMemCmp: // [DllImport] msvcrt.dll if (memcmp(ByPu1, ByPu2, LeseLen) != 0) { return 0; } break; case compForNxt: // C# interner Byte-für-Byte-Vergleich for (int b = 0; b < LeseLen; b++) { if (ByPu1[b] != ByPu2[b]) { return 0; } } break; case compUnsafe: // unsafe MemCmp if (!MemCmp(ByPu1, ByPu2, (int)LeseLen)) { return 0; } break; case compRtlCompareMemory: // [DllImport] kernel32.dll RtlCompareMemory IntPtr len = (IntPtr)LeseLen; //IntPtr rc = RtlCompareMemory(ByPu1, ByPu2, len); var rCod = RtlCompareMemory(ByPu1, ByPu2, len); if (rCod != len) { return 0; } break; case compCompareByteArray: // [DllImport] CompareByteArray.dll von HK if (CompareByteArray(ByPu1, ByPu2, (int)LeseLen) != 0) { return 0; } break; } // switch (VergleichsMethode) RestLen -= LeseLen; LeseLen = Math.Min(RestLen, MaxPuffL); } // while (LeseLen > 0) return 1; } }
Die neue Funktion
VergleicheDateien0
ist jetzt völlig unabhängig vom User-Interface. Was viele Vorteile hat, z.B. dass man sie dadurch unabhängig vom User-Interface testen kann.
-
ps:
Hast duReadAsync
auch mal ausprobiert?var r1 = fs1.ReadAsync(ByPu1, 0, (int)LeseLen); fs2.Read(ByPu2, 0, (int)LeseLen); r1.Wait();
-
@hustbaer
leider bin ich kein so ausgefuchster Programmierer, wie Du, aber ich kann viel von Dir lernen - DANKE dafür und auch an all die anderen hier im Forum.
Ich habe jetzt erst einmal die USINGs eingebaut und die GOTOs entfernt. Braucht man bei USING keine CLOSE mehr ?
ReadAsync werde ich nun auch noch ausprobieren, ob damit noch ein paar Sekunden herauszuholen sind.//================================================= // Datei 1 und 2 lesen und vergleichen //================================================= private bool VergleicheDateien(string pDsn1, string pDsn2, Int64 lenFile, string pPfad) { bool rc = true; Int64 RestLen = lenFile; // Dateilänge beider Dateien Int64 LeseLen = Math.Min(RestLen, MaxPuffL); using (FileStream fs1 = new FileStream(pDsn1, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) using (FileStream fs2 = new FileStream(pDsn2, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) { while (LeseLen > 0 && rc) { Application.DoEvents(); if (Abbruch > 0) { return false; } GesBytes += LeseLen; // Anzahl gelesene Bytes gesamt this.Text = "CompHk " + GesBytes.ToString("###,###,###,###,##0") + " Bytes " + pPfad; fs1.Read(ByPu1, 0, (int)LeseLen); fs2.Read(ByPu2, 0, (int)LeseLen); AnzPuff++; // Anzahl verglichene Puffer if (LeseLen < 9) { for (int b = 0; b < LeseLen; b++) { if (ByPu1[b] != ByPu2[b]) { rc = false; // Es gibt Differenzen, Vergleich abbrechen } } break; } switch (VergleichsMethode) // Welche Art des Vergleiches { case compSequenceEqual: // C# interne Funktion if (!(ByPu1.Take((int)LeseLen).SequenceEqual(ByPu2.Take((int)LeseLen)))) { rc = false; // Es gibt Differenzen, Vergleich abbrechen } break; case compMemCmp: // [DllImport] msvcrt.dll if (memcmp(ByPu1, ByPu2, LeseLen) != 0) { rc = false; // Es gibt Differenzen, Vergleich abbrechen } break; case compForNxt: // C# interner Byte-für-Byte-Vergleich for (int b = 0; b < LeseLen; b++) { if (ByPu1[b] != ByPu2[b]) { rc = false; // Es gibt Differenzen, Vergleich abbrechen } } break; case compUnsafe: // unsafe MemCmp if (!MemCmp(ByPu1, ByPu2, (int)LeseLen)) { rc = false; // Es gibt Differenzen, Vergleich abbrechen } break; case compRtlCompareMemory: // [DllImport] kernel32.dll RtlCompareMemory IntPtr len = (IntPtr)LeseLen; //IntPtr rc = RtlCompareMemory(ByPu1, ByPu2, len); var rCod = RtlCompareMemory(ByPu1, ByPu2, len); if (rCod != len) { rc = false; // Es gibt Differenzen, Vergleich abbrechen } break; case compCompareByteArray: // [DllImport] CompareByteArray.dll von HK if (CompareByteArray(ByPu1, ByPu2, (int)LeseLen) != 0) { rc = false; // Es gibt Differenzen, Vergleich abbrechen } break; } // switch (VergleichsMethode) RestLen -= LeseLen; LeseLen = Math.Min(RestLen, MaxPuffL); } // while (LeseLen > 0 && rc) } if (rc) { AnzFilesOK++; } // es gibt keine Differenzen else { ByteDiff++; } // es gibt Differenzen //fs1.Close(); //fs2.Close(); return rc; } // static bool VergleicheDateien(string pDsn1, string pDsn2, Int64 lenFile)