[PYTHON] Overhead-Messung zum Lesen von Dateien

einpacken

  1. Der Aufwand für das Lesen von Dateien scheint groß zu sein.
  2. Es scheint, dass dieser Overhead erheblich reduziert werden kann, indem mehrere Dateien in eine eingelesen werden.
  3. Ich wollte diesen Effekt mit konkreten Zahlen sehen, also suchte ich nach einem geeigneten Datensatz und maß ihn unter Berücksichtigung der Umgebung.
  4. Infolgedessen war der Overhead größer als ich erwartet hatte.

Einführung

In den letzten Jahren hat die Nachfrage nach dem Lesen einer großen Anzahl von Dateien für maschinelles Lernen zugenommen. Wenn Sie jedoch versuchen, eine große Anzahl von Dateien zu lesen, ist der Aufwand für das Lesen der Dateien möglicherweise größer als die Hauptverarbeitung im Programm. Beispielsweise speichert CIFAR-10 mehrere Bilder in einer Datei, um den Lastaufwand zu verringern.

Ich war neugierig auf diesen Effekt und habe mit CIFAR-10 untersucht, wie sich der Aufwand für das Lesen von Dateien auf das Programm auswirkt.

Mokuji

  1. Datensatz
  2. Messprogramm
  3. Ergebnis
  4. [Erklärung](# Erklärung)
  5. Appendix

Datensatz

Der zu messende Datensatz ist CIFAR-10, der in der Bilderkennungswelt bekannt ist. CIFAR-10 ist eine Gruppe von Bildern, die aus 10 Klassen mit 32 x 32 Pixeln besteht. Verwenden Sie die als Binärdatei auf der oben genannten Site verteilte. Bilddaten für 10.000 Bilder werden in einer Binärdatei beschrieben. Die Struktur der Binärdatei ist wie folgt. cifar10_.png

Die Kapazität eines Bildes beträgt 1 Byte für Beschriftung + 32 x 32 x 3 Byte für Bilddaten = 3073 Byte, sodass eine Binärdatei etwa 30 MB groß ist. Durch Lesen wird der Overhead beim Lesen der Datei gemessen.

Messprogramm

Um den Overhead beim Lesen von Dateien zu messen, haben wir die folgenden drei Programme vorbereitet.

  1. open_time.cpp
  2. open_time_individual.cpp
  3. open_time_loop.cpp

open_timeIst cifar-Ein Programm, das 10 Binärdateien direkt liest. fopen () und fclose () werden während der Ausführung nur einmal aufgerufen. open_time_individualIst cifar-Es ist ein Programm, das 10 Binärdateien aus dem Verzeichnis liest, das durch Teilen jedes Bildes im Voraus gespeichert wurde. fopen () und fclose () werden im Programm 10000 Mal aufgerufen, dh die Anzahl der Bilder. open_time_loopIst cifar-Es ist ein Programm, das 10 Binärdateien direkt liest.open_timeIm Gegensatz zu fopen für jedes Bild()、fclose()Ist ein Programm, das aufruft. open_time_individualWie offen()、fclose()Wird während der Ausführung 10000 Mal aufgerufen.

Mit Ausnahme des obigen Lesens der Datei wird die diesen drei Programmen gemeinsame Verarbeitung erläutert. Die Ausführungszeit wird mit system_clock in der Chronobibliothek gemessen. Wie in Dataset erwähnt, ist das erste Byte der Binärdatei die Bildbezeichnung, daher überspringt "fseek (fp, 1L, SEEK_CUR)" 1 Byte. Das Bild wird von `` fread (pic, sizeof (uint8_t), 3072, fp) `gelesen, und der Wert jedes Pixels wird geladen, hinzugefügt und als Prozess in der Schleife gespeichert. Beachten Sie, dass die Fehlerbehandlung für Dateivorgänge weggelassen wird.

open_time.cpp


#include <stdio.h>
#include <chrono>
int main(int argc, char** argv) {
    chrono::system_clock::time_point start, end;
    uint8_t pic[3072] = {0};
    start = chrono::system_clock::now();
    auto fp = fopen("./cifar-10-batches-bin/data_batch_1.bin", "rb");
    for(int j=0;j<10000;++j){
        fseek(fp,1L,SEEK_CUR);
        fread(pic, sizeof(uint8_t), 3072, fp);
        for(int i=0;i<3072;++i){
            pic[i]++;
        }
    }
    fclose(fp);
    end = chrono::system_clock::now();
    double time = static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
    printf("time %lf[ms]\n", time);

    return 0;

open_time_individual.cpp


#include <stdio.h>
#include <chrono>
#include <string>
int main(int argc, char** argv) {
    chrono::system_clock::time_point start, end;
    std::string filenames[10000] = {""};
    for(int j=0; j<10000;++j){
        filenames[j] = "./cifar10-raw/" + std::to_string(j) + ".bin";
    }
    uint8_t pic[3072] = {0};
    start = chrono::system_clock::now();
    for(int j=0;j<10000;++j){
        auto fp = fopen(filenames[j].c_str(), "rb");
        fseek(fp,1L,SEEK_CUR);
        fread(pic, sizeof(uint8_t), 3072, fp);
        for(int i=0;i<3072;++i){
            pic[i]++;
        }
        fclose(fp);
    }
    end = chrono::system_clock::now();
    double time = static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
    printf("time %lf[ms]\n", time);

    return 0;

open_time_loop.cpp


#include <stdio.h>
#include <chrono>
int main(int argc, char** argv) {
    chrono::system_clock::time_point start, end;
    uint8_t pic[3072] = {0};
    start = chrono::system_clock::now();
    for(int j=0;j<10000;++j){
        auto fp = fopen("./cifar-10-batches-bin/data_batch_1.bin", "rb");
        fseek(fp,1L+3073L*j,SEEK_CUR);
        fread(pic, sizeof(uint8_t), 3072, fp);
        for(int i=0;i<3072;++i){
            pic[i]++;
        }
        fclose(fp);
    }
    end = chrono::system_clock::now();
    double time = static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
    printf("time %lf[ms]\n", time);

    return 0;
}

Ergebnis

Das Ergebnis der tatsächlichen Ausführung ist unten dargestellt.

 % ./open_time
time 62.964000[ms]
 % ./open_time_individual
time 1154.943000[ms]
 % ./open_time_loop
time 1086.277000[ms]

open_timeGegenopen_time_individualWannopen_time_loopでは約20倍の実行時間がかかるこWannがわかります。 Sie können auch sehen, dass die Ausführungszeiten von `open_time_individual``` und` open_time_loop``` ungefähr gleich sind.

Kommentar

open_timeWannopem_time_loopIst ein Programm, das denselben Datenbereich liest, aber die Ausführungszeit ist offen()Sie können sehen, dass es darauf ankommt. Da die Ausführungszeiten von `open_time_individual``` und` open_time_loop``` ungefähr gleich sind, können wir sehen, dass die Ausführungszeit von der Anzahl der Male abhängt, nicht vom Dateityp, der fopen ().

Bei fopen () muss die Datei mit einem Systemaufruf geöffnet und vom Benutzermodus in den Kernelmodus gewechselt werden. Im Falle eines Speicherzugriffs kann ein einmal zugewiesener Adressraum ausgeführt werden, ohne den Overhead zu wechseln. Es stellt sich heraus, dass für Bilder von CIFAR-10 oder so die Verarbeitung von fopen () länger dauert als der Speicherzugriff.

Appendix Shell-Skript zum Generieren einer geteilten Binärdatei für jedes Bild aus der CIFAR-10-Binärdatei

for i in `seq 0 9999` 
do
    t=$(($i * 3073))
    tail -c +$t cifar-10-batches-bin/data_batch_1.bin | head -c 3073 > "cifar10-raw/"$i".bin"
done

Python-Skript zum Konvertieren in PNG, um festzustellen, ob die geteilte Binärdatei als Bild korrekt ist

import numpy as np
from PIL import Image

fp = open("sample.bin", "rb")
label = fp.read(1)
data = np.zeros(3072, dtype='uint8')
for i in range(3072):
    data[i] =  int.from_bytes(fp.read(1), 'little')

fp.close()
data = data.reshape(3, 32, 32)
data = np.swapaxes(data, 0, 2)
data = np.swapaxes(data, 0, 1)
with Image.fromarray(data) as img:
    img.save("sample.png ")

Recommended Posts

Overhead-Messung zum Lesen von Dateien
Datei lesen
Lesen Sie die CSV-Datei: pandas
Lesen Sie die Python-CSV-Datei
Dateien lesen und schreiben
Dateien schreiben und lesen
[Hinweis] Lesen Sie eine Datei aus einem anderen Verzeichnis
CSV-Datei mit Python lesen (CSV-Datei herunterladen und analysieren)
Lesen wir die RINEX-Datei mit Python ①
[GO-Sprache] Lesen wir die YAML-Datei
Lesen Sie die Datei Zeile für Zeile mit Python
Lesen Sie die Datei, indem Sie den Zeichencode angeben.
Zeichendatendatei mit numpy lesen
[Python] Lesen Sie die HTML-Datei und üben Sie das Scraping
[Python] Lesen Sie die angegebene Zeile in der Datei
[Automatisierung] Lesen Sie E-Mails (Nachrichtendatei) mit Python