Wird bei DllImport die DLL-Datei pro Aufruf geladen ?



  • @hustbaer sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    Wie lange braucht dein C# Programm denn wenn du die PInvoke Aufrufe einfach weglässt?

    Ich danke Dir für Deine Beharrlichkeit - Du hast Recht und ich ärgere mich, dass ich nicht selbst einmal diesen Versuch gemacht habe.
    Ohne jeden Vergleich läuft das Programm mit den gleichen Dateien 28' 10", d.h. der Vergleich spielt keine Rolle bei der Laufzeit und deshalb ist meine Vermutung DllImport sei der Bremser offenbar falsch - Du hattest Recht ☺ .
    Dann sind also die Zugriffsmethoden, die ich bei C# benutze im Vergleich zu denen in Delphi die Bremser.

    So mache ich das in C#
    Zunächst lese ich die beiden Dateien als Stream ein,
    später dann immer Blöcke aus dem Stream in einer max. Blocklänge von 1 TB, falls die Dateien > 1 TB sind.
    Diese Blöcke werden anschließend verglichen

                    FileStream fs1 = new FileStream(Dsn1, FileMode.Open, FileAccess.Read);
                    FileStream fs2 = new FileStream(Dsn2, FileMode.Open, FileAccess.Read);
    
                    long VerglRestLen = VerglLenGes;
                    long VerglOffset = 0;
                    int VerglLen = 0;
                    int isDiff = 0;
    
                    ByPu1 = new byte[(int)MaxPufL + 0x1000];
                    ByPu2 = new byte[(int)MaxPufL + 0x1000];
    
                NxtPuVergleich:
    
                    if (VerglRestLen > MaxPufL)
                    { VerglLen = (int) MaxPufL; }
                    else
                    { VerglLen = (int) VerglRestLen; }
    
                    fs1.Read(ByPu1, 0, (int)VerglLen);
                    fs2.Read(ByPu2, 0, (int)VerglLen);
    

    Hier Schnipsel des Delphiprogrammes

           {$I-}
            AssignFile(Handle1, DSN1);
            FileMode := 0;    { Reset nur für Eingabe }
            Reset(Handle1,1);
            Error1 := IOResult; { Null = Open OK }
            {$I+}
            :::
            {$I-}
            BlockRead(Handle1, Puffer1, ReadLen, Laenge1);
            Error1 := IOResult; { Null = Read OK }
            if Error1 <> 0 then
              Laenge1 := 0;
            {$I+}
    

    Das sind im Prinzip die Zugriffe, die es bereits unter DOS gab und die ich in Borlands Turbo Pascal schon benutzt habe.
    Die kann man im aktuellen Delphi 10 aber auch noch benutzen.

    Ich muss nun prüfen, ob man so elementare Zugriffe zum simplen sequentiellen byteweisen lesen von Blöcken einer beliebigen Datei auch in C# machen kann. Die stream-Methode ist für so ein Programm wahrscheinlich völlig ungeeignet. Bei C++ gibt es auch noch diese ursprünglichen DOS-Zugriffe.



  • Wenn du´s wirklich ausreizen möchtest kannst du ja mal diesen Ansatz ausprobieren:

    1. beide Dateien mit OpenFile öffnen
    2. FileMapping für beide Dateien erzeugen (CreateFileMapping)
    3. blockweise jeweils die gleichen X Byte pro Datei in den Speicher einblenden (MapViewOfFile)
    4. Speicherbereiche per RtlCompareMemory vergleichen (Vorsicht: undokumentierte Funktion)
    5. Blockgröße und Offset anpassen, weiter bei 3) oder Ende, falls Dateiende erreicht

    Vielleicht kann Windows da intern etwas optimieren.



  • Für .NET gibt es dafür die Klasse MemoryMappedFile, s.a. Benchmark-Vergleich in C# MemoryMappedFile Example.

    PS:
    @hkdd sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    später dann immer Blöcke aus dem Stream in einer max. Blocklänge von 1 TB, falls die Dateien > 1 TB sind.

    Du meinst GB??!



  • @Th69

    Ich wusste nicht, dass es sowas in .NET gibt. Aber meine Absicht war eigentlich so viel wie möglich nicht in C# zu machen, sondern möglichst viel von der WINAPI direkt erledigen zu lassen. Mit der MemoryMappedFile Klasse braucht man ja schon wieder .NET Streams, während RtlCompareMemory direkt mit Adressen arbeitet.



  • @Th69 sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    Du meinst GB

    ja, natürlich

    Wie ist das eigentlich bei File.ReadAllBytes
    https://learn.microsoft.com/de-de/dotnet/api/system.io.file.readallbytes?view=net-6.0
    In dem Link werden die möglichen Ausnahmen aufgezeigt.
    Was passiert aber, wenn die Datei so groß ist, dass dafür kein byte[] Array im verfügbaren Speicher angelegt werden kann.
    Muss man das byte[] Array selbst wieder freigeben ?
    Ich benutze deshalb die max.Puffergröße von 1 GB, bei meinem derzeitigen RAM von 32 GB könnte es auch etwas mehr sein.
    Auf meinem PC gibt es aber Dateien, die deutlich größer als 1 TB sind.
    Eine Ausnahme sinngemäß "Datei für vorhandenen Speicher zu groß" habe ich nicht gefunden.

    07.09.2022  09:39    18.353.100.330 2022-09-06 xxx HD.mp4
    08.09.2022  16:22    17.293.742.949 2022-09-07 xxx HD.mp4
    09.09.2022  10:37    17.293.367.198 2022-09-08 xxx HD.mp4
    10.09.2022  07:32     8.115.634.344 2022-09-09 xxx HD.mp4
    22.09.2022  08:38     9.881.955.469 2022-09-21 xxx HD.mp4
    23.09.2022  07:25    15.526.817.738 2022-09-22 xxx HD.mp4
                   6 Datei(en), 86.464.618.028 Bytes
    


  • @hkdd sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    @Th69 sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:
    Wie ist das eigentlich bei File.ReadAllBytes
    https://learn.microsoft.com/de-de/dotnet/api/system.io.file.readallbytes?view=net-6.0
    In dem Link werden die möglichen Ausnahmen aufgezeigt.
    Was passiert aber, wenn die Datei so groß ist, dass dafür kein byte[] Array im verfügbaren Speicher angelegt werden kann.

    Dann fliegt eine OutOfMemoryException.

    Muss man das byte[] Array selbst wieder freigeben ?

    Nö. Dafür gibt's ja garbage collection. Nur bringst du den GC halt mächtig unter Druck wenn du alles über Funktionen einliest die immer neue Arrays erzeugen.

    Ich benutze deshalb die max.Puffergröße von 1 GB, bei meinem derzeitigen RAM von 32 GB könnte es auch etwas mehr sein.
    Auf meinem PC gibt es aber Dateien, die deutlich größer als 1 TB sind.

    Wenn du ein grosses Pagefile hast bzw. die "automatisch verwalten" Einstellung, dann wird das vermutlich erstmal richtig langsam werden, und dann wird irgendwann trotzdem eine OutOfMemoryException fliegen.

    Und wenn du kein/nur ein kleines Pagefile hast, dann wird es nicht so lange dauern bis die OutOfMemoryException geflogen kommt 🙂

    Eine Ausnahme sinngemäß "Datei für vorhandenen Speicher zu groß" habe ich nicht gefunden.

    Ja, OutOfMemoryException kann von so vielen Funktionen geworfen werden dass sie das nicht extra dokumentieren.



  • ps:
    @hkdd sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

                FileStream fs1 = new FileStream(Dsn1, FileMode.Open, FileAccess.Read);
                FileStream fs2 = new FileStream(Dsn2, FileMode.Open, FileAccess.Read);
    

    Ich würde empfehle das mit using zu machen, damit die Files auch garantiert immer wieder zugemacht werden. Ohne using kann es u.U. lange dauern bis die FileStream Objekte vom GC weggeräumt werden - und bis dahin bleibt das File-Handle dann offen.

    using (FileStream fs1 = new FileStream(Dsn1, FileMode.Open, FileAccess.Read))
    using (FileStream fs2 = new FileStream(Dsn2, FileMode.Open, FileAccess.Read))
    {
    
        // ...
    
    } // fs1 und fs2 werden hier automatisch geschlossen, egal wie der Block verlassen wird (normal, return, Exception)
    


  • @hustbaer sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    Ich würde empfehle das mit using zu machen

    Ich mache doch am Ende in jedem Fall folgende Close-Aufrufe - ist das nicht identisch ?

                    fs1.Close();
                    fs2.Close();
    
    

    Bei der Anwendung meines Programmes ist es ja so, dass zwar viele Dateien und Bytes verglichen werden, es dabei i.d.R. aber weder Lesefehler noch Differenzen gibt. Dor Standard ist als alle Dateien sind OK und stimmen überein.
    Trotzdem lasse ich nach Sicherungen meiner wichtigen Dateien (meist auf externe HDDs) danach diesen Vergleich laufen. Damit habe ich eine Lesekontrolle und die Sicherheit, dass alles stimmt.



  • @hustbaer sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    Dann fliegt eine OutOfMemoryException.

    Damit ist diese Funktion nur für relativ kleine Dateien nutzbar.



  • @hkdd sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    @hustbaer sagte in Wird bei DllImport die DLL-Datei pro Aufruf geladen ?:

    Ich würde empfehle das mit using zu machen

    Ich mache doch am Ende in jedem Fall folgende Close-Aufrufe - ist das nicht identisch ?

                    fs1.Close();
                    fs2.Close();
    
    

    Bei der Anwendung meines Programmes ist es ja so, dass zwar viele Dateien und Bytes verglichen werden, es dabei i.d.R. aber weder Lesefehler noch Differenzen gibt. (...)

    Mir ist das im Prinzip Wurst wie du das machst 🙂 Ich wollte dich nur darauf hinweisen dass man das üblicherweise in .NET halt nicht so macht. Sondern eben using verwendet. Weil es eben nicht identisch ist. Deine Close Aufrufe werden halt nur erreicht wenn keine Exception fliegt. Die using Blöcke machen die Files dagegen auch in dem Fall zuverlässig zu.

    Damit ist diese Funktion nur für relativ kleine Dateien nutzbar.

    Ich würde ein paar GB schon als relativ gross bezeichnen. Ist halt relativ 😄


Anmelden zum Antworten