Dateien schnell einlesen



  • AntonWert schrieb:

    @volkard
    deine Hilfe in Ehren finde es echt Super dass du ein Beispiel anbietest 👍
    Aber für mich (ich komme aus der Mikrokontroller-C-Ecke, ist diese "Hardcore"-Variante irgendwie unverständlich 😞
    Klar ich will mich schon durchbeißen und den Performancegewinn von ReadFile Testen, aber ich muss kleinere Schritte machen dass ich das verstehe.
    Bin ich denn mit

    BOOL WINAPI ReadFile(
      __in         HANDLE hFile,
      __out        LPVOID lpBuffer,
      __in         DWORD nNumberOfBytesToRead,
      __out_opt    LPDWORD lpNumberOfBytesRead,
      __inout_opt  LPOVERLAPPED lpOverlapped
    );
    

    noch auf dem richtigen Dampfer?

    Aber ja!
    Die windows-Sachen hab ich nur weggekapselt, damit ich gleichnamige unix-Kapsel automatisch nehmen kann.

    File openFileForRead(FileNameChar const* fileName) {
    	File file=CreateFile(fileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    	if (file==INVALID_HANDLE_VALUE)
    		throw 0;
    	return file;
    }
    
    Size readFile(File file,char* buffer,Size size){
    	DWORD bytesRead;
    	BOOL rc=ReadFile(file,buffer,size,&bytesRead,NULL);
    	if(rc==FALSE)
    		throw 0;
    	return bytesRead;
    }
    


  • Aber es haben fopen und fread bereits so einen netten Puffer und sollten nur wenige Prozent unter dem Optimum liegen, wenn Du nicht gerade einzelne Bytes liest.
    Blockgröße ab 8k ist gut. 16k ist etwas schneller, 32k nur noch minimal schneller. Bei Deinen 8M brauchst Du sicher nicht zu befürchten, es sei zu klein.
    Was heißt, die Platten laufen auf nur 30%? Läuft dabei die CPU auf 100% oder weniger? Bei weniger liegts nicht an Deiner Programmierung, fürchte ich. Ist das Kopieren der Files denn spürbar schneller Dein Lesen?



  • volkard schrieb:

    ...
    Was heißt, die Platten laufen auf nur 30%? Läuft dabei die CPU auf 100% oder weniger? Bei weniger liegts nicht an Deiner Programmierung, fürchte ich. Ist das Kopieren der Files denn spürbar schneller Dein Lesen?

    Wenn ich meinen Lesedurchsatz anschaue wird bei Verwendung meines Programmes nur ca 30% dessen genutzt was ich als Maximum mit anderen Programmen gesehen habe.

    -----------------------------------

    Das Kopieren der Datenen geht schneller, doch befürchte ich dass irgendwo im Hinterfrund ein Cache gefüllt wird, denn selbst die 2. Ausführung meines Programmes läuft schneller.

    ------------------------------------

    @volkard
    Geht es auch mit der Verwendung von Puffern, dass man zuerst einen Block mit ReadFile liest, diesen auswertet, und dann den nächsten ließt?
    Oder anders formuliert: Wie kann ich nach einlesen des ersten Blockes (wenn mein Puffer zu klein war) den 2. Block lesen?



  • Ok, ich bastel Dir schnell was, was eine Checksumme berechnet.



  • #include <iostream>
    #include <windows.h>
    using namespace std;
    
    int main(){
    	HANDLE hFile;
    	char buffer[65536];
    	hFile=CreateFile("D:/200MB.tmp",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    	if(hFile==INVALID_HANDLE_VALUE){
    		cout<<"Datei nicht da odwer so\n";
    		return 1;
    	}
    	char* end=buffer;//egal
    	char* readPos=end;//gleich ende anzeigen, damit der erste block gleich geladen wird
    	unsigned int chkSum=1;
    	for(;;){
    		if(readPos==end){//wenn buffer leer, dann buffer einlesen
    //			cout<<"read block\n";
    			DWORD bytesRead;
    			BOOL rc=ReadFile(hFile,buffer,65536,&bytesRead,NULL);
    			if(rc==FALSE){
    				cout<<"unbekannter fehler\n";
    				break;
    			}
    			if(bytesRead==0){
    				cout<<"fertig\n";
    				break;
    			}
    			readPos=buffer;
    			end=buffer+bytesRead;
    		}
    		chkSum=chkSum*31+static_cast<unsigned char>(*readPos);//checksumme updaten
    		++readPos;//zeichen weiterschalten
    	}
    	cout<<"checksum: "<<chkSum<<'\n';
    }
    

    Die Schleife ist so komisch, damit nur ein bedingter Sprung drin ist und trotzdem die Berechnungszeile die Daten byteweise kommen sieht, nicht blockweise.



  • Vielen Dank für das Beispiel volkard. Damit konnte ich meine Anwendung umbauen.

    Sie ist nun etwas schneller als mit der klassischen C Variante und fopen, fread, fclose. Aber nur ca 4%.

    Aus irgend einem Grund wird das Ganze nicht schneller, obwohl meine CPU nicht bei 100% ist. Keine Ahnugn warum....



  • Also bei mir hüpften 900M in wenigen 10 Sekunden rein, weil die 900M noch ins RAM passen und die Platte sich nicht zu bewegen brauchte. Bei 3800M mußte die Platte liefern und es war im Viele-Minuten-Bereich. Also die Verarbeitungszeit und die Funktionsaufrufe sind wohl völlig irrelevant.
    Ist die Datei stark fragmentiert?



  • Hallo volkard,
    zwei Dinge kommen noch zusammen, zum einen handelt es sich nicht nur um eine Datei, zum anderen sind nicht alle Dateien sehr groß.
    Als ein Vergleich könnte recht gut eine zufällige Ansammlung der Dateien einer Festplatte dienen.
    Kann ich bei keinen Dateien noch was optimieren?



  • AntonWert schrieb:

    Hallo volkard,
    zwei Dinge kommen noch zusammen, zum einen handelt es sich nicht nur um eine Datei, zum anderen sind nicht alle Dateien sehr groß.
    Als ein Vergleich könnte recht gut eine zufällige Ansammlung der Dateien einer Festplatte dienen.
    Kann ich bei keinen Dateien noch was optimieren?

    Zuerst würde mir einfallen, die Dateien auf der Platte zusammenzurücken mit MyDefrag http://www.mydefrag.com/
    Oder, falls möglich noch besser, sie werden alle in der Reihenfolge, wie sie gelesen werden sollen, zusammenkopiert, und eine zweite Indexdatei (die später im RAM liegt) sagt, welcher Dateiname welchem Bereich innerhalb der großen Datei entspricht. (SetFilePointer).
    Die große Datei kann dann mit Contig http://technet.microsoft.com/de-de/sysinternals/bb897428.aspx defragmentiert werden.



  • Mal in eine andere Richtung gedacht: Wie viele CPUs / Kerne sind im (Produktiv-) System vorhanden? Wenn es zwei oder mehr sind: Da es sowieso schon um mehrere Dateien geht, könnte man das Einlesen und Verarbeiten auf mehrere Threads verteilen?



  • Die API-Funktionen sind die schnellsten...

    #define RET_ASSERT(x, ret) if (!(x)) return ret
    
    	/////////////////////////////////////////////////////////////////
    	////
    	///		Read File
    	//
    	//////////////////////////////////////////////////////////////
    
    	LPVOID ReadFileBinaryEx(LPTSTR _ptcFileName, size_t _stFileSize, size_t _stBytesToRead, size_t _stExtension)
    	{
    		HANDLE hfile;
    		PCHAR result;
    		DWORD fread;
    
    		RET_ASSERT((_ptcFileName != NULL) && (_stFileSize != NULL, NULL));
    
    		hfile = CreateFile(_ptcFileName, 
    			GENERIC_READ, 
    			FILE_SHARE_READ, 
    			NULL, 
    			OPEN_EXISTING, 
    			FILE_ATTRIBUTE_NORMAL,
    			NULL);
    
    		RET_ASSERT(hfile != INVALID_HANDLE_VALUE, NULL);
    
    		_stFileSize = (_stBytesToRead == NULL) ? GetFileSize(hfile, NULL) : _stBytesToRead;
    
    		RET_ASSERT(_stFileSize != 0, NULL);
    
    		if (_stExtension != NULL)
    			_stFileSize += _stExtension;
    
    		result = new char[_stFileSize];
    
    		if (!ReadFile(hfile, result, _stFileSize, &fread, NULL))
    		{
    			delete [] result;
    			result = NULL;
    		}
    
    		CloseHandle(hfile);
    
    		return result;
    	}
    
    	LPVOID ReadFileBinary(LPTSTR _ptcFileName, size_t _stFileSize)
    	{
    		char * result;
    
    		result = (char*)ReadFileBinaryEx(_ptcFileName, _stFileSize, NULL, NULL);
    
    		RET_ASSERT(result != NULL, NULL);
    
    		return result;
    	}
    
    	LPSTR ReadAllText(LPTSTR _ptcFileName)
    	{
    		PCHAR result;
    		size_t length;
    
    		result = (PCHAR)ReadFileBinaryEx(_ptcFileName, 0, NULL, 1);
    
    		RET_ASSERT(result != NULL, NULL);
    
    		*(result + length - 1) = '\0';
    
    		return result;
    	}
    
    	/////////////////////////////////////////////////////////////////
    	////
    	///		Write File
    	//
    	//////////////////////////////////////////////////////////////
    
    	DWORD WriteFileBinaryEx(LPTSTR _lpFileName, LPVOID _lpvData, size_t _stSize, INT _nCreationDisposition, DWORD _dwDesiredAccess)
    	{
    		HANDLE hfile;
    		DWORD fwritten;
    
    		RET_ASSERT(_lpvData != NULL && _lpFileName != NULL && _stSize != NULL, NULL);
    
    		hfile = CreateFile(
    			_lpFileName,
    			_dwDesiredAccess,
    			FILE_SHARE_READ | FILE_SHARE_WRITE,
    			NULL,
    			_nCreationDisposition,
    			FILE_ATTRIBUTE_NORMAL,
    			NULL);
    
    		RET_ASSERT(hfile != NULL, NULL);
    		RET_ASSERT(WriteFile(hfile, _lpvData, _stSize, &fwritten, NULL), NULL);
    
    		CloseHandle(hfile);
    
    		return fwritten;
    	}
    
    	void WriteAllText(LPTSTR _lpFileName, LPSTR _lpvData)
    	{
    		DWORD ret;
    
    		RET_ASSERT(_lpvData != NULL,);
    
    		ret = WriteFileBinaryEx(_lpFileName, _lpvData, strlen((PCHAR)_lpvData), GENERIC_WRITE, OPEN_ALWAYS);
    
    		RET_ASSERT(ret != NULL,);
    	}
    
    	int AppendFile(LPTSTR _FileName, void * _Data, size_t _Size, BOOL _CreateIfNotExist)
    	{
    		HANDLE hfile;
    		DWORD pos, written;
    
    		RET_ASSERT((_FileName != NULL) && (_Data != NULL) && (_Size != 0), NULL);
    
    		hfile = CreateFile(_FileName,
    					FILE_APPEND_DATA, 
    					FILE_SHARE_WRITE,
    					NULL,
    					(_CreateIfNotExist)? OPEN_ALWAYS : OPEN_EXISTING,
    					FILE_ATTRIBUTE_NORMAL,
    					NULL);
    
    		pos = SetFilePointer(hfile, 0, NULL, FILE_END);
    		LockFile(hfile, pos, 0, _Size, 0);
    		RET_ASSERT(WriteFile(hfile, _Data, _Size, &written, NULL), NULL);
    		UnlockFile(hfile, pos, 0, _Size, 0);
    
    		return (int)written;
    	}
    
    	int AppendFile(LPTSTR _FileName, LPSTR _String, BOOL _CreateIfNotExist)
    	{
    		return AppendFile(_FileName, _String, strlen(_String), _CreateIfNotExist);
    	}
    
    	int AppendFile(LPTSTR _FileName, LPSTR _String)
    	{
    		return AppendFile(_FileName, _String, strlen(_String), TRUE);
    	}
    
    	BOOL FileExist(LPTSTR _FileName)
    	{
    		return (GetFileAttributes(_FileName) == INVALID_FILE_ATTRIBUTES)? FALSE : TRUE;
    	}
    


  • Joe_M. schrieb:

    Mal in eine andere Richtung gedacht: Wie viele CPUs / Kerne sind im (Produktiv-) System vorhanden? Wenn es zwei oder mehr sind: Da es sowieso schon um mehrere Dateien geht, könnte man das Einlesen und Verarbeiten auf mehrere Threads verteilen?

    Die Anzahl der möglichen Kerne auf denen die Anwendung ausgeführt wird sind 1-4. Daher eher schiwierig.

    Aber man könnte das ganze mal durchdenken was währe wenn... (zB mit Systemabfrage)

    Bei einem kern ist kein Vorteil zu erwarten.
    Was ist bei mehreren Kernen, geht es schneller wenn die Anwendung aus mehreren Dateien ließt?



  • Flaschenhals ist die Festplatte.


Anmelden zum Antworten