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

Langage PHP Discussion :

traiter un fichier CSV par morceau pour moins utiliser de mémoire


Sujet :

Langage PHP

  1. #1
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut traiter un fichier CSV par morceau pour moins utiliser de mémoire
    Bonjour,

    mon application lit des fichiers CSV afin d'en traiter les données.

    Je lis en entier le fichier et stocke toutes les données dans un tableau. Le principe fonctionne bien sur des petits fichiers, mais pour traiter un fichier qui pèse 32 mO, je tombe sur une limite d'utilisation de la mémoire
    Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 335544320 bytes) in C:\projets\ticket_rawsrc\vendor\rawsrc\pdoplusplus\PDOPlusPlus.php on line 1296
    , mais la limite fixée dans php.ini (memory_limit) est atteinte (comme je suis en local, je peux la modifier ; je l'ai mise à 15000 mO (ce qui est monstrueux mais visiblement, ça suffit pas)). Pour gérer ce problème mon idée est de limiter la quantité de données lues dans le fichier, donc de traiter cette lecture par morceaux, donc de ne lire qu'un morceau, le traiter, puis lire un autre morceau, le traiter, également, et ainsi jusqu'à atteindre la fin du fichier CSV. Peut-on s'y prendre ainsi, et si oui, comment ?
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  2. #2
    Membre émérite Avatar de vttman
    Homme Profil pro
    Développeur "couteau mosellan"
    Inscrit en
    Décembre 2002
    Messages
    1 140
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur "couteau mosellan"
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2002
    Messages : 1 140
    Points : 2 286
    Points
    2 286
    Par défaut
    Bonjour,
    Je ne vois pas trop le soucis techniquement parlant LaurentSc ?
    Voici ma proposition ...
    A voir aussi l'intérêt de stocker des informations par paquet de n lignes dans un 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
     
    Maxtab = 1000
    i=0 
    init montableau de 1000
    ouvrir csv 
    lire csv  
    tant que non EOF du csv
     i++
     tant que i <= Maxtab
      csv -> tab(i)
       i++
     fin tant que
     traitement tab par 1000 éléments (tab(1) -> tab(1000))
     init montableau de 1000
     i=0 
     lire csv  
    fin tant que
    fermeture csv
    Emérite, émérite je ne pense pas ... plutôt dans le développement depuis FORT FORT longtemps, c'est mon job, ça oui
    A part ça ... Il ne pleut jamais en Moselle !

  3. #3
    Membre éclairé
    Homme Profil pro
    Webdesigner
    Inscrit en
    Juin 2014
    Messages
    415
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Hautes Pyrénées (Midi Pyrénées)

    Informations professionnelles :
    Activité : Webdesigner
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Juin 2014
    Messages : 415
    Points : 831
    Points
    831
    Par défaut
    Bonjour.
    La quantité de mémoire utilisée n'est pas normale, même en ouvrant un fichier de 32MO.
    Je te suggère de placer à diverses étapes de ton traitement l'affichage de la mémoire utilisée par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    echo memory_get_usage();
    Il doit y avoir des variables que tu peux détruire ou réinitialiser.

  4. #4
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 690
    Points : 20 211
    Points
    20 211
    Par défaut
    Il suffit de lire le fichier ligne par ligne et de traiter les lignes au fur et à mesure.
    Ex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $file = fopen('mon_fichier.csv', 'r');
    while (($line = fgetcsv($file)) !== FALSE) {
       var_dump($line);
    }
    fclose($file);
    Si chaque ligne doit donner lieu à une modification dans la base de données , il faudra alors envisager soit de grouper les insertions par lot (par exemple garder en mémoire 100 résultats et insérer une fois) ou passer par des transactions SQL qui devront elles aussi être groupées par lot.
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    @vttman : OK. J'avais pas pensé qu'on pouvait ne lire que quelques lignes, faire un traitement puis reprendre la lecture...

    @domi65 : je vais faire ça, car OK, la consommation de mémoire est disproportionnée par rapport à la taille du tableau.

    @grunk : certes, l'énorme consommation de mémoire vient très probablement d'une mauvaise conception du traitement mais bon....
    J'étais parti avec cette conception :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //1-lecture ligne par ligne du fichier et stockage des données dans le tableau self::$data
    while (($row = fgetcsv(self::$handle, $buffer, self::$separator, $enclosure,"")) !== false) {
                    self::$data[] = array_combine(self::$header, $row);
     
    //2- stockage du tableau dans une variable :
    $app_data = CSVImport::getData(); 
     
    //3- traitement du tableau stocké dans la variable :
    foreach ($app_data as $line_csv) //pour chq ligne, on l'analyse et si correcte, en bdd
            {
    ...
            }
    Je vais tout d'abord suivre le conseil de domi65 mais de toute façon adopter la proposition de Grunk : au lieu de stocker chaque ligne dans un tableau puis le traiter, après la lecture d'une ligne, traiter celle-ci tout de suite puis passer à la suivante.
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  6. #6
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    Je viens de calculer ma consommation de mémoire avant et après la lecture du fichier :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
                $mem = memory_get_usage();
                echo "( memory avant lecture :".memory_get_usage()."<br/>";
     
                while (($row = fgetcsv(self::$handle, $buffer, self::$separator, $enclosure,"")) !== false) {
     
                    self::$data[] = array_combine(self::$header, $row);
                } 
     
                $mem = memory_get_usage();
                echo "memory après lecture :".memory_get_usage()."<br/>";
    et on passe de 937 672 octets à 252 009 440. Vu qu'il n'y a aucun traitement (seulement de la lecture puis stockage dans un tableau et que le fichier lu pèse 32 276kO). Y a pas un problème ?
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  7. #7
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    Bonsoir,
    j'ai appliqué, non sans mal, l'approche proposée par grunk (donc je traite chaque ligne après l'avoir lue, plutôt que la stocker dans un tableau) et ça a l'air de moins solliciter la mémoire car j'ai jamais été si loin : dans le fichier CSV, il y a plus de 109000 lignes et pour l'instant, il y en a 35000 de traitées (donc analyse puis écriture en bdd) alors qu'avant, ça n'atteignait pas les 10000 lignes. Je vais le laisser tourner toute la nuit et on verra demain (OK, conception pourrie car pour un expert, ça serait plié en 10 secondes, alors que ça doit faire 15 minutes que ça tourne, mais bon...)
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  8. #8
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    zut, 79198 lignes lues, puis
    Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 335544320 bytes) in C:\projets\ticket_rawsrc\vendor\rawsrc\pdoplusplus\PDOPlusPlus.php on line 1296
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  9. #9
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 690
    Points : 20 211
    Points
    20 211
    Par défaut
    Est ce que la lecture de ton fichier sans aucun traitement va jusqu'au bout sans problème ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    $file = fopen('mon_fichier.csv', 'r');
    $nbLines = 0;
    while (($line = fgetcsv($file)) !== FALSE) {
       $nbLines++;
    }
    fclose($file);
    echo $nbLines.' lues';
    Si oui , il faut aller un cran plus bas dans ton algorithme et comprendre qui consome de la mémoire. Une fois que tu auras compris le fautif , il faudra surement forcer la libération de la mémoire sur les éléments qui pose problème.
    Une piste de reflexion : Si tu créer des instances d'objet à chaque ligne ca risque d'être un problème.
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  10. #10
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    Je viens de le faire (à 10h49). En quelques secondes (2 ou 3 peut-être), 109473 lues, donc OK pour aller plus loin.

    Si je crée des objets, c'est sûr ; la première ligne est $this->lineCSV = array_combine(CSVParser::getHeader(), $row); et y en a d'autres...Je pense commencer par remplacer celui-là par une simple variable....
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  11. #11
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    Je viens de remplacer cet objet par une simple variable et ai relancé l'application vers 11h12. Devant de nouveau m'absenter dans 5 minutes, on verra cet après-midi...
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  12. #12
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 039
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 039
    Points : 8 065
    Points
    8 065
    Billets dans le blog
    17
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $this->lineCSV = array_combine(CSVParser::getHeader(), $row);
    Est-ce que cela veut dire que pour chaque ligne parcourue sur ton fichier tu relances un parser pour réouvrir le fichier et obtenir l'en-tête ?
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

  13. #13
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    et non, getHeader est un vrai getter :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    private static $header = [];
    ...
    public  static function getHeader()
        {
            return self::$header;
        }

    Le début du code est :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $this->filepath = CSVFile::ManageUpload($data);
     
    CSVParser::openCSV($this->filepath); //on ouvre le fichier
     
    CSVImport::uploadFile($this->filepath);
     
    CSVParser::setEnclosureHeaderAndSoOn($this->filepath);
     
    $nb_lines = 0;
     
    while (($row = CSVParser::getRow($this->filepath)) !== false) {
         $lineCSV = array_combine(CSVParser::getHeader(), $row);
    ...etc
    donc je n'ouvre le fichier qu'une seule fois (ligne 3) et ne calcule le header qu'une seule fois aussi (ligne 7).
    Le fait d'avoir remplacé l'objet $this->lineCSV par la simple variable $lineCSV (on le voit ligne 12) m'a fait gagner une seule ligne : 79198 -> 79199

    Je vais donc chercher d'où ça vient avec memory_get_usage().
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  14. #14
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    Est-on sûr de la fiabilité de memory_get_usage car je l'ai mis à 4 niveaux dans mon code (le chiffre, c'est le numéro de ligne), donc je m'attend à ce que le nombre soit constamment supérieur au précédent et ce n'est pas le cas !

    exemple :
    nb_line :84144/83 /mémoire :358304320
    99 /mémoire :358303960
    110 après analyse licence /mémoire :352011952
    115 après bdd licence /mémoire :358305408
    nb_line :84145/83 /mémoire :358306384
    Code php : 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
    43
    44
    45
    46
    <?php
    while (($row = CSVParser::getRow($this->filepath)) !== false) {
        echo "nb_line :".$nb_lines++."/83 /mémoire :".memory_get_usage().'<br/>';
        $lineCSV = array_combine(CSVParser::getHeader(), $row);
     
        if (!array_filter($lineCSV))/* array_filter va supprimer les éléments vides du tableau. Si entièrement
                vide, elle va retourner false donc la condition sera true
                */
        {
            Record::errorEmptyLine($nb_lines+1);
            continue; //on va directement à la boucle suivante du while
        }
        $lineCSV = UtilFct::cleanLineCSV($lineCSV);/*on remplace les espaces par des underscores sur tte la
                 ligne (pour les clés) */
     
        extract($lineCSV);
     
        dispResult::incNbReadLines();
        echo "99 /mémoire :".memory_get_usage().'<br/>';
     
        switch (CSVParser::getCSVType())
        {
     
            case "license":
     
                // analyses
                self::createLicensesObjects($lineCSV);
     
                $this->errors = Record::analyze_license($this->refTableLicense);
                echo "110 après analyse licence /mémoire :".memory_get_usage().'<br/>';
                if (Record::getLastError() == "no error")
                {
                    CSVImport::putInDBLicense($this->refTableLicense);
                }
                echo "115 après bdd licence /mémoire :".memory_get_usage().'<br/>';
                break;
     
            case "ticket":
     
               ...
     
                break;
     
        }
     
    }
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  15. #15
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 842
    Points : 6 522
    Points
    6 522
    Par défaut
    Les bizarreries que tu constates à propos de la mémoire sont dues à la manière dont PHP alloue de la mémoire pour les tableaux. Cette quantité allouée est toujours une puissance de 2.

    Prenons un exemple bidon (pour ce qui est des nombres): un tableau stocke des données qui nécessite 600 octets, PHP allouera donc 1024 (2 puissance 10) octets pour le tableau, ce qui en proportion représente une perte énorme. Pourquoi fait-il ça? Pour ne pas avoir à allouer de la mémoire toutes les 5 minutes, c'est un dilemme entre vitesse et mémoire nécessaire. Si le tableau venait à grossir au delà de 1024 octet, PHP allouera de nouveau de la mémoire et passera à la puissance de 2 supérieure, soit 2048 (2 puissance 11).

    C'est la raison pour laquelle tu ne constates pas de changement d'occupation de la mémoire alors que ton tableau grossit. PHP utilise la mémoire qui a déjà été allouée au tableau.




    Au delà de cette histoire de tableau et de puissances de 2, la manière dont PHP stocke les données en mémoire fait que celles-ci occupent environ 3 fois plus de place que la taille que tu peux observer dans ton fichier. PHP ne se contente pas de les stocker de manière quasi brute comme on le ferait en langage C, mais construit toute une structure autour. C'est cette structure qui permet par exemple de manipuler facilement et rapidement un tableau, qui permet le fonctionnement du ramasse-miette, ou encore de stocker le type d'une donnée et d'en changer en cours de route... En résumer, ce qui fait la simplicité (au sens où cela épargne beaucoup de préoccupations bas niveau au programmeur) et la flexibilité de PHP.

    Autre chose, le fait d'associer une clef à chaque valeur de tes sous-tableaux va pratiquement doubler la taille occupée. Ce ne sera pas le cas si tu les laisses sous forme de liste indexée. Ce n'est pas pour rien que dans nombre de langages, il y a une distinction stricte entre une liste (un tableau indexé), et un dictionnaire (un tableau associatif sans ordre précis). PHP propose sa version du tableau, hybride, qui est très flexible, mais à un moment ou un autre on en paie le prix, soit en mémoire, soit en vitesse.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  16. #16
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 842
    Points : 6 522
    Points
    6 522
    Par défaut
    $row = CSVParser::getRow($this->filepath)
    Je trouve très étrange qu'il soit nécessaire de repréciser le filepath pour utiliser cette méthode.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  17. #17
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par CosmoKnacki Voir le message
    Je trouve très étrange qu'il soit nécessaire de repréciser le filepath pour utiliser cette méthode.
    En effet, il ne sert à rien. J'ai du croire un moment que c'était nécessaire, puis oublié de le supprimer. C'est fait :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     public static function getRow(int $buffer = 4096) {
            return(fgetcsv(self::$handle, $buffer, self::$separator, self::$enclosure,""));
        }

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    while (($row = CSVParser::getRow()) !== false) {
    ...
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  18. #18
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 335
    Points : 5 704
    Points
    5 704
    Billets dans le blog
    1
    Par défaut
    Cosmo, j'ai saisi ton explication pour ce que je trouvais bizarre avec memory_get_usage mais du coup, ça rend ardu son utilisation pour la recherche des gros consommateurs de mémoire. Que faut-il en penser ?

    Autre question :
    Citation Envoyé par grunk Voir le message
    Une piste de reflexion : Si tu créer des instances d'objet à chaque ligne ca risque d'être un problème.
    Faut-il éliminer les objets ?
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

  19. #19
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 842
    Points : 6 522
    Points
    6 522
    Par défaut
    Quoi qu'il en soit get_memory_usage() reste utile pour savoir où on en est, ou pour débusquer un monstre, mais c'est pas non plus le scalpel de la mémoire.

    La POO n'a pas pour vocation la performance, son but est d'atteindre un niveau d'abstraction plus élevé pour pouvoir déployer la logique du code à un autre niveau que celui des structures de base. Il est clair qu'une instance de classe faite maison ne peut pas être plus légère que les structures de base du langage qu'elle réunit, elle a donc un coût en vitesse et en mémoire.

    Cela ne veut pas dire qu'il faille bannir la POO, mais cela ne veut pas dire non plus en mettre partout et tout le temps. La difficulté se trouve là.
    Dans le même ordre d'idée, vouloir un certain confort dans son code en préférant des tableaux associatifs avec des clefs bien nommées pour pouvoir ensuite utiliser extract() semble parfaitement légitime, pourtant utiliser extract() est un "code smell" (car son utilisation peut écraser des variables déjà définies et sans rapport avec ce qu'on est en train de faire) et la consommation de mémoire du tableau est presque doublée quand on lui ajoute des clefs. C'est sans conséquences la plupart du temps, mais lorsque la quantité de données commence à devenir importante...

    Suggestions:

    D'après ce que j'ai pu lire, tu es confronté à deux problèmes:
    • le temps de traitement de ta ligne de csv (la stratégie ligne par ligne de grunk ne devrait pas prendre autant de temps).
    • Dans le cas du chargement de l'intégralité du csv (ou même par block), le problème de mémoire.


    Pour ce qui est du temps de traitement: Pourquoi le test avec array_filter(), ton csv est-il censé avoir des lignes vides ou des lignes où toutes les colonnes sont vides? Dans ce cas (pas testé) tu peux tenter plutôt if ( !empty(implode('', $lineCSV) ) qui crée une chaîne plutôt qu'un nouveau tableau contrairement à array_filter().
    Le reste à optimiser se trouve dans du code que tu n'as pas posté.

    Pour ce qui est de la mémoire: Utiliser un tableau n'est pas une obligation que ce soit pour tout stocker ou par bloc. Désormais, PHP dispose de nouvelles structures de données dans son module ds (qu'il faut installer), dont une particulièrement intéressante du point de vue de la mémoire: Ds\Vector. Elle résout le problème "puissance de 2" d'autres structures comme le tableau en allouant précisément la quantité de mémoire nécessaire. Tu peux l'utiliser aussi bien pour les colonnes d'un ligne que pour l'ensemble des lignes du fichier ou du bloc.

    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    while (($row = CSVParser::getRow()) !== false) {
        if ( empty(implode('', $row)) ) {
            // trucs à faire dans ce cas
            continue;
        }
     
        self::$data->push(new \Ds\Vector($row));
    }
     
    // puis on boucle sur self::$data pour effectuer les traitements et l'enregistrement en base dans un second temps (en prenant soin de démarrer la transaction avant la boucle et pas dans la boucle si transaction il y a).
    Ici self::$data est lui même une instance de Ds\Vector et doit être déclaré comme tel dans la classe.

    Une instance de Ds\Vector peut s'utiliser comme un tableau sur bien des aspects, comme boucler dessus avec foreach. Le point noir de cette approche est qu'un vecteur est uniquement indexé, ce n'est pas un tableau associatif. Mais si tu tiens absolument à pouvoir nommer ces index, tu peux toujours ajouter dans ta classe un tableau associatif qui fait le lien entre les headers du csv et les index. Il suffit quand tu as récupéré les headers de faire: self::$headers = array_flip($headers). Ainsi pour récupérer la valeur pour une ligne d'une colonne d'après son nom: $line->get(self::$headers['nomcolonne']).
    Ce faisant tu évites de recopier les headers comme clefs de tableaux associatifs X le nombre de lignes de ton CSV (ou de ton bloc).
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  20. #20
    Expert éminent
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 039
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 039
    Points : 8 065
    Points
    8 065
    Billets dans le blog
    17
    Par défaut
    Je ne pense pas que le parcours en tant que tel de ton modeste CSV pose problème (32 Mo, c'est pas grand chose)
    Le problème est sûrement un cran au dessus, au niveau du traitement effectué

    T'as l'air de bien aimer les propriétés statiques, je mets 1 piécette sur un tableau statique qui accumule de la data et qui finit par provoquer ton memory overflow
    Bon ça peut aussi être un tableau classique, quelque part...

    Il se passe quoi ici par exemple ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    self::createLicensesObjects($lineCSV);
    $this->errors = Record::analyze_license($this->refTableLicense);
    Un problème exposé clairement est déjà à moitié résolu
    Keep It Smart and Simple

Discussions similaires

  1. buffer "par morceau" pour gros fichiers binaires
    Par Benoit_T dans le forum Langage
    Réponses: 11
    Dernier message: 09/11/2009, 11h58
  2. Réponses: 5
    Dernier message: 18/10/2008, 03h59
  3. [Macro] ouverture d'un fichier csv par macro différent du double-clic
    Par Caro-Line dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 19/04/2007, 17h36
  4. Réponses: 2
    Dernier message: 13/03/2007, 12h19

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