Warum klappt Binary bei mir nicht
-
@hustbaer
ich habe jetzt auch mal sehr große Videodateien verglichen, da sind die Puffer-Vergleiche von Th69 und meine ASM-DLL die schnellsten. Sie kommen nahe an mein Delphi-Programm heran.
Die Methode von Th69 hat den Charm, dass man kein DllImport und damit keine Zusatzdateien zur EXE braucht, also alles läuft C# intern.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CompHKw - Dateien Vergleichen - (C) 07.09.2022 Hartmut, Dresden ----------------------------------------------------------------- Parameter: "N:\VideosN" "N:\VideosN" "/U" "/VJ" "/F" "/M:2" "/A" ----------------------------------------------------------------- Vergleich: N:\VideosN\ 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:42:22 Ende 19:34:59, Dauer = 00:52:37 ========================================================<Ende>=== Linke MausTaste / ESC = Schließen Rechte MausTaste / F1 = Info ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Start = 08.12.2022 18:30:45 CompCs Vergleichsmethode = unsafe MemCmp von Th69 MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 08.12.2022 19:28:56, Dauer = 00:58:11.2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Start = 08.12.2022 19:45:03 CompCs Vergleichsmethode = CompareByteArray (C++ ASM DLL von HK) MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße 208.144.569.612 Bytes eingelesen 83 Directories 2.446 Dateien 14.071 Puffer 2.446 identische Dateien Ende = 08.12.2022 20:38:14, Dauer = 00:53:11.7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
Hast du denn immer das Programm mindestens 2x laufen lassen, ehe du die Zeiten vergleichst?
Beim ersten Programmstart, direkt nach dem Kompilieren, muß ja erst noch der Jitter aktiviert werden, um das Programm zu optimieren.
Es werden zwar nur einige Prozente sein, aber nur dann ist der Vergleich sinnvoll.
-
@Th69 sagte in Warum klappt Binary bei mir nicht:
Hast du denn immer das Programm mindestens 2x laufen lassen, ehe du die Zeiten vergleichst?
Beim ersten Programmstart, direkt nach dem Kompilieren, muß ja erst noch der Jitter aktiviert werden, um das Programm zu optimieren.Soweit ich weiss muss das Programm immer geJITed werden, sofern es keinen "strong name" hat, also signiert ist. Und selbst bei Programmen mit "strong name" bin ich nicht sicher ob die native images automatisch gecached werden. Sollte aber eh egal sein, weil die JIT Zeit da wohl keinen signifikanten Unterschied machen wird. Das Programm ist ja nicht gross. Das Framework JITen dauert lange, aber das sollte ja bereits lange gecached sein.
-
@Th69
bei knapp einer Stunde Laufzeit überlegt man es sich, das zweimal laufen zu lassen.
Ich kann es aber noch einmal machen.
Ich werde berichten
-
@Th69 sagte in Warum klappt Binary bei mir nicht:
if ((length & 4) != 0) { if (*(int*)x1 != *(int*)x2) return false; x1 += 4; x2 += 4; } if ((length & 2) != 0) { if (*(short*)x1 != *(short*)x2) return false; x1 += 2; x2 += 2; } if ((length & 1) != 0) // ???? return *x1 == *x2;
Das stimmt so nicht.
x1
undx2
sindlong
Zeiger, mit+= 4
bzw.+= 2
schiebst du die 4x8 bzw. 2x8 Bytes weiter. Und beim letzten Vergleich für das einzelne Byte fehlt der Cast nachbyte*
.
-
@hustbaer
So sieht das in der überarbeiteten Version aus, da ist dieser Fehler behoben.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; }
-
-
@hustbaer: Es gibt aber immer Unterschiede zwischen dem 1. und weiteren Läufen (ab dem 2. sind die Zeiten dann bis auf 1/10s gleich, beim ersten Lauf sind es aber auch mal einige Sekunden mehr - bei meinen Tests mit ca. 10s Laufzeit.)
@hkdd: Du kannst es ja erst einmal mit einem kleinen Ordner laufen lassen.
-
@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.