Ces dernières années, il y a eu une demande croissante de lecture d'un grand nombre de fichiers pour l'apprentissage automatique. Toutefois, si vous essayez de lire un grand nombre de fichiers, la surcharge de lecture des fichiers peut être plus importante que le traitement principal dans le programme. Par exemple, CIFAR-10 stocke plusieurs images dans un seul fichier pour réduire la surcharge de charge.
J'étais curieux de connaître cet effet, alors j'ai utilisé CIFAR-10 pour étudier comment la surcharge de lecture de fichier affecte le programme.
L'ensemble de données à mesurer est CIFAR-10, qui est familier dans le monde de la reconnaissance d'image. CIFAR-10 est un groupe d'images composé de 10 classes de 32 x 32 pixels. Utilisez celui distribué sous forme de fichier binaire sur le site ci-dessus. Les données d'image pour 10 000 images sont décrites dans un fichier binaire. La structure du binaire est la suivante.
La capacité d'une image est de 1 octet pour l'étiquette + 32 x 32 x 3 octets pour les données d'image = 3073 octets, donc un fichier binaire fait environ 30 Mo. En lisant ceci, la surcharge de lecture du fichier est mesurée.
Pour mesurer la surcharge de lecture des fichiers, nous avons préparé les trois programmes suivants.
open_time.cpp
open_time_individual.cpp
open_time_loop.cpp
open_time
Est cifar-Un programme qui lit directement 10 fichiers binaires.
fopen () et fclose () ne sont appelés qu'une seule fois lors de l'exécution.
open_time_individual
Est cifar-C'est un programme qui lit 10 fichiers binaires à partir du répertoire enregistré en divisant chaque image à l'avance.
fopen () et fclose () sont appelés 10000 fois dans le programme, ce qui correspond au nombre d'images.
open_time_loop
Est cifar-C'est un programme qui lit directement 10 fichiers binaires,open_time
Contrairement à fopen pour chaque image()、fclose()Est un programme qui appelle.
open_time_individual
Comme fopen()、fclose()Est appelé 10000 fois pendant l'exécution.
A l'exception de la lecture de fichier ci-dessus, le traitement commun à ces trois programmes est expliqué.
Le temps d'exécution est mesuré par system_clock dans la bibliothèque chrono.
Comme mentionné dans Dataset, le premier octet du fichier binaire est le libellé de l'image, donc fseek (fp, 1L, SEEK_CUR) ''
saute 1 octet.
L'image est lue par `` fread (pic, sizeof (uint8_t), 3072, fp) '', et la valeur de chaque pixel est chargée, ajoutée et stockée en tant que processus dans la boucle.
Notez que la gestion des erreurs pour les opérations sur les fichiers est omise.
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;
}
Le résultat de l'exécution réelle est indiqué ci-dessous.
% ./open_time
time 62.964000[ms]
% ./open_time_individual
time 1154.943000[ms]
% ./open_time_loop
time 1086.277000[ms]
open_time
Contreopen_time_individual
Quandopen_time_loop
では約20倍の実行時間がかかるこQuandがわかります。
Vous pouvez également voir que les temps d'exécution de open_time_individual
et open_time_loop
sont à peu près les mêmes.
open_time
Quandopem_time_loop
Est un programme qui lit la même zone de données, mais le temps d'exécution est fopen()Vous pouvez voir que cela dépend de.
De plus, comme les temps d'exécution de open_time_individual
et open_time_loop
sont à peu près les mêmes, nous pouvons voir que le temps d'exécution dépend du nombre de fois, pas du type de fichier à fopen ().
Lorsque fopen (), il est nécessaire d'ouvrir le fichier avec un appel système, et il est nécessaire de passer du mode utilisateur au mode noyau. Dans le cas d'un accès à la mémoire, l'espace d'adressage une fois alloué peut être exécuté sans surcharge de commutation. Il s'avère que pour les images de CIFAR-10 ou plus, il faut plus de temps pour traiter fopen () que pour accéder à la mémoire.
Appendix Script shell utilisé pour générer un fichier binaire fractionné pour chaque image à partir du fichier binaire CIFAR-10
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
Script Python à convertir en png pour déterminer si le fichier binaire divisé est correct en tant qu'image
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