Warum klappt Binary bei mir nicht



  • @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 und x2 sind long 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 nach byte*.



  • @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;
    		}
    


  • @hkdd @Th69
    Upps, sorry, hab übersehen dass der Code in der alten Version stand 👍



  • @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öße

    Das muss mit C# zusammenhängen.



  • @hkdd
    Du könntest auch versuchen beim Öffnen FileOptions.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öße

    Das 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 hast FileShare.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 ?



  • @hkdd

    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.


Anmelden zum Antworten