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

Threads & Processus C++ Discussion :

conseils multi threads


Sujet :

Threads & Processus C++

  1. #1
    Candidat au Club
    Femme Profil pro
    architecte
    Inscrit en
    Mars 2024
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 36
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : architecte
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2024
    Messages : 3
    Points : 3
    Points
    3
    Par défaut conseils multi threads
    bonjour tout le monde
    Je travaille actuellement sur une petit programme qui a pour but de vérifier si des points se trouvent à l'extérieur ou à l'intérieur de polygones (je dois tester plusieurs milliers de points sur plusieurs milliers de polygones ).
    Pour accélérer le calcul je voudrais utiliser plusieurs threads pour traiter plusieurs points simultanément
    j'ai donc fait des test sur un bout de code ou je découpe l'exécution d'une fonction suivant le nombre de threads choisi

    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
    #include <iostream>
    #include <vector>
    #include <thread>
    #include <chrono>
    #include <utility>
     
    std::vector <std::pair<int, int>> Pair(const std::vector<int>& arr, int start, int end)
    {
        std::pair<int, int > paire;
        std::vector <std::pair<int, int>> Vec_paire;
        for (int i = start; i < end; ++i)
        {
            paire = { arr[i], arr[i] + 1 };
            //std::cout << pair.first << ", " << pair.second << std::endl;
            Vec_paire.emplace_back(paire);
        }
     
        return Vec_paire;
    }
     
    int main()
    {
    	const int num_threads = 2;
    	const int array_size = 1000;
    	std::vector<int> arr(array_size);
     
        // initilisation vector arr avec des valeurs de 0 à array_size-1
        for (int i = 0; i < array_size; ++i)
        {
            arr[i] = i;
        }
     
        // création  vector de threads
        std::vector<std::thread> threads;
     
        // division de vect arr en parts égale en fonction du nombre de theards choisi
        int parts_size = array_size / num_threads;
        std::vector <std::vector<std::pair<int, int>>> results(parts_size);
     
        auto start1 = std::chrono::high_resolution_clock::now();
        for (int i = 0; i < num_threads; ++i)
        {
            int start = i * parts_size;
            int end = (i + 1) * parts_size;
            threads.emplace_back([&arr, start, end, &results, i]()
                { results[i] = Pair(arr, start, end); });
        }
        // attendre fin d'exécution des threads 
        for (auto& thread : threads)
        {
            thread.join();
        }
        auto end1 = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double, std::milli> float_ms1 = end1 - start1;
        std::cout << "temps " << float_ms1.count() << " ms " << std::endl;
     
        // affichage des valeurs 
        for (int i = 0; i < results.size(); i++)
        {
     
            for (int j = 0; j < results[i].size(); j++)
                std::cout << results[i][j].first << ", " << results[i][j].second << std::endl;
        }
     
     
        return 0;
    }
    Pourriez vous le dire si je procède de la bonne façon et me donner quelques conseils
    et comment gérer les cas ou le nombre de points ( array_size ) ne se divise pas par le nombre de threads ?
    merci d'avance

  2. #2
    Membre éprouvé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    264
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 264
    Points : 945
    Points
    945
    Billets dans le blog
    2
    Par défaut
    Dans l'idée ça a l'air bon.
    Est-ce que tu as testé ton code ? Est-il plus rapide que sans les threads ?

    comment gérer les cas ou le nombre de points ( array_size ) ne se divise pas par le nombre de threads ?
    Faudrait plutôt faire une division euclidienne, si ça ne tombe pas rond, bah du coup un thread va prendre en rab les restes.

    Sinon, est-ce que tu es au courant de std::for_each avec std::execution::par_unseq ?
    https://en.cppreference.com/w/cpp/algorithm/for_each

    Teste, et dis-moi si c'est plus rapide avec ma proposition.

  3. #3
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    C'est clairement une implémentation très très naïve qui ne prend pas en compte énormément de choses (coût transfert de données, coût création de thread, coût ordonnancement de thread, cache miss, etc...).
    Il y a très peu de chance que cette implémentation soit plus rapide qu'une approche bêtement séquentielle.

    Il y a des librairies de calcul mathématique à foison et un très grand nombre gèrent les GPU.
    Et votre problème est quasi programmé dans le silicium d'une carte graphique.

    Donc, ne reprogrammez pas une roue carrée et utilisez une librairie dédiée.

  4. #4
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Bonjour,

    Les threads, dans ce cas, ça complexifie sans apporter de vrais gains. J'ai essayé ce code avec array_size = 1000000 sinon clairement les threads sont de la perte.
    sans threads, temps 8.4623 ms
    num_threads 2 temps 7.6172 ms
    num_threads 3 temps 6.6198 ms
    num_threads 4 temps 7.2099 ms
    Mais en ajoutant juste Vec_paire.reserve(end - start); juste après la création de Vec_paire pour éviter les réallocations intempestives.
    sans threads, temps 2.7094 ms
    num_threads 2 temps 1.9898 ms
    num_threads 3 temps 1.8675 ms
    num_threads 4 temps 2.0051 ms
    On a divisé le temps par 3 avec cette ligne! 3 threads sont un peu mieux qu'aucun.

    Mais avec les threads, on obtient un tableau de résultats qui faut regrouper après et c'est lui le plus gros coût! En mesurant avec ce regroupement:
    sans threads, temps 2.7195 ms
    num_threads 2 temps 5.1629 ms
    num_threads 3 temps 4.6343 ms
    num_threads 4 temps 4.8014 ms
    Même pour 1 millions de paires, les threads ne sont pas rentables ici.

  5. #5
    Candidat au Club
    Femme Profil pro
    architecte
    Inscrit en
    Mars 2024
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 36
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : architecte
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2024
    Messages : 3
    Points : 3
    Points
    3
    Par défaut
    merci pour vos réponses
    en effet il n'y a aucun intérêt dans mon exemple a utiliser les threads mais c'était juste pour mettre un exemple d'utilisation de plusieurs threads j'avais des doutes,

    Il y a des librairies de calcul mathématique à foison et un très grand nombre gèrent les GPU
    bacelar tu aurais des noms de librairies a me donner que je regarde de plus près ?

    Faudrait plutôt faire une division euclidienne
    merci pour l'idée je n'y avais pas pensé

    Sinon, est-ce que tu es au courant de std::for_each avec std::execution::par_unseq ?
    non mais je viens de me pencher dessus les résultats sont meilleurs

    Nom : Capture temps.JPG
Affichages : 118
Taille : 46,5 Ko

    avec les threads, on obtient un tableau de résultats qui faut regrouper après et c'est lui le plus gros coût!
    on peu évité cette étape avec std::for_each

    je remets mon code avec les modifications que j'y ai apporté

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    #include <iostream>
    #include <vector>
    #include <thread>
    #include <chrono>
    #include <utility>
    #include <algorithm>
    #include <execution>
     
    std::vector <std::pair<int, int>> Pair(const std::vector<int>& arr, int start, int end)
    {
        std::pair<int, int > paire;
        std::vector <std::pair<int, int>> Vec_paire;
     
        for (int i = start; i < end; ++i)
        {
            paire = { arr[i], arr[i] + 1 };
            //std::cout << pair.first << ", " << pair.second << std::endl;
            Vec_paire.emplace_back(paire);
        }
     
        return Vec_paire;
    }
     
    std::pair<int, int> Pair_each(int a)
    {
        std::pair<int, int > paire;
        paire = { a, a + 1 };
        return paire;
    }
     
    int main()
    {
    	int num_threads;
    	const int array_size = 100;
    	std::vector<int> arr(array_size);
     
        for (int i = 1; i < 5 ; i++)
        { 
            num_threads = i;
            // initilisation vector arr avec des valeurs de 0 à array_size-1
            for (int i = 0; i < array_size; ++i)
            {
                arr[i] = i;
            }
     
            // création  vector de threads
            std::vector<std::thread> threads;
     
            // division de vect arr en parts égale en fonction du nombre de theards choisi
            int parts_size = array_size / num_threads;
            int rest = array_size % num_threads;
     
            std::vector <std::vector<std::pair<int, int>>> results(num_threads);
     
            auto start1 = std::chrono::high_resolution_clock::now();
            for (int i = 0; i < num_threads; ++i)
            {
                int start = i * parts_size;
                int end = (i + 1) * parts_size;
     
                if (i == num_threads-1)
                    end = (i + 1) * parts_size + rest;
     
                threads.emplace_back([&arr, start, end, &results, i]()
                    { results[i] = Pair(arr, start, end); });
            }
            // attendre fin d'exécution des threads 
            for (auto& thread : threads)
            {
                thread.join();
            }
            auto end1 = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double, std::milli> float_ms1 = end1 - start1;
            std::cout << "temps execusion pour " << num_threads << " threads : " << float_ms1.count() << " ms " << std::endl;
     
        // affichage des valeurs 
            std::cout << "taille du vecteur resuts : " << results.size() << std::endl;
     
            for (int i = 0; i < results.size(); i++)
            {
                std::cout << "contenu de resuts[" << i << "]" << std::endl;
                for (int j = 0; j < results[i].size(); j++)
                    std::cout << "(" << results[i][j].first << ", " << results[i][j].second << "); ";
                std::cout << std::endl;
            }
        }
     
        // test for_each //
        std::vector<std::pair<int, int>> results_each(array_size);
     
        auto start2 = std::chrono::high_resolution_clock::now();
     
        std::for_each(std::execution::par_unseq, arr.begin(), arr.end(), [&results_each](int x) {results_each[x] = Pair_each(x); });
     
        auto end2 = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double, std::milli> float_ms2 = end2 - start2;
        std::cout << "temps execusion avec for_each : " << float_ms2.count() << " ms " << std::endl;
     
        return 0;
    }

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Je pensais à des librairies comme BLAS (calculs scientifiques), CUDA ou CGAL pour le parallélisme sur GPU.
    Moi, j'ai plus l'habitude d'une approche plus bas niveau, avec des shaders.

  7. #7
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    @jennif56l,

    J'ai l'impression que tu fais tes tests en mode debug, plutôt qu'en release! C'est le release qu'il faut optimiser!
    Ton code sur mon PC:
    temps execusion pour 1 threads : 0.7577 ms
    temps execusion pour 2 threads : 0.3738 ms
    temps execusion pour 3 threads : 0.4267 ms
    temps execusion pour 4 threads : 0.5177 ms
    temps execusion avec for_each : 0.1014 ms
    Mais en mode debug, c'est nettement plus lent:
    temps execusion pour 1 threads : 5.5856 ms
    temps execusion pour 2 threads : 4.2825 ms
    temps execusion pour 3 threads : 5.2942 ms
    temps execusion pour 4 threads : 6.3771 ms
    temps execusion avec for_each : 0.904 ms

  8. #8
    Membre éprouvé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    264
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 264
    Points : 945
    Points
    945
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par jennif56l Voir le message
    merci pour l'idée je n'y avais pas pensé

    non mais je viens de me pencher dessus les résultats sont meilleurs

    on peu évité cette étape avec std::for_each

    je remets mon code avec les modifications que j'y ai apporté
    Le C++ a encore gagné, il se suffit a lui même. C'est un langage qui ne fait qu'évoluer.

    Si tu estimes que nos réponses ont résolu ton problème, n'hésite pas à mettre la discussion en résolue

  9. #9
    Candidat au Club
    Femme Profil pro
    architecte
    Inscrit en
    Mars 2024
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 36
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : architecte
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2024
    Messages : 3
    Points : 3
    Points
    3
    Par défaut
    je reviens sur mon sujet avec un autre petit problème avec for_each
    j'ai tenté de faire ceci mais ça plante de façon aléatoire

    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
     
    #include <iostream>
    #include <vector>
    #include <execution>
    #include <utility>
    int main()
    {
    	std::vector<std::pair<int, int>> results_each;
    	std::vector<int> inte(10000);
     
     
    	for (int i = 0; i < 10000; ++i)
    	{
    		inte[i] = i;
    	}
     
    	std::for_each(std::execution::par_unseq, inte.begin(), inte.end(), [&](int nump)
    		{
    			if(nump %7 == 0)
    			{
    				std::pair<int, int> paire = { nump, nump * nump };
    				//std::cout << paire.first << ",  " << paire.second << std::endl;
    				results_each.push_back(paire);
     
    			}
    		});
    	for (int i = 0; i < inte.size(); i++)
    		std::cout << results_each[i].first << "² = " << results_each[i].second << std::endl;
    	return 0;
    }
    je pense que le problème vient de push_back parce que plusieurs "paire" peuvent être insérées en même temps au vector.
    Comment peut on contourner le problème car contrairement a mes exemples précédents je ne connais pas la taille du vector de sortie ?

  10. #10
    Membre éprouvé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    264
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 264
    Points : 945
    Points
    945
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par jennif56l Voir le message
    je reviens sur mon sujet avec un autre petit problème avec for_each
    j'ai tenté de faire ceci mais ça plante de façon aléatoire

    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
     
    #include <iostream>
    #include <vector>
    #include <execution>
    #include <utility>
    int main()
    {
    	std::vector<std::pair<int, int>> results_each;
    	std::vector<int> inte(10000);
     
     
    	for (int i = 0; i < 10000; ++i)
    	{
    		inte[i] = i;
    	}
     
    	std::for_each(std::execution::par_unseq, inte.begin(), inte.end(), [&](int nump)
    		{
    			if(nump %7 == 0)
    			{
    				std::pair<int, int> paire = { nump, nump * nump };
    				//std::cout << paire.first << ",  " << paire.second << std::endl;
    				results_each.push_back(paire);
     
    			}
    		});
    	for (int i = 0; i < inte.size(); i++)
    		std::cout << results_each[i].first << "² = " << results_each[i].second << std::endl;
    	return 0;
    }
    je pense que le problème vient de push_back parce que plusieurs "paire" peuvent être insérées en même temps au vector.
    Comment peut on contourner le problème car contrairement a mes exemples précédents je ne connais pas la taille du vector de sortie ?
    Salut, il faut sécuriser l'écriture avec une mutex par exemple. Ici il y a bien concurrence de la ressource.

  11. #11
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Pour un std::execution::par_unseq, un mutex n'a pas utilisable (pas de synchro possible en non séquencé). Ca marcherais avec un std::execution::par mais le résultat doit être déplorable si chaque phase n'a qu'un élément.
    Il reste la possibilité de revenir à faire des traitements par partie avec std::execution::par sans mutex, puis de regrouper les résultats partiels.

Discussions similaires

  1. Tri multi-threadé
    Par Tifauv' dans le forum C
    Réponses: 8
    Dernier message: 28/06/2007, 09h00
  2. Réponses: 2
    Dernier message: 15/05/2004, 18h33
  3. Réponses: 16
    Dernier message: 30/01/2004, 11h05
  4. [VB6][active x] faire du multi-thread avec vb
    Par pecheur dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 20/05/2003, 12h01
  5. [Kylix] exception qtinft.dll et multi-threading
    Par leclaudio25 dans le forum EDI
    Réponses: 3
    Dernier message: 27/03/2003, 18h09

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