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.
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.
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.
Um den Overhead beim Lesen von Dateien zu messen, haben wir die folgenden drei Programme vorbereitet.
open_time.cpp
open_time_individual.cpp
open_time_loop.cpp
open_time
Ist cifar-Ein Programm, das 10 Binärdateien direkt liest.
fopen () und fclose () werden während der Ausführung nur einmal aufgerufen.
open_time_individual
Ist 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_loop
Ist cifar-Es ist ein Programm, das 10 Binärdateien direkt liest.open_time
Im Gegensatz zu fopen für jedes Bild()、fclose()Ist ein Programm, das aufruft.
open_time_individual
Wie 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;
}
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_time
Gegenopen_time_individual
Wannopen_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.
open_time
Wannopem_time_loop
Ist 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