Dateidekomprimierung mit Multi-Threading beschleunigen?



  • Hallo allerseits!

    Vorwort
    Für viele mag es unsinnig erscheinen, an dieser Stelle zu optimieren aber ich habe mir den Intel VTune Performance Analyzer zugelegt (Student :P) und möchte etwas Routine bei diesem komplexen Programm bekommen. Ich finde das Thema recht interessant, lernt man dabei doch Einiges vom zugrundeliegenden API.

    Motivation
    Zu Beginn habe ich 3 simple Varianten einer Methode geschrieben. Diese soll eine Datei öffnen, deren (binären) Inhalt in einen passenden Puffer einlesen, die Datei wieder schließen und schließlich den Puffer wieder freigeben.

    • Variation 1 (C-Style)
      Verwendung von FILE, malloc, free.
    • Variation 2 (C++-Style)
      Verwendung von ifstream, new, delete[].
    • Variation 3 (WinAPI-Style, siehe Code unten
      Verwendung von FileRead, HeapAlloc, HeapFree.

    Die Test-Datei war relativ klein und so kommt der Overhead der jeweiligen Variationen stark zum tragen. Betrachtet man nun ausschließlich die Methoden selbst und nicht den zum Teil großen Overhead der Standardbibliotheken bei der Anwendungsinitialisierung unter der Haube kommt man zu dem Resultat, dass die C-Version 2x, die C++-Version 8x länger braucht, als die direkte Nutzung der WinAPI (Anm.: Die Standardbibliotheken stellen im Prinzip Wrapper dieser WinAPI Funktionen dar).

    Jetzt kommt der Moment, in dem der Frosch...
    Damit der bis jetzt eigentlich unsinnige Vergleich nicht wie so viele andere begonnene Projekte in der Versenkung verschwindet, möche ich Variation 3 ausbauen. Ich möchte eine komprimierte Datei einlesen und in einen Puffer dekomprimieren. Ich möchte aber nicht den langweiligen "Standardweg" gehen, sondern mir kam in den Sinn, dass durch die Latenz des Festplattenzugriffs Rechenzeit flöten geht. Dies könnte man doch durch gleichzeitiges (rechenintensiveres) Dekomprimieren der bereits eingelesenen Daten ausbügeln. Damit das auch hinhaut, müssten diese beiden Vorgänge synchronisiert werden. Bei dem Wort synchronisieren schossen mir zwei Schlagwörter in den Kopf: "Threads" und "(A)synchrone E/A". Beides scheinen sehr komplexe Themen zu sein und ich habe mich noch nicht wirklich mit ihnen befasst.

    Hier kommt ihr in's Spiel 😉
    Wäre das denn überhaupt ein richtiger Ansatz bzw. haben meine Vermutungen überhaupt mit meinem Vorhaben zu tun? Hat der ein oder andere vielleicht ein paar Schlagwörter parat, mit denen ich z.B. mit Hilfe der MSDN schneller zum Ziel komme? Schreibt lieber zu viel als zu wenig und seit experimentierfreudig. Aber ich gehe davon aus, dass ihr das seit, wenn ihr bis hierhin gelesen habt. 💡

    Variation 3:

    VOID ReadFile(LPCTSTR lpFileName)
    {
        HANDLE	hFile;
    	DWORD	nNumberOfBytesToRead;
    	HANDLE	hHeap;
    	LPVOID	lpBuffer;
    	DWORD	NumberOfBytesRead;
    
    	hFile					= CreateFile(lpFileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
    	nNumberOfBytesToRead	= GetFileSize(hFile, 0);
    	hHeap					= GetProcessHeap();
    	lpBuffer				= HeapAlloc(hHeap, 0, nNumberOfBytesToRead);
    
    	ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, &NumberOfBytesRead, 0);
    	CloseHandle(hFile);
    
    	HeapFree(hHeap, 0, lpBuffer);
    }
    


  • Über Synchronization gibt's ein eigenes Kapitel in der MSDN. Sicher ist es lohnend, darüber zu lernen.

    Dass es aber schneller wird, bezweifle ich :). Die Festplatten und Filesysteme haben schließlich einen Read Ahead Cache, der den Vorteil deines komplizierten Verfahrens zunichte macht.



  • Wie wär's mit Variation 4: Memory Mapped Files?



  • Hat man eine Datei erstmal eingelesen, sind sie ohnehin im Cache aber es gibt Flags um dies zu vermeiden. Die Dateien, dür die ich die Decode-Methode schreiben möchte befinden sich übrigens auf CD und auch wenn die CD den Spin erreicht hat um Daten von ihr zu lesen, dauert dies in der Regel länger als von der Festplatte zu lesen. Naja ich werde erst mich erst Mal mit den Vorgeschlagenen Sachen befassen. Danke schonmal!


Anmelden zum Antworten