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

Langages de programmation Discussion :

Python 3.11 et C++ : les performances de ces deux langages en matière de simulation


Sujet :

Langages de programmation

  1. #1
    Chroniqueur Actualités

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Mars 2013
    Messages
    8 461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Mars 2013
    Messages : 8 461
    Points : 197 892
    Points
    197 892
    Par défaut Python 3.11 et C++ : les performances de ces deux langages en matière de simulation
    Python 3.11 et C++ : quelles sont les performances de ces deux langages en matière de simulation ?
    Comparaison de vitesse à l'aide d'une simulation scientifique

    Python 3.11, la dernière version langage, serait significativement plus rapide que les versions précédentes. Python 3.11 présenterait des améliorations de performance spectaculaires par rapport à Python 3.10 et aux versions antérieures. Après un test réalisé récemment, Bram Wasti, ingénieur système chez Meta, affirme que Python 3.11 est 3 fois plus rapide que la version 3.8. Wasti pense tout de même qu'il y a encore du chemin à faire en matière d'améliorations des performances.

    Si Python a gagné en popularité, en particulier dans le domaine de la science des données, que représente ce gain de vitesse face à des langages comme C++ dans le même domaine ? C'est la question à laquelle a tenté de répondre un développeur en proposant un benchmark spécialement conçu pour cette discipline (science des données).


    Un langage qui gagne en popularité, en particulier dans le domaine de la science des données

    Depuis un an déjà, Python occupe la première place de l'index TIOBE. Lors de son arrivée en pole position, Paul Jansen, PDG de Tiobe, notait déjà que c'était une première en 20 ans :

    « Pour la première fois depuis plus de 20 ans, nous avons un nouveau chef de file : le langage de programmation Python. L'hégémonie de longue date de Java et C est terminée. Python, qui a commencé comme un simple langage de script, comme alternative à Perl, est devenu mature. Sa facilité d'apprentissage, son énorme quantité de bibliothèques et son utilisation répandue dans toutes sortes de domaines en ont fait le langage de programmation le plus populaire d'aujourd'hui. Félicitations Guido van Rossum ! Proficiat ! »

    Python est un langage de programmation interprété, multi-paradigme et multi-plateformes. Il favorise la programmation impérative structurée, fonctionnelle et orientée objet. Il est doté d'un typage dynamique fort, d'une gestion automatique de la mémoire par récupérateur de mémoire et d'un système de gestion d'exceptions ; il ressemble ainsi à Perl, Ruby, Scheme, Smalltalk et Tcl.

    Python gagne en popularité ces temps-ci, en partie à cause de l'essor de la science des données et de son écosystème de bibliothèques logicielles d'apprentissage automatique comme NumPy, Pandas, TensorFlow de Google et PyTorch de Facebook.

    En effet, Python continuerait d'être la norme et la compétence la plus recherchée dans le domaine de la science des données, dépassant de loin les autres technologies et outils, comme R, SAS, Hadoop et Java. C'est ce que suggère une analyse réalisée par Terence Shin, un spécialiste des données, qui a indiqué que l'adoption de Python pour la science des données continue de croître alors même que le langage R, plus spécialisé, est en déclin. Bien entendu, cela ne veut pas dire que les spécialistes des données vont abandonner R de sitôt. L'on continuera probablement à voir Python et R utilisés pour leurs forces respectives.

    Entre Python 3.11, Cython et C++, qui est le plus rapide ?

    Multi-Agent AI s'est amusé à comparer les performances de Python 3.11, Cython vs C++ pour les simulations, indiquant que son approche est adaptée aux scientifiques des données et aux personnes ayant des connaissances dans le domaine qui souhaitent créer une simulation ou quelque chose de similaire : « il devrait être clair à l'avance que C++ est toujours plus rapide que Python. La question est de savoir de combien ? »

    Pour mémoire, Cython est un langage de programmation et un compilateur qui simplifient l'écriture d'extensions compilées pour Python. La syntaxe du langage est très similaire à Python mais il supporte en plus un sous-ensemble du langage C/C++. Le premier intérêt de Cython est qu'il produit du code nettement plus performant.

    Comme exemple de simulation scientifique, voici les résultats d'une simulation de Multi-Agent AI :


    Un système de simulation à grande échelle ne doit pas être un programme monolithique où tout se fait au même endroit. Il est logique de séparer par exemple la simulation de base à partir de la visualisation ou de l'analyse des données. Pour cet exercice, Multi-Agent AI s'est intéressé uniquement aux performances de la simulation, et la visualisation est donc déplacée vers un autre programme qui n'est pas traité ici.

    De toute évidence, un système de simulation doit communiquer avec la visualisation, soit hors ligne à l'aide de fichiers de données de sortie, soit en ligne à l'aide de flux réseau, par exemple. Ces deux méthodes sortent du cadre de cet exercice. En fait, toutes les sorties sont simplement désactivées à cet effet.

    Python 3.10 et Python 3.11

    La simulation est une simple simulation spatiale de la relation prédateur-proie. Tous les animaux, prédateurs et proies, sont modélisés comme des agents. La programmation orientée objet est un choix naturel. Chaque agent est un objet, et nous avons différents types d'agents. Multi-Agent AI définit le comportement générique d'un agent dans la classe Agent(). C'est la responsabilité de la méthode update() de calculer la nouvelle position d'un agent. L'ensemble de la simulation est assez simple, mais elle montre un comportement assez complexe.

    Code Python : 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
    import random
    from datetime import datetime
    random.seed(datetime.now().timestamp())
     
    class Agent():
        def __init__(self, x=None, y=None):
            # here: agent initialization
        def update(self, food=()):
            self.age = self.age + 1
            # here: agent position update logic
     
    class Predator(Agent):
        def __init__(self, x=None, y=None):
            super().__init__()
            self.vmax = 2.5
     
    class Prey(Agent):
        def __init__(self, x=None, y=None):
            super().__init__()
            self.vmax = 2.0
     
    class Plant(Agent):
        def __init__(self, x=None, y=None):
            super().__init__()
            self.vmax = 0
     
    def main():
        # create initial agents
        preys = [Prey() for i in range(10)]
        predators = [Predator() for i in range(10)]
        plants = [Plant() for i in range(100)]
        timestep = 0
     
        while timestep < 10000:
            # update all agents
            [a.update(food=plants) for a in preys]
            [a.update(food=preys) for a in predators]
     
            # here: handle eaten plants and create new plants
            # here: handle eaten prey and create new prey
            # here: handle old predators and create new predators
     
            timestep = timestep + 1
     
    if __name__ == "__main__":
        main()

    Comment mesurer le temps

    Une fois que nous avons décidé de mesurer la rapidité d'un programme, nous devons définir exactement comment nous allons le faire. Au début, cela semble être une tâche triviale. Exécutez simplement le programme et mesurez le temps qu'il a fallu.

    La simulation test est initialement peuplée de prédateurs, de proies et de plantes. Ils ont chacun une position aléatoire. Or, il est connu d'ensemencer le générateur de nombres aléatoires à l'aide d'une valeur fixe. Cela garantit que si vous exécutez le même programme deux fois, le résultat sera le même.

    Si vous écrivez un programme dans deux langages différents, il peut y avoir de subtiles différences de comportement. Dans le cas de simulations comme celle que nous utilisons ici, par exemple, les erreurs d'arrondi peuvent faire la différence. Un langage peut utiliser une méthode différente pour faire de l'arithmétique ou utiliser une notation interne et une précision différentes pour les nombres à virgule flottante.

    Même avec exactement le même point de départ, le résultat peut être complètement différent, et donc aussi le temps d'exécution.

    Une approche possible consiste à amorcer le générateur de nombres aléatoires en utilisant une valeur aléatoire, comme l'heure actuelle. Exécutez ensuite l'expérience plusieurs fois et utilisez un temps d'exécution moyen.

    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # Seeding the RNG in Python
    import random
    from datetime import datetime
    random.seed(datetime.now().timestamp())
     
    // Seeding the RNG in C++
    #include <random>
    #include <ctime>
    srand (time(NULL));

    Il existe une commande UNIX appelée time qui mesure le temps d'exécution d'un autre programme. C'est très pratique pour des estimations rapides. Les valeurs qu'il rapporte sont :
    • real : Temps réel écoulé (horloge murale) ;
    • user : Temps total utilisé directement par le processus (en mode utilisateur) ;
    • sys: temps total utilisé par le système pour le compte du processus (en mode noyau).

    Pour notre propos, c'est le temps réel qui nous intéresse. L'exécution du programme a pris 8,33 secondes dans l'exemple ci-dessous :

    Nom : deux.png
Affichages : 449213
Taille : 5,3 Ko

    Voici le code pour les plus curieux :

    Code Python : 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
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    import math
    import random
     
    from datetime import datetime
    random.seed(datetime.now().timestamp())
     
    WORLD_WIDTH = 2560
    WORLD_HEIGHT = 1440
     
    class Agent():
        def __init__(self, x=None, y=None):
            super().__init__()
     
            # default values
            self.vmax = 2.0
     
            # initial position
            self.x = x if x else random.randint(0, WORLD_WIDTH)
            self.y = y if y else random.randint(0, WORLD_HEIGHT)
     
            # initial velocity
            self.dx = 0
            self.dy = 0
     
            # inital values
            self.is_alive = True
            self.target = None
            self.age = 0
            self.energy = 0
     
        def update(self, food=()):
            self.age = self.age + 1
     
            # we can't move
            if self.vmax == 0:
                return
     
            # target is dead, don't chase it further
            if self.target and not self.target.is_alive:
                self.target = None
     
            # eat the target if close enough
            if self.target:
                squared_dist = (self.x - self.target.x) ** 2 + (self.y - self.target.y) ** 2
                if squared_dist < 400:
                    self.target.is_alive = False
                    self.energy = self.energy + 1
     
            # agent doesn't have a target, find a new one
            if not self.target:
                min_dist = 9999999
                min_agent = None
                for a in food:
                    if a is not self and a.is_alive:
                        sq_dist = (self.x - a.x) ** 2 + (self.y - a.y) ** 2
                        if sq_dist < min_dist:
                            min_dist = sq_dist
                            min_agent = a
                if min_dist < 100000:
                    self.target = min_agent
     
            # initalize 'forces' to zero
            fx = 0
            fy = 0
     
            # move in the direction of the target, if any
            if self.target:
                fx += 0.1*(self.target.x - self.x)
                fy += 0.1*(self.target.y - self.y)
     
            # update our direction based on the 'force'
            self.dx = self.dx + 0.05 * fx
            self.dy = self.dy + 0.05 * fy
     
            # slow down agent if it moves faster than it max velocity
            velocity = math.sqrt(self.dx ** 2 + self.dy ** 2)
            if velocity > self.vmax:
                self.dx = (self.dx / velocity) * (self.vmax)
                self.dy = (self.dy / velocity) * (self.vmax)
     
            # update position based on delta x/y
            self.x = self.x + self.dx
            self.y = self.y + self.dy
     
            # ensure it stays within the world boundaries
            self.x = max(self.x, 0)
            self.x = min(self.x, WORLD_WIDTH)
            self.y = max(self.y, 0)
            self.y = min(self.y, WORLD_HEIGHT)
     
    class Predator(Agent):
        def __init__(self, x=None, y=None):
            super().__init__()
            self.vmax = 2.5
     
    class Prey(Agent):
        def __init__(self, x=None, y=None):
            super().__init__()
            self.vmax = 2.0
     
    class Plant(Agent):
        def __init__(self, x=None, y=None):
            super().__init__()
            self.vmax = 0
     
     
    def main():
        # open the ouput file
        f = open('output.csv', 'w')
        print(0, ',', 'Title', ',', 'Predator Prey Relationship / Example 02 / Pthon', file=f)
     
        # create initial agents
        preys = [Prey() for i in range(10)]
        predators = [Predator() for i in range(10)]
        plants = [Plant() for i in range(100)]
     
        timestep = 0
        while timestep < 10000:
            # update all agents
            #[f.update() for f in plants]  # no need to update the plants; they do not move
            [a.update(food=plants) for a in preys]
            [a.update(food=preys) for a in predators]
     
            # handle eaten and create new plant
            plants = [p for p in plants if p.is_alive is True]
            plants = plants + [Plant() for i in range(2)]
     
            # handle eaten and create new preys
            preys = [p for p in preys if p.is_alive is True]
     
            for p in preys[:]:
                if p.energy > 5:
                    p.energy = 0
                    preys.append(Prey(x = p.x + random.randint(-20, 20), y = p.y + random.randint(-20, 20)))
     
            # handle old and create new predators
            predators = [p for p in predators if p.age < 2000]
     
            for p in predators[:]:
                if p.energy > 10:
                    p.energy = 0
                    predators.append(Predator(x = p.x + random.randint(-20, 20), y = p.y + random.randint(-20, 20)))
     
            # write data to output file
            #[print(timestep, ',', 'Position', ',', 'Predator', ',', a.x, ',', a.y, file=f) for a in predators]
            #[print(timestep, ',', 'Position',  ',', 'Prey', ',', a.x, ',', a.y, file=f) for a in preys]
            #[print(timestep, ',', 'Position',  ',', 'Plant', ',', a.x, ',', a.y, file=f) for a in plants]
     
            timestep = timestep + 1
     
        print(len(predators), len(preys), len(plants))
     
    if __name__ == "__main__":
        main()

    Résultats du côte de Python 3.10, Python 3.11, Cython et C++

    Résultats Python 3.10

    Jusqu'à présent, les tests ont été effectués avec Python 3.10. Ainsi, lorsque nous examinons la sortie de synchronisation ci-dessus, nous constatons que l'exécution de la simulation dans Python 3.10 a pris 9,5 secondes.

    C'est la valeur de base pour une expérimentation ultérieure. C'est la valeur que nous voulons battre.

    Une note sur l'ordinateur utilisé pour effectuer ces mesures. Il a une spécification raisonnablement normale qui devrait être représentative d'un système utilisé pour effectuer de petites simulations scientifiques.
    • Processeur Intel Core i7–8700 à 3,20 GHz
    • 6 cœurs de processeur
    • 32 Go de RAM

    Résultats Python 3.11

    Il y avait beaucoup de bruit autour de la nouvelle version Python 3.11. On dit qu'elle est beaucoup plus rapide que les versions précédentes de Python. De nombreux articles confirment ce fait. La raison, cependant, est profondément enfouie dans les notes de version de la nouvelle version de Python :

    L'idée générale est que bien que Python soit un langage dynamique, la plupart du code a des régions où les objets et les types changent rarement. Ce concept est connu sous le nom de stabilité de type.

    Au moment de l'exécution, Python essaiera de rechercher des modèles communs et la stabilité du type dans le code d'exécution. Python remplacera alors l'opération en cours par une autre plus spécialisée. Cette opération spécialisée utilise des raccourcis disponibles uniquement pour les cas/types d'utilisation, qui surpassent généralement leurs homologues génériques.
    Fort de cette information, Multi-Agent AI a exécuté à nouveau la même simulation, cette fois en utilisant Python 3.11. Et voici les résultats :

    Nom : onze.png
Affichages : 18623
Taille : 12,4 Ko

    Le résultat est plus impressionnant qu'avec la version précédente : 5,8 secondes, ce qui est beaucoup plus rapide que les 9,5 secondes mesurées auparavant. Les améliorations fonctionnent mieux si le programme passe la plupart du temps dans une fonction ou une boucle de base interne. Si vous le pouvez, utilisez la dernière version de Python, cela peut potentiellement vous faire gagner beaucoup de temps.

    Les simulations typiques passent la plupart de leur temps à calculer la même formule mathématique encore et encore - c'est exactement là que cette spécialisation des opérations mentionnée ci-dessus est la plus importante.

    Résultats Cython

    Normalement, le code Python est interprété à chaque fois qu'une ligne est exécutée. Cython, cependant, compile une fonction en code machine rapide (via C, d'où son nom). Il utilise la même syntaxe de base que Python, est entièrement interopérable et ajoute quelques fonctionnalités de langage supplémentaires pour optimiser les performances.

    Le langage Cython est un sur-ensemble du langage Python qui prend également en charge l'appel de fonctions C et la déclaration de types C sur des variables et des attributs de classe. Cela permet au compilateur de générer du code C très efficace à partir du code Cython. Le code C est généré une fois, puis compilé avec tous les principaux compilateurs C/C++.

    Apprendre Cython n'est pas difficile, mais cela nécessite un peu d'effort supplémentaire par rapport à Python. C'est plus compliqué à écrire, et plus d'étapes sont nécessaires pour exécuter le code.

    Il existe différentes techniques d'optimisation disponibles dans Cython. La plus simple, et probablement la plus efficace, est la déclaration des types de variables. Cette étape simple libère le programme de la vérification du type d'une variable à chaque étape (exactement ce qui rend Python 3.11 si rapide).

    Code cython : 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
    cdef class Agent():
    
        cdef readonly double x
        cdef readonly double y
    
        cdef double dx
        cdef double dy
    
        cdef Agent target
    
        # here: more definitions
    
        def __init__(self, x=None, y=None, world_width=0, world_height=0):
           
        # here: agent initialization
      
        cpdef void update(self, list food) except *:
            cdef double min_dist
            cdef double squared_dist
            cdef double fx
            cdef double fy
    
            self.age = self.age + 1
    
            # here: agent position update logic

    Cette nouvelle version de l'exemple de simulation s'exécute maintenant en 4,2 secondes. C'est deux fois plus rapide que le programme original, et toujours plus rapide que Python 3.11. La seule partie réécrite dans Cython était la classe Agent() comme indiqué ci-dessus. Le reste du programme a été laissé en Python normal.

    Nom : cython.png
Affichages : 18583
Taille : 12,9 Ko

    Pour les plus curieux, voici le code :

    Code Cython : 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
    """
    python setup.py build_ext --inplace
    """
    
    import random
    
    from datetime import datetime
    random.seed(datetime.now().timestamp())
    
    WORLD_WIDTH = 2560
    WORLD_HEIGHT = 1440
    
    from agent import Agent
    
    class Predator(Agent):
        def __init__(self, x=None, y=None, world_width=0, world_height=0):
            super().__init__(world_width=world_width, world_height=world_height)
            self.vmax = 2.5
    
    class Prey(Agent):
        def __init__(self, x=None, y=None, world_width=0, world_height=0):
            super().__init__(world_width=world_width, world_height=world_height)
            self.vmax = 2.0
    
    class Plant(Agent):
        def __init__(self, x=None, y=None, world_width=0, world_height=0):
            super().__init__(world_width=world_width, world_height=world_height)
            self.vmax = 0
    
    
    def main():
        # open the ouput file
        f = open('output.csv', 'w')
        print(0, ',', 'Title', ',', 'Predator Prey Relationship / Example 02 / Cython', file=f)
    
        # create initial agents
        preys = [Prey(world_width=WORLD_WIDTH, world_height=WORLD_HEIGHT) for i in range(10)]
        predators = [Predator(world_width=WORLD_WIDTH, world_height=WORLD_HEIGHT) for i in range(10)]
        plants = [Plant(world_width=WORLD_WIDTH, world_height=WORLD_HEIGHT) for i in range(100)]
    
        timestep = 0
        while timestep < 10000:
            # update all agents
            #[f.update([]) for f in plants]  # no need to update the plants; they do not move
            [a.update(plants) for a in preys]
            [a.update(preys) for a in predators]
    
            # handle eaten and create new plant
            plants = [p for p in plants if p.is_alive is True]
            plants = plants + [Plant(world_width=WORLD_WIDTH, world_height=WORLD_HEIGHT) for i in range(2)]
    
            # handle eaten and create new preys
            preys = [p for p in preys if p.is_alive is True]
    
            for p in preys[:]:
                if p.energy > 5:
                    p.energy = 0
                    preys.append(Prey(x = p.x + random.randint(-20, 20), y = p.y + random.randint(-20, 20), world_width=WORLD_WIDTH, world_height=WORLD_HEIGHT))
    
            # handle old and create new predators
            predators = [p for p in predators if p.age < 2000]
    
            for p in predators[:]:
                if p.energy > 10:
                    p.energy = 0
                    predators.append(Predator(x = p.x + random.randint(-20, 20), y = p.y + random.randint(-20, 20), world_width=WORLD_WIDTH, world_height=WORLD_HEIGHT))
    
            # write data to output file
            #[print(timestep, ',', 'Position', ',', 'Predator', ',', a.x, ',', a.y, file=f) for a in predators]
            #[print(timestep, ',', 'Position',  ',', 'Prey', ',', a.x, ',', a.y, file=f) for a in preys]
            #[print(timestep, ',', 'Position',  ',', 'Plant', ',', a.x, ',', a.y, file=f) for a in plants]
    
            timestep = timestep + 1
    
        print(len(predators), len(preys), len(plants))
    
    if __name__ == "__main__":
        main()

    Résultat C++

    Python est un langage interprété avec typage dynamique. Cela signifie que chaque ligne de code est interprétée pendant l'exécution du programme et traduite en code machine à la volée. Le type de variables n'est pas connu à l'avance et peut même changer.

    Le C++, quant à lui, est compilé et utilise le typage statique. Cela a des avantages de vitesse. Le compilateur peut analyser l'ensemble du programme et optimiser le code machine à l'avance. Et il n'est pas nécessaire de vérifier le type des variables pendant l'exécution du programme.

    Les deux langages sont des langages orientés objet, et la structure de notre programme est identique.

    Code C++ : 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
    using namespace std;
     
    class Agent {
        // here: Agent definition
    }
     
    class Predator: public Agent {
        public:
        Predator() : Agent() {
             vmax = 2.5;
        }
    };
     
    // here: the same for Prey and Plant classes
     
    int main(int argc, char *argv[]) {
        srand (time(NULL));
     
        // create initial agents        
        list<Agent*> predators;
        for (int i = 0; i < 10; i++) {
            Predator *p = new Predator();
            predators.push_back(p);
        }
     
        // here: the same for Preys and Plants
     
        int timestep = 0;
        while (timestep < 10000) {
            // update all agents
            for (auto p: predators) { p->update(preys); }
            for (auto p: preys) { p->update(plants); }
     
            // here: handle eaten plants and create new plants
            // here: handle eaten prey and create new prey
            // here: handle old predators and create new predators
     
            timestep++;
        }
    }

    Lorsque nous compilerons ceci, nous pourrions être surpris. Le temps d'exécution peut être assez lent, dans la région de la vitesse de Python. La raison en est que, par défaut, le compilateur laisse de nombreuses vérifications dans le code. C'est un peu comme Python alors et ralentit vraiment la simulation.

    L'activation des indicateurs d'optimisation oblige le compilateur à tenter d'améliorer les performances et/ou la taille du code au détriment du temps de compilation et éventuellement de la possibilité de déboguer le programme. Le compilateur effectue une optimisation basée sur la connaissance qu'il a du programme.
    g++ example_02.cpp -o example_02 -O3
    Si nous activons l'optimisation du temps de compilation à l'aide de l'indicateur -O3, le résultat est différent :

    Nom : cpp.png
Affichages : 18579
Taille : 11,3 Ko

    La même simulation qu'avant s'exécute maintenant en 0,78 seconde, soit 10 fois plus rapidement que l'équivalent Python.

    Voici le code pour les plus curieux :

    Code C++ : 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
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    // Use something like this to compile:
    // g++ example_02.cpp -o example_02  -std=c++11 -O3
     
    #include <iostream>
    #include <random>
    #include <list>
    #include <fstream>
     
    using namespace std;
     
    const double WORLD_WIDTH = 2560.0;
    const double WORLD_HEIGHT = 1440.0;
     
    class Agent {
    public:
        double vmax = 0.0;
        double x;
        double y;
        double dx = 0.0;
        double dy = 0.0;
        bool is_alive = true;
        Agent* target = NULL;
        int age = 0;
        int energy = 0;
     
        Agent() {
            x = WORLD_WIDTH * (rand() / (RAND_MAX + 1.0));
            y = WORLD_HEIGHT * (rand() / (RAND_MAX + 1.0));
        }
     
        void update(const list<Agent*>& food) {
            age++;
     
            // we can't move
            if (vmax == 0.0) { return; }
     
            // target is dead, don't chase it further
            if ((target != NULL) && (target->is_alive == false)) {
                target = NULL;
            }
     
            // eat the target if close enough
            if (target != NULL) {
                double squared_dist = pow((x - target->x), 2) + pow((y - target->y), 2);
                if (squared_dist < 400) {
                    target->is_alive = false;
                    energy++;
                }
            }
     
            // agent doesn't have a target, find a new one
            if (target == NULL) {
                double min_dist = 9999999;
                Agent* min_agent = NULL;
     
                for (const auto a: food) {
                    if (a->is_alive == true) {
                        double squared_dist = pow((x - a->x), 2) + pow((y - a->y), 2);
                        if (squared_dist < min_dist) {
                            min_dist = squared_dist;
                            min_agent = a;
                        }
                    }
                }
                if (min_dist < 100000) {
                    target = min_agent;
                }
            }
     
            // initialize forces to zero
            double fx = 0;
            double fy = 0;
     
            // move in the direction of the target, if any
            if (target != NULL) {
                fx += 0.1*(target->x - x);
                fy += 0.1*(target->y - y);
            }
     
            // update our direction based on the 'force'
            dx += 0.05 * fx;
            dy += 0.05 * fy;
     
            // slow down agent if it moves faster than its max velocity
            double velocity = sqrt(pow(dx, 2) + pow(dy, 2));
            if (velocity > vmax) {
                dx = (dx / velocity) * (vmax);
                dy = (dy / velocity) * (vmax);
            }
     
            // update position based on delta x/y
            x += dx;
            y += dy;
     
            // ensure it stays within the world boundaries
            x = max(x, 0.0);
            x = min(x, WORLD_WIDTH);
            y = max(y, 0.0);
            y = min(y, WORLD_HEIGHT);
        }
    };
     
     
    class Predator: public Agent {
        public:
        Predator() : Agent() {
             vmax = 2.5;
        }
    };
     
    class Prey: public Agent {
        public:
        Prey() : Agent() {
             vmax = 2.0;
        }
    };
     
    class Plant: public Agent {
        public:
        Plant() : Agent() {
             vmax = 0.0;
        }
    };
     
    int main(int argc, char *argv[]) {
        srand (time(NULL));
     
        std::ios_base::sync_with_stdio(false);
     
        list<Agent*> predators;
        list<Agent*> preys;
        list<Agent*> plants;
     
        list<Agent*> empty;
     
        // create initial agents
        for (int i = 0; i < 10; i++) {
            Predator *p = new Predator();
            predators.push_back(p);
        }
        for (int i = 0; i < 10; i++) {
            Prey *p = new Prey();
            preys.push_back(p);
        }
        for (int i = 0; i < 100; i++) {
            Plant *p = new Plant();
            plants.push_back(p);
        }
     
        std::ofstream outfile;
        outfile.open ("output.csv");
     
        int timestep = 0;
        outfile << timestep << ',' << "Title" << ',' << "Predator Prey Relationship / Example 02 / C++" << endl;
     
        while (timestep < 10000) {
            // update all agents
            for (auto p: predators) { p->update(preys); }
            for (auto p: preys) { p->update(plants); }
            //for (auto p: plants) { p->update(empty); }  // no need to update the plants; they do not move
     
            // handle eaten and create new plants
            plants.remove_if([](const Agent* a) { return (a->is_alive == false) ? true : false; });
            for (int i=0; i < 2; i++) { plants.push_back(new Plant()); };
     
            // handle eaten and create new preys
            preys.remove_if([](const Agent* a) { return (a->is_alive == false) ? true : false; });
     
            for (auto p: preys) {
                if (p->energy > 5) {
                    p->energy = 0;
                    Prey* np = new Prey();
                    np->x = p->x + -20 + 40 * (rand() / (RAND_MAX + 1.0));
                    np->y = p->y + -20 + 40 * (rand() / (RAND_MAX + 1.0));
                    preys.push_back(np);
                }
            }
     
            // handle old and create new predators
            predators.remove_if([](const Agent* a) { return (a->age > 2000) ? true : false; });
     
            for (auto p: predators) {
                if (p->energy > 10) {
                    p->energy = 0;
                    Predator* np = new Predator();
                    np->x = p->x + -20 + 40 * (rand() / (RAND_MAX + 1.0));
                    np->y = p->y + -20 + 40 * (rand() / (RAND_MAX + 1.0));
                    predators.push_back(np);
                }
            }
     
            // write data to output file
            /*
            for (const auto p: predators) {
                outfile << timestep << ',' << "Position" << ',' << "Predator" << ',' << p->x << ',' << p->y << endl;
            }
            for (const auto p: preys) {
                outfile << timestep << ',' << "Position" << ',' << "Prey" << ',' << p->x << ',' << p->y << endl;
            }
            for (const auto p: plants) {
                outfile << timestep << ',' << "Position" << ',' << "Plant" << ',' << p->x << ',' << p->y << endl;
            }
            */
     
            timestep++;
        }
     
        outfile.close();
        cout << predators.size() << ", " << preys.size() << ", " << plants.size() << endl;
    }

    Et Multi-Agent AI d'aller de ce commentaire :

    C++ est la norme de facto pour les simulations basées sur la physique. Pour de bonnes raisons; il est bien connu, capable de gérer des structures de données complexes et l'un des langages les plus rapides disponibles.

    Il est environ 10 fois plus rapide que Python, selon la taille et la complexité de la simulation. Ces exemples de jouets prennent ici quelques secondes à s'exécuter, donc le gain de performances n'a pas vraiment d'importance. Pour des simulations à grande échelle, un gain de performances de cet ordre de grandeur est très important. C'est vraiment important si une simulation dure une journée ou une semaine.
    Conclusion

    Voici le résumé des résultats obtenus par langage :


    Langage Temps (en secondes)
    Python 3.10 9,5
    Python 3.11 5,8
    Cython 4,2
    C++ 0.78

    Cette approche suggère que Python 3.11 peut être deux fois plus rapide que les anciennes versions de Python. Pourtant, il existe un écart important par rapport aux performances du C++ optimisé. Toutefois, il faut prendre ces résultats avec du recul, étant donné que cela dépend vraiment de vos besoins. D'ailleurs, Multi-Agent AI fait les recommandations suivantes aux professionnels dans le domaine des data science :
    • Si vous voulez un prototype de simulation, faites-le en Python. Il est propre et facile à lire à la fois par les développeurs et les personnes connaissant le domaine.
    • S'il y a une partie qui est au cœur de la simulation, envisagez de la réécrire dans Cython. Surtout si elle est utilisée par d'autres personnes comme module stable.
    • Si vous construisez une simulation à grande échelle ou travaillez sur un projet de plus longue durée, divisez la simulation en parties significatives, comme la simulation, la journalisation, la visualisation et l'analyse des données.
    • Ensuite, réécrivez la simulation réelle en C++, par exemple. Pour les autres parties, utilisez les langages qui conviennent le mieux*; comme Python pour l'analyse des données.
    • Pour obtenir les meilleures performances, utilisez toutes les techniques ensemble. Une simulation vectorisée en C++ s'exécutant sur plusieurs processeurs est la plus rapide. De plus, c'est le plus difficile à programmer et à maintenir.

    Source : Muti-Agent AI

    Et vous ?

    Lequel de ces langages utilisez-vous ?
    Que pensez-vous de cette approche pour mesurer la vitesse de ces langages ?
    Êtes-vous surpris par le classement ? Les écarts de vitesse ?
    Un langage devrait-il être utilisé en fonction du besoin ou en fonction de la compétence ? Pourquoi ?
    Contribuez au club : Corrections, suggestions, critiques, ... : Contactez le service news et Rédigez des actualités

  2. #2
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 691
    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 691
    Points : 20 222
    Points
    20 222
    Par défaut
    Je vois pas bien l'intérêt de comparer Python et C++ en terme de perf.
    Python est un des langaes interprété les plus lent , C++ un des langages compilé les plus rapide. Tout le monde le sait.

    Python est un super outil car il permet de mettre en place des choses rapidement , mais dès que les performances sont importante on le remplace
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Août 2008
    Messages
    26 619
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur de recherche
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2008
    Messages : 26 619
    Points : 188 594
    Points
    188 594
    Par défaut
    Citation Envoyé par grunk Voir le message
    Python est un des langaes interprété les plus lent , C++ un des langages compilé les plus rapide. Tout le monde le sait.
    Tout le monde le sait, mais c'est faux ? Certes, C++ est toujours dans le haut du classement en termes de performance (sauf code mal écrit), mais Python fait d'énormes progrès en termes de performance. Par exemple, la version 3.11 est prévue pour être 10 à 60 % plus rapide que la 3.10. Stéphane en parle bien dans son message. Certes, on sera toujours loin de C++, mais sur cet exemple Python passe de 12 fois plus lent à 7 fois, c'est un beau progrès, ça limite le besoin de passer à du code compilé.
    Vous souhaitez participer aux rubriques Qt (tutoriels, FAQ, traductions) ou HPC ? Contactez-moi par MP.

    Créer des applications graphiques en Python avec PyQt5
    Créer des applications avec Qt 5.

    Pas de question d'ordre technique par MP !

  4. #4
    Membre averti
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 185
    Points : 426
    Points
    426
    Par défaut
    On peut aussi avoir un programme Python dont le temps d’exécution est principalement passé dans les bibliothèques (Numpy, Panda, Tensorflow…), et du coup, le temps passé dans l’interpréteur Python apparaît moins important. Et le caractère pratique de la bibliothèque est prédominant.

  5. #5
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Juin 2010
    Messages
    794
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2010
    Messages : 794
    Points : 987
    Points
    987
    Par défaut
    Citation Envoyé par floyer Voir le message
    Le on peut aussi avoir un programme Python dont le temps d’exécution est principalement passé dans les bibliothèques (Numpy, Panda, Tensorflow…), et du coup, le temps passé dans l’interpréteur Python apparaît moins important. Et le caractère pratique de la bibliothèque est prédominant.
    Elles sont toutes écrites en C ou C++ pratiquement aucun code python n'entre en jeu en utilisant ces bibliothèques

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    Le code C++ a de sérieux problèmes de syntaxe ( pointeurs nus?, pas de delete?, std::list? ) et de conception (ya pas besoin d'héritage en fait..)

  7. #7
    Membre chevronné Avatar de Mister Nono
    Homme Profil pro
    Ingénieur Mathématiques et Informatique
    Inscrit en
    Septembre 2002
    Messages
    2 232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Mathématiques et Informatique
    Secteur : Santé

    Informations forums :
    Inscription : Septembre 2002
    Messages : 2 232
    Points : 1 897
    Points
    1 897
    Par défaut
    Mais arrêtons de comparer ce qui n'est pas comparable :
    • Un script (Python) n'est pas un langage (C++)
    • L'un n'est pas compilé alors que l'autre GRAND frère l'est
    • L'un est fortement typé alors que son pseudo-très-petit frère ne l'est pas
    • etc...


    A croire que ceux qui rédigent ce type d'article ne connaissent pas grand chose aux BA.BA de l'informatique.

    PS : Je n'ai pas lu cet article qui rebute déjà par son titre. Je n'apprécie pas particulièrement le C++ et j'utilise le python dans le cadre de projets de data science.
    La connaissance ne sert que si elle est partagée.
    http://ms2i.net

  8. #8
    Membre émérite
    Profil pro
    retraité
    Inscrit en
    Décembre 2010
    Messages
    806
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : Décembre 2010
    Messages : 806
    Points : 2 310
    Points
    2 310
    Par défaut
    Sinon gros soucis de mémoire sur le code C++

    L'héritage ne sert à rien, il suffit juste de mettre un vmax de départ dans le constructeur

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2012
    Messages
    192
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2012
    Messages : 192
    Points : 312
    Points
    312
    Par défaut
    Citation Envoyé par Stéphane le calme Voir le message
    Lequel de ces langages utilisez-vous ?
    Les 2 mon capitaine (et Rust en plus).

    Il n'y a pas que la vitesse d’exécution qui compte sinon c'est le langage assembleur qui serait le seul utilisé. Le C a été inventé pour s'affranchir de son bas niveau d'abstraction: le compilateur a traduit le langage C en assembleur. Mais la compilation prend du temps d'où l'intérêt de Python, sans compter que Python est encore de plus haut niveau (proche du langage naturel).

    Tant que Python n'est pas rédhibitoire en terme d’exécution (tourne même sur un Raspberry, si, si), il est préférable tant au niveau de l'écriture du code (le plus rapide) que de sa maintenance (le plus concis et lisible).

    Dans l'autre cas, rien n'empêche, comme il est dit dans l'article, d'écrire les briques en C/C++/Rust et le ciment en Python. Ou même d'imaginer un tandem avec un programme d'interface écrit en Python s’exécutant un Raspberry et un moteur écrit en C s'exécutant sur un supercalculateur.

  10. #10
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2020
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Janvier 2020
    Messages : 3
    Points : 5
    Points
    5
    Par défaut Je ne comprendrai jamais ces comparaisons
    Pourquoi comparer du code "interprété" avec du code compilé ? ...

    Que ce soit du code généré en pyton, js, php ou n'improte quel autre langage qui utilise un preprocesseur pour "pré-compilier" puis exécuter le code , celui-ci sera toujours plus lent qu'un code compilé ..

  11. #11
    Membre averti
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 185
    Points : 426
    Points
    426
    Par défaut
    @redcurve : je sais que la rapidité des bibliothèques citées est dû à leur écriture en C/C++, mais ce n’est pas gênant. Si ces bibliothèques suffisent à monter un projet assez efficace dans un langage de haut niveau, on aurait tord de chercher plus loin.

    Évidemment, si cela ne suffit pas, il faudra coder tout ou partie du projet en C/C++ (ou autre comme Rust ?, peut-être Julia)

  12. #12
    Expert éminent sénior

    Homme Profil pro
    Directeur des systèmes d'information
    Inscrit en
    Avril 2002
    Messages
    2 793
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Directeur des systèmes d'information
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2002
    Messages : 2 793
    Points : 18 952
    Points
    18 952
    Par défaut
    Citation Envoyé par pebaroiller Voir le message
    Pourquoi comparer du code "interprété" avec du code compilé ? ...
    Pour avoir des données précises, et au vu des informations prendre les décisions adéquates en fonction du type de projet.
    Ne prenez pas la vie au sérieux, vous n'en sortirez pas vivant ...

  13. #13
    Membre averti
    Homme Profil pro
    autre
    Inscrit en
    Septembre 2015
    Messages
    185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Septembre 2015
    Messages : 185
    Points : 426
    Points
    426
    Par défaut
    De plus, avec Cython, on a du code compilé. Il
    Est donc intéressant de situer ce compilateur.

    Et même s’il est évident qu’un language semi-compilé (compilé en byte code) sera moins performant que le langage C compilé, avoir des ordres de grandeur de la différence peut être utile. Est-ce un facteur 2 ? 3 ? 10 ? …

  14. #14
    Membre émérite
    Profil pro
    retraité
    Inscrit en
    Décembre 2010
    Messages
    806
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : Décembre 2010
    Messages : 806
    Points : 2 310
    Points
    2 310
    Par défaut
    Citation Envoyé par archqt Voir le message
    Sinon gros soucis de mémoire sur le code C++

    L'héritage ne sert à rien, il suffit juste de mettre un vmax de départ dans le constructeur
    Que celui qui moinsse de façon débile ou compulsive explique s'il en a le courage pourquoi d'après lui il n'y a pas de soucis de mémoire, et aussi à quoi sert l'héritage dans ce cas.

  15. #15
    Membre actif
    Homme Profil pro
    Architecte technique
    Inscrit en
    Juin 2019
    Messages
    105
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Architecte technique
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juin 2019
    Messages : 105
    Points : 241
    Points
    241
    Par défaut
    Lequel de ces langages utilisez-vous ?
    C/C++ les 9/10e du temps, pour tout ce qui est démons, qui doit être rapide ou qui tape dans le bas niveau.
    Pour le haut niveau (IHM, script utilisateurs ou qui doivent s'installer facilement, ...), c'est du Lua.

    J'avais testé plusieurs langages de scripts dont Python, mais il était beaucoup trop lent par rapport à Lua ... et surtout avec une empreinte mémoire sans commune mesure.

  16. #16
    Membre extrêmement actif
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2022
    Messages
    755
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Août 2022
    Messages : 755
    Points : 0
    Points
    0
    Par défaut
    Mouais ...
    Très relatif comme comparatif.

    Après plus de 10 ans à faire de tout, notamment des portages de jeux vidéo avec ma boite, par exemple le C# est largement au dessus en termes de performance de tout les autres langages, car plus adapté à ce que demande comme contrainte ce domaine (une boucle qui execute 60 fois par secondes, de multiples boucles dans cette boucle, de la temporisation, ...)

    C'est un peu comme comparer des pommes et des chaises ...
    Il faut déjà poser un contexte pour pouvoir avoir un tel comparatif.

    Une simulation ça veut tout et rien dire.

  17. #17
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 28
    Points : 52
    Points
    52
    Par défaut La performance d'un langage dépend aussi du développeur
    Python peut être très très véloce quand on fait des optimisations, je l'utilise dans beaucoup de projets, certes les premiers jets des programmes sont quelques fois pas terrible en performance, mais python a des outils pour mesurer la conso cpu de chaque branche de code. Du code python optimisé peut être ultra efficace, et surtout beaucoup infiniment moins lourd à développer qu'un équivalent C++.

    Le C++ peut aussi être extrêmement gourmand si on n'y fais pas attention, j'ai souvenir entre autre d'un usage de multimap stl lent, j'ai finis par bricoler une classe à base de vector et cela a multiplié par 20 les perfs. Le manque de performance d'un langage se situe avant tout, entre la chaise et le clavier.

  18. #18
    Membre du Club
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Mars 2022
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2022
    Messages : 6
    Points : 41
    Points
    41
    Par défaut Un exemple emblématque
    Alors bon .. comment dire ?
    Je rejoins ceux qui ne voient pas trop l'intérêt de cet article.
    Comparer Python 3.10 à Python 3.11, pourquoi pas.
    Cela permet de vérifier que l'on ne nous vend pas un nième épisode de
    la grande saga "Les prochaines versions seront plus rapides".
    (Je vois passer ces histoires depuis 20 ans à propos de Java, et s'il y a effectivement
    des améliorations, elle passent toujours par la même porte:
    un petit peu moins d'interprétation et un peu plus de compilation).
    Au final, rien ne change fondamentalement.

    Ce qui m'a intéressé ici est la version C++ de ce benchmark.
    Oulà ! On peut dire que ça pique les yeux. Je me demande quel développeur à
    écrit cela.

    Je passe sur l'intérêt d'utiliser l'héritage ici.
    Techniquement, il ne sert à rien dans ce cas précis, comme cela a déjà été dit.
    Mais dans le cadre d'une simulation plus importante, pourquoi pas.
    Mais dans ce cas, il faudra aussi du polymorphisme.

    Pour ce qui est de l'écriture du code, on sent clairement que c'est une transcription de la version Python.
    - Encapsulation ? Y'en a pas.
    - Construction propre ? Y'en a pas non plus. Les objets sont construits, puis modifiés (par chance, tout est public, c'est commode comme du Python)
    - RAII pour la fermeture automatique du flux ? Ah bon, ça existe ?

    - Les calculs sont menés en dépit du bon sens :
    ex: tirage d'un nombre aléatoire entre [0.0 et 1.0] : (rand() / (RAND_MAX + 1.0)
    ben non, désolé, ce n'est pas ça. On obtient ici un nombre entre [0 et quelque chose qui est inférieur à 1]
    Je sais ici ce qui s'est passé : la division entière à donné un résultat inattendu, et le "programmeur" s'est rendu compte que ça "marchait" en ajoutant 1.0.

    Autre exemple de code tordu : { return (a->is_alive == false) ? true : false; }
    ah bon ? { return !a->is_alive ; } ça ne serait pas un peu plus clair ?

    Un autre petit pour la route : double squared_dist = pow((x - target->x), 2) + pow((y - target->y), 2);
    Tu m'étonnes que sans l'option -O2 la programme n'aille pas vite !
    La fonction pow est tout sauf simple (souvent log et exp sont de la partie) alors pour calculer un carré
    par pitié, utilisez un produit ! Il se trouve que le compilateur est plus compétent que le programmeur: avec -O2
    l'exponentiation est remplacée par un produit. J'ai vérifié.

    - Et il y a aussi des erreurs de logique dans le code.

    - Mais le pire du pire est la gestion de la mémoire. Comme certains l'on dit (mais il semble que ça n'intéresse personne)
    ce programme est un gigatesque memory leak. Voici ce que dit valgrind :

    ==13641== HEAP SUMMARY:
    ==13641== in use at exit: 1,567,552 bytes in 22,579 blocks
    ==13641== total heap usage: 45,155 allocs, 22,576 frees, 2,190,672 bytes allocated
    ==13641==
    ==13641== LEAK SUMMARY:
    ==13641== definitely lost: 1,421,696 bytes in 22,214 blocks
    ==13641== indirectly lost: 22,976 bytes in 359 blocks
    ==13641== possibly lost: 0 bytes in 0 blocks
    ==13641== still reachable: 122,880 bytes in 6 blocks
    ==13641== suppressed: 0 bytes in 0 blocks

    Mazette ! 1,567,552 bytes, ce n'est pas du goutte à goutte.
    Ceci montre à quel point il y a des programmeurs qui ne possèdent pas les bases
    les plus élémentaires. Et je pense que plus Python prend de l'importance (enseignement et industrie)
    plus le nombre de ces programmeurs augmente.

    A première vue, il suffit de désallouer les acteurs pointés par les différents pointeurs contenus dans les listes,
    à la fin du programme, et ceux qui sont retirés des listes en cours de simulation.
    En fait, c'est un peu plus compliqué, car chaque prédateur comporte un pointeur target vers sa proie.
    Il est donc fréquent qu'un acteur soit "mort", mais encore référencé pour au moins un cycle de simulation,
    par un ou plusieurs prédateurs.
    il n'est pas simple de le desallouer au bon moment.
    A moins de faire de grosses acrobaties, un compteur
    de références s'impose. J'ai remplacé tous les pointeurs nus par des shared_ptr (5 minutes), et tout se passe bien.
    L'empreinte mémoire est aussi plus faible à l'instant t.
    ==14063==
    ==14063== HEAP SUMMARY:
    ==14063== in use at exit: 0 bytes in 0 blocks
    ==14063== total heap usage: 67,606 allocs, 67,606 frees, 2,966,744 bytes allocated
    ==14063==
    ==14063== All heap blocks were freed -- no leaks are possible
    ==14063==

    Si shared_ptr est trop luxueux (pas besoin d'être thread safe) un pointeur intelligent maison plus rustique marche aussi très bien
    et consomme moins de mémoire.

    ==14316==
    ==14316== HEAP SUMMARY:
    ==14316== in use at exit: 0 bytes in 0 blocks
    ==14316== total heap usage: 45,220 allocs, 45,220 frees, 2,252,760 bytes allocated
    ==14316==
    ==14316== All heap blocks were freed -- no leaks are possible
    ==14316==

    Vous vous doutez que j'ai vérifié les performances (rapidement, sur 10 20 ou 30 exec)
    Effectivement Python 3.11 est plus rapide que 3.10 sur cet exemple. Les chiffres avancés
    me semblent en accord avec ce que j'ai constaté.
    En ce qui concerne Python 3.11 vs C++ (compilé en C++20, -O2), j'ai un ratio de 10 à 18 selon les séries,
    mais souvent plus proche de 10. Je précise ce ces chiffres concerne la version sans fuite de mémoire.

    Un phénomène m'intrigue : les résultats en C++ sont assez stables (ils varient dans un rapport de 1 à 2 environ)
    par contre en Python 3.10 on a des pics aléatoires d'un facteur 5 (de 15 à 75 sec par exemple !). Cela se produit aussi en Python 3.11
    mais c'est moins fréquent (de 10 à 50 par exemple). Si un connaisseur de Python a une explication, je suis preneur.

  19. #19
    Membre à l'essai
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Octobre 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : Octobre 2016
    Messages : 15
    Points : 15
    Points
    15
    Par défaut tests sous Windows
    Est-il possible de faire les mêmes tests sous Windows (commande multitime) ?

  20. #20
    Membre averti
    Homme Profil pro
    Dev
    Inscrit en
    Novembre 2006
    Messages
    112
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev

    Informations forums :
    Inscription : Novembre 2006
    Messages : 112
    Points : 350
    Points
    350
    Par défaut
    Tout d'abord
    Bonne année

    Je connais un peu Python, mais ne suis pas un expert de Python.

    Vous pouvez aller voir ce lien pour avoir des explications sur le fonctionnement interne https://medium.com/aiguys/how-python...r-b2455c1bc555

    Je connais un peu plus Java et son fonctionnement interne que celui de Python:

    Quand je lis des pages sur le fonctionnement interne de python, je pense un peu au fonctionnement de java

    en Java par exemple dans ce code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    	public int m(A a){
    	        if (a!=null){
    		        return a.b;
    	        }   
    	        return 0;
    	}
    la VM peut inliner le code de cette méthode pour ne pas faire un appel supplémentaire (la JVM peut inliner jusqu'à une profondeur de 10 vs un Compilateur C++ qui le fait jusqu'à 3)

    si durant les premières exécutions de cette méthode , la JVM constate que A n'est jamais null . quand la VM va générer du code natif, elle supprimera le test.
    Au pire si A est null un jour, il y a une violation d'acces mémoire levé par la CPU et récupéré par la VM qui repassera en mode interprété du bytecode et invalidera le code assembleur généré
    et le recréera en tenant compte que A peut etre null. (elle fera le test à l'avenir).

    Une autre optimisation que peut faire la VM est avec le polymorphisme .
    Si vous avec une interface I qui a deux implémentations A et B.

    Si dans une méthode vous appelez une méthode M sur l'interface I, si la VM voit que le type de I est A, alors elle peut vérifier que c'est A et ensuite inliner le code de la methode M de A.

    il y a surement des techniques similaires en python pour optimiser le code.

Discussions similaires

  1. les performances de Python
    Par jiggyOne dans le forum Général Python
    Réponses: 17
    Dernier message: 07/07/2019, 12h10
  2. Réponses: 0
    Dernier message: 08/06/2011, 22h57
  3. Les performances de python
    Par jkalzsmu dans le forum Général Python
    Réponses: 1
    Dernier message: 18/08/2008, 10h37
  4. Petite question sur les performances de Postgres ...
    Par cb44 dans le forum PostgreSQL
    Réponses: 5
    Dernier message: 13/01/2004, 13h49

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