![]() |
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 |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:23 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz