IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Lecture de fichiers binaires


Sujet :

C#

  1. #1
    Membre chevronné Avatar de petitours
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Février 2003
    Messages
    1 935
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2003
    Messages : 1 935
    Points : 2 013
    Points
    2 013
    Par défaut Lecture de fichiers binaires
    Bonjour

    J'ai besoin de lire des fichiers binaires qui contiennent le contenu de petites mémoires dans des systèmes d'enregistrement electroniques. La taille de ces fichiers est donc de max la taille des mémoires soit 256Mo.

    Je dois traiter ces données en partant du début du fichier et en avant byte par byte (ou plus, pour lire des int32 ou doubles si je suis sur une trame de donnée) et mon problème est que ca prend une infinité de temps, plusieurs minutes sur un i7 récent, un temps insoutenable sur des plus petites machines.

    Ci dessous un des codes que j'ai fait, un des plus simple puisqu'il ne fait que tester le contenu d'un fichier de test contenant 0 1 2 3.....252 253 254 0 1 2 3....
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
            private void btTesterFichierTest_Click(object sender, EventArgs e)
            {
                richTextBox2.Clear();
                long i = 0;
     
                const string fileName = "toto2.dav";
     
                if (!File.Exists(fileName))
                {
                    MessageBox.Show("Le fichier n'existe pas encore. Il faut lire la mémoire en HighPSeed");
                }
                else
                {
                    using (BinaryReader MonReader = new BinaryReader(File.Open(fileName, FileMode.Open)))
                    {
                        MonReader.BaseStream.Position = 0;
                        int ReferenceTest = 0;
     
                        int comptageAdresseReelleMemoireFlash = 0;
     
                        bool stop = false;
     
                        while (MonReader.BaseStream.Position < MonReader.BaseStream.Length && stop == false)
                        {
     
                            if (comptageAdresseReelleMemoireFlash < 2048)
                            {
                                int valeur = MonReader.ReadByte();
                                if (valeur != ReferenceTest)
                                {
                                       stop = true;
                                    StatusProgressTextColor(100, System.Drawing.Color.LimeGreen, "TestFichier. Pos : " + (MonReader.BaseStream.Position - 1).ToString() + " ValeurAttendue :" + ReferenceTest.ToString() + " Valeur :" + valeur.ToString() + " \r\n");
                                }
                            }                       
     
     
                            ReferenceTest +=1;
                            comptageAdresseReelleMemoireFlash += 1;
                            if (ReferenceTest == 255) ReferenceTest = 0;
                            if (comptageAdresseReelleMemoireFlash == 2112) comptageAdresseReelleMemoireFlash = 0;
                        }
                    }
    Comment puis je m'y prendre pour lire mes fichiers autrement ? Une manière radicalement différente qui me permettrait d'aller infiniment plus vite ?
    Je me demandais si charger le tableau en ram serait plus rapide ? avec une crainte sur l'alignement en mémoire qui ferait que mes 256Mo prendrait 8fois plus de place sur une machine 64bit et peut être pas la même efficacité pour lire le flux (pas de readbyte, readdouble, readInt32...)

    Merci par avance pour vos conseils
    Il y a 10 sortes de personnes dans le monde : ceux qui comprennent le binaire et les autres

  2. #2
    Membre expert Avatar de jopopmk
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2011
    Messages
    1 856
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 1 856
    Points : 3 570
    Points
    3 570
    Par défaut
    Salut,

    j'ai eu le même souci hier (mais en C) : je devais faire du char à char sur des gros fichier et j'avais des temps d'enfants chiens femelles. Je pouvais atteindre pas loin de 7" pour une fichier de 100Mo. J'ai refait le code en full mémoire et maintenant je suis en-dessous de la seconde. Ça m'a pris moins de 5' à coder (sans casser l'existant, avec un tit #define/#ifdef semé de partout). Du coup ça vaudrait le coup pour toi de faire le test. Il faut évidemment que la machine qui fait tourner ton appli supporte la charge.

    Si les perfs sont toujours cracra faudra creuser l'opti du traitement, voire du prog en général.
    Plus je connais de langages, plus j'aime le C.

  3. #3
    Membre chevronné Avatar de petitours
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Février 2003
    Messages
    1 935
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2003
    Messages : 1 935
    Points : 2 013
    Points
    2 013
    Par défaut
    Je viens à l'instant de finir un test avec chargemetn en mémoire

    bonne nouvelle ,les tableaux de byte semblent ne pas souffrir de l'alignement, ca me prend trés peu de ram (les 256Mo)
    La mauvaise nouvelle c'est que le temps de traitement est encore plus long ! ca parle en minutes, prés de 5 ( 300secondes) là pour 200Mo !

    il y a forcément un truc que je fais mal
    Dans mon premier post le code avec des readbyte successif sur le fichier

    et ici le code avec un chargement dans un tableau de byte puis traitement du tableau
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
                    using (BinaryReader MonReader = new BinaryReader(File.Open(fileName, FileMode.Open)))
                    {
                        byte[] contenu = new byte[MonReader.BaseStream.Length];
     
                        MonReader.Read(contenu, 0, (Int32)MonReader.BaseStream.Length);
     
     
                        Int32 Position = 0;
                        byte ReferenceTest = 0;
     
                        Int32 comptageAdresseReelleMemoireFlash = 0;
     
                        bool stop = false;
     
                        while (Position < MonReader.BaseStream.Length && stop == false)
                        {
     
                            if (comptageAdresseReelleMemoireFlash < 2048)
                            {
                                if (contenu[Position++] != ReferenceTest)
                                {
                                    stop = true;
                                    StatusProgressTextColor(100, System.Drawing.Color.LimeGreen, "TestFichier. Pos : " + (Position-1).ToString() + " ValeurAttendue :" + ReferenceTest.ToString() + " Valeur :" + contenu[Position -1].ToString() + " \r\n");
                                }
                            }
     
                            ReferenceTest += 1;
                            comptageAdresseReelleMemoireFlash += 1;
                            if (ReferenceTest == 255) ReferenceTest = 0;
                            if (comptageAdresseReelleMemoireFlash == 2112) comptageAdresseReelleMemoireFlash = 0;
                        }
                    }
    ça fonctionne mais c'est pire en temps de traitement !

    Merci
    Il y a 10 sortes de personnes dans le monde : ceux qui comprennent le binaire et les autres

  4. #4
    Membre expérimenté
    Homme Profil pro
    Développeur .Net / Delphi
    Inscrit en
    Juillet 2002
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur .Net / Delphi
    Secteur : Finance

    Informations forums :
    Inscription : Juillet 2002
    Messages : 738
    Points : 1 745
    Points
    1 745
    Par défaut
    Bonjour,

    Evite le test sur la taille du fichier à chaque boucle en stockant sa valeur dans une variable :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    long Eof = MonReader.BaseStream.Length;
    while (Position < Eof && !stop)
    {...}
    Tu vas sentir la différence !!

  5. #5
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    readbyte doit en effet être à éviter


    le plus simple à coder et qui devrait être déjà beaucoup plus performant c'est
    system.io.file.readAllBytes (qui retourne un tableau d'octets de tout le fichier directement)
    la lecture devrait se faire sans pause et après tu fais ton traitement sur le tableau (256Mo à lire à 20Mo/s (dd lent) ca fait une grosse dizaine de secondes))
    l'avantage aussi c'est de pouvoir utiliser le stopwatch pour savoir le temps de lecture puis le temps de traitement, et savoir lequel optimiser

    après ce qui doit etre le plus performant et qui ne prendrait pas 256Mo de ram serait de lire et de traiter le tout sur 2 threads différents (en lisant quelques Mo à la fois, à mettre dans un queue, et l'autre thread dequeue)
    mais bon de nos jours 250Mo de ram ca n'est rien (le Go étant autour de 10€ je crois) donc ca serait plus compliqué à coder mais seulement si tu veux gagner 5 secondes ^^
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  6. #6
    Membre chevronné Avatar de petitours
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Février 2003
    Messages
    1 935
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2003
    Messages : 1 935
    Points : 2 013
    Points
    2 013
    Par défaut
    Il faut que j'affine mes tests peut être entre RAM et pas RAM mais en supprimant la recherche de la taille du fichier à chaque boucle je viens de passer de 5min à 2s environ (j'ai un SSD performant)

    je savais bien que j'avais fait une énormité...

    merci beaucoup

    je vais fignoler mais la révolution est là !

    re merci
    Il y a 10 sortes de personnes dans le monde : ceux qui comprennent le binaire et les autres

  7. #7
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    EDIT : non, rien.

  8. #8
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    Juste pour mettre mon grain de sel.

    Autant lire byte par byte, c'est effectivement une très mauvaise pratique (même si en soit, c'est pas aussi lent que ça, C# et Windows ne sont pas débiles au point de faire une lecture physique pour chaque appel).

    Autant faire un "ReadAllBytes", je ne comprends même pas pourquoi ça compile autrement qu'en mode "DEBUG".

    Pour moi, c'est juste inadmissible d'avoir une telle instruction dans un programme qui va être utilisé en production.

    Et ce, pour plusieurs raisons :
    - Lent : il n'est pas rare qu'on lise dans un fichier pour au final n'utiliser qu'une partie de ce dernier. Il est donc inutile de charger une quantité volumineuse de données pour rien.
    - Dangereux : au moment de la conception d'un programme, on a un cahier des charges précis (256 Mo max pour les fichiers par exemple). Seulement, en informatique, les choses évoluent. On réutilise le code aussi, sans forcément le relire ni tester tous les cas à chaque fois. Et demain, le même code peut servir à lire dans des fichiers qui font non plus 256 Mo, mais 256 Go. Et dans ce cas, ReadAllBytes va juste saturer les IO disque, puis la mémoire, avant de planter lamentablement en laissant un boulot colossale de nettoyage au garbage collector qui va être mis à mal lui aussi. Bref, de quoi mettre à genoux un serveur pendant plusieurs minutes. Donc en plus de planter, le programme risque de mettre en périls le bon fonctionnement d'autres applications sur la même machine. Pas terrible.
    - Sale : outre la dangerosité et le manque d'optimisation, le fait de travailler avec des objets en mémoire dont on n'a aucune idée de la taille, en soit, c'est tout sauf propre. Il est absolument impossible de mettre en place quel que type d'optimisation que ce soit par exemple, car chaque technique d'optimisation a des avantages et des inconvénients qui dépendent grandement de la nature de ce qu'on veut optimiser. Le meilleur exemple, c'est pour le tri : le simple tri à bulle est plus rapide que les autres sur des petits volumes, tandis qu'il s'effondre lorsqu'on a de gros volumes. Ne pas connaître à l'avance la taille d'un tableau est donc un risque important quant à la maintenabilité du programme par la suite.

    Bref.

    Tout ça pour dire que lorsqu'on travaille avec ds fichiers volumineux (et même à l'heure du Go à moins de 10 €, un fichier de plus d'une dizaine de Mo, ça reste un fichier volumineux), on ne travaille pas comme un goret en chargeant tout en mémoire, mais avec un buffer.
    => On dimensionnera donc un byte[] à quelques Mo (5 Mo est tout à fait raisonnable) et on va travailler avec ce buffer plutôt qu'avec un byte[] monstrueux.

    Vous trouverez dans la conversation ci-dessous des exemple de différentes techniques pour effectuer des lectures dans des fichiers volumineux :
    http://www.developpez.net/forums/d14...xt-volumineux/
    On ne jouit bien que de ce qu’on partage.

  9. #9
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    Autant faire un "ReadAllBytes", je ne comprends même pas pourquoi ça compile autrement qu'en mode "DEBUG".

    Pour moi, c'est juste inadmissible d'avoir une telle instruction dans un programme qui va être utilisé en production.
    nous sommes tous influencés par notre contexte de développement

    la plupart du temps on sait au moment du dev quelle taille de fichier on risque de traiter
    en cas de modification ultérieure de cette taille (ce qui n'est pas toujours le cas) rien n’empêche de recoder

    cette instruction permet un gain en temps de développement, ce qui parfois est la priorité par rapport aux performances
    pour un truc fait une fois par jour sur un serveur non sollicité, quelle importance que ca dure 5 secondes que ca prenne 20Mo de ram au lieu de 500Mo ?
    aucune.

    une fois de plus la généralité est à éviter, c'est au développeur une fois de plus de connaitre l'outil et d'adapter ce qu'il fait à l'environnement

    c'est comme ceux qui veulent bannir le select *, bien sur que c'est une hérésie dans 99% des cas, mais si c'est un besoin on l'utilise, on ne va pas s'en priver
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  10. #10
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Je reste quand même très frileux du "ReadAllBytes", qui plus est sur un fichier de 256 Mo.

    Même si aujourd'hui, c'est une taille "peanuts", même pour un PC de bureautique (et encore, y'a des tas de PC qui tournent avec 2 Go de RAM et un OS qui en bouffe les 3/4), ça reste relativement gros.

    Économiser sur le temps de développement, c'est avant tout s'exposer à des pertes de temps monstrueuses en maintenance et en exploitation.

    Ecrire (ou repomper de façon éhontée sur le net) une librairie capable de bufferiser les lectures dans un fichier, ça prends pas des heures, et c'est parfaitement réutilisable à volonté.

    Pour faire le parallèle avec le SQL, comme vous, ce serait un peu pareil que de dire :

    Ben tu fais un SELECT * sans WHERE sur chacune des deux tables.
    Pis tu fais deux boucles imbriquées pour remettre les lignes qui vont ensemble.
    Ce sera plus rapide que d'apprendre à faire une jointure en SQL.
    Alors oui, ça marche.
    Si on a deux tables de 100 lignes. Ca fait boucler sur 10000 occurrences, et 100 lignes, c'est quoi ? 100 Ko en comptant ultra large ? OSEF.

    Mais jamais de la vie je ne peux laisser dire ça sans me rouler par terre. Et je pense que d'autres membres actifs de la catégorie SGBD pourraient faire une attaque rien qu'à l'idée de lire un tel conseil !

    Pour du DEBUG, c'est super de faire comme ça. Y'a pas meilleur outil d'analyse qu'un tableau croisé dynamique dans Excel.

    Mais bon, c'est pas de la programmation. Je peux pas laisser conseiller un truc pareil, pas ici :o
    On ne jouit bien que de ce qu’on partage.

  11. #11
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    dans l'ensemble tu as bien raison de préciser que ca peut être aberrant de faire un readallbytes, alors je tendais en effet à dire l'inverse

    concernant le select * je parlais d'un besoin, dans certains cas on veut toutes les colonnes et si on a des ajouts dynamiques de colonnes on ne les connait pas en avance
    mais c'est un besoin spécifique et surement rare

    et là ou bufferiser prend quelques lignes de code en plus, et mélange lecture et traitement, spécifier ses colonnes et mettre un where c'est 3 mots à rajouter, donc là ce n'est même plus de la fainéantise, c'est en effet de la connerie de filtrer derrière ce qu'on cherchait ^^
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 8
    Dernier message: 09/10/2012, 09h00
  2. Réponses: 2
    Dernier message: 19/04/2011, 08h56
  3. thread ->private void exit
    Par chabacano dans le forum Concurrence et multi-thread
    Réponses: 0
    Dernier message: 03/06/2010, 19h13
  4. Identifier "object sender"
    Par koyot3 dans le forum Windows Forms
    Réponses: 5
    Dernier message: 27/10/2008, 17h42
  5. object^ sender c++.net
    Par TrollTop dans le forum VC++ .NET
    Réponses: 1
    Dernier message: 08/07/2007, 10h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo