[PYTHON] Mesure de la surcharge de lecture de fichier

emballer

  1. La surcharge de lecture des fichiers semble être importante.
  2. Il semble que cette surcharge puisse être considérablement réduite en lisant plusieurs fichiers en un seul.
  3. Je voulais voir cet effet avec des chiffres concrets, j'ai donc cherché un ensemble de données approprié et l'ai mesuré en tenant compte de l'environnement.
  4. En conséquence, les frais généraux étaient plus importants que ce à quoi je m'attendais.

introduction

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.

Mokuji

  1. Dataset
  2. [Programme de mesure](# programme de mesure)
  3. Résultat
  4. Explication
  5. Appendix

base de données

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. cifar10_.png

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.

Programme de mesure

Pour mesurer la surcharge de lecture des fichiers, nous avons préparé les trois programmes suivants.

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

open_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;
}

résultat

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.

Commentaire

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

Mesure de la surcharge de lecture de fichier
Lire le fichier
Lire le fichier CSV: pandas
Lire le fichier csv Python
Lire et écrire des fichiers
Écrire et lire des fichiers
[Note] Lire un fichier depuis un autre répertoire
Lire le fichier CSV avec python (Télécharger et analyser le fichier CSV)
Lisons le fichier RINEX avec Python ①
[GO language] Lisons le fichier YAML
Lisez le fichier ligne par ligne avec Python
Lisez le fichier en spécifiant le code de caractère.
Lire le fichier de données de caractères avec numpy
[python] Lisez le fichier html et entraînez-vous au scraping
[Python] Lire la ligne spécifiée dans le fichier
[Automation] Lire le courrier (fichier msg) avec Python