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.cppopen_time_individual.cppopen_time_loop.cppopen_timeEst 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_individualEst 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_loopEst cifar-C'est un programme qui lit directement 10 fichiers binaires,open_timeContrairement à fopen pour chaque image()、fclose()Est un programme qui appelle.
open_time_individualComme 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_timeContreopen_time_individualQuandopen_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_timeQuandopem_time_loopEst 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