none
Accelerer le remplissage d'une LISTBOX RRS feed

  • Question

  • Hello

    Quelqu'un a t'il une idee pour remplir rapidement une Listbox (Ma listbox contient plus de 1 millions d'item)

    J'ai essayé un tas de chose mais rien à faire, ca rame (+ de 10 mn pour ajouter 1 millions d'items)

    merci pour vos reponses

    NoopTax
    jeudi 12 novembre 2009 21:15

Toutes les réponses

  • Bonjour,

    J'imagine que c'est une listbox Win32 (pas Winform ni WPF) ? Dans ce cas, est-ce que la listbox a le style LBS_HASSTRING ? Si oui, c'est la raison pour laquelle ça rame. Est-ce le cas ? Si oui je peux détailler la solution.

    Pierre
    jeudi 12 novembre 2009 22:09
    Auteur de réponse
  • Remarque à 2€, afficher 1 000 000 d'items dans une Listbox, c'est pas super super user friendly au niveau ergonomie, non ?
    Paul Bacelar, Ex - MVP VC++
    jeudi 12 novembre 2009 23:33
    Modérateur
  • Bonjour,

    J'imagine que c'est une listbox Win32 (pas Winform ni WPF) ? Dans ce cas, est-ce que la listbox a le style LBS_HASSTRING ? Si oui, c'est la raison pour laquelle ça rame. Est-ce le cas ? Si oui je peux détailler la solution.

    Pierre

    Bonjour,

    Oui c'est une list win32, plus exactement une Combox

    J'ai retiré tous les styles plus quelque bidouilles mais rien à faire, c'est assez long (10mn de remplissage)
    Par contre, niveau vitesse du prog apres remplissage, c'est impeccable...

    Je suis preneur de toute solution ;)
    vendredi 13 novembre 2009 16:45
  • Remarque à 2€, afficher 1 000 000 d'items dans une Listbox, c'est pas super super user friendly au niveau ergonomie, non ?
    Paul Bacelar, Ex - MVP VC++

    Au contraire, je trouve ca super super user friendly de sortir des sentiers battus ;)
    vendredi 13 novembre 2009 16:51
  • Je connais deux moyens d'accélerer le remplissage d'une listbox. Plus une troisième astuce qui n'est pas liée aux listbox.  Ce problème est assez général, et arrive dès qu'on dépasse quelques milliers d'éléments.

    Je n'étais jamais allé jusqu'à un million d'élément, mais je me suis basé sur le code suivant qui insère un million de lignes. Il s'exécute sur ma machine en 690 secondes en release ! Soit environ les 10 minutes mentionnées.

    // 690 sec
    for (int i=0; i<1000000; i++)
        pCombo->AddString(L
    "Hello world");


    1. L'astuce la plus simple et qui fait gagner beaucoup de temps est d'empêcher la listbox de demander sa mise à jour graphique lors de chaque insertion d'item en appelant la méthode SetRedraw. Cela fonctionne pour une listbox comme pour une combobox. On tombe de 690 à 149 secondes pour le code suivant :

    // 149 sec
    pCombo->SetRedraw(FALSE);
    for (int i=0; i<1000000; i++)
        pCombo->AddString(L
    "Hello world");
    pCombo->SetRedraw(TRUE);
    pCombo->UpdateWindow();

    2. Enfin, deuxième astuce, un peu plus compliquée à mettre en oeuvre, c'est celle dont je parlais dans mon premier message. Il s'agit de ne pas confier la gestion de la liste des chaines de caractères à la combobox/listbox, mais de gérer ces chaînes de caractères en mémoire dans une liste séparée. Et de transmettre le pointeur de chaque chaîne à la combobox/listbox.
    En effet, par défaut, une listbox "contient" le texte de chaque ligne. Quand on appelle AddString, la listbox stocke la chaîne de caractère insérée. Si on insère 1 million de lignes, la listbox doit stocker un million de chaines de caractères, ce qui est lourd à gérer au point de vue de la mémoire. Par exemple, si on insère 1 million de chaines de 100 caractères, la listbox doit allouer 100 Mo de mémoire (en ANSI).

    Les listbox/combobox ont un autre mode de fonctionnement, lié au mode owner-drawn. Ce mode permet de remplacer le dessin de chaque ligne de la liste par du code spécifique. Ce mode permet également d'empêcher la listbox de gérer la mémoire des chaines insérées.

    Par défaut, la listbox/combobox a le style LBS_HASSTRING/CBS_HASSTRING, et n'est pas WS_OWNERDRAWN. En passant en mode OwnDrawn Fixed (propriétés de la listbox/combobox) et en ne mettant pas le style Has String, alors la gestion de la liste devient plus simple : pour chaque ligne ajoutée, la listbox/combobox stocke seulement un pointeur. Pour 1 million de lignes, la listbox n'alloue plus que 4 Mo de mémoire.

    En spécifiant le style Owndrawn fixed et en supprimant le style hasstring, le code suivant qui ajoute 1 million de lignes s'exécute en 50 secondes :

    // 50 sec
    pCombo->SetRedraw(FALSE);
    for (int i=0; i<1000000; i++)
    {
        // insère une ligne vide dans la liste
        pCombo->AddString(NULL);
        // Associé un pointeur à la ligne ajoutée
        pCombo->SetItemData(i, p);
    }
    pCombo->SetRedraw(TRUE);
    pCombo->UpdateWindow();

    Il faut de plus implémenter le code des évenements OnMeasureItem et OnDrawItem (voir documentation sur les listbox/combobox owndrawn)

    3. Enfin, dernière possibilité pour donner une impression de rapidité est de créer un thread indépendant et de mettre le code qui remplit la combobox dans ce thread. Ainsi, la boite de dialogue s'affiche et la combobox se remplit une fois que la boite de dialogue est affichée. C'est plus "user friendly". Quitte à mettre un message du style "calcul en cours".

    samedi 14 novembre 2009 14:09
    Auteur de réponse
  • Je connais deux moyens d'accélerer le remplissage d'une listbox. Plus une troisième astuce qui n'est pas liée aux listbox.  Ce problème est assez général, et arrive dès qu'on dépasse quelques milliers d'éléments.

    Je n'étais jamais allé jusqu'à un million d'élément, mais je me suis basé sur le code suivant qui insère un million de lignes. Il s'exécute sur ma machine en 690 secondes en release ! Soit environ les 10 minutes mentionnées.

    // 690 sec
    for (int i=0; i<1000000; i++)
        pCombo->AddString(L
    "Hello world");


    1. L'astuce la plus simple et qui fait gagner beaucoup de temps est d'empêcher la listbox de demander sa mise à jour graphique lors de chaque insertion d'item en appelant la méthode SetRedraw. Cela fonctionne pour une listbox comme pour une combobox. On tombe de 690 à 149 secondes pour le code suivant :

    // 149 sec
    pCombo->SetRedraw(FALSE);
    for (int i=0; i<1000000; i++)
        pCombo->AddString(L
    "Hello world");
    pCombo->SetRedraw(TRUE);
    pCombo->UpdateWindow();

    2. Enfin, deuxième astuce, un peu plus compliquée à mettre en oeuvre, c'est celle dont je parlais dans mon premier message. Il s'agit de ne pas confier la gestion de la liste des chaines de caractères à la combobox/listbox, mais de gérer ces chaînes de caractères en mémoire dans une liste séparée. Et de transmettre le pointeur de chaque chaîne à la combobox/listbox.
    En effet, par défaut, une listbox "contient" le texte de chaque ligne. Quand on appelle AddString, la listbox stocke la chaîne de caractère insérée. Si on insère 1 million de lignes, la listbox doit stocker un million de chaines de caractères, ce qui est lourd à gérer au point de vue de la mémoire. Par exemple, si on insère 1 million de chaines de 100 caractères, la listbox doit allouer 100 Mo de mémoire (en ANSI).

    Les listbox/combobox ont un autre mode de fonctionnement, lié au mode owner-drawn. Ce mode permet de remplacer le dessin de chaque ligne de la liste par du code spécifique. Ce mode permet également d'empêcher la listbox de gérer la mémoire des chaines insérées.

    Par défaut, la listbox/combobox a le style LBS_HASSTRING/CBS_HASSTRING, et n'est pas WS_OWNERDRAWN. En passant en mode OwnDrawn Fixed (propriétés de la listbox/combobox) et en ne mettant pas le style Has String, alors la gestion de la liste devient plus simple : pour chaque ligne ajoutée, la listbox/combobox stocke seulement un pointeur. Pour 1 million de lignes, la listbox n'alloue plus que 4 Mo de mémoire.

    En spécifiant le style Owndrawn fixed et en supprimant le style hasstring, le code suivant qui ajoute 1 million de lignes s'exécute en 50 secondes :

    // 50 sec
    pCombo->SetRedraw(FALSE);
    for (int i=0; i<1000000; i++)
    {
        // insère une ligne vide dans la liste
        pCombo->AddString(NULL);
        // Associé un pointeur à la ligne ajoutée
        pCombo->SetItemData(i, p);
    }
    pCombo->SetRedraw(TRUE);
    pCombo->UpdateWindow();

    Il faut de plus implémenter le code des évenements OnMeasureItem et OnDrawItem (voir documentation sur les listbox/combobox owndrawn)

    3. Enfin, dernière possibilité pour donner une impression de rapidité est de créer un thread indépendant et de mettre le code qui remplit la combobox dans ce thread. Ainsi, la boite de dialogue s'affiche et la combobox se remplit une fois que la boite de dialogue est affichée. C'est plus "user friendly". Quitte à mettre un message du style "calcul en cours".


    Merci beaucoup ;)

    La vitesse est tout à fait acceptable maintenant...
    J'ai juste un petit probleme d'affichage avec le mode Ownerdraw de la listbox

    @+

    lundi 16 novembre 2009 17:03
  • Super ! :)
    mardi 17 novembre 2009 22:36
    Auteur de réponse