Suche Buffered-File Klasse



  • Hi!

    Die MSVC CRT erlaubt nur maximal 2048 gleichzeitig offene Files. Dummerweise hab' ich jetzt aber eine (Server-) Applikation, wo ich mehr bräuchte. Das Limit ist in der CRT, Windows selbst hat überhauptkein Problem mit etlichen Zehntausend offenen Files in einem Prozess (hab mal 60k probiert -> kein Problem).

    Direkt HANDLEs zu verwenden wäre natürlich einfach. Ich hätte aber schon ganz gerne "buffered IO", da sonst einige Dinge ziemlich langsam werden.

    Kennt jemand eine fertige Klasse die sowas anbietet? Bzw. eine (kleine) Library die sowas beinhaltet? Hab nämlich nicht unbedingt Lust mir sowas selbst zu schreiben, wenn es nicht sein muss... 🙂

    (Ich weiss, das ist MSVC spezifisch, aber das MSVC spezifische ist ja eigentlich nur das Problem. Was ich suche ist ja eigentlich Plattform-neutral, daher poste ich im C++- und nicht im MSVC- oder WinAPI-Forum)



  • hustbaer schrieb:

    Direkt HANDLEs zu verwenden wäre natürlich einfach. Ich hätte aber schon ganz gerne "buffered IO", da sonst einige Dinge ziemlich langsam werden.

    Wenn man es nicht explizit abstellt ist der Zugriff doch buffered, oder?



  • Nachfrager schrieb:

    hustbaer schrieb:

    Direkt HANDLEs zu verwenden wäre natürlich einfach. Ich hätte aber schon ganz gerne "buffered IO", da sonst einige Dinge ziemlich langsam werden.

    Wenn man es nicht explizit abstellt ist der Zugriff doch buffered, oder?

    Nur im Kernel-Mode, über den normalen File-Cache.
    100.000x ReadFile mit einem Byte ist allerdings *VIEL* langsamer, als wenn im User-Mode nochmal ein (kleiner) Buffer verwendet wird.

    (auf Deutsch: jeder einzige ReadFile Befehl führt dazu dass eine User-Mode -> Kernel-Mode Transition stattfindet, ein neuer IRP erstellt wird, der IRP an den zuständigen Treiber übergeben wird, dann irgendwann "bedient", dann wieder freigegeben, dann wieder eine Kernel-Mode -> User-Mode Transition, und dann irgendwann, 100 Jahre später, kommt ReadFile zurück. Und alle diese Zwischen-Schritte sind (vergleichsweise) teuer. Auf jeden Fall 100x teurer als ein paar einfache "ifs" im User-Mode. Selbes für Spiel WriteFile.)

    Probier mal aus was es für einen Unterschied macht, wenn du ein File byteweise kopierst. 1x über FILE*, und 1x über HANDLE. Dann weisst du was ich meine, und warum CRT Implementierungen üblicherweise ihren eigenen User-Mode Buffer verwenden.

    Natürlich könnte man auch das Programm so umstellen, dass Daten in ausreichend grossen Stücken gelesen/geschrieben werden. Ist aber umständlicher, als das ganze gleich "eine Ebene tiefer", nämlich in der File-Schnittstelle zu implementieren. "Eine ebene höher" sind es nämlich viel mehr Stellen die man anpassen müsste. Und ich mache lieber 1x was ich 1x machen kann, und nicht Nx machen muss 🙂



  • Was brauchst du denn alles an Funktionalität? Nur rohes (binäres) Lesen und Schreiben (und Seeken) oder auch die Stream-Sachen?



  • Nur binary IO reicht vollkommen.
    Seeken brauch ich auch, ja.
    (Eine Klasse die mir gleich "read at" und "write at" anbeitet wäre sogar ideal. Über seek+read bzw. seek+write geht's aber natürlich auch.)

    Eigentlich der klassische Anwendungsfall für die FILE* Funktionen, aber wie gesagt: die MSVCRT macht mir da leider einen Strich durch die Rechnung.

    BTW: bitte niemand memory-mapped IO vorschlagen. Wenn ich genügend Adressraum übrig hätte, um mit memory-mapped IO zu arbeiten, bräuchte ich garkeine Files 😉
    (Portierung auf 64 Bit möchte ich vermeiden wenns geht)



  • Ich habe leider nur bedingt eine Ahnung davon, wie man unter Windows an die Kernelmodezugriffe herankommt. Aber Du scheinst ja ein Testprogramm dafür gemacht zu haben.

    Aber wirklich plattformneutral wird es wohl schwierig werden, wenn die Standard
    IO-Funktionen nicht die gewünschte Funktionalität bieten.

    Könntest Du nichht auf Grundlage der von Dir benutzen Kernelmode-Funktionen eine eigene filebuf-Klasse implementieren, welche die gewünschte Funktionalität anbietet? Die Filebuffer lassen sich leicht austauschen, wenn man die Plattform wechselt (auf *n*x*-Systemen könnte man z.B. die Standardvariante benutzen).

    Naja, vermutlich ist die Idee völlig scheisse...



  • blöde Frage: was spricht nochmal genau gegen std::fstream? der enthaltene filebuf ist doch gepuffert. Und wenn dir dort die Pufferung nicht genug ist, dann kannst du mit streambuf::pubsetbuf() einen größeren Puffer zur Verfügung stellen. (wobei du da bei zigtausend files natürlich irgendwann aufpassen musst).



  • pumuckl schrieb:

    blöde Frage: was spricht nochmal genau gegen std::fstream? der enthaltene filebuf ist doch gepuffert. Und wenn dir dort die Pufferung nicht genug ist, dann kannst du mit streambuf::pubsetbuf() einen größeren Puffer zur Verfügung stellen. (wobei du da bei zigtausend files natürlich irgendwann aufpassen musst).

    std::fstream schafft aber nicht mehr als 2048 offene Handles gleichzeitig. Und wenn ich das richtig verstanden habe, liegt genau da das Problem. Defaultmäßig scheint es sogar noch weniger zu sein, aber über _setmaxstdio lässt sich das immerhin auf 2048 erhöhen.

    Ich habe mich gerade mal aus Interesse etwas schlau gemacht. Mit der WinAPI kommt mit den Default-Einstellungen auf 10000 offene Handles. Und es gibt Funktionen, mit denen man dies noch weiter erhöhen kann.
    Bei den Funktionen aus der CRT kommt man allerdings tatsächlich nicht über 2048.
    Ich würde da tatsächlich eine eigene filebuf Klasse auf der Grundlage der WinAPI-Funktionen implementieren. So ist es mit minimalen Aufwand möglich, das Programm zu portieren.

    Edit: Siehe auch hier



  • Tachyon schrieb:

    Ich habe leider nur bedingt eine Ahnung davon, wie man unter Windows an die Kernelmodezugriffe herankommt. Aber Du scheinst ja ein Testprogramm dafür gemacht zu haben.

    Aber wirklich plattformneutral wird es wohl schwierig werden, wenn die Standard
    IO-Funktionen nicht die gewünschte Funktionalität bieten.

    Die Lösung muss auch nicht unbedingt Plattform-Neutral sein, es reicht Windows. Ich meinte bloss, dass eine Decorator-Klasse, die auf einem ungepufferten "read/write/seek" Interface ein gepuffertes macht, eigentlich nicht OS-spezifisch ist. So war das zu verstehen. Die Windows Kernel Calls kenne ich ausreichend gut, das wäre nicht das Problem.

    Könntest Du nichht auf Grundlage der von Dir benutzen Kernelmode-Funktionen eine eigene filebuf-Klasse implementieren, welche die gewünschte Funktionalität anbietet? Die Filebuffer lassen sich leicht austauschen, wenn man die Plattform wechselt (auf *n*x*-Systemen könnte man z.B. die Standardvariante benutzen).

    Naja, vermutlich ist die Idee völlig scheisse...

    Die Idee ist nicht völlig scheisse, aber eigentlich genau das was ich vermeiden möchte: nämlich selbst was zu basteln. Solche Sachen sind extrem nervig zu testen, und ich bin (zumindest bevor ich angefangen habe zu suchen) davon ausgegangen, dass es sowas doch schon zu Hauf geben müsste.



  • pumuckl schrieb:

    blöde Frage: was spricht nochmal genau gegen std::fstream? der enthaltene filebuf ist doch gepuffert. Und wenn dir dort die Pufferung nicht genug ist, dann kannst du mit streambuf::pubsetbuf() einen größeren Puffer zur Verfügung stellen. (wobei du da bei zigtausend files natürlich irgendwann aufpassen musst).

    Nönö, siehe Tachyons Beitrag: die Pufferung von fstream wäre 100% OK, bloss verwendet die fstream Implementierung in der MSVC Library eben die FILE* Funktionen, und die FILE* Funktionen sind auf 2048 Files limitiert. Genau das ist ja das Problem. Sprich: ich kann 2048-3=2045 fstream Objekte aufmachen (fstream.open()), beim 2046. bekomm ich einfach nen Fehler. (Schon ausprobiert, also nicht bloss hörensagen 😉 )



  • Tachyon schrieb:

    pumuckl schrieb:

    blöde Frage: was spricht nochmal genau gegen std::fstream? der enthaltene filebuf ist doch gepuffert. Und wenn dir dort die Pufferung nicht genug ist, dann kannst du mit streambuf::pubsetbuf() einen größeren Puffer zur Verfügung stellen. (wobei du da bei zigtausend files natürlich irgendwann aufpassen musst).

    std::fstream schafft aber nicht mehr als 2048 offene Handles gleichzeitig. Und wenn ich das richtig verstanden habe, liegt genau da das Problem. Defaultmäßig scheint es sogar noch weniger zu sein, aber über _setmaxstdio lässt sich das immerhin auf 2048 erhöhen.

    Ganz genau. Default ist 512, was aber nicht das Problem wäre. Das Limit von 2048 bei _setmaxstdio ist das Problem.

    Ich habe mich gerade mal aus Interesse etwas schlau gemacht. Mit der WinAPI kommt mit den Default-Einstellungen auf 10000 offene Handles. Und es gibt Funktionen, mit denen man dies noch weiter erhöhen kann.

    Hm. Wo gibt's da ein 10k Limit? Ich hab ohne irgendwelche Tricks 60k offene Files hinbekommen...

    Bei den Funktionen aus der CRT kommt man allerdings tatsächlich nicht über 2048.
    Ich würde da tatsächlich eine eigene filebuf Klasse auf der Grundlage der WinAPI-Funktionen implementieren. So ist es mit minimalen Aufwand möglich, das Programm zu portieren.

    Hm.
    Hier müsste ich dann basic_streambuf implementieren, oder wie? Leider hab' ich von der ganzen iostream Geschichte kaum eine Ahnung (also speziell was die lower-level Sachen ala basic_streambuf angeht) 😞
    Wie kompliziert wäre das? Gibt's da irgendwelche Hilfsteile, z.B. in der Boost? Werde diesbezüglich wohl mal selbst gucken, aber wenn jmd. was weiss, bitte lasst es mich wissen 🙂



  • könnt man ned die crt hacken???????



  • klein kind schrieb:

    könnt man ned die crt hacken???????

    Klar.
    Man könnte sich auch ein Loch ins Knie schiessen, und dann raustrinken.



  • 10k Limit: http://support.microsoft.com/kb/327699
    Scheint aber variabel zu sein.
    Simon



  • hustbaer schrieb:

    Hm. Wo gibt's da ein 10k Limit? Ich hab ohne irgendwelche Tricks 60k offene Files hinbekommen...

    Das habe ich selbst nur aus einem Forum. 🤡
    Kann also gut Blödsinn sein. Wie im EIngangspost geschrieben, habe ich nur bedingt Ahnung, und hab mich nur aus Interesse etwas (halb) schlau gemacht.

    hustbaer schrieb:

    Hier müsste ich dann basic_streambuf implementieren, oder wie? Leider hab' ich von der ganzen iostream Geschichte kaum eine Ahnung[...]

    Ich gucke morgen noch einmal nach. Vielleicht kann auch einer der Stream-Spezis was Genaueres aus dem Stegreif sagen (Werner?).



  • theta schrieb:

    10k Limit: http://support.microsoft.com/kb/327699
    Scheint aber variabel zu sein.
    Simon

    scheint sich nur auf user32.dll zu beziehen.



  • So.

    Hab mir mal die Boost.Iostreams ganz kurz angeguckt. Sieht so aus, als ob da alles drin wäre was ich brauche (und mehr).
    boost::iostreams::stream<boost::iostreams::file_descriptor> sieht auf den ersten Blick mal genau nach dem aus, was ich gesucht habe.
    Fix & fertig, out-of-the-box gepufferte IO Klasse, auf Basis von OS-Handles! 🙂

    Ich werde berichten wie das Ding sich macht. Speziell was Memory-Overhead angeht. Performance werd ich erstmal nicht gröber testen -- solange es deutlich schneller als direkte OS-Calls ist, wird es für meine Zwecke reichen.



  • Aus Spaß an der Sache hab ich da mal was gebastelt: *klick*. Wie eine streambuf-Lösung aussieht, würde mich aber auch interessieren.

    Interface von dem Ding sieht so aus:

    class BufferedFile
    {
    	public:
    		explicit BufferedFile( const std::string& filename, std::ios_base::openmode open_mode );
    		~BufferedFile();
    
    		void seek( long long offs, std::ios_base::seek_dir dir=std::ios::beg );
    
    		size_t read( void* buffer, size_t to_read );
    		size_t readAt( void* buffer, size_t to_read, long long offs, std::ios_base::seek_dir dir=std::ios::beg );
    
    		size_t write( const void* buffer, size_t to_write );
    		size_t writeAt( void* buffer, size_t to_write, long long offs, std::ios_base::seek_dir dir=std::ios_base::beg );
    
    		unsigned long long tell() const;
    		unsigned long long size() const;
    };
    

    edit: Mist... 🙂

    edit2: Link nochmal rausgenommen, war zu Testzwecken was gefährliches drin, korrigiere morgen

    edit3: Ist wieder da und in Ordnung gebracht.



  • @Badestrand:
    Das ist nur die halbe Miete, denn Writes werden garnicht gepuffert 😉



  • hustbaer schrieb:

    @Badestrand:
    Das ist nur die halbe Miete, denn Writes werden garnicht gepuffert 😉

    Argh, natürlich! Bin bis jetzt mit gepufferten Writes quasi nie direkt in Kontakt gekommen, da hab ich's einfach vergessen.. 🙂 Gut. Hm, nagut, hab ich halt ein wenig rumgespielt. Übrigens hab ich auch generell keine Ahnung von Buffering/Caching. Ich weiß, du willst es nur anwenden, aber hast du eventuell Links zu Artikeln? Ist an sich ja ein interessantes Feld.


Anmelden zum Antworten