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

Documents Java Discussion :

Conso mémoire/POI vs JExcel


Sujet :

Documents Java

  1. #1
    Membre expérimenté
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    477
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Septembre 2006
    Messages : 477
    Points : 1 526
    Points
    1 526
    Par défaut Conso mémoire/POI vs JExcel
    Bonjour à tous,

    Je souhaite exporter des documents xls de grande taille.
    La librairie POI (utilisée) semble consommer beaucoup trop de mémoire pour ma JVM. Je suis ainsi régulièrement bloqué par des exports de fichiers de 4Mo.

    Je cherche donc une nouvelle solution "coté API". J'ai lu sur un forum Java que Jexcel consommait beaucoup moins de mémoire que Jakarta POI.

    Je cherche donc des retours d'expériences dans ce sens.

    Conso mémoire : POI ou JExcel?

    Merci d'avance

  2. #2
    Expert éminent sénior


    Profil pro
    Inscrit en
    Mai 2003
    Messages
    3 240
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 3 240
    Points : 11 101
    Points
    11 101
    Par défaut
    Nous avons tout d'abord utilisé POI pour créer nos fichier Excel.
    Et nous avons été confronté à plein de problèmes mémoire, et de lenteur également. La raison est que POI initialise les tableaux avec un certain nombre de ligne, et une ligne avec un certain nombre de colonnes. Comme nos tableaux contenaient beaucoup de colonnes, il devait à chaque fois réajuster en interne son tableau de colonne pour les lignes. D'où une dégradation de performance, et une consommation accrue de la mémoire.

    On a essayé Jexcel, et le problème était résolu.

    Donc, je dirais JExcel.

    Vincent
    Vincent Brabant

    Ne pas me contacter par MP ni par mail pour des questions techniques. Ma liste d'amis restera vide.

    Cours et tutoriels pour apprendre Java , FAQ Java, et Forum Java

  3. #3
    Membre habitué
    Homme Profil pro
    SAQ
    Inscrit en
    Novembre 2005
    Messages
    167
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Canada

    Informations professionnelles :
    Activité : SAQ
    Secteur : Service public

    Informations forums :
    Inscription : Novembre 2005
    Messages : 167
    Points : 194
    Points
    194
    Par défaut JExcell vs POI
    je me suis penché sur l'utilisation des deux API cete année pour un projet qui permettait de générer du code html à partir de données excell qui augmentent le risque d'erreur lorsque saisie a la main (beaucoup de nombres à virgules.. très précis)

    Et je dois dire que, même si au premier abord je m'attendais à du bon boulot de la part d'Apache, j'ai été déçu par les perfomances de POI.. en + de mal reconnaitre les types de certaines cellules (voyait des String alors que c'est des nombres ).. côté mémoire c'est relativement lourd lors de gros traitements....

    du côté de JExcell... bien que le code ne soit pas aussi propre que POI, les performances compensent.. pas d'erreurs de type et autres trucs impossibles à débuguer à 2 h du mat API légèrre en plus... à essayer...
    Si derrière tout homme il y a une femme, devant ce même homme il y a l'ordinateur que cette femme a bousillé
    ---------

    Documentation is like sex: when it is good, it is very, very good; and when it is bad, it is better than nothing. (Dick Brandon)

  4. #4
    Expert éminent sénior


    Profil pro
    Inscrit en
    Mai 2003
    Messages
    3 240
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2003
    Messages : 3 240
    Points : 11 101
    Points
    11 101
    Par défaut
    Pour donner plus de détails sur les problèmes de performances, voici comment cela se passait à l'époque.

    Lorsqu'on créait un tableau HSSFSheet, il créait automatiquement un ArrayList de 20 rows. Chaque fois qu'il fallait plus que 20 rows dans un HSSFSheet, l'ArrayList devait étendre l'Array interne. Et cela consomme du temps et de la mémoire. Mais cela a été résolu entretemps puisque maintenant ils utilisent un TreeMap. Mais tu peux toujours voir les commentaires pour ce qui est de INITIAL_CAPACITY: http://svn.apache.org/repos/asf/jaka...HSSFSheet.java

    J'aime surtout le commentaire:
    /**
    * Used for compile-time optimization. This is the initial size for the collection of
    * rows. It is currently set to 20. If you generate larger sheets you may benefit
    * by setting this to a higher number and recompiling a custom edition of HSSFSheet.
    */
    On a déjà fait mieux comme flexibilité :-)

    Mais lorsqu'on crée une HSSFRow, il réserve de la place pour 5 cellules. Et là, c'est un vrai tableau:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    private HSSFCell[] cells=new HSSFCell[INITIAL_CAPACITY];
    avec INITIAL_CAPACITY qui est ici égal à 5.

    Et lorsque tu regardes la méthode addCell, voici le code:
    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
     
        /**
         * used internally to add a cell.
         */
     
        private void addCell(HSSFCell cell)
        {
            short column=cell.getCellNum();
            if (row.getFirstCol() == -1)
            {
                row.setFirstCol(column);
            }
            if (row.getLastCol() == -1)
            {
                row.setLastCol(column);
            }
     
            if(column>=cells.length)
            {
              HSSFCell[] oldCells=cells;
              int newSize=oldCells.length*2;
              if(newSize<column+1) newSize=column+1;
              cells=new HSSFCell[newSize];
              System.arraycopy(oldCells,0,cells,0,oldCells.length);
            }
            cells[column]=cell;
     
            if (column < row.getFirstCol())
            {
                row.setFirstCol(column);
            }
            if (column > row.getLastCol())
            {
                row.setLastCol(column);
            }
        }
    Et là, qu'est ce qu'on voit. S'il a atteint la limite (5 au début) il crée un nouveau tableau d'au moins le double (10), d'au plus la taille correspondant au numéro de la cellule qu'on rajoute +1. Et donc, lorsqu'on doit comme cela remplir par ligne 200 colonnes, il va créer à chaque fois des tableaux de taille 10, 20,40, 80, 160, 320. Et ce pour chaque row.

    C'est seulement maintenant que je viens de me rendre compte que dans le cas où tu sais à l'avance le nombre de cellule par ligne, il aurait été bien d'avoir un truc du style
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    HSSFRow row = HSSFSheet.createRow(numéroDeRow, nombreDeColonne)
    Et qu'alors, il aurait pu initialiser dans le constructeur de HSSFRow le tableau de HSSFCell avec la taille passée en paramètre. Cela n'aurait peut être pas aidé à l'occupation mémoire (quoi que si, car pas besoin d'avoir au même moment deux tableaux en mémoire, pour copier des données de l'un à l'autre) , mais au moins, à la vitesse d'exécution.

    voir le code source de HSSFRow : http://svn.apache.org/repos/asf/jaka...l/HSSFRow.java.

    (Faudrait que je pense une fois écrire un article ou tout le moins un billet là dessus).

    Vincent
    Vincent Brabant

    Ne pas me contacter par MP ni par mail pour des questions techniques. Ma liste d'amis restera vide.

    Cours et tutoriels pour apprendre Java , FAQ Java, et Forum Java

  5. #5
    Membre expérimenté
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    477
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Septembre 2006
    Messages : 477
    Points : 1 526
    Points
    1 526
    Par défaut
    Merci pour toutes vos réponses. Elles confirment d'autres avis.
    Vbrabant, ce mode de fonctionnement est effectivement étrange. Depuis le temps, il est curieux qu'ils n'aient pas résolu ce problème ^^

    Je me livre en ce moment à des tests de performance sur un échantillon.
    Il s'agit de créer un document xls de 32000 lignes et 70 colonnes, composé poui moitié de texte et moitié de nombres.

    Les résultats sont pour le moment clairs nets et précis : Jexcel va plus vite que POI.

    Options de la JVM : -Xms100m -Xmx300m


    Durée génération = construction du document en mémoire
    Durée Ecriture = écriture sur le DD

    POI
    ====> Durée génération POI : 31339.0
    ====> Durée Ecriture POI : 1330.0
    ====> Durée totale POI : 32765.0
    ====> Mémoire utilisée à la fin de la procédure : 279.28169599999995

    JExcel
    ====> Durée génération JExcel : 3080.0
    ====> Durée Ecriture JExcel : 7852.0
    ====> Durée totale JExcel : 10933.0
    ====> Mémoire utilisée à la fin de la procédure : 173.88335999999998

    Cela me semble assez net, effectivement.

    Pour mémoire, voici le code source de chaque procédure (si quelqu'un remarque des erreurs...) :

    Jexcel
    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    private void startGenerate(String fileName) {
     
    		WritableWorkbook workbook = null;
    		WritableSheet sheet = null;
     
    		double timeStartGenerate = 0;
    		double timeEndGenerate = 0;
    		double gapGenerate = 0;
     
    		double timeStartWrite = 0;
    		double timeEndWrite = 0;
    		double gapWrite = 0;
     
    		timeStartGenerate = System.currentTimeMillis();
     
    		try {
    			workbook = Workbook.createWorkbook(new File(fileName));
    			sheet = workbook.createSheet("First Sheet", 0);
     
     
    			for (int idRow = 0; idRow < 32000; idRow++) {
    				for (int idCol = 0; idCol < 70; idCol = idCol + 2) { 
     
    					jxl.write.Number number = new jxl.write.Number(idCol, idRow, 3.1459);
    					Label label = new Label(idCol+1, idRow, "Hello world!");
    					sheet.addCell(label);
    					sheet.addCell(number);
    				}
    			}
    		}
    		catch (RowsExceededException e) {
    			System.out.println("RowsExceededException : " + fileName);
    			System.out.println(e);
    		}
    		catch (WriteException e) {
    			System.out.println("WriteException : " + fileName);
    			System.out.println(e);
    		}
    		catch (IOException e) {
    			System.out.println("IOException : " + fileName);
    			System.out.println(e);
    		}
     
    		System.out.println ("====> Jexcel Generate : " + Runtime.getRuntime().totalMemory() / 1000000d + " - " + Runtime.getRuntime().freeMemory() / 1000000d + " - Utilisée : " + ((Runtime.getRuntime().totalMemory() / 1000000d) - (Runtime.getRuntime().freeMemory() / 1000000d)));
    		timeEndGenerate = System.currentTimeMillis();
    		gapGenerate = timeEndGenerate - timeStartGenerate;
     
    		timeStartWrite = System.currentTimeMillis();
    		try {
    			workbook.write();
    			workbook.close();
    		}
    		catch (IOException e) {
    			System.out.println("IOException : " + fileName);
    			System.out.println(e);
    		}
    		catch (WriteException e) {
    			System.out.println("WriteException : " + fileName);
    			System.out.println(e);
    		}
    		timeEndWrite = System.currentTimeMillis();
    		gapWrite = timeEndWrite - timeStartWrite;
    		System.out.println ("====> Durée génération JExcel : " + gapGenerate);
    		System.out.println ("====> Durée Ecriture JExcel : " + gapWrite);
    	}
    POI
    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
    43
    44
     
    private void startGenerate(String fileName) {
     
    		HSSFWorkbook wb = new HSSFWorkbook();
    	    HSSFSheet sheet = wb.createSheet("new sheet");
     
    		double timeStartGenerate = 0;
    		double timeEndGenerate = 0;
    		double gapGenerate = 0;
     
    		double timeStartWrite = 0;
    		double timeEndWrite = 0;
    		double gapWrite = 0;
     
    		timeStartGenerate = System.currentTimeMillis();
     
    		for (int idRow = 0; idRow < 32000; idRow ++) {
    			for (int idCol = 0; idCol < 70; idCol = idCol+2) { 
    				    HSSFRow row = sheet.createRow((short)idRow);
     
    				    row.createCell((short)idCol).setCellValue(3.1459);
    				    row.createCell((short)(idCol+1)).setCellValue("Hello world!");
    			}
    		}	
    		System.out.println ("====> POI Generate : " + Runtime.getRuntime().totalMemory() / 1000000d + " - " + Runtime.getRuntime().freeMemory() / 1000000d + " - Utilisée : " + ((Runtime.getRuntime().totalMemory() / 1000000d) - (Runtime.getRuntime().freeMemory() / 1000000d)));
    		timeEndGenerate = System.currentTimeMillis();
    		gapGenerate = timeEndGenerate - timeStartGenerate;
     
    		timeStartWrite = System.currentTimeMillis();
    		try {
    			FileOutputStream fileOut = new FileOutputStream(fileName);
    		    wb.write(fileOut);
    		    fileOut.close();
    		} 
    		catch (IOException e) {
    			System.out.println("IOException : " + fileName);
    			System.out.println(e);
    		}
    		timeEndWrite = System.currentTimeMillis();
    		gapWrite = timeEndWrite - timeStartWrite;
    		System.out.println ("====> Durée génération POI : " + gapGenerate);
    		System.out.println ("====> Durée Ecriture POI : " + gapWrite);
     
    	}
    -------------------------------------------------------------------------
    Nouveau test avec un peu de Styles, ce qui change complêtement la donne :
    - c'est beaucoup plus lent;
    - ça prend beaucoup plus de mémoire.

    Ainsi, j'ai dû réduire le nombre de lignes à 8000 pour que ça tienne dans les 300Mo alloués.

    POI
    ====> Durée génération POI : 450918.0
    ====> Durée Ecriture POI : 1295.0
    ====> Durée totale POI : 452309.0
    ====> Mémoire utilisée à la fin de la procédure : 187.244848

    JExcel
    ====> Durée génération JExcel : 2616.0
    ====> Durée Ecriture JExcel : 3212.0
    ====> Durée totale JExcel : 5829.0
    ====> Mémoire utilisée à la fin de la procédure : 151.508688

    Code :

    Jexcel :
    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    	private void startGenerate(String fileName) {
     
    		WritableWorkbook workbook = null;
    		WritableSheet sheet = null;
     
    		double timeStartGenerate = 0;
    		double timeEndGenerate = 0;
    		double gapGenerate = 0;
     
    		double timeStartWrite = 0;
    		double timeEndWrite = 0;
    		double gapWrite = 0;
     
    		timeStartGenerate = System.currentTimeMillis();
     
    		try {
    			workbook = Workbook.createWorkbook(new File(fileName));
    			sheet = workbook.createSheet("First Sheet", 0);
     
     
    			for (int idRow = 0; idRow < 8000; idRow++) {
    				for (int idCol = 0; idCol < 70; idCol = idCol + 2) { 
     
    					 WritableFont arial10font = new WritableFont(WritableFont.ARIAL, 10);
    					 WritableCellFormat arial10format = new WritableCellFormat (arial10font); 
     
    					 WritableFont arial10font2 = new WritableFont(WritableFont.ARIAL, 15);
    					 WritableCellFormat arial15format = new WritableCellFormat (arial10font2); 
     
    					jxl.write.Number number = new jxl.write.Number(idCol, idRow, 3.1459, arial10format);
    					Label label = new Label(idCol+1, idRow, "Hello world!", arial15format);
    					sheet.addCell(label);
    					sheet.addCell(number);
    				}
    			}
    		}
    		catch (RowsExceededException e) {
    			System.out.println("RowsExceededException : " + fileName);
    			System.out.println(e);
    		}
    		catch (WriteException e) {
    			System.out.println("WriteException : " + fileName);
    			System.out.println(e);
    		}
    		catch (IOException e) {
    			System.out.println("IOException : " + fileName);
    			System.out.println(e);
    		}
     
    		System.out.println ("====> Jexcel Generate : " + Runtime.getRuntime().totalMemory() / 1000000d + " - " + Runtime.getRuntime().freeMemory() / 1000000d + " - Utilisée : " + ((Runtime.getRuntime().totalMemory() / 1000000d) - (Runtime.getRuntime().freeMemory() / 1000000d)));
    		timeEndGenerate = System.currentTimeMillis();
    		gapGenerate = timeEndGenerate - timeStartGenerate;
     
    		timeStartWrite = System.currentTimeMillis();
    		try {
    			workbook.write();
    			workbook.close();
    		}
    		catch (IOException e) {
    			System.out.println("IOException : " + fileName);
    			System.out.println(e);
    		}
    		catch (WriteException e) {
    			System.out.println("WriteException : " + fileName);
    			System.out.println(e);
    		}
    		timeEndWrite = System.currentTimeMillis();
    		gapWrite = timeEndWrite - timeStartWrite;
    		System.out.println ("====> Durée génération JExcel : " + gapGenerate);
    		System.out.println ("====> Durée Ecriture JExcel : " + gapWrite);
    	}
    POI :

    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    private void startGenerate(String fileName) {
     
    		HSSFWorkbook wb = new HSSFWorkbook();
    	    HSSFSheet sheet = wb.createSheet("new sheet");
     
    		double timeStartGenerate = 0;
    		double timeEndGenerate = 0;
    		double gapGenerate = 0;
     
    		double timeStartWrite = 0;
    		double timeEndWrite = 0;
    		double gapWrite = 0;
     
    		timeStartGenerate = System.currentTimeMillis();
     
    		for (int idRow = 0; idRow < 8000; idRow ++) {
    			for (int idCol = 0; idCol < 70; idCol = idCol+2) { 
    				    HSSFRow row = sheet.createRow((short)idRow);
     
    				    HSSFFont font1 = wb.createFont();
    				    font1.setFontHeightInPoints((short)10);
    				    font1.setFontName("Arial");
     
    				    HSSFCellStyle style1 = wb.createCellStyle();
    				    style1.setFont(font1);
     
    				    HSSFFont font2 = wb.createFont();
    				    font2.setFontHeightInPoints((short)15);
    				    font2.setFontName("Arial");
     
    				    HSSFCellStyle style2 = wb.createCellStyle();
    				    style2.setFont(font2);
     
    				    HSSFCell cell1 = row.createCell((short)idCol);
    				    cell1.setCellValue(3.1459);
    				    HSSFCell cell2 = row.createCell((short)(idCol+1));
    				    cell2.setCellValue("Hello world!");
     
    				    cell1.setCellStyle(style1);
    				    cell2.setCellStyle(style2);
    			}
    		}	
    		System.out.println ("====> POI Generate : " + Runtime.getRuntime().totalMemory() / 1000000d + " - " + Runtime.getRuntime().freeMemory() / 1000000d + " - Utilisée : " + ((Runtime.getRuntime().totalMemory() / 1000000d) - (Runtime.getRuntime().freeMemory() / 1000000d)));
    		timeEndGenerate = System.currentTimeMillis();
    		gapGenerate = timeEndGenerate - timeStartGenerate;
     
    		timeStartWrite = System.currentTimeMillis();
    		try {
    			FileOutputStream fileOut = new FileOutputStream(fileName);
    		    wb.write(fileOut);
    		    fileOut.close();
    		} 
    		catch (IOException e) {
    			System.out.println("IOException : " + fileName);
    			System.out.println(e);
    		}
    		timeEndWrite = System.currentTimeMillis();
    		gapWrite = timeEndWrite - timeStartWrite;
    		System.out.println ("====> Durée génération POI : " + gapGenerate);
    		System.out.println ("====> Durée Ecriture POI : " + gapWrite);
     
    	}
    Edit : bien sûr, pour utiliser les style sans encombrer sa mémoire, il faut en affecter les références. Pas instancier une nouvelle occurence, comme dans les codes source ci-dessus.

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

Discussions similaires

  1. Surveiller conso mémoire
    Par agh dans le forum Administration système
    Réponses: 2
    Dernier message: 23/02/2011, 11h29
  2. Réponses: 0
    Dernier message: 09/09/2009, 08h59
  3. POI ou JExcel
    Par nutix2003 dans le forum Documents
    Réponses: 2
    Dernier message: 25/12/2007, 15h07
  4. Différences POI et JExcel
    Par seyar666 dans le forum Documents
    Réponses: 1
    Dernier message: 20/06/2007, 14h35
  5. [Java2D] BufferedImage et conso mémoire
    Par Guybrush dans le forum 2D
    Réponses: 2
    Dernier message: 07/11/2005, 12h11

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