Warum ist Datei-E / A in großen Abschnitten LÄNGER als in kleinen Blöcken?

8

Wenn Sie ReadFile einmal mit etwa 32 MB als Größe aufrufen, dann ist es dauert deutlich länger als wenn Sie die entsprechende Anzahl von Bytes mit einer kleineren Chunk-Größe wie 32 KB lesen.

Warum?

(Nein, meine Festplatte ist nicht ausgelastet.)

Bearbeiten 1:

Vergessen zu erwähnen - ich mache das mit FILE_FLAG_NO_BUFFERING !

Bearbeiten 2:

Seltsam ...

Ich habe keinen Zugriff mehr auf meine alte Maschine (PATA), aber als ich sie dort getestet habe, dauerte es etwa zweimal so lange, manchmal mehr. Auf meiner neuen Maschine (SATA) bekomme ich nur einen ~ 25% Unterschied.

Hier ist ein Stück Code zum Testen:

%Vor%

Ergebnis:

  

Groß gelesen: 1076 für 67108864 Bytes
  Kleine Lesevorgänge: 842 für 67108864 Byte

Irgendwelche Ideen?

    
Mehrdad 18.05.2011, 07:40
quelle

6 Antworten

1

Ihr Test umfasst die Zeit, die zum Einlesen von Dateimetadaten benötigt wird, insbesondere die Zuordnung von Dateidaten zur Festplatte. Wenn Sie das Dateihandle schließen und erneut öffnen, sollten Sie für jedes Timing ähnliche Timings erhalten. Ich habe das lokal getestet, um sicherzugehen.

Der Effekt ist wahrscheinlich schwerer mit starker Fragmentierung, da Sie mehr Datei-zu-Datenträger-Mappings einlesen müssen.

EDIT: Um klar zu sein, ich habe diese Änderung lokal ausgeführt und sah fast identische Zeiten mit großen und kleinen Reads. Indem ich das gleiche Datei-Handle wiederverwendete, sah ich ähnliche Timings von der ursprünglichen Frage.

    
MSN 10.06.2011, 19:05
quelle
1

Dies ist nicht spezifisch für Windows. Ich habe vor einiger Zeit einige Tests mit der C ++ iostream-Bibliothek durchgeführt und festgestellt, dass es eine optimale Puffergröße für Lesevorgänge gibt, über die sich die Leistung verschlechtert. Leider habe ich die Tests nicht mehr und kann mich nicht mehr an die Größe erinnern :-). Aus diesem Grund gibt es viele Probleme, wie zum Beispiel einen großen Puffer, der in anderen Anwendungen, die zur gleichen Zeit ausgeführt werden, einen Paging-Vorgang verursacht (da der Puffer nicht ausgelagert werden kann).

    
Neil Butterworth 18.05.2011 07:47
quelle
1

Wenn Sie die 1024 * 32KB-Lesevorgänge ausführen, lesen Sie immer wieder in denselben Speicherblock oder ordnen Sie auch insgesamt 32MB zu rad zu und füllen die gesamten 32MB?

Wenn Sie die kleineren Lesevorgänge in denselben 32K-Speicherblock lesen, ist der Zeitunterschied wahrscheinlich einfach, dass Windows den zusätzlichen Speicher nicht auffängt.

Aktualisierung basierend auf der FILE_FLAG_NO_BUFFERING Ergänzung zu der Frage:

Ich bin nicht 100% sicher, aber ich glaube, wenn% ce_de% verwendet wird, wird Windows den Puffer in den physischen Speicher sperren, damit der Gerätetreiber mit physischen Adressen umgehen kann (zB mit DMA direkt in den Puffer). Es könnte (glaube ich) dies tun, indem man eine große Anfrage in kleinere Anfragen aufteilt, aber ich vermute, dass Microsoft die Philosophie haben könnte: "Wenn Sie nach FILE_FLAG_NO_BUFFERING fragen, dann nehmen wir an, Sie wissen, was Sie tun und wir sind dir nicht in die Quere kommen. "

Natürlich erfordert das gleichzeitige Sperren von 32 MB anstelle von 32 KB mehr Ressourcen. Das wäre also meine anfängliche Vermutung, aber eher auf der physischen als auf der virtuellen Ebene.

Da ich jedoch nicht für MS arbeite und keinen Zugriff auf die Windows-Quelle habe, gehe ich durch vage Erinnerungen aus Zeiten, als ich näher mit dem Windows-Kernel und Gerätetreibermodell gearbeitet habe (also mehr oder weniger Spekulation).

    
Michael Burr 18.05.2011 07:47
quelle
0

Wenn Sie FILE_FLAG_NO_BUFFERING ausgeführt haben, bedeutet dies, dass das Betriebssystem die E / A nicht puffert. Jedes Mal, wenn Sie die Lesefunktion aufrufen, wird ein Systemaufruf ausgeführt, der die Daten von der Festplatte einmal holt. Um dann eine Datei mit einer festen Größe zu lesen, wenn Sie weniger Puffergröße verwenden, werden mehr Systemaufrufe benötigt, so dass mehr Benutzerraum für den Kernelraum und für jedes Mal, wenn eine Platten-E / A initiiert wird, erforderlich ist. Wenn Sie stattdessen eine größere Blockgröße verwenden, sind weniger Systemaufrufe erforderlich, damit die gleiche Dateigröße gelesen werden kann, so dass die Anzahl der Benutzer für Kernel-Space-Switches geringer ist und die Anzahl der Initialisierungen des Festplatten-I / O ebenfalls geringer ist. Aus diesem Grund benötigt ein größerer Block normalerweise weniger Zeit zum Lesen.

Versuchen Sie, die Datei nur 1 byte zu einem Zeitpunkt ohne Pufferung zu lesen, und versuchen Sie dann mit 4096 bytes block und sehen Sie den Unterschied.

    
phoxis 18.05.2011 08:01
quelle
0

Eine mögliche Erklärung wäre meiner Meinung nach Befehlswarteschlange mit FILE_FLAG_NO_BUFFERING , da dies DMA-Lesevorgänge auf niedriger Ebene steuert.

Eine einzelne große Anfrage wird natürlich immer noch in Unteranfragen aufgeteilt, aber diese werden wahrscheinlich mehr oder weniger nacheinander gesendet (weil der Fahrer die Seiten sperren muss und wahrscheinlich nur ungern mehrere sperren wird) Megabyte, damit das Kontingent nicht erreicht wird.)

Wenn Sie auf der anderen Seite ein Dutzend oder zwei Dutzend Anfragen an den Treiber richten, werden diese einfach auf die Festplatte und die Festplatte weitergeleitet und nutzen NCQ.

Nun, das ist, was ich denke, könnte der Grund sein (dies erklärt nicht, warum genau das gleiche Phänomen mit gepufferten Lesevorgängen passiert, wie in dem Q, mit dem ich oben verlinkt habe).

    
Damon 10.06.2011 19:57
quelle
0

Was Sie wahrscheinlich beobachten, ist, dass bei Verwendung von kleineren Blöcken der zweite Datenblock gelesen werden kann, während der erste verarbeitet wird, dann der dritte gelesen wird, während der zweite verarbeitet wird, usw., so dass das Geschwindigkeitslimit ist langsamer von der physikalischen Lesezeit oder der Verarbeitungszeit. Wenn die Verarbeitung eines Blocks die gleiche Zeit in Anspruch nimmt wie die Berechnung des nächsten Blocks, könnte die Geschwindigkeit doppelt so groß sein, als wenn die Verarbeitung und das Lesen getrennt wären. Wenn größere Blöcke verwendet werden, wird die Datenmenge, die während der Verarbeitung des ersten Blocks gelesen wird, auf einen kleineren Wert als die Blockgröße begrenzt. Wenn der Code für den nächsten Datenblock bereit ist, wird ein Teil davon gelesen, einige davon jedoch nicht. es wird daher notwendig sein, dass der Code wartet, während der Rest der Daten abgerufen wird.

    
supercat 18.06.2011 01:27
quelle

Tags und Links