Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Eindeutiger Vergleich für große Dateien gesucht (https://www.delphipraxis.net/50896-eindeutiger-vergleich-fuer-grosse-dateien-gesucht.html)

dahead 3. Aug 2005 00:32

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: efg MMF), muss aber zugeben, dass ich dieses 'system' noch nie verwendet. hätte ich mir wohl genauer ansehen sollen.

danke für deinen code! ich werde ihn mir morgen früh mal genauer ansehen und testen.

bigg 3. Aug 2005 00:37

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)

dahead 3. Aug 2005 00:41

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;

bigg 3. Aug 2005 00:45

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.

negaH 3. Aug 2005 10:08

Re: Eindeutiger Vergleich für große Dateien gesucht
 
@DaHead:

;) denoch machst du den Vergleich sehr umständlich !

Delphi-Quellcode:
      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;
das geht einfacher mit

Delphi-Quellcode:
  result := (FSFile1.Size = FSFile2.Size) and CompareMem(FSFile1.Memory, FSFile2.Memory, FSFile1.Size);
Denn das MMF hat ja schon die komplette Datei in den Speicher virtuell eingeblendet.
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

dahead 3. Aug 2005 10:30

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:
P := MapViewOfFile(FMapping, FILE_MAP_READ, 0, 0, 0);
wieviel wird hier eigentlich in den speicher gelesen? laut meiner hilfe ist der letzte parameter dwNumberOfBytesToMap ja auf 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:
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;
edit, hat sich erledigt mit der inkrementierung in obigem code, ist klar.

dahead 3. Aug 2005 10:32

Re: Eindeutiger Vergleich für große Dateien gesucht
 
Zitat:

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.
kann gut sein, dass der test nicht aussagekräftig ist. siehe meinen vorherigen post.

Sharky 3. Aug 2005 10:36

Re: Eindeutiger Vergleich für große Dateien gesucht
 
Zitat:

Zitat von dahead
... wo bei folgendem code die erhöhung der akt. lese position stattfindet:

Hai,

.Read setzt doch die Position immer auf das letze gelesene Byte + 1 oder?

dahead 3. Aug 2005 10:38

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.

negaH 3. Aug 2005 10:44

Re: Eindeutiger Vergleich für große Dateien gesucht
 
Zitat:

was ich auch nicht ganz verstehe, ist wo bei folgendem code die erhöhung der akt. lese position stattfindet:
Na bei .Read() wird die .Position um +SizeOf(Buffer) erhöht.

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

dahead 3. Aug 2005 10:59

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:
D.h. man sollte die Buffergröße inkrementell so lange erhöhen bis sich die größte Performance pro Byte ergibt.
ja, das wäre am besten.

aus der msdn:
Zitat:

dwFileOffsetLow: A low-order DWORD of the file offset where the view is begins. The combination of the high and low offsets must specify an offset within the file that matches the memory allocation granularity of the system, or the function fails. That is, the offset must be a multiple of the allocation granularity. To obtain the memory allocation granularity of the system, use the GetSystemInfo function, which fills in the members of a SYSTEM_INFO structure.
d.h. ich muss erst ermitteln, welchen wert das jeweilige system überhaupt zulässt.

edit:

Delphi-Quellcode:
function GetSystemAllocationGranularity: Cardinal;
var
  PSysInfo: TSystemInfo;
begin
  GetSystemInfo(PSysInfo);
  Result := PSysInfo.dwAllocationGranularity;
end;

negaH 3. Aug 2005 12:03

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

dahead 3. Aug 2005 12:28

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:
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;
wie du siehst, frage ich im create nicht MapViewOfFile ab, da ich dies erst beim vergleich mache.

hier die vergleichs prozedur:

Delphi-Quellcode:
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;
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.

mein problem ist auch, dass ich durch MapViewOfFile nicht die Length kenne. daher übergebe ich AllocGran.

dahead 3. Aug 2005 12:31

Re: Eindeutiger Vergleich für große Dateien gesucht
 
ich glaube ich habe den fehler gefunden:

Delphi-Quellcode:
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;
so gehts. ist das korrekt? bzw. ist es auch korrekt das MapViewOfFile im Create wegzulassen. ich denke schon, oder?

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.

negaH 3. Aug 2005 13:26

Re: Eindeutiger Vergleich für große Dateien gesucht
 
Delphi-Quellcode:
 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);
Ich sehe zwei Fehler

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:
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;
Eventuelle Fehler in obigen Pseudocode darfst du behalten ;)

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 !!

dahead 3. Aug 2005 13:41

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!

dahead 3. Aug 2005 14:09

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!

negaH 3. Aug 2005 15:15

Re: Eindeutiger Vergleich für große Dateien gesucht
 
Jo 2^32 = 2 hoch 32 = 1 shl 32 =

Delphi-Quellcode:

const
  Power2of32: Int64 = $100000000;
Alle 2^32 mit der Konstanten Power2of32 ersetzen, oder besser so:

Delphi-Quellcode:
CurPosLo := Int64Rec(CurPos).Lo;
CurPosHi := Int64Rec(CurPos).Hi;
Du könnste dann sogar die beiden Variablen weg optimieren und gleich mit Int64Rec() casten -> SysUtils.

Ich würde ebenfalls eine einzigste Function coden, warum will man noch eine aufwendige Klasse für diese Spezialaufgabe coden ?

Gruß Hagen

dahead 3. Aug 2005 15:46

Re: Eindeutiger Vergleich für große Dateien gesucht
 
ah, danke für diesen hinweis!

Zitat:

Ich würde ebenfalls eine einzigste Function coden, warum will man noch eine aufwendige Klasse für diese Spezialaufgabe coden ?
ja, ist ohne die extra klasse sogar kompakter/übersichtlicher und da ich sowieso in keiner anderen prozedur darauf zugreifen muss, habe ich es wie in deinem beispiel-code gelöst.

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!

supermuckl 3. Aug 2005 16:27

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

dahead 3. Aug 2005 16:31

Re: Eindeutiger Vergleich für große Dateien gesucht
 
@supermuckl:

mit was willst du vergleichen? mit TFileStream?

supermuckl 3. Aug 2005 16:58

Re: Eindeutiger Vergleich für große Dateien gesucht
 
jep

dahead 3. Aug 2005 17:16

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;
Quelle

und hier die prozedur die mittlerweile entstanden ist.

Delphi-Quellcode:
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;
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.

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).

Flocke 3. Aug 2005 17:21

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;

dahead 3. Aug 2005 17:42

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!

negaH 3. Aug 2005 17:51

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

dahead 3. Aug 2005 17:55

Re: Eindeutiger Vergleich für große Dateien gesucht
 
@negaH:

Zitat:

Falls nämlich irgendeine API Funktion fehlschlägt so wird eine Exception auftreten und schwups finden wir den eventuellen Fehler.
ja, siehe obigen post.

Zitat:

"System Error Code 1132: Die angegebene Basisadresse oder Dateioffset ist falsch ausgerichtet."
edit1:
jetzt weiß ich auch die stelle:

Delphi-Quellcode:
if P2 <> nil then
 try
  Result := CompareMem(P1, P2, CurSize);
 finally
 UnmapViewOfFile(P2);
 end else
 RaiseLastWin32Error; // <- hier kommt die AV
edit2:
http://www.support.softwaretechnolog...ain/R10307.htm
"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: http://support.microsoft.com/kb/q125713/
Zitat:

For the lpvBase parameter specified in a call to MapViewOfFileEx(), you should use an integral multiple of the system's allocation granularity. On Windows NT, not specifying such a value will cause MapViewOfFileEx() to fail, and GetLastError() to return ERROR_MAPPED_ALIGNMENT (1132). On Windows 95, the address is rounded down to the nearest integral multiple of the system's allocation granularity.

supermuckl 3. Aug 2005 18:23

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;

dahead 3. Aug 2005 19:00

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.

dahead 4. Aug 2005 00:07

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:

http://blogs.borland.com/dcc/archive...3/19/2380.aspx

supermuckl 4. Aug 2005 00:55

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

FriFra 4. Aug 2005 09:59

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;

negaH 4. Aug 2005 10:54

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.

FriFra 4. Aug 2005 11:19

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:

Zitat von Mein Programm
Systemfehler. Code: 1132
Die angegebene Basisadresse oder der angegwebene Dateioffset ist falsch ausgerichtet.

Es scheint also CurPosHi und/oder CurPosLo nicht zu passen :roll: ...

dahead 4. Aug 2005 11:33

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.

negaH 4. Aug 2005 12:01

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:
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;
So. Bei meinen Test ist CompareFile() ca. 140% schneller als CompareFile1().
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

negaH 4. Aug 2005 12:15

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

negaH 4. Aug 2005 13:09

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:
type
  PMD4Digest = ^TMD4Digest;
  TMD4Digest = array[0..3] of Cardinal;

  PMD4Buffer = ^TMD4Buffer;
  TMD4Buffer = array[0..15] of Cardinal;
//
// Rest im Anhang des Postings
//
[edit=Sharky]Nach Rücksprache den gesamt Code als Datei angehängt. Mfg, Sharky[/edit]

dahead 4. Aug 2005 13:17

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:

PS: wer's in die CodeLib stellen will soll es selber tuen Wink
deine arbeit, also musst/darfst du das tun. ehre wem ehre gebührt.

bigg 4. Aug 2005 13:20

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.
Seite 2 von 3     12 3      

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