![]() |
Re: Eindeutiger Vergleich für große Dateien gesucht
@negaH:
gerade lese ich noch einen beitrag von dir über RPNG's (stichwort: radioaktiver zerfall), und da kommt schon wieder eine antwort auf diesen thread, danke! zum thema: ja, memory mapped files habe ich in diesem zusammenhang auch schonmal gehört (genauer gesagt heute mittag, als ich nach einer schnellen möglichkeit für den byte-per-byte vergleich gesucht habe: ![]() danke für deinen code! ich werde ihn mir morgen früh mal genauer ansehen und testen. |
Re: Eindeutiger Vergleich für große Dateien gesucht
moin hagen,
ich denke nicht, das man mit diesen Funktionen einen wesentlichen Geschwindigkeitszuwachs erzielen wird, finde aber FileOpen() logischer und einfacher zu Hand haben. (zwecks Portierung wirst du damit keine Probleme bekommen, mit der API vielleicht schon) |
Re: Eindeutiger Vergleich für große Dateien gesucht
also ich habe während ich vorhin den o.g. beitrag von negaH gelesen habe, einen test mit TFileStream laufen lassen.
dabei wurden wieder die beiden doppelten dateien verglichen (s. screenshot irgendwo vorne). dauer ca. 700 sekunden. edit: also byte-per-byte. gerade habe ich negaH's code eingebaut und der vergleich dauert 3,44 sekunden. erstaunlich. ich muss mir mal die hilfe zu den api befehlen (CreateFileMapping, usw.) ansehen, damit ich genauer verstehe, was sich hinter memory mapped files genau verbirgt. edit2: falls du es auch testen willst, hier der entsprechende code (ohne negaH's o.g. code. den habe ich einfach mal in die unit mit reinkopiert. einzige änderung war, dass ich Tmmf in TMemoryMappedFile umbenannt habe):
Delphi-Quellcode:
function Tmf.FilesAreBinaryEqual(const File1, File2: String): Boolean;
const BlockSize = 65536; var //FSFile1, FSFile2: TFileStream; FSFile1, FSFile2: TMemoryMappedFile; L1, L2: Integer; B1, B2: Array[1..BlockSize] of Byte; begin Result := False; //FSFile1 := TFileStream.Create(File1, fmOpenRead or fmShareDenyWrite); FSFile1 := TMemoryMappedFile.Create(File1); try //FSFile2 := TFileStream.Create(File2, fmOpenRead or fmShareDenyWrite); FSFile2 := TMemoryMappedFile.Create(File2); try if FSFile1.Size = FSFile2.Size then begin while FSFile1.Position < FSFile1.Size do begin L1 := FSFile1.Read(B1[1], BlockSize); L2 := FSFile2.Read(B2[1], BlockSize); if L1 <> L2 then Exit; if not CompareMem(@B1[1], @B2[1], L1) then Exit; end; Result := True; end; finally FSFile2.Free; end; finally FSFile1.Free; end; end; |
Re: Eindeutiger Vergleich für große Dateien gesucht
Das hört sich doch sehr gut.
Könnte gut möglich sein, dass das mit der Auslagerungsdatei zusammenhängt. |
Re: Eindeutiger Vergleich für große Dateien gesucht
@DaHead:
;) denoch machst du den Vergleich sehr umständlich !
Delphi-Quellcode:
das geht einfacher mit
if FSFile1.Size = FSFile2.Size then
begin while FSFile1.Position < FSFile1.Size do begin L1 := FSFile1.Read(B1[1], BlockSize); L2 := FSFile2.Read(B2[1], BlockSize); if L1 <> L2 then Exit; if not CompareMem(@B1[1], @B2[1], L1) then Exit; end; Result := True;
Delphi-Quellcode:
Denn das MMF hat ja schon die komplette Datei in den Speicher virtuell eingeblendet.
result := (FSFile1.Size = FSFile2.Size) and CompareMem(FSFile1.Memory, FSFile2.Memory, FSFile1.Size);
Du musst halt nur aufpassen das die beiden Dateien NICHT zu groß sind. Eine 2 Gb Datei wird man eben nicht vollständig in den Speicher einblenden können. Um denoch mit MMF's arbeiten zu können musst du dann aber mit MapViewOfFile() die Startposition + Pagegröße aus der Zieldatei stückchenweise einblenden. Am besten du baust dir eine reine API-Funktion die über das Windows-API + MMF's zwei Dateien binär vergleicht, also ohne VCL. Schneller ist das weil du immer mehr auf Zwischengepufferte Daten verzichtest, sprich keinerlei Caches und Speicherkopierungen mehr durchgeführt werden. Das OS lädt nämlich ohne Umwege die Datei in den Speicher. Aber 3,44 im Verhältnis zu 700 Sekunden hätte ich weis Gott nicht vermutet, da hätte ich Bigg mit seiner Aussage ebenfalls Recht gegeben. Bist du dir sicher das du auch wirklich die kompletten Dateien verglichen hast ? Schiebe mal ein FSFile1.Position := 0; und FSFile2.Position :=0; mit rein, oder benutze gleich meinen obigen Vorschlag. Ich kanns nämlich auch nicht so recht glauben das die MMF's rund 200x schneller sein sollen. Gruß Hagen |
Re: Eindeutiger Vergleich für große Dateien gesucht
@negaH:
ja, danke für den hinweis mit dem umständlichen code. ich war auch gerade dabei diesen zu optimieren, wollte mir allerdings vorher die entsprechenden msdn artikel zur gemüte führen.
Delphi-Quellcode:
wieviel wird hier eigentlich in den speicher gelesen? laut meiner hilfe ist der letzte parameter dwNumberOfBytesToMap ja auf 0.
P := MapViewOfFile(FMapping, FILE_MAP_READ, 0, 0, 0);
um das 2gb problem zu beheben müsste ich dwFileOffsetLow u. dwFileOffsetHigh erhöhen. was ich auch nicht ganz verstehe, ist wo bei folgendem code die erhöhung der akt. lese position stattfindet:
Delphi-Quellcode:
edit, hat sich erledigt mit der inkrementierung in obigem code, ist klar.
while FSFile1.Position < FSFile1.Size do
begin L1 := FSFile1.Read(B1[1], BlockSize); L2 := FSFile2.Read(B2[1], BlockSize); if L1 <> L2 then Exit; if not CompareMem(@B1[1], @B2[1], L1) then Exit; end; |
Re: Eindeutiger Vergleich für große Dateien gesucht
Zitat:
|
Re: Eindeutiger Vergleich für große Dateien gesucht
Zitat:
.Read setzt doch die Position immer auf das letze gelesene Byte + 1 oder? |
Re: Eindeutiger Vergleich für große Dateien gesucht
@sharky:
ja, du hast recht. ist mir gerade auch klar geworden als ich da zwei breakpoints auf die entsprechenden read stellen gesetzt habe. |
Re: Eindeutiger Vergleich für große Dateien gesucht
Zitat:
Um die MMF's richtig zu nutzen solltest du: 1.) GetFileSize() in einen Int64 speichern, also alle Zähler auf Int64 umbauen 2.) MapViewOfFile() muß mit einen (OffsetLo,OffsetHi) = Int64 arbeiten 3.) MapViewOfFile() sollte bei dwNumberOfBytesToMap mit zb. > 64Kb arbeiten 4.) die innerste Schleife muß also wiederholt mit MapVieOfFile() solche 64Kb Stückchen laden, dann mit CompareMem() vergleichen und wieder freigeben, zumindestens so lange wie sich beide Dateien gleichen Duch die Verwendung von Happen a >64Kb hast du nun nochmals einen Geschwindigkeitsvorteil. Denn nun bricht deine Laderoutine bei großen Datei die aber unterschiedlich sind viel frühzeitiger ab. Angenommen zwei 1Gb Dateien die sich aber schon in den ersten Bytes unterscheiden. Bei der jetzigen Methode muß das OS 2Gb an daten in den Speicher einlesen, das dauert. Bei der Methode mit 64Kb Stückchen geht das viel schneller. Und die Wahrscheinlichkeit das zwei gleichgroße Dateien unterschiedlich sind ist viel größer als das sie sich gleichen. Die 64Kb Stückchengröße sollte man jetzt durch Test ermitteln. Es gilt den korrekten Breakeven Point zu finden an dem die Größe der Stückchen im Verhältnis zum zeitlichen Overhead für MapViewOfFile() und UnmapViewOfFile() den schnellsten Vergleich ergibt. D.h. man sollte die Buffergröße inkrementell so lange erhöhen bis sich die größte Performance pro Byte ergibt. Ich persönlich würde dies dynamisch zur Laufzeit ermitteln. D.h. der Vergleichscode misst sich selber aus und inkremenmtiert successive die Buffergröße bis sich die größte Performance einstellt. Der Vorteil ist ja das dieser Dateivergleich sehr oft pro Session durchgeführt werden muß. Gruß Hagen |
Re: Eindeutiger Vergleich für große Dateien gesucht
hallo,
ja, das mit .Read ist mir jetzt klar (s. oben). 1.) GetFileSize speichere ich bereits als Int64. 2.) ok, werde ich einbauen. 3.) ok 4.) ja, so habe ich mir das auch gedacht. ich muss jetzt nur aus diesen beiden prozeduren (FilesAreBinaryEqual u. TMemoryMappedFile.Create) eine basteln, die das besagte problem löst. das mit den 64 kb blöcken ist ebenfalls logisch, wie du siehst habe ich ja auch in der TFileStream variante daten in dieser block größe eingelesen.
Delphi-Quellcode:
ja, das wäre am besten.
D.h. man sollte die Buffergröße inkrementell so lange erhöhen bis sich die größte Performance pro Byte ergibt.
aus der msdn: Zitat:
edit:
Delphi-Quellcode:
function GetSystemAllocationGranularity: Cardinal;
var PSysInfo: TSystemInfo; begin GetSystemInfo(PSysInfo); Result := PSysInfo.dwAllocationGranularity; end; |
Re: Eindeutiger Vergleich für große Dateien gesucht
GetSystemAllocationGranularity ermittelt also die Basis deiner "Buffergröße". Dein Buffer zb. 64Kb muß also ohne Rest durch GetSystemAllocationGranularity teilbar sein. In diesem Moment wird dwFileOffsetLo/Hi automatisch den geforderten Bedinungen gerecht, denn nach jedem Buffervergleich inkrementierst du ja die Int64-Offsets beider Dateien um die Buffergröße. dwFileOffsetLow = Int64Offset mod $100000000; dwFileOffsetHigh := Int64Offset div $100000000;
Diese zusätzliche Bedingung des API's zeigt sehr deutlich wie Hardwarenah die Funktionalität eigentlich ist. Gruß Hagen |
Re: Eindeutiger Vergleich für große Dateien gesucht
Ja, das stimmt. bei meinem system ist der durch GetSystemAllocationGranularity ermittelte buffer sogar genau 64 kb groß.
das TMemoryMappedFile erstelle ich durch eine leicht von deiner version abgewandelte prozedur (ich habe mir z.b. die property fSize eingebaut):
Delphi-Quellcode:
wie du siehst, frage ich im create nicht MapViewOfFile ab, da ich dies erst beim vergleich mache.
constructor TMemoryMappedFile.Create(const FileName: String);
var P: Pointer; begin fFileHandle := FileOpen(FileName, fmOpenRead or fmShareDenyWrite); if fFileHandle = INVALID_HANDLE_VALUE then raise Exception.Create(Format('Datei %s konnte nicht geöffnet werden!', [Filename])); //if fFileHandle > 0 then if fFileHandle <> INVALID_HANDLE_VALUE then begin fSize := GetFileSize(fFileHandle, nil); if fSize > 0 then begin fFileMap := CreateFileMapping(fFileHandle, nil, PAGE_READONLY, 0, 0, nil); { if fFileMap <> 0 then begin P := MapViewOfFile( fFileMap, FILE_MAP_READ, 0, // beginn der map 0, // 0); // zu lesende bytes, fSize liest kompl. Datei ein if P <> nil then inherited SetPointer(P, fSize); end; } end; end; inherited Create; end; hier die vergleichs prozedur:
Delphi-Quellcode:
allerdings bekomme ich bei CompareMem eine AV. ich glaube ich übergebe entweder die Length von Comparemem nicht korrekt oder es hapert bereits bei der P1, P2 pointer zuweisung.
function Tmf.CompareMemoryMappedFiles(const File1, File2: String): Boolean;
var MMF1, MMF2: TMemoryMappedFile; P1, P2: Pointer; CurPos: Int64; AllocGran: Cardinal; begin Result := False; AllocGran := GetSystemAllocationGranularity; //Todo: hier raus nehmen, nicht bei jedem aufruf erneut abfragen MMF1 := TMemoryMappedFile.Create(File1); MMF1.Position := 0; try MMF2 := TMemoryMappedFile.Create(File2); MMF2.Position := 0; try begin CurPos := 0; while CurPos < MMF1.Size do begin P1 := MapViewOfFile(MMF1.fFileMap, FILE_MAP_READ, CurPos, CurPos + AllocGran, AllocGran); P2 := MapViewOfFile(MMF2.fFileMap, FILE_MAP_READ, CurPos, CurPos + AllocGran, AllocGran); if not CompareMem(P1, P2, AllocGran) then //hier kriege ich die AV Exit; Inc(CurPos, AllocGran); end; Result := True; end; finally MMF2.Free; end; finally MMF1.Free; end; end; mein problem ist auch, dass ich durch MapViewOfFile nicht die Length kenne. daher übergebe ich AllocGran. |
Re: Eindeutiger Vergleich für große Dateien gesucht
ich glaube ich habe den fehler gefunden:
Delphi-Quellcode:
so gehts. ist das korrekt? bzw. ist es auch korrekt das MapViewOfFile im Create wegzulassen. ich denke schon, oder?
MapViewOfFile(MMF1.fFileMap, FILE_MAP_READ, CurPos, CurPos + AllocGran, AllocGran);
MapViewOfFile(MMF2.fFileMap, FILE_MAP_READ, CurPos, CurPos + AllocGran, AllocGran); if not CompareMem(@MMF1.fFileMap, @MMF2.fFileMap, AllocGran) then Exit; edit: tut mir leid dass ich hier soviel poste. diese 'lösung' ist nicht korrekt. die überprüfung ergibt hierbei immer false, er spring bei comparemem raus. ist also keine lösung. |
Re: Eindeutiger Vergleich für große Dateien gesucht
Delphi-Quellcode:
Ich sehe zwei Fehler
P1 := MapViewOfFile(MMF1.fFileMap, FILE_MAP_READ, CurPos, CurPos + AllocGran, AllocGran);
P2 := MapViewOfFile(MMF2.fFileMap, FILE_MAP_READ, CurPos, CurPos + AllocGran, AllocGran); if not CompareMem(P1, P2, AllocGran) then Exit; Inc(CurPos, AllocGran); 1.) MapViewOfFile() erwartet den Dateioffset der gemappt werden soll als 2x DWord. Du musst also CurPos als Int64 in zwei DWords zerlegen 2.) MapViewOfFile() kann fehlschlagen und dann ist P1 und/oder P2 = nil ! Nun, da du CurPos in MapViewOfFile() FALSCH übergibst ist sogar P1 und P2 = nil und daher deine AV. Richtiger so
Delphi-Quellcode:
Eventuelle Fehler in obigen Pseudocode darfst du behalten ;)
var
CurPosHi,CurPosLo: DWord; CurSize: DWord; CurPos: Int64; FileHandle1, FileHandle2: THandle; MapHandle1, MapHandle2: THandle; FileSize: Int64; begin Result := False; FileHandle1 := CreateFile(..., FileName1, ...); if FileHandle1 <> INVALID_HANDLE_VALUE then try FileHandle2 := CreateFile(..., FileName2, ...); if FileHandle2 <> INVALID_HANDLE_VALUE then try FileSize := GetFileSizeInt64(FileHandle1); if FileSize = GetFileSizeInt64(FileHandle1) then begin Result := True; if FileSize1 > 0 then begin MapHandle1 := CraeteFileMapping(FileHandle1); if MapHandle1 <> INVALID_HANDLE_VALUE then try MapHandle2 := CreateFileMapping(FileHandle2); if MapHandle2 <> INVALID_HANDLE_VALUE then try CurSize := FileSize mod AllocGran; if CurSize = 0 then CurSize := AllocGran; CurPos := FileSize - CurSize; repeat CurPosHi := CurPos div 2^32; CurPosLo := CurPos mod 2^32; P1 := MapViewOfFile(MapHandle1, ..., CurPosHi, CurPosLo, CurSize); if P1 <> nil then try P2 := MapViewOfFile(MapHandle2, ..., CurPosHi, CurPosLo, CurSize); if P2 <> nil then try Result := CompareMem(P1, P2, CurSize); finally UnmapViewOfFile(P2); end else RaiseLastWin32Error; finally UnmapViewOfFile(P1); end else RaiseLastWin32Error; CurPos := CurPos - CurSize; CurSize := AllocGran; until (CurPos < 0) or not Result; finally CloseFileMapping(MapHandle2); end else RaiseLastWin32Error; finally CloseFileMapping(MapHandle1); end else RaiseLastWin32Error; end; end; finally CloseFile(FileHandle2); end else RaiseLastWin32Error; finally CloseFile(FileHand1e1); end else RaiseLastWin32Error; end; Die Idee das Filemapping erst wirklich in der Vergleichs-Operation zu erzeugen halte ich für absolut richtig. Das wäre mein nächster Vorschlag gewesen. Alledings hätte ich sogar noch das CreateFileMapping() mit in die Vergleichsfunktion integriert. Gruß hagen Edit: shit nun zum 3. mal eine Änderung. Code arbeitet die Datei nun von Hinten nach Vorne ab, da die Wahrscheinlichkeit das sich de dateien am Ende unterscheiden höher sein dürfte. Das Exit/Break wurde entfernt, so ist es sauberer. Try Finallys niemals vergessen !! |
Re: Eindeutiger Vergleich für große Dateien gesucht
hallo negaH,
erstmal danke für deine ausführliche hilfe. ich werde versuchen deinen vorschlag umsetzen. ich melde mich wieder, wenn es geklappt hat und poste dann die fertige (hoffentlich) prozedur. nochmals vielen dank für deine hilfe! |
Re: Eindeutiger Vergleich für große Dateien gesucht
wow!
das muss ich aber erstmal verdauen, sprich für meinen bestehenden code anpassen. eventuell mach ich den vergleich dann wirklich in nur dieser einen prozedur (sprich create u. vergleich in einer prozedur). achja, was mir gerade noch einfällt: bevor du vorhin deinen code bearbeitet hattest, hab ich versucht zu testen. allerdings meckert bei mir der compiler bei 2^32. da sagt er mir pointer type required. er denkt wohl dass ich den pointer dereferenzieren möchte. das ^ bedeutet doch "hoch" (z.B. 2^2=4), oder täusche ich mich? nochmals vielen dank für deine mühe! |
Re: Eindeutiger Vergleich für große Dateien gesucht
Jo 2^32 = 2 hoch 32 = 1 shl 32 =
Delphi-Quellcode:
Alle 2^32 mit der Konstanten Power2of32 ersetzen, oder besser so:const Power2of32: Int64 = $100000000;
Delphi-Quellcode:
Du könnste dann sogar die beiden Variablen weg optimieren und gleich mit Int64Rec() casten -> SysUtils.
CurPosLo := Int64Rec(CurPos).Lo;
CurPosHi := Int64Rec(CurPos).Hi; Ich würde ebenfalls eine einzigste Function coden, warum will man noch eine aufwendige Klasse für diese Spezialaufgabe coden ? Gruß Hagen |
Re: Eindeutiger Vergleich für große Dateien gesucht
ah, danke für diesen hinweis!
Zitat:
ich bin zwar noch am testen (speicherlücken usw.) aber wenn die abgeschlossen sind, poste ich hier den fertigen code. vielleicht kann ja noch jemand was damit anfangen, bzw. vielleicht findet ihr ja noch fehler. vielen dank nochmal an alle die mitgeholfen haben, insbesondere an dich negaH! |
Re: Eindeutiger Vergleich für große Dateien gesucht
mal ne doofe frage.
wieso benutzt ihr MMFs für "non Random Reads" ? den vorteil den dir MMF bringt (das cachen dem OS überlassen) womit random reads eventuell schneller ausgeführt werden können, wirkt sich eventuell sogar negativ auf sequenzielle reads aus, wie ich sie beim comparen mache ?! wenn ich 10MB oder sogar 30 aufeinmal von der HDD lese, müsste doch da der HDDcache positiv beschleunigen, wobei ich mir bei MMF da immernoch nicht sicher bin. wieso also mit kanonen auf spatzen schießen ? ein vergleich wäre sinnvoll |
Re: Eindeutiger Vergleich für große Dateien gesucht
@supermuckl:
mit was willst du vergleichen? mit TFileStream? |
Re: Eindeutiger Vergleich für große Dateien gesucht
jep
|
Re: Eindeutiger Vergleich für große Dateien gesucht
Liste der Anhänge anzeigen (Anzahl: 2)
ok, hier ist der vergleich. ich weise aber darauf hin, dass die prozedur CompareFilesFileStream die daten von vorne nach hinten liest, während CompareFilesMemoryMapped von hinten nach vorne (s. negaH's post). das heisst die prozedur CompareFilesFileStream müsste dahingehend optimiert werden um einen einigermaßen adequaten vergleich zu erzielen.
zum vergleich: daten die überprüft wurden: 22 dateien, gesamtgröße: 6.480.244.455 Bytes. dabei war die datei, auf die es ankommt (wwdofficialnltls7l1.exe, 1.438.201.380 Bytes) zweifach vorhanden, um zu testen ob sie als unterschiedlich oder gleich erkannt wird. hier der verwendete code:
Delphi-Quellcode:
function CompareFilesFileStream(const File1, File2: String): Boolean;
const BlockSize = 65536; var FSFile1, FSFile2: TFileStream; L1, L2: Integer; B1, B2: Array[1..BlockSize] of Byte; begin Result := False; FSFile1 := TFileStream.Create(File1, fmOpenRead or fmShareDenyWrite); try FSFile2 := TFileStream.Create(File2, fmOpenRead or fmShareDenyWrite); try if FSFile1.Size = FSFile2.Size then begin while FSFile1.Position < FSFile1.Size do begin L1 := FSFile1.Read(B1[1], BlockSize); L2 := FSFile2.Read(B2[1], BlockSize); if L1 <> L2 then Exit; if not CompareMem(@B1[1], @B2[1], L1) then Exit; end; Result := True; end; finally FSFile2.Free; end; finally FSFile1.Free; end; end; ![]() und hier die prozedur die mittlerweile entstanden ist.
Delphi-Quellcode:
ich hoffe, dass soweit alles fehlerfrei ist. am besten wäre natürlich, wenn du beide prozeduren selbst mal testest und mir eventuelle fehler nennst.
function GetHugeFileSize(const Filename: String): Int64;
var hFile: Longword; Data: WIN32_FIND_DATA; Size: LARGE_INTEGER; begin Result := -1; hFile := FindFirstFile(PChar(Filename), Data); try if hFile <> INVALID_HANDLE_VALUE then begin Size.LowPart := Data.nFileSizeLow; Size.HighPart := Data.nFileSizeHigh; Result := Size.QuadPart; end; finally Windows.FindClose(hFile); end; end; function GetSystemAllocationGranularity: Cardinal; var PSysInfo: TSystemInfo; begin GetSystemInfo(PSysInfo); Result := PSysInfo.dwAllocationGranularity; end; function CompareFilesMemoryMapped(const File1, File2: String; SysAllocSize: Cardinal): Boolean; var CurSize: DWord; CurPos: Int64; hFile1, hFile2: THandle; hMap1, hMap2: THandle; FileSize: Int64; P1, P2: Pointer; begin Result := False; hFile1 := CreateFile(@File1[1], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if hFile1 <> INVALID_HANDLE_VALUE then try hFile2 := CreateFile(@File2[1], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if hFile2 <> INVALID_HANDLE_VALUE then try FileSize := GetHugeFileSize(File1); //GetFileSizeInt64(hFile1); if FileSize = GetHugeFileSize(File2) then //GetFileSizeInt64(hFile1) then begin Result := True; if FileSize > 0 then begin hMap1 := CreateFileMapping(hFile1, nil, PAGE_READONLY, 0, 0, nil); if hMap1 <> INVALID_HANDLE_VALUE then try hMap2 := CreateFileMapping(hFile2, nil, PAGE_READONLY, 0, 0, nil); if hMap2 <> INVALID_HANDLE_VALUE then try CurSize := FileSize mod SysAllocSize; if CurSize = 0 then CurSize := SysAllocSize; CurPos := FileSize - CurSize; repeat P1 := MapViewOfFile(hMap1, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if P1 <> nil then try P2 := MapViewOfFile(hMap2, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if P2 <> nil then try Result := CompareMem(P1, P2, CurSize); finally UnmapViewOfFile(P2); end; // else RaiseLastWin32Error; finally UnmapViewOfFile(P1); end; // else RaiseLastWin32Error; CurPos := CurPos - CurSize; CurSize := SysAllocSize; until (CurPos <= 0) or (not Result); finally CloseHandle(hMap2); end; // else RaiseLastWin32Error; finally CloseHandle(hMap1); end; // else RaiseLastWin32Error; end; end; finally CloseHandle(hFile2); end; // else RaiseLastWin32Error; finally CloseHandle(hFile1); end; // else RaiseLastWin32Error; end; hinweis: ich habe während der beiden vergleiche noch andere programme/dienste laufen usw. d. h. die vergleiche sind nicht sonderlich aussagekräftig, aber ich denke das ergebnis spricht für sich (wie gesagt, falls alles richtig ist). das maßgebliche an den screenshots ist die zeit (statusbar rechts unten). |
Re: Eindeutiger Vergleich für große Dateien gesucht
Ich will mal nicht unken, aber 0,20 Sekunden KANN NICHT SEIN, bei keiner Festplatte der Welt!
[Nachtrag] Modifizier die Routine mal so (rote Stellen), dann bekommst du mit, ob ein Fehler aufgetreten ist (was ich mir denke):
Code:
function CompareFilesMemoryMapped(const File1, File2: String; SysAllocSize: Cardinal): [color=red]integer[/color];
var CurSize: DWord; CurPos: Int64; hFile1, hFile2: THandle; hMap1, hMap2: THandle; FileSize: Int64; P1, P2: Pointer; begin [color=red]Result := -1;[/color] hFile1 := CreateFile(@File1[1], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if hFile1 <> INVALID_HANDLE_VALUE then try hFile2 := CreateFile(@File2[1], GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if hFile2 <> INVALID_HANDLE_VALUE then try FileSize := GetHugeFileSize(File1); //GetFileSizeInt64(hFile1); if FileSize = GetHugeFileSize(File2) then //GetFileSizeInt64(hFile1) then begin Result := True; if FileSize > 0 then begin hMap1 := CreateFileMapping(hFile1, nil, PAGE_READONLY, 0, 0, nil); if hMap1 <> INVALID_HANDLE_VALUE then try hMap2 := CreateFileMapping(hFile2, nil, PAGE_READONLY, 0, 0, nil); if hMap2 <> INVALID_HANDLE_VALUE then try CurSize := FileSize mod SysAllocSize; if CurSize = 0 then CurSize := SysAllocSize; CurPos := FileSize - CurSize; repeat P1 := MapViewOfFile(hMap1, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if P1 <> nil then try P2 := MapViewOfFile(hMap2, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if P2 <> nil then try [color=red]if CompareMem(P1, P2, CurSize) then Result := 1 else Result := 0;[/color] finally UnmapViewOfFile(P2); end; // else RaiseLastWin32Error; finally UnmapViewOfFile(P1); end; // else RaiseLastWin32Error; CurPos := CurPos - CurSize; CurSize := SysAllocSize; until (CurPos <= 0) or (not Result); finally CloseHandle(hMap2); end; // else RaiseLastWin32Error; finally CloseHandle(hMap1); end; // else RaiseLastWin32Error; end; end; finally CloseHandle(hFile2); end; // else RaiseLastWin32Error; finally CloseHandle(hFile1); end; // else RaiseLastWin32Error; end; |
Re: Eindeutiger Vergleich für große Dateien gesucht
wie gesagt, es können noch fehler enthalten sein.
ich habe mal das boolean statement durch integer ersetzt und bekomme zwar auch je nach datei mal eine -1 zurück, aber soweit ich das sehe nur deshalb, weil die dateien ungleich sind. doppelte (gleiche/identische) dateien werden richtig bestimmt. ich gebe dir recht, die benötigte zeit ist absolut erstaunlich, aber selbst mit breakpoints scheint er zumindest bei fragwürdigen dateien alles korrekt auszuführen. wo könnte der fehler sein? edit: jetzt habe ich mal negaH's RaiseLastWin32Error wieder aktiviert. ich bekomme einen "System Error Code 1132: Die angegebene Basisadresse oder Dateioffset ist falsch ausgerichtet." werde mal überprüfen wie und warum. danke für den hinweis! |
Re: Eindeutiger Vergleich für große Dateien gesucht
Ziehe mal eine weitere Kopie der doppelten Datei und ändere in dieser Datei möglichst nur 1 Byte exakt in der Mitte der Datei.
Auch ich meine das 0.2 Sekunden enorm schnell ist, einfach zu schnell. Das Result auf Integer zu setzen ist keine gute Idee und lass doch mal alle RaiseLastWin32Error drinnen. Falls nämlich irgendeine API Funktion fehlschlägt so wird eine Exception auftreten und schwups finden wir den eventuellen Fehler. Gruß Hagen |
Re: Eindeutiger Vergleich für große Dateien gesucht
@negaH:
Zitat:
Zitat:
jetzt weiß ich auch die stelle:
Delphi-Quellcode:
edit2:
if P2 <> nil then
try Result := CompareMem(P1, P2, CurSize); finally UnmapViewOfFile(P2); end else RaiseLastWin32Error; // <- hier kommt die AV ![]() "The base address or the file offset specified does not have the proper alignment." "ERROR_MAPPED_ALIGNMENT" muss mal kucken wann und weshalb der auftritt. edit3: quelle: ![]() Zitat:
|
Re: Eindeutiger Vergleich für große Dateien gesucht
ich würde dich auch noch bitten (wenn du die fehler korrigiert hast) auch noch meine methode zu testen, da ich damit mit größeren blöcken arbeite und mach meiner theorie bei großen dateien, dort mehr speed rauskommen KÖNNTE
Delphi-Quellcode:
function SameFile(File1,File2:String):boolean;
var s1,s2:Tfilestream; block:int64; buffer1,buffer2: string; begin result := false; try s1 := Tfilestream.Create(File1,fmOpenRead); s2 := Tfilestream.Create(File2,fmOpenRead); if s1.Size <> s2.Size then begin result := false; end else begin s1.Position := 0; s2.Position := 0; block := 10485760; //10MB hier eventuell mal verschiedene werte ausprobieren (1,10,20,30,100) if s1.Position+block > s1.Size-1 then block := s1.Size-s1.Position; result := true; setlength(buffer1,block); setlength(buffer2,block); while( s1.Position <> s1.Size ) do begin if s1.Position+block > s1.Size-1 then block := s1.Size-s1.Position; s1.Read(buffer1[1],block); s2.Read(buffer2[1],block); if buffer1 <> buffer2 then begin result := false; break; end; end; end; finally s1.Free; s2.Free; end; end; |
Re: Eindeutiger Vergleich für große Dateien gesucht
Liste der Anhänge anzeigen (Anzahl: 1)
den fehler hab ich zwar noch nicht korrigiert, aber vorhin deine methode getestet.
bei gleichem verzeichnis wie oben hat es 715 sek. gedauert. |
Re: Eindeutiger Vergleich für große Dateien gesucht
@supermuckl: bezüglich der performance (i/o u. mmf) habe ich einen relativ interessanten artikel gefunden:
![]() |
Re: Eindeutiger Vergleich für große Dateien gesucht
genau den beitrag hatte ich mal vor ner zeit inhaliert. und dann eine große mmf klasse geschrieben.
die allerdings DB like funktioniert und nicht byte für byte |
Re: Eindeutiger Vergleich für große Dateien gesucht
@negah: Ich hab auch mal versucht Deinen Code umzusetzen... leider kommt es beim Vergleich hin und wieder zu Fehlern...
Delphi-Quellcode:
Result := False;
FileHandle1 := FileOpen(FileName1, fmOpenRead or fmShareDenyWrite); if FileHandle1 <> INVALID_HANDLE_VALUE then try FileHandle2 := FileOpen(FileName2, fmOpenRead or fmShareDenyWrite); if FileHandle2 <> INVALID_HANDLE_VALUE then try FileSize := GetFileSize(FileHandle1, nil); if FileSize = GetFileSize(FileHandle2, nil) then begin Result := True; if FileSize > 0 then begin MapHandle1 := CreateFileMapping(FileHandle1, nil, PAGE_READONLY, 0, 0, nil); if MapHandle1 <> INVALID_HANDLE_VALUE then try MapHandle2 := CreateFileMapping(FileHandle2, nil, PAGE_READONLY, 0, 0, nil); if MapHandle2 <> INVALID_HANDLE_VALUE then try CurSize := FileSize mod AllocGran; if CurSize = 0 then CurSize := AllocGran; CurPos := FileSize - CurSize; repeat CurPosHi := CurPos div 4294967296; CurPosLo := CurPos mod 4294967296; P1 := MapViewOfFile(MapHandle1, FILE_MAP_READ, CurPosHi, CurPosLo, CurSize); if P1 <> nil then try P2 := MapViewOfFile(MapHandle2, FILE_MAP_READ, CurPosHi, CurPosLo, CurSize); if P2 <> nil then try Result := CompareMem(P1, P2, CurSize); finally UnmapViewOfFile(P2); end else RaiseLastWin32Error; finally UnmapViewOfFile(P1); end else RaiseLastWin32Error; CurPos := CurPos - CurSize; CurSize := AllocGran; until (CurPos < 0) or not Result; finally CloseHandle(MapHandle2); end else RaiseLastWin32Error; finally CloseHandle(MapHandle1); end else RaiseLastWin32Error; // <------------ Hier wird der Fehler angezeigt // MapHandle1 ist also INVALID_HANDLE_VALUE // Warum? end; end; finally FileClose(FileHandle2); end else RaiseLastWin32Error; finally FileClose(FileHandle1); end else RaiseLastWin32Error; |
Re: Eindeutiger Vergleich für große Dateien gesucht
keine Ahnung ?!
hast du mal gecheckt iff AnsiCompareText(FileName1, FileName2) <> 0 !!! eventuell kann es also sein das beide oder eine der Dateien schon geöffnet wurde und du keinen Zugriff mit deinem Zugriffsflags bekommst. fmShareDenyWrite könnte das Problem sein falls ein anderer Process die Datei mit Vollzugriff oder sogar exklusiv geöffnet hat. Dann treten Fehler NICHT sofort mit FileOpen(), GetFileSize() etc. pp. auf sondern erst wenn man Daten aus der Datei lesen möchte. Dieser Fehler kann dir aber mit jeder Methode zu jeder Zeit auftreten, das OS und die anderen Anwendungen möchte halt auch ihre Daten speichern ;) Gruß Hagen PS: als Entschuldigung für denMist muß ich auch sagen das der obige Source von mir niemals getestet wurde, nochnichtmal das er auf meinem Rechner jemals das Licht eines Compilers gesehen hätte ;) ich habe ihn hier live programmiert. |
Re: Eindeutiger Vergleich für große Dateien gesucht
:oops: da hatte ich doch gerade den "falschen" Fehler markiert...
Viel häufiger tritt jedoch an folgender Stelle ein Fehler auf:
Delphi-Quellcode:
...
P1 := MapViewOfFile(MapHandle1, FILE_MAP_READ, CurPosHi,
CurPosLo, CurSize); Zitat:
|
Re: Eindeutiger Vergleich für große Dateien gesucht
@frifra, @negaH:
genau den selben fehler erhalte ich ja auch. ich hab mich schon gewundert warum du einen anderen bekommst. ich habe gestern noch einiges zu dem thema gelesen, und es muss wohl wirklich an CurPosHi u. CurPosLo liegen. Irgendwie werden die falsch geteilt, bzw. der rest ist nicht mit "dwAllocationGranularity" kompatibel. ich weiß aber momentan auch nicht wirklich warum. |
Re: Eindeutiger Vergleich für große Dateien gesucht
Jaja, wenn man einfach stupid irgendwas abtippt das ein Fachidiot ohne eigene Tests gepostet hat ;)
Beachtet bitte die beiden Zeilen die mit 1.) und 2.) makiert wurden.
Delphi-Quellcode:
So. Bei meinen Test ist CompareFile() ca. 140% schneller als CompareFile1().
function GetFileSizeInt64(const FileName: String): Int64;
var Handle: THandle; Data: WIN32_FIND_DATA; begin Result := -1; Handle := FindFirstFile(Pointer(FileName), Data); if Handle <> INVALID_HANDLE_VALUE then try Int64Rec(Result).Hi := Data.nFileSizeHigh; Int64Rec(Result).Lo := Data.nFileSizeLow; finally Windows.FindClose(Handle); end; end; function GetSystemAllocationGranularity: DWord; var Info: TSystemInfo; begin GetSystemInfo(Info); Result := Info.dwAllocationGranularity; end; function CompareFile(const FileName1, FileName2: String): Boolean; procedure DoError; begin RaiseLastWin32Error; // auskommentieren falls keine Exceptions erwünscht // Result := False; end; var AllocSize: DWord; CurSize: DWord; CurPos: Int64; FileHandle1, FileHandle2: THandle; MapHandle1, MapHandle2: THandle; FileSize1, FileSize2: Int64; View1, View2: Pointer; begin Result := AnsiCompareText(FileName1, FileName2) = 0; if Result then Exit; FileSize1 := GetFileSizeInt64(FileName1); FileSize2 := GetFileSizeInt64(FileName2); if FileSize1 or FileSize2 >= 0 then begin Result := FileSize1 = FileSize2; if not Result or (FileSize1 = 0) then Exit; AllocSize := GetSystemAllocationGranularity * 8; Assert(AllocSize < MaxInt); // CompareMem() Size Param is Integer FileHandle1 := CreateFile(Pointer(FileName1), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if FileHandle1 <> INVALID_HANDLE_VALUE then try FileHandle2 := CreateFile(Pointer(FileName2), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if FileHandle2 <> INVALID_HANDLE_VALUE then try MapHandle1 := CreateFileMapping(FileHandle1, nil, PAGE_READONLY, 0, 0, nil); if MapHandle1 <> INVALID_HANDLE_VALUE then try MapHandle2 := CreateFileMapping(FileHandle2, nil, PAGE_READONLY, 0, 0, nil); if MapHandle2 <> INVALID_HANDLE_VALUE then try CurSize := FileSize1 mod AllocSize; if CurSize = 0 then CurSize := AllocSize; CurPos := FileSize1 - CurSize; repeat Assert(CurPos >= 0); Assert(CurPos mod AllocSize = 0); View1 := MapViewOfFile(MapHandle1, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if View1 <> nil then try View2 := MapViewOfFile(MapHandle2, FILE_MAP_READ, Int64Rec(CurPos).Hi, Int64Rec(CurPos).Lo, CurSize); if View2 <> nil then try Result := CompareMem(View1, View2, CurSize); finally UnmapViewOfFile(View2); end else DoError; finally UnmapViewOfFile(View1); end else DoError; CurSize := AllocSize; // 1.) CurPos := CurPos - CurSize; // 2.) until not Result or (CurPos < 0); finally CloseHandle(MapHandle2); end else DoError; finally CloseHandle(MapHandle1); end else DoError; finally CloseHandle(FileHandle2); end else DoError; finally CloseHandle(FileHandle1); end else DoError; end else DoError; end; function CompareFile1(const FileName1, FileName2: String): Boolean; procedure DoError; begin RaiseLastWin32Error; // Result := False; end; const BufferSize = 65535; var FileSize1,FileSize2: Int64; CurSize: Integer; Stream1,Stream2: TStream; Buffer1,Buffer2: array of Byte; begin Result := AnsiCompareText(FileName1, FileName2) = 0; if Result then Exit; FileSize1 := GetFileSizeInt64(FileName1); FileSize2 := GetFileSizeInt64(FileName2); if FileSize1 or FileSize2 >=0 then begin Result := FileSize1 = FileSize2; if not Result or (FileSize1 = 0) then Exit; Stream1 := TFileStream.Create(FileName1, fmOpenRead or fmShareDenyWrite); try Stream2 := TFileStream.Create(FileName2, fmOpenRead or fmShareDenyWrite); try SetLength(Buffer1, BufferSize); SetLength(Buffer2, BufferSize); while Result and (FileSize1 > 0) do begin CurSize := BufferSize; if CurSize > FileSize1 then CurSize := FileSize1; Stream1.Read(Buffer1[0], CurSize); Stream2.Read(Buffer2[0], CurSize); Result := CompareMem(@Buffer1[0], @Buffer2[0], CurSize); FileSize1 := FileSize1 - CurSize; end; finally Stream2.Free; end; finally Stream1.Free; end; end else DoError; end; Aber am wichtigsten dürfter der Fakt sein das CompareFile() umso schneller arbeitet um so öfters eine Datei als MMF in den Speicher geladen wurde, sprich schon im Cache ist. Das ist gut so denn ich nehme an das zumindestens eine der beiden Dateien kurz vorher, ebenfalls per MMF, gehasht wurde. D.h. sie ist schon im Speicher weil man ja noch den Hash per MD4 vorher ziehen wird. Die andere Datei stellt ja die Referenz die schon in der Datenbank gespeichert wurde,also auch deren Hash Wert. Somit dürften die MMF's tatsächlich einiges an Speed bringen. Gruß Hagen |
Re: Eindeutiger Vergleich für große Dateien gesucht
Wenn man es aber genauer betrachtet so sind 140% zu wenig falls man mit Hashs arbeiten wird. Die Wahrscheinlichkeit das nach einer MD4 Hash Überprüfung die Funktion CompareFile() aufgerufen werden muß ist einfach viel zu gering um nicht mit den VCL-Streams zu arbeiten, besondersim Hinblick auf die negativen Seiteneffekte der MMF's wie beim Borland Link oben beschrieben. (kannte ich selber noch nicht)
Bei einer MD4 Hash Überprüfung hat man ja zumindestens schonmal einen Hashwert einer der beiden Dateien in einer DB gespeichert. Die Erzeugung der MD4 Prüfsumme für die zweite Datei muß ja nur diese zweite Datei öffnen und laden. Das verringert schonmal um 200% den nötigen Aufwand. Gruß Hagen |
Re: Eindeutiger Vergleich für große Dateien gesucht
Liste der Anhänge anzeigen (Anzahl: 1)
So nachfolgend noch ein Code um einen MD4 über eine Datei zu ziehen.
Dieses mal sieht die Performance schon anders aus. Es gibt in fact keinen Unterschied mehr zwischen MMF's und normalen Streams, also sollten Streams benutzt werden. Allerdings unterscheidet sich die Laufzeit zwischen MD4HashFile1() und CompareFile() enorm. Mit meinen 160Mb großen Testdateien, die noch NICHT im Cache lagen, sieht es nun so aus: CompareFile1() benötigt 55 Sekunden. HashFile1() benötigt 10 Sekunden. Was lernen wir daraus ? Das ein zweimaliger Aufruf von HashFile1() ca. 20 Sekunden benötigt und somit mehr als doppelt so schnell ist wie nur ein Aufruf von CompareFile1() !! Gruß Hagen PS: wer's in die CodeLib stellen will soll es selber tuen ;)
Delphi-Quellcode:
[edit=Sharky]Nach Rücksprache den gesamt Code als Datei angehängt. Mfg, Sharky[/edit]
type
PMD4Digest = ^TMD4Digest; TMD4Digest = array[0..3] of Cardinal; PMD4Buffer = ^TMD4Buffer; TMD4Buffer = array[0..15] of Cardinal; // // Rest im Anhang des Postings // |
Re: Eindeutiger Vergleich für große Dateien gesucht
Liste der Anhänge anzeigen (Anzahl: 1)
@negaH:
ich habe die korrigierte fassung der comparefile prozedur (also mmf) über das selbe verzeichnis laufen lassen. ich dachte zwar schon, das programm hätte sich aufgehängt, aber nach 1391 sekunden war er dann doch noch erfolgreich fertig. jaja, die 0,2 sek. wären auch zu schön um wahr zu sein. trotzdem vielen dank für die mühe u. arbeit die du dir hier gemacht hast. Zitat:
|
Re: Eindeutiger Vergleich für große Dateien gesucht
@hagen:
die asm-prozedur sieht lustig aus :) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:53 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz