All you can eat

NT-Fehler: File-Cache verschluckt sich an großen Dateien

Wissen | Know-how

Nach dem Motto ‘all you can eat’ benutzt NT freien Hauptspeicher fürs File-Caching. Leider lähmt der Mechanismus unter Umständen das System.

Der File-Cache spielt unter NT eine zentrale Rolle - ohne ihn geht überhaupt nichts. Ob im lokalen Dateisystem oder im LAN - alle gängigen Dateioperationen laufen mit seiner Hilfe ab. Das verspricht analog zu Smartdrive aus DOS- und Windows-3.x-Zeiten enorme Geschwindigkeitsvorteile: Ständig benötigte Daten schöpft NT aus schnellem RAM, statt sie von der langsamen Festplatte zu lesen. Analog verfährt es bei Dateiänderungen, indem es sie im Cache puffert und gesammelt zurückschreibt (Lazy Writing).

Der Cache-Manager von Windows NT benutzt dafür einen Teil des freien Hauptspeichers. So gebührt bei NT dem ‘freien’ Speicher besondere Bedeutung. Anders als DOS mit SmartDrive kennt die Cache-Verwaltung keine Maximalgröße, sondern paßt sich dynamisch an die Speicherverhältnisse an - jedenfalls der Theorie nach: Bei Helen Custer [1] kann man nachlesen, daß der Cache den Speicher räumte, wenn andere Systemaktivitäten dies verlangen (auf Seite 253ff.) - doch das ist leider nicht der Fall.

Der Fehler in der Cache-Implementation NTs tritt merklich indes nur bei großen Dateien auf. Gemeinerweise könnte man sagen, der Algorithmus arbeite wie die Firmenstrategie Microsofts: das gesamte Terrain langsam und schrittweise überfluten und ausfüllen; was bisher unbesetzt war, belegen und nichts mehr hergeben. Das Schließen der betroffenen Datei bringt den Cache wieder zur Räson; diese Notbremse fehlt leider im anderen Fall.

Beim Lesen füllt NT den Cache. Der freie Hauptspeicher nimmt dabei stetig ab. Kommt währenddessen eine Speicheranforderung, rückt der Cache keinen Speicher heraus. Statt dessen lagert NT andere Speicherinhalte aus und füllt dabei mitunter sogar das Paging-File zusätzlich. Der Erfolg: das Programm läuft viel langsamer als nötig. Das Symptom: exzessives Pagen, erkennbar an ständigen Festplattenzugriffen. Als Ursache dafür kann man den Cache ausmachen. Der Task-Manager zeigt auf der ‘Performance’-Seite die Größe des ‘File Cache’ an.

Ideales Werkzeug, um dieser Sache weiter auf den Grund zu gehen, hat NT mit dem Performance-Monitor bereits an Bord. Wir entwickelten Test-Programme, um das Verhalten zu reproduzieren. Und siehe da, ein einfaches Programm reicht, um das Fehlverhalten des NT-Cache-Managers zu provozieren. Der Performance-Monitor läßt erkennen, wie der freie Hauptspeicher abnimmt, der Cache ins Unermeßliche wächst, die Speicheranforderung ihm nichts anhaben kann, und sich statt dessen das Paging-File vergrößert.

Klar, daß das Programm angesichts der unnötigen Systemaktivitäten deutlich langsamer läuft. Das maßlose Cachen würde nur dann seinen Zweck erfüllen, wenn das Programm die Daten noch einmal liest, was aber beim sequentiellen Einlesen einer Datei eher unwahrscheinlich scheint. Negativ stößt dieses Verhalten indes nur dann auf, wenn große Dateien zu bewältigen sind, also die ohnehin dem Cache zugedachte Menge an Hauptspeicher deutlich überschritten wird.

Erstaunlicherweise ist der Fehler den Microsoft-Entwicklern schon mehrfach zu Gehör gebracht worden. Abgestellt haben sie ihn deshalb jedoch nicht. Wir standen mit Lou Perazzoli, Director Core OS der Microsoft Corporation, in regem EMail-Kontakt. Nach anfänglicher Skepsis nahm er sich der Sache an. Obwohl wir ihm sogar unser Testprogramm zur Verfügung stellten, stritt man die Existenz des Fehlers letztlich ab. Endgültiger Stand der Korrespondenz: Server und Workstation verhalten sich angeblich unterschiedlich - eine Aussage, die unser Testprogramm klar widerlegt.

Wenigstens erhielten wir aus Redmond Hinweise, wie das Problem zu umschiffen sei - fast ausschließlich Dinge, die ohnehin in der Win32-Dokumentation zu finden sind: Die Win32-Funktion zum Öffnen von Dateien weist ein spezielles Flag (FILE_FLAG_NO_BUFFERING) auf, das Operationen auf einer der Datei ohne Zuhilfenahme des Cache arbeiten läßt. Für die Nutzung des Flags muß man laut Dokumentation indes bezahlen: Dateizugriffe mit diesem Flag sollen auf Sektorgrenzen ausgerichtet erfolgen - eine enorme Limitation für gängige Programmlogik.

Daß diese Einschränkung tatsächlich zu beachten sei, darauf insistierten unsere Ansprechpartner bei Microsoft. Pikanterweise weiß die aktuelle NT-Version nichts davon. Trotz besagtem Flag funktionierten bei uns Zugriffe auch losgelöst von Sektorgrenzen. Allerdings klappte das Abschalten des Cache mit diesem Flag nicht für komprimierte Dateien - deshalb und aufgrund der Warnung in der Dokumentation keine empfehlenswerte Lösung. Ein weiterer Hinweis lautete: Memory Mapped Files. Dahinter steckt ein alternativer Zugriffsmechanismus auf Dateien, den NT zum Beispiel benutzt, um Programme und DLLs zu laden.

Ein Programm kann über Memory Mapped Files von Mechanismen der virtuellen Speicherverwaltung NTs profitieren. Dies stellt das Gegenstück zu Paging dar. Beim Pagen simuliert eine Datei Hauptspeicher, bei Memory Mapped Files hingegen bildet das System Zugriffe auf eine Datei im Speicher ab. Programme benutzen also schlicht Zeigeroperationen. Der nachträgliche Einbau des Mechanismus in ein bestehendes Programm, das bislang tradionelle Funktionen für File-I/O benutzt, bringt jedoch viel Arbeit mit sich.

Memory Mapped Files weisen weitere Nachteile auf: Die Datenkohärenz bei gleichzeitigen Zugriffen über ein Mapping und mit traditionellen Funktionen auf eine Datei ist nicht garantiert. Im Netz sind sie, wegen der Kohärenz, nur zum Lesen zu gebrauchen. Und, nicht zuletzt, muß ein Programm bei fortlaufendem Beschreiben von Memory Mapped Files regelmäßig den View (den in den Adreßraum eingeblendeten Teil der Datei) vergrößern. Der Mechanismus verspricht erst Vorteile, wenn er bereits beim Design des Programms berücksichtigt wird. Als Bug-Fix ist er untauglich.

Ebenfalls ins Leere traf Microsofts Hinweis auf Einträge der Registrierung (Registry). Von der Einstellung für LargeSystemCache (0 oder 1) zeigte sich NT unbeeindruckt. Damit hatten die NT-Entwickler ihr Pulver verschossen. In der ganzen Angelegenheit tat sich nur ein Microsoft-Mitarbeiter hervor, der in München für Entwicklersupport zuständig ist: Er lieferte einen Hinweis auf ein weiteres Flag (FILE_FLAG_SEQUENTIAL_SCAN) beim CreateFile-Aufruf - es verhalf FILE_FLAG_NO_BUFFERING auf komprimierten (NTFS-) Dateien erst zur gewünschten Wirkung.

Letztlich läßt sich das Verhalten des Cache mit FILE_ FLAG_SEQUENTIAL_SCAN immer in gewünschter Manier beeinflussen, also sowohl für unkomprimierte als auch komprimierte Dateien. NT liest damit selbst Dateien, die ein Vielfaches des Hauptspeichers umfassen, ohne daß der Cache immens anwächst. Der Hauptspeicher steht somit dem Programm zur Verarbeitung der Daten zur Verfügung. Ein Hinweis von Microsoft auf FILE_FLAG_SEQUENTIAL_ SCAN hätte unser ursprüngliches Problem lösen können...

Für Entwickler, die das Caching-Problem beim sequentiellen Verarbeiten von großen Dateien rechtzeitig bemerken, bietet NT demnach Abhilfe - das aber nur im Extrem: entweder mit oder ohne Cache. Nicht jeder Entwickler jedoch wird gleich darauf stoßen. Nicht einmal Microsoft höchstselbst hat schließlich erkannt, wo der Hase im Pfeffer lag; uns hätte schließlich ein Tip auf das Flag für sequentielles Lesen genügt. Der Verdacht drängt sich auf, daß viele weitere Applikationen davon betroffen sind. (ps)


Den Fehler in der NT-Cache-Implementierung deutlich vorzuführen, ist nicht ganz einfach. Versuchen, ihn zu reproduzieren, macht die dynamische Speicherverteilung des Systems oft einen Strich durch die Rechnung: Nach Lauf eines Programms, das große Speichermengen bewegt, befindet sich das System in einem anderen Zustand; das heißt, waren vor dem Lauf nur noch wenige MByte frei, sind es hinterher oft mehr als ein Dutzend MByte (auf einem 32-MByte-System).

NT gibt nämlich den vom File-Cache belegten Speicher etwa zehn Sekunden, nachdem eine große Datei geschlossen wurde, wieder frei. Ist der Cache sehr groß, steht der Speicher sofort anderen Programmen zur Verfügung. Unter Umständen hat NT, um den freien Speicher für den Cache zu bekommen, sogar die Paging-Datei mit anderen Daten gefüllt. Das heißt, ein Testlauf kann durchaus den freien Speicher ‘vermehrt’ haben. Bei normaler Benutzung kommt so etwas eher selten vor. Die Größe des File-Cache und des freien Speichers pendeln sich dann auf normale Werte ein.

Das Testprogramm ('cachetst' in ct9701.zip), das in der c’t-Mailbox, auf unserem ftp-Server und über die Sammeldiskette erhältlich ist, treibt das Fehlverhalten auf die Spitze (siehe Abbildung): Es liest auf einem System mit 128 MByte RAM eine 244 MByte große Datei sequentiell. Zu Anfang nimmt der freie Speicher kontinuierlich ab, während der File-Cache entsprechend wächst. Nachdem 60 Prozent der Datei gelesen sind, fordert das Programm die Hälfte des beim Start freien Hauptspeichers en bloc an. Zugriffe darauf sorgen dafür, daß NT tatsächlich auch Speicher liefert.

Nach der Speicheranforderung (erster, großer Knick in der blauen Kurve) läßt sich nur ein sanftes Abnehmen des File-Cache (gelb) beobachten. Statt dessen wächst die Auslastung des Paging-Files von sechs auf 40 Prozent; das entspricht bei einer Gesamtgröße des Paging-Files von 140 MByte ungefähr 48 MByte.

Nach der Speicheranforderung sinkt die Prozessorbelastung auf ein Drittel ab; das System ist nur mit Pagen beschäftigt. Erst beim zweiten, kleineren Knick in der blauen Kurve steht der angeforderte Speicher vollständig zur Verfügung; diese Anforderung dient lediglich als Marke. Sie läßt erkennen, wann der Speicher tatsächlich komplett zur Verfügung steht. Auf mit weniger RAM bestückten NT-Systemen macht sich der Fehler dagegen erst bei einem zweiten Lauf bemerkbar. Dann nimmt die Speicheranforderung des Programms wegen der nach dem ersten Lauf größeren Freispeichermenge beim Start auch ein erhebliches Maß an. Ansonsten tritt der Effekt nur schwach in Erscheinung und läßt sich im Performance-Monitor kaum erkennen. Finden die Speicheranforderungen peu à peu statt, so tritt der Fehler ebenfalls nicht so krass hervor, macht sich allerdings in der Programmlaufzeit dennoch deutlich bemerkbar - gegenüber einem Lauf ohne Cache läuft das Programm bis 30 Prozent langsamer.

Anzeige
Anzeige