Warum wird gepuffertes Schreiben in eine fmemopen () 'ed FILE schneller als ungepuffert?

8

Sicher ist gepufferte E / A zu einer Datei auf der Festplatte schneller als ungepuffert. Aber warum gibt es einen Vorteil, wenn Sie in einen Speicher schreiben?

Das folgende Benchmark-Codebeispiel wurde mit gcc 5.40 unter Verwendung der Optimierungsoption -O3 kompiliert, die mit glibc 2.24 verknüpft ist. (Seien Sie sich bewusst, dass der allgemeine glibc 2.23 Fehler bezüglich fmemopen () hat.)

%Vor%

Ergebnis für die gepufferte Version:

%Vor%

Ergebnis für die ungepufferte Version

%Vor%     
ralfg 11.08.2016, 13:30
quelle

3 Antworten

2

Der gepufferte Versionsleistungseintrag:

%Vor%

der ungepufferte Versionssatz:

%Vor%

Das diff:

%Vor%

Nach dem Einstieg in den Quellcode habe ich festgestellt, dass fwrite , das ist _IO_fwrite in iofwrite.c, nur eine Wrapperfunktion der eigentlichen Schreibfunktion _IO_sputn ist. Und auch gefunden:

%Vor%

Da die __xsputn -Funktion tatsächlich die _IO_file_xsputn ist, die wie folgt gefunden werden kann:

%Vor%

Endlich, unten in der Funktion _IO_new_file_xsputn in Datei.c, ist der zugehörige Teil des Codes wie folgt:

%Vor%

Auf einem RHEL 7.2 ist% ce_de% gleich 8192, wenn der Puffer aktiviert wurde, ansonsten gleich 1.

Also gibt es Fälle:

  • Fall 1: mit aktiviertem Puffer

    do_write = to_do - (zu_do% block_size) = to_do - (to_do% 8192)

In unserem Fall %Code% also das block_size und ruft die Funktion to_do = sizeof(uint32) auf.

  • Fall 2: ohne Puffer

do_write = 0 Funktion, danach _IO_default_xsputn ist Null. Und new_do_write ist nur ein Wrapper-Aufruf von to_do

%Vor%

Wie wir sehen können, ist der new_do_write tatsächlich der _IO_SYSWRITE Aufruf. Daher wird der Leistungsunterschied durch den _IO_SYSWRITE -Aufruf verursacht. Und das hat der bisherige Leistungsausweis bewiesen.

Endlich ist diese Frage sehr gut, und ich bin sehr daran interessiert, und es hilft mir, einige IO-Funktionen unter der Oberfläche zu lernen. Weitere Informationen zu anderen Plattformen finden Sie Ссылка .

    
oxnz 12.08.2016 08:26
quelle
1

Danke Leute für eure Hilfe bis jetzt.

Ich habe den Bibliotheks-Quellcode von glibc 2.24 überprüft, und es scheint, dass die zusätzliche Logik zum Hinzufügen eines 0-Bytes bei jedem Flush für den Zeitaufwand verantwortlich ist. Siehe auch die man-Seite:

  

Wenn ein Stream, der zum Schreiben geöffnet wurde, geleert wird (fflush (3))   oder geschlossen (fclose (3)), wird am Ende des Bytes ein Nullbyte geschrieben   puffern, wenn Platz vorhanden ist.

Im ungepufferten Modus wird dieses Null-Byte nach jedem fwrite () hinzugefügt, nur um mit dem nächsten fwrite () überschrieben zu werden.

Ich kopiere den Quellcode der Bibliothek für fmemopen_write (), für diejenigen, die sich auch über dieses merkwürdige Verhalten wundern ...

%Vor%     
ralfg 11.08.2016 15:14
quelle
0

Beim Aufruf in eine Bibliothek wird die Optimierungsstufe des Codes nicht vom Code beeinflusst und wäre konstant.

Aus diesem Grund beeinflusst das Ändern der Schreibgröße nicht das Verhältnis innerhalb der Testgrenzen. (Wenn die Schreibgröße zu Ihrer Datengröße tendiert, würde Ihr Code dominieren.)

Die Kosten für den Aufruf von fwrite entscheiden darüber, ob die Daten gelöscht werden oder nicht.

Obwohl ich nicht sicher bin, ob fwrite für Speicher-Streams implementiert wird, ist es wahrscheinlich, dass der syscall oder das Sicherheitsgate der OS-Funktion die Kosten dominiert, wenn der Aufruf dem Kernel nahe kommt. Aus diesem Grund ist das Schreiben von Daten am besten mit dem zugrunde liegenden Geschäft kompatibel.

Empirisch habe ich festgestellt, dass Dateisysteme mit 8kb Chunks recht gut funktionieren. Ich würde 4kb für ein Speichersystem betrachten - da dies die Größe einer Prozessor-Seitengrenze ist.

    
mksteve 11.08.2016 14:27
quelle

Tags und Links