Warum klappt Binary bei mir nicht
-
Ja, das ist mir klar, daß es nur in VS 2022 und .NET 7 funktioniert (wollte nur schauen, ob es noch weitere Performanceverbesserungen dazu gibt).
Ähnliches beim Ordner- und Dateieinlesen: Warum langsamere (und speicherintensivere) Methoden verwenden, wenn es auch besser geht?@hkdd sagte in Warum klappt Binary bei mir nicht:
Oder geht es hier um den Vergleich der evtl. Restlänge, die sich aus der Division ergibt ?
length / sizeof(long)
Ja genau, es geht um die Restlänge, falls die Länge nicht exakt durch
sizeof(long)
(=8
) teilbar ist - dann werden noch die restlichen Bytes verglichen (zuerst evtl.int
[=4
], dannshort
[=2
] und zuletzt nochbyte
[=1
]).
-
@hkdd sagte in Warum klappt Binary bei mir nicht:
if ((length & 4) != 0)
Mit diesem Test soll festgestellt werden, ob die Länge ein Vielfaches von 4 ist oder anders gesagt ohne Rest durch 4 teilbar ist.
Nein. Es wird getestet, ob noch mindestens 4 Bytes da sind, die man testen kann. Bei 7 soll hier also in den Block gesprungen werden, weil noch 4 Bytes am Stück verglichen werden können. Man hätte auch
if (remaining_length >= 4)
schreiben können (dazu hätte man dann dieremaining_length
aber immer mitzählen müssen).Und direkt vor mir (hatte ich nicht gesehen, als ich die Antwort zu schreiben angefangen habe) hat @Th69 das ja bereits erklärt, sodass meine AW eigentlich überflüssig ist.
-
@wob
alles klarIch hätte noch eine Frage im Zusammenhang mit [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] oder [LibraryImport("kernel32.dll")].
Man kann ja auch selbst eine DLL schreiben und importieren.
Ich habe versucht, den DLL-Namen als Variable anzugeben, z.B.sostring MyDLL = "msvcrt.dll"; [DllImport(MyDLL, CallingConvention = CallingConvention.Cdecl)]
Das wird nicht akzeptiert. Der Compiler will selbst Zugriff auf die DLL haben zum Compilierungszeitpunkt.
Das Programm selbst greift auch auf die DLL zu zum Ausführungszeitpunkt.
Dann kann die EXE aber ganz woanders stehen. Wo wird nach der DLL gesucht ?
Im Ordner der EXE ?
In einem bestimmten Windows Order ?
Ich habe auf meiner C: mit Windows 10 mal nach "msvcrt.dll" gesucht und habe eine Menge unterschiedlichster DLL mit diesem Namen gefunden. Auf welche greift die EXE beim Ausführen zu und auf welche der Compiler zum Compilierungszeitpunkt. Man kann den Namen ja ganz ausführlich mit Laufwerk und Ordner angeben.
Wenn da aber nur "msvcrt.dll" angegeben ist, welche der vielen Dateien wird von wem (Compiler / EXE) benutzt und warum ?
Genügt es (bei einer eigenen DLL) diese im gleichen Ordner, wie die EXE zu speichern ?
Wenn man das Programm jemandem übergibt, weiß man doch nicht, wo dieser es abspeichert und von dort ausführt - wie wird da eine DLL gefunden wenn man im Source-Code bereits explizit den Dateinamen angeben muss? In den SYSTEM32 Ordner sollte man es ja auch nicht speichern, zumal man nicht weiß, ob ein anderer Programmierer auch eine Dackel.DLL geschrieben hat, dann wäre dieser Name bereits vorhanden, macht aber etwas ganz anderes.
Unproblematisch wäre es, wenn man die DLL in die EXE einbinden könnte und von dort benutzen, z.B. als Ressource.Datentr„ger in Laufwerk C: ist C(W) RyWin10 Sam 980 m.2 Volumeseriennummer: 44C6-DD4C Verzeichnis von C:\#hk\SpieleXP 14.04.2008 05:52 343.040 msvcrt.dll 1 Datei(en), 343.040 Bytes Verzeichnis von C:\#hk\SW\Gws4 24.09.1998 10:03 254.005 MSVCRT.DLL 1 Datei(en), 254.005 Bytes Verzeichnis von C:\Program Files\Pinnacle\Studio 24\programs 26.10.2020 08:02 634.880 msvcrt.dll 1 Datei(en), 634.880 Bytes Verzeichnis von C:\Program Files (x86)\Epson Software\Event Manager 24.12.2003 01:08 254.005 Msvcrt.dll 1 Datei(en), 254.005 Bytes Verzeichnis von C:\Program Files (x86)\Epson Software\Event Manager\Assistants\Scan Assistant 24.12.2003 01:08 254.005 Msvcrt.dll 1 Datei(en), 254.005 Bytes Verzeichnis von C:\Program Files (x86)\Nero\Nero 2021\Nero Burning ROM 19.07.2022 09:53 773.912 msvcrt.dll 1 Datei(en), 773.912 Bytes Verzeichnis von C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2311.1.11\amd64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_af4e7d20fdb56824\f 10.06.2021 05:58 7.722 msvcrt.dll 1 Datei(en), 7.722 Bytes Verzeichnis von C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2311.1.11\amd64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_af4e7d20fdb56824\r 10.06.2021 05:58 7.753 msvcrt.dll 1 Datei(en), 7.753 Bytes Verzeichnis von C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2311.1.11\wow64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_b9a3277332162a1f\f 10.06.2021 05:38 2.920 msvcrt.dll 1 Datei(en), 2.920 Bytes Verzeichnis von C:\Windows\servicing\LCU\Package_for_RollupFix~31bf3856ad364e35~amd64~~19041.2311.1.11\wow64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_b9a3277332162a1f\r 10.06.2021 05:38 3.064 msvcrt.dll 1 Datei(en), 3.064 Bytes Verzeichnis von C:\Windows\System32 08.09.2022 04:07 637.360 msvcrt.dll 1 Datei(en), 637.360 Bytes Verzeichnis von C:\Windows\SysWOW64 08.09.2022 04:07 775.256 msvcrt.dll 1 Datei(en), 775.256 Bytes Verzeichnis von C:\Windows\WinSxS\amd64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_af4e7d20fdb56824 08.09.2022 04:07 637.360 msvcrt.dll 1 Datei(en), 637.360 Bytes Verzeichnis von C:\Windows\WinSxS\amd64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_af4e7d20fdb56824\f 08.09.2022 04:07 7.722 msvcrt.dll 1 Datei(en), 7.722 Bytes Verzeichnis von C:\Windows\WinSxS\amd64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_af4e7d20fdb56824\r 08.09.2022 04:07 7.753 msvcrt.dll 1 Datei(en), 7.753 Bytes Verzeichnis von C:\Windows\WinSxS\wow64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_b9a3277332162a1f 08.09.2022 04:07 775.256 msvcrt.dll 1 Datei(en), 775.256 Bytes Verzeichnis von C:\Windows\WinSxS\wow64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_b9a3277332162a1f\f 08.09.2022 04:07 2.920 msvcrt.dll 1 Datei(en), 2.920 Bytes Verzeichnis von C:\Windows\WinSxS\wow64_microsoft-windows-msvcrt_31bf3856ad364e35_10.0.19041.546_none_b9a3277332162a1f\r 08.09.2022 04:07 3.064 msvcrt.dll 1 Datei(en), 3.064 Bytes Anzahl der angezeigten Dateien: 18 Datei(en), 5.381.997 Bytes 0 Verzeichnis(se), 738.345.742.336 Bytes frei
-
@hkdd sagte in Warum klappt Binary bei mir nicht:
Wo wird nach der DLL gesucht ?
Siehe:
https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-orderIst auch ein mögliches Ziel von Angriffen, wenn falsch gemacht und ein Angreifer Zugriff auf einen Ordner hat, der vor dem von dir geplanten durchsucht wird.
-
@hkdd sagte in Warum klappt Binary bei mir nicht:
Das wird nicht akzeptiert. Der Compiler will selbst Zugriff auf die DLL haben zum Compilierungszeitpunkt.
Du kannst eine Konstante anlegen:
const string MyDLL = "msvcrt.dll"; [DllImport(MyDLL, CallingConvention = CallingConvention.Cdecl)]
(bes. sinnvoll, wenn man mehrere Funktionen aus einer DLL ansprechen möchte, anstatt jedesmal wieder das String-Literal anzugeben)
Und dein 2. Satz davon stimmt nicht, denn so wie @wob verlinkt hat, wird erst zur Laufzeit nach der DLL gesucht.
-
Danke für Euere Erläuterungen. Ich habe eine kleine DLL für den Puffervergleich gemacht, analog der ASM-Routine in der Delphi Version. Die DLL ist mit C++ gemacht und wird mit dem cl-Compiler erstellt.
//puvgl.cpp Vergleich zweier Pufferinhalte 07.12.2022 extern "C" __declspec(dllexport) int PuVgl(char *b1, char *b2, int Psize) { int rc = 0; __asm { PUSH ESI PUSH EDI PUSH EBX PUSH ECX PUSH DS PUSH SS XOR EBX,EBX // EBX löschen MOV ESI,[b1] MOV EDI,[b2] MOV ECX,Psize // Länge in Bytes AND ECX,ECX // Länge = 0 ? JZ Ende CMP ECX,5 // Länge < 5 JL CmpBy // --> kein DW Vergleich MOV BL,CL // Bit 0+1 in BL retten SHR ECX,2 // Länge / 4 : in DWords AND BL,3 // Restlänge = 0 ? JZ CmpDW // --> ja, nur DWords vergleichen CLD REPZ CMPSD // DWords vergleichen JNZ Diff // --> Differenz MOV ECX,EBX // Restlänge 1..3 CmpBy: CLD REPZ CMPSB // Restlänge vergleichen JZ Ende Diff: POP SS POP DS MOV [rc],1 // RCode = 1 : Differenz ! PUSH DS PUSH SS JMP Ende CmpDW: CLD REPZ CMPSD // DWords vergleichen JNZ Diff // --> Differenz Ende: POP SS POP DS POP ECX POP EBX POP EDI POP ESI } return rc; }
Für das Compilieren benutze ich folgenden Batch :
REM MakePuVglDLL.bat cl puvgl.cpp /LD /EHsc PAUSE
Und zum Kopieren der DLL in die Ordner, wo sie benutzt wird, habe ich auch einen Batch
REM CopyPuVglDLL.bat copy puvgl.dll U:\#hk\SW copy puvgl.dll V:\#PgmHK\VC20xx\HKcs\CompHK\bin\Debug copy puvgl.dll V:\#PgmHK\VC20xx\HKcs\CompHK\bin\Release copy puvgl.dll V:\#PgmHK\VC20xx\HKcs\#TestPGMe\AllFilesInDir\bin\Debug copy puvgl.dll V:\#PgmHK\VC20xx\HKcs\#TestPGMe\AllFilesInDir\bin\Release PAUSE
Im C# Programm wird diese Routine folgendermaßen benutzt, puvgl.dll muss im EXE-Ordner stehen
[DllImport("puvgl.dll", CallingConvention = CallingConvention.Cdecl)] static extern int PuVgl(byte[] b1, byte[] b2, int size); :::: case compPuVgl: // [DllImport] PuVgl.dll von HK if (PuVgl(ByPu1, ByPu2, (int)LeseLen) != 0) { ByteDiff++; // es gibt Differenzen return; // Vergleich abbrechen } break;
Und so sieht das Ergebnis (Laufzeit) aus:
Start = 07.12.2022 17:19:49 Vergleichsmethode = PuVgl MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße 18.069.912.569 Bytes eingelesen 770 Directories 15.228 Dateien 16.152 Puffer 0 fehlende Dateien 0 fehlende Unterpfade 0 Dateien mit unterschiedlicher Länge 0 ungleiche Dateien Ende = 07.12.2022 17:19:57, Dauer = 00:00:08.2
Statt der DWORDS (4 Bytes) könnte man auch 8 Bytes mit den aktuellen CPUs benutzen.
Diese Routine stammt noch aus Turbo Pascal Zeiten, ist aber von der Methode mit der Routine von Th69 (dort 8 Bytes statt 4) ziemlich ähnlich.Ich habe gerade in der ASS-DLL noch einen Fehler erkannt für den Fall, dass es sich um eine sehr kleine Datei handelt, im konkreten Fall gibt es bei meinem Testordner eine Datei, die die Länge = 2 Bytes hat, d.h. es darf kein DWORD Vergleich (4 Bytes) stattfinden, das gibt ein falsches Ergebnis - es wird eine Differenz gemeldet, die keine ist.
Man kann natürlich im übergeordneten Programm kleine Dateien abfangen abfangen. Alle Längen < 8 werden mit der for-Schleife verglichen, die anderen Routinen werden nur für größere Dateien benutzt. Bei Länge == 0 wird kein Vergleich ausgeführt.
-
@Th69 sagte in Warum klappt Binary bei mir nicht:
Für den notwendigen Vergleich muss man deshalb immer die zu vergleichende Länge mitgeben, die ist meistens deutlich kleiner, als die Pufferlänge.
@Jockelx hatte dir doch im anderen Thema den Code dazu gegeben:
ByPu1.Take(count1).SequenceEqual(ByPu2.Take(count2)
Aber da diese Methode in .NET 4 noch unoptimiert ist (sondern allgemeingültig für alle Arten von Sequenzen (
IEnumerable
)), ist sie deswegen für Arrays nicht zu empfehlen.Daher hätte ich ja Spans vorgeschlagen
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr RtlCompareMemory(byte[] b1, byte[] b2, IntPtr length);
Sollte funktionieren. Zu beachten ist natürlich wie der Rückgabewert funktioniert: das Ding gibt die Anzahl der Bytes zurück die ab Start der Arrays gleich sind.
Auf jeden Fall besser als sich unnötig selbst eine DLL zu stricken.
-
@hustbaer
das habe ich so gemacht, bei mir klappt nur dieser DllImport[DllImport("ntdll.dll", EntryPoint = "RtlCompareMemory", SetLastError = false)] static extern long RtlCompareMemory(byte[] b1, byte[] b2, long length);
Wenn ich den Import aus kernel32.dll benutze, findet die Routine unzutreffender Weise eine Menge ungleiche Dateien.
Start = 08.12.2022 05:27:02 Vergleichsmethode = RtlCompareMemory [DllImport] kernel32.dll MitRead=True, MitComp=True 16.777.216 Bytes = Puffergröße 2.918.526.970 Bytes eingelesen 770 Directories 15.228 Dateien 15.217 Puffer 0 fehlende Dateien 0 fehlende Unterpfade 0 Dateien mit unterschiedlicher Länge 15.217 ungleiche Dateien Ende = 08.12.2022 05:28:04, Dauer = 00:01:01.8
Im Debugger kann ich nachverfolgen, um welche Dateien es sich Handelt.
Beide Dateien sind zwingend identisch, da ich ja den Pfad mit sich selbst vergleiche.
Es sind jpg-, zip- u.a. Dateien.Wenn ich den von Dir vorgeschlagenen DllImport
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr RtlCompareMemory(byte[] b1, byte[] b2, IntPtr length);
benutze und dazu folgenden Aufruf
case compRtlCompareMemory: // [DllImport] kernel32.dll RtlCompareMemory IntPtr len = (IntPtr)LeseLen; IntPtr rc = RtlCompareMemory(ByPu1, ByPu2, len); if ((int) rc != LeseLen) { ByteDiff++; // es gibt Differenzen return; // Vergleich abbrechen } break;
erhalte ich folgende Fehlermeldung beim Ausführen:
System.EntryPointNotFoundException: "Der Einstiegspunkt "RtlCompareMemory" wurde nicht in der DLL "kernel32.dll" gefunden."
Ich benutze Windo10 Pro 64 bit die aktuelle 22H2 Version.
@Th69 sagte in Warum klappt Binary bei mir nicht:
zuletzt noch byte [= 1])
Dazu habe ich noch eine Frage
if ((length & 1) != 0) return *x1 == *x2;
Müsste das nicht so lauten ?
if ((length & 1) != 0) return *(byte*)x1 == *(byte*)x2;
bei *x1 == *x2 würden doch 8 Bytes verglichen, obwohl nur noch eines im Puffer vorhanden ist.
-
Dieser Beitrag wurde gelöscht!
-
@hkdd: Ja, du hast Recht, der Code ist jetzt komplett falsch.
Ich wollte die Hauptschleife direkt mitlong
-Zeiger durchführen, anstatt den Cast zu verwenden (undx++
anstattx += 8
), aber danach wird für die Berechnungen einbyte*
benötigt...
Mea culpa - also nimm wieder den Originalcode aus dem von mir verlinkten Artikel.Warum jedoch "kernel32.dll" bei dir nicht funktioniert, weiß ich nicht - ich bin davon ausgegangen, daß "ntdll.dll" eher für Treiber ist (ich selber habe auch Win10 Pro, aber noch 21H2).
Evtl. findet er bei dir imPATH
eine andere "kernel32.dll" (das wäre dann aber generell fatal) - ruf mal "where kernel32.dll" in der Konsole auf.
-
@Th69
ich denke so ist es OK in der letzten Zeile.return *(byte*)x1 == *(byte*)x2;
-
Nee, dann passen die Inkrements
x1 += 4
etc. nicht!
-
@Th69
so sieht es bei mir jetzt aus//======================================== // Zwei Byte-Array vergleichen //======================================== static unsafe bool MemCmp(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 = 8 Bytes = 64 Bit for (int i = 0; i < length / sizeof(long); i++, x1++, x2++) if (*x1 != *x2) // 8 Bytes vergleichen return false; // Rest 0..7 vergleichen if ((length & 4) != 0) // Rest >= 4 ? { if (*(int*)x1 != *(int*)x2) // 4 Bytes vergleichen return false; x1 += 4; x2 += 4; } if ((length & 2) != 0) // Rest >= 2 ? { if (*(short*)x1 != *(short*)x2) // 2 Bytes vergleichen return false; x1 += 2; x2 += 2; } if ((length & 1) != 0) // Rest = 1 ? return *(byte*)x1 == *(byte*)x2; // 1 Byte vergleichen return true; } } // static unsafe bool MemCmp(byte[] a1, byte[] a2, int length)
was klappt da nicht.
wenn das x1 += 4; steht macht der + 8 * 4 statt nur +4 ?
-
Exakt, dann werden 4
long
-Adressen aufaddiert, anstatt 4 Bytes.
Nimmbyte* x1 = p1, x2 = p2
sowie die Originalschleife aus 5 ways to compare two byte arrays. Benchmarking ("Optimized unsafe method from Hafor Stefanson").
-
@Th69
ich habe gerade bei Debugger nachgesehen.
der pointer x1 wird von 49a8 auf 49c8 erhöht, d.h. + 0x020 = +32 = +4*8
-
@Th69 sagte in Warum klappt Binary bei mir nicht:
sowie die Originalschleife aus
Ich glaube, dort ist auch eine kleine Unkorrektheit
static unsafe bool MemCmp(byte[] a1, byte[] a2, int length) { if (a1 == null || a2 == null || a1.Length != a2.Length || a1.Length < length) return false; fixed (byte* p1 = a1, p2 = a2) { byte* x1 = p1, x2 = p2; int l = a1.Length; for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8) if (*((long*)x1) != *((long*)x2)) return false; if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; } if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; } if ((l & 1) != 0) if (*((byte*)x1) != *((byte*)x2)) return false; return true; } }
Das wird mit int l = a1.Length; die Länge von a1 benutzt, um später die zu vergleichende Restlänge zu bestimmen.
Wenn a1.Length > length ( zu vergleichende Länge bei großen Dateien ein Zwischenblock, der meistens ein Vielfaches von 16 lang ist) ist, dann muss die Restlänge aus length und nicht aus l bestimmt werden. Das war bei Deiner Version richtig.
-
Den
length
-Parameter habe ich ja hinzugefügt, und die Bedingung entsprechend geändert.Ich habe aber jetzt mal meine Variante abgewandelt (und noch ca. 0.2s Performance herausgeholt: 10.2s statt vorher 10.4s):
static unsafe bool MemCmp(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; } }
PS:
Was ich die ganze Zeit schon schreiben wollte:
Diese Methode geht davon aus, daß die übergebenenbyte
-Arrays immer auf einerlong
-Adresse beginnen (da sie ja vom OS bereitgestellt werden).
In der nativenmemcmp
wird jedoch auch überprüft, ob die Anfangsadresse "ungerade" ist, s. z.B. memcmp.c.
-
@Th69
jetzt ist das ja alles korrekt und sauber programmiert, falls int mal 20 Bytes lang wird in der übernächsten Generation.
Ich habe mal versucht, auch noch decimal einzubinden ( 16 Bytes statt long=8 Bytes), das klappt, es dauert aber länger.
-
Decimal
ist kein nativer Prozessor-Datentyp, daher hier nicht zu gebrauchen.
-
@Th69
bei c++ gibt es long long, das kennt C# wohl (noch) nicht.So ist es bei Delphi
Ausrichtung von strukturierten Typen
Per Voreinstellung sind die Werte in einem strukturierten Typ zur Beschleunigung des Zugriffs an Word- oder Double-Word-Grenzen ausgerichtet.Ich habe mir im Debugger die Adressen angeschaut, sie haben in der letzten Stelle eine 8, sind demnach auf long-Grenzen ausgerichtet.