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

Python Discussion :

Optimisation bacth avec multiprocessing ?


Sujet :

Python

  1. #1
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut Optimisation bacth avec multiprocessing ?
    Bonjour à tous,

    J’ai scripté en python une analyse financière et je cherche à optimiser les temps de traitement.
    Après quelques recherches, je suis tombé sur la librairie "multiprocessing" qui semble être la mieux adaptée à ce que je recherche.
    Je ne sais pas trop comment m'y prendre pour implémenter tout ça et je ne suis pas sûr que ce soit le bon choix...

    Dans ce programme, je charge un cube XBasesX (list à 3 dimensions) à partir d'une base de données.

    # Dimension 1 : les courses de l'hippodrome (560 courses du meeting d'hiver 2017 à Vincennes en attelé)
    # Dimension 2 : les chevaux de la course (de 9 à 20 partants, par défaut, j'ai fixé cette dimension à 20, c'est peut-être une erreur pour l'optimisation)
    # Dimension 3 : les rubriques des chevaux et les zones de travail (14 rubriques de classements qualificatifs indices 4 à 17, les indices 0 à 3 et 18 servent aux calculs et 19 au stockage des rapports)

    Voici la fonction que j'appelle en lui donnant en paramètres les coefficients multiplicateurs à appliquer à chacune des rubriques, cette fonction appelle aussi en fin de traitement une autre fonction qui enregistre le résultat de l'analyse si le bilan financier s'est amélioré.

    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
    # Fonction de traitement d'un cycle complet sur le cube
    def CycleToutesCourses(CoefRub):
        global XBasesX
        global NbCycles
        global LieuCourse
        global Discipline
        global SvNbCoursesGains
        global SvMontantGains
        # Initialisation des zones de calcul pour le bilan financier
        NbCoursesGains = 0
        MontantGains = 0
        MontantJeux = 0
        CptEcartMoyen = 0
        CptEcartMaxi = 0
        Z4Calcules = 0
        EcartMoyen = 0
        EcartMaxi = 0
        Ecart = 0
        # on boucle sur les courses charges dans XBasesX
        for BasesX in XBasesX:
            # Calcul des points cumulés pour chaque cheval de la course lignes 0 à 19
            for Bases in BasesX:
                # Cumul des 14 rubriques colonnes 3 à 17
                for Rub in range(0,14):
                    Bases[3] = Bases[3] + (CoefRub[Rub] * Bases[Rub + 4])
            # Tri préalable sur colonne 8 classement ClaStatsCombi
            BasesX = sorted(BasesX, key=itemgetter(8))
            # Tri sur colonne 3 Cumul des valeurs dégressives de 5 à 1 points pour les 5 premiers des classements de chaque rubriques
            BasesX = sorted(BasesX, key=itemgetter(3), reverse=True)
            # Bilan Z4 combiné 8 chevaux 70€
            MontantJeux += 70
            Z4 = 0
            for i in range(0,9):
                if BasesX[i][2] == 1 or BasesX[i][2] == 2 or BasesX[i][2] == 3 or BasesX[i][2] == 4:
                    Z4 += 1
            if Z4 == 4:
                NbCoursesGains += 1
                if BasesX[0][19] > 0:
                    MontantGains = MontantGains + BasesX[0][19]
                else:
                    # Gains approximatifs calculés lorsque le rapport du Z4 est inconnu 
                    if (BasesX[1][19] * BasesX[2][19] * BasesX[3][19] / 10) > 0:
                        MontantGains = MontantGains + (BasesX[1][19] * BasesX[2][19] * BasesX[3][19] / 10)
                    else:
                        # 80 € par défaut si incalculable
                        MontantGains = MontantGains + 80
                    Z4Calcules += 1
                if CptEcartMaxi > EcartMaxi:
                    EcartMaxi = CptEcartMaxi
                EcartMoyen = EcartMoyen + CptEcartMoyen
                if CptEcartMoyen != 0:
                    Ecart += 1
                CptEcartMoyen = 0
                CptEcartMaxi = 0
            else:
                CptEcartMoyen += 1
                CptEcartMaxi += 1
            # on sort du cycle sous les conditions suivantes =>
    	    # Au bout 10 courses analysées, si moins de 2 courses ont été pronostiquées
    	    # Au bout 20 courses analysées, si moins de 5 courses ont été pronostiquées 
    	    # Au bout 50 courses analysées, si moins de 11 courses ont été pronostiquées 
    	    # Au bout 100 courses analysées, si moins de 21 courses ont été pronostiquées 
            # Si l'écart maximum est supérieur à 10
            if (MontantJeux > 630 and NbCoursesGains < 2) or (MontantJeux > 1330 and NbCoursesGains < 5) or (MontantJeux > 3430 and NbCoursesGains < 11) or (MontantJeux > 6930 and NbCoursesGains < 21) or EcartMaxi > 10:
                break
        # Enregistrement du bilan financier dans Parametres
        # Si le montant des gains ou le nombre de courses gagnées s'est amélioré
        if NbCoursesGains > SvNbCoursesGains or MontantGains > SvMontantGains:
            if EcartMoyen > 0:
                EcartMoyen = round(EcartMoyen / Ecart, 2)
            EnregistreParam(CoefRub,EcartMoyen,EcartMaxi,NbCourses,NbCoursesGains,MontantJeux,MontantGains,Z4Calcules)
            print(NbCycles," - ",strftime("%Y%m%d %H:%M",gmtime())," - ",CoefRub," - NbCG=",NbCoursesGains," - Gains=",MontantGains," - Z4C=",Z4Calcules)
            if NbCoursesGains > SvNbCoursesGains:
                SvNbCoursesGains = NbCoursesGains
            if MontantGains > SvMontantGains:
                SvMontantGains = MontantGains
        else:
            if NbCycles % 50000 == 0:
                print(NbCycles," - ",strftime("%Y%m%d %H:%M",gmtime())," - ",CoefRub," - NbCG=",NbCoursesGains," - Gains=",MontantGains," - Z4C=",Z4Calcules)
    Et j'appelle cette fonction à partir de cette boucle où je fais varier les coefficients multiplicateurs en boucles imbriquées :

    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
    # Boucles de calcul de l'analyse financière
    SvNbCoursesGains = 0
    SvMontantGains = 0
    NbCycles = 0
    # Rubrique CX ***
    for ClaCX in range(0,3):
        # Rubrique CRI ***
        for ClaCRIFX in range(0,3):
            # Rubrique FOR ***
            for IndForme in range(0,3):
                # Rubrique GEN ***
                for ClaTMatic in range(0,3):
                    # Rubrique STR ***
                    for ClaStatsCombi in range(0,3):
                        # Rubrique DRI ***
                        for ClasRD in range(0,3):
                            # Rubrique ENT ***
                            for ClasRE in range(0,3):
                                # Rubrique CM ***
                                for ClaCote in range(0,3):
                                    # Rubrique REU ***
                                    for ClasCoefReussite in range(0,3):
                                        # Rubrique AR ***
                                        for ClaAR in range(0,3):
                                            # Rubrique OR ***
                                            for ClaOR in range(0,3):
                                                # Rubrique RUB ***
                                                for ClaRub in range(0,3):
                                                    # Rubrique HIS ***
                                                    for ClaHisto in range(0,3):
                                                        # Rubrique ST ***
                                                        for ClasStats in range(0,3):
                                                            # Liste des coefficients des 14 rubriques
                                                            CoefRub = [ClaCX,ClaCRIFX,IndForme,ClaTMatic,ClaStatsCombi,ClasRD,ClasRE,ClaCote,ClasCoefReussite,ClaAR,ClaOR,ClaRub,ClaHisto,ClasStats]
                                                            NbCycles += 1
                                                            CycleToutesCourses(CoefRub)
    Déjà, si vous avez des suggestions pour optimiser au maximum ce code, je suis preneur.

    D'autre part, l'un d'entre vous pourrait-il m'aider à implémenter le "multiprocessing" sur ce code s.v.p.

    Jusqu'à maintenant j'ai pu faire varier mes coefficients multiplicateurs de 0 à 2, ce traitement dans cette configuration a duré moins de 10 heures.
    Mais je souhaiterais les faire varier de 0 à 5 et là, selon mes calculs, j'en ai pour plusieurs dizaines d'années !

    Merci de votre aide. Tchicken.

  2. #2
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Salut

    Avant de penser multiprocess il y a déjà plein d'optimisation à faire dans votre code. Votre ordinateur fait des opérations. Dites vous bien que si vous lui en épargner, vous gagnez du temps d'éxcution.

    Donc le premier travail c'est de mettre un chrono sur votre fonction CycleToutesCourses
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    from time import time
     
    t0=time()
    ### execution de votre fonction
    print("temps d'execution (en secondes): ",time()-t0)
    et une fois que vous avez cet indicateur réduiser le nombre d'opérations de votre fonction et constater l'impact sur le temps d'éxécution.

    Même si vous ne gagner que quelque micro secondes, c'est déjà énorme. Car Dites vous bien qu'après avec vos 14 boucles for imbriquées, avec 3 valeurs différentes pour chaque vous faites 3 puissance 14 fois appel à cette fonction, soit environ 5 million de fois !

    Je vous donne quelques exemples :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if BasesX[i][2] == 1 or BasesX[i][2] == 2 or BasesX[i][2] == 3 or BasesX[i][2] == 4:
    s'écrit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if BasesX[i][2] in (1,2,3,4):
    ce qui vous évite l'extraction de la donnée 4 fois.
    voire même si vous savez que BasesX[i][2] est nécéssairement un entier, vous pouvez réduire le nombre de test ainsi :
    et vous passer de 4 extractions + 4 tests + 3 connecteurs logique à 1 extraction + 2 tests + 1 connecteur logique (de 11 opérations, on passe à 4)

    Autre exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                    if (BasesX[1][19] * BasesX[2][19] * BasesX[3][19] / 10) > 0:
                        MontantGains = MontantGains + (BasesX[1][19] * BasesX[2][19] * BasesX[3][19] / 10)
    Pourquoi faire calculer à votre ordinateur 2 fois cette grosse expression ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
                  a = BasesX[1][19] * BasesX[2][19] * BasesX[3][19] / 10
                  if a > 0:
                        MontantGains += a
    Et je ne parle pas là des variables qui sont en global, dont on ne sait absolument pas d'où elles viennent, et qui probablement pourraient être construites de manière plus judicieuse certainement (des array numpy peut etre), ainsi que des doubles tris qui ont l'air très suspects ...

  3. #3
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    J'en profite aussi pour vous donner la syntaxe réduite de vos 14 boucles for immonde imbriquées, qui montre déjà l'utilité de numpy :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    import numpy as np
     
    for CoefRub in np.ndindex( (3,)*14 ):
           NbCycles += 1
           CycleToutesCourses(CoefRub)

  4. #4
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Tout d'abord un grand merci pour ces précieux conseils.
    Et je vous demande pardon pour mon code exécrable (Après 30 ans de VB, je suis passé au python le mois dernier)
    Du coup j'ai fait une revue de mon code et je le remet en ligne ci-dessous en intégralité.
    Détail de la création du Cube avec la boucle sur la base de données qui appelle les fonctions AMHippo et SHHippo pour l'alimenter.
    Les variables global me simplifient le partage de données avec les différentes fonctions

    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
    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
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    # -*- coding: utf-8 -*
    # BasesX(i, 0)   numéro
    # BasesX(i, 1)   nombre de citations dans les 5 premiers de chaque classement rubriques
    # BasesX(i, 2)   place à* l'arrivée
    # BasesX(i, 3)   cumul des valeurs dégressives de 5 à 1 points pour les 5 premiers des classements rubriques
    # BasesX(i, 4)   classement ClaCX
    # BasesX(i, 5)   classement ClaCRIFX
    # BasesX(i, 6)   classement IndForme
    # BasesX(i, 7)   classement ClaTMatic
    # BasesX(i, 8)   classement ClaStatsCombi
    # BasesX(i, 9)   classement ClasRD
    # BasesX(i, 10)  classement ClasRE
    # BasesX(i, 11)  classement ClaCote
    # BasesX(i, 12)  classement ClasCoefReussite
    # BasesX(i, 13)  classement ClaAR
    # BasesX(i, 14)  classement ClaOR
    # BasesX(i, 15)  classement ClaRub
    # BasesX(i, 16)  classement ClaHisto
    # BasesX(i, 17)  classement ClasStats
    # BasesX(i, 18)  classement BasesX(i, 3) cumul des valeurs dégressives de 5 à*1 points pour les 5 premiers des classements rubriques
    # BasesX(i, 19)  QuarteD, CouplePlace12, CouplePlace13, CouplePlace23 colonnes 0 à 3
    import sqlite3
    import numpy
    from time import gmtime, strftime
    from operator import itemgetter
    from time import time
    # connexion BDD
    conn = sqlite3.connect("Vincennes.sqlite")
    conn.row_factory = sqlite3.Row
    # Initialisation du tableau de X courses XBasesX constitué de tableaux de courses BasesX
    XBasesX = []
    # Tableau des noms des 14 ou 16 rubriques étudiées
    #LibRub = ["ClaCX","ClaIDC","ClaCRIFX","IndForme","ClaTMatic","ClaStatsCombi","ClasRD","ClasRE","ClaCote","ClaCFP","ClasCoefReussite","ClaAR","ClaOR","ClaRub","ClaHisto","ClasStats"]
    LibRub = ["ClaCX","ClaCRIFX","IndForme","ClaTMatic","ClaStatsCombi","ClasRD","ClasRE","ClaCote","ClasCoefReussite","ClaAR","ClaOR","ClaRub","ClaHisto","ClasStats"]
    SvNbCoursesGains = 0
    SvMontantGains = 0
    # Rubriques classiques
    def AMHippo(NumGeny,NumCourse,Partants,Distance,LibRub,Rub):
        global BasesX
        global conn
        global LieuCourse
        global Discipline
        # TabChevaux = [[0 for i in range(2)] for j in range(20)]
        TabChevaux = [[0] * 2 for _ in range(20)]
        cur = conn.cursor()
        cur.execute("SELECT Numero, PlaceArrivee FROM Chevaux WHERE NumGeny='" + NumGeny + "' and NumCourse=" + str(NumCourse) + " ORDER BY " + LibRub)
        for row in cur.fetchall():
            TabChevaux[i][0] = row["Numero"]
            TabChevaux[i][1] = row["PlaceArrivee"]
        if Discipline == "ATTELE" or Discipline == "MONTE":
            if Distance < 2701:
                if Distance < 2401:
                    DistanceMin = "1500"
                    DistanceMax = "2400"
                else:
                    DistanceMin = "2401"
                    DistanceMax = "2700"
            else:
                DistanceMin = "2701"
                DistanceMax = "4500"
        elif Discipline == "PLAT":
            if Distance < 1801:
                if Distance < 1501:
                    DistanceMin = "500"
                    DistanceMax = "1500"
                else:
                    DistanceMin = "1501"
                    DistanceMax = "1800"
            else:
                DistanceMin = "1801"
                DistanceMax = "4500"
        else:
            if Distance < 7500:
                if Distance < 5001:
                    if Distance < 3501:
                        DistanceMin = "2500"
                        DistanceMax = "3500"
                    else:
                        DistanceMin = "3501"
                        DistanceMax = "5000"
                else:
                    DistanceMin = "5001"
                    DistanceMax = "7500"
        if Partants > 8 and Partants < 13:
            PartantsMin = "9"
            PartantsMax = "12"
        elif Partants > 12 and Partants < 17:
            PartantsMin = "13"
            PartantsMax = "16"
        else:
            PartantsMin = "17"
            PartantsMax = "20"
        cur = conn.cursor()
        cur.execute("SELECT Classement, PrctFreqT, PrctFreqZ FROM StatsRubHippo WHERE LieuCourse='" + LieuCourse + "' and Discipline='" + Discipline + "' and Rubrique='" + LibRub + "' and DistanceMin=" \
                    + DistanceMin + " and DistanceMax=" + DistanceMax + " and PartantsMin=" + PartantsMin + " and PartantsMax=" + PartantsMax + " ORDER BY PrctFreqZ desc, Classement")
        for row in cur.fetchall():
            if row["Classement"] <= Partants:
                for j in range(0,Partants):
                    if BasesX[j][0] == TabChevaux[row["Classement"]][0]:
                        BasesX[j][1] += 1
                        BasesX[j][2] = TabChevaux[row["Classement"]][1]
                        BasesX[j][Rub + 4] += (row["PrctFreqZ"])
     
    # Rubriques combinées
    def SHHippo(DateReunion,NumReunion,NumCourse):
        global BasesX
        global conn
        cur = conn.cursor()
        cur.execute("SELECT Numero, PlaceArrivee, FrequenceReussite FROM DatesChevaux WHERE DateReunion='" + DateReunion + "' and NumReunion=" + str(NumReunion) + " and NumCourse=" + str(NumCourse) + " ORDER BY ClaFrqReussite")
        for row in cur.fetchall():
            for j in range(0,20):
                if BasesX[j][0] == row["Numero"] :
                    BasesX[j][1] += 1
                    BasesX[j][2] = row["PlaceArrivee"]
                    BasesX[j][8] += (19 - (j * 2))
     
    # Enregistrement du résultat de l'analyse financière dans la base de données
    def EnregistreParam(CoefRub,EcartMoyen,EcartMaxi,NbCourses,NbCoursesGains,MontantJeux,MontantGains,Z4Calcules):
        global conn
        global LieuCourse
        global Discipline
        global DateDebPer
        global DateFinPer
        #LibCoef = ["CoefRubCX","CoefRubCRI","CoefRubIDC","CoefRubFOR","CoefRubTGE","CoefRubSTR","CoefRubDRI","CoefRubENT","CoefRubCM","CoefRubCFP","CoefRubREU","CoefRubAR","CoefRubOR","CoefRubRUB","CoefRubHIS","CoefRubST"]
        LibRub = ["RubCX","RubCRI","RubFOR","RubTGE","RubSTR","RubDRI","RubENT","RubCM","RubREU","RubAR","RubOR","RubRUB","RubHIS","RubST"]
        LibCoef = ["CoefRubCX","CoefRubCRI","CoefRubFOR","CoefRubTGE","CoefRubSTR","CoefRubDRI","CoefRubENT","CoefRubCM","CoefRubREU","CoefRubAR","CoefRubOR","CoefRubRUB","CoefRubHIS","CoefRubST"]
        # Chargement des bilans positifs en fonction des valeurs paramètrées
        cur = conn.cursor()
        cur.execute("SELECT LieuCourse, Discipline, Jeu, NbCoursesGains, MontantGains FROM Parametres WHERE Jeu='nb'")
        row = cur.fetchone()
        if NbCoursesGains > row["NbCoursesGains"]: 
            Bilan = "UPDATE Parametres set "
            for i in range(0,14):
                if CoefRub[i] == 0:
                    Bilan = Bilan + LibRub[i] + "=0, " + LibCoef[i] + "=" + str(CoefRub[i]) + ","
                else:
                    Bilan = Bilan + LibRub[i] + "=1, " + LibCoef[i] + "=" + str(CoefRub[i]) + ","
            Bilan = Bilan + " EcartMoyen=" + str(EcartMoyen) + ", EcartMaxi=" + str(EcartMaxi) + ", Difficulte=100, NbCourses=" + str(NbCourses) + ", NbCoursesGains=" + str(NbCoursesGains)
            Bilan = Bilan + ", MontantJeux="+ str(MontantJeux) + ", MontantGains=" + str(MontantGains) + ", Z4Calcules=" + str(Z4Calcules) + ", DateDebPer='" + DateDebPer + "', DateFinPer='" + DateFinPer + "'"
            Bilan = Bilan + " WHERE LieuCourse='" + LieuCourse + "' and Discipline='" + Discipline + "' and Jeu='nb'"
            cur.execute(Bilan)
            conn.commit()
        cur.execute("SELECT LieuCourse, Discipline, Jeu, NbCoursesGains, MontantGains FROM Parametres WHERE Jeu='mt'")
        row = cur.fetchone()
        if MontantGains > row["MontantGains"]: 
            Bilan = "UPDATE Parametres set "
            for i in range(0,14):
                if CoefRub[i] == 0:
                    Bilan = Bilan + LibRub[i] + "=0, " + LibCoef[i] + "=" + str(CoefRub[i]) + ","
                else:
                    Bilan = Bilan + LibRub[i] + "=1, " + LibCoef[i] + "=" + str(CoefRub[i]) + ","
            Bilan = Bilan + " EcartMoyen=" + str(EcartMoyen) + ", EcartMaxi=" + str(EcartMaxi) + ", Difficulte=100, NbCourses=" + str(NbCourses) + ", NbCoursesGains=" + str(NbCoursesGains)
            Bilan = Bilan + ", MontantJeux="+ str(MontantJeux) + ", MontantGains=" + str(MontantGains) + ", Z4Calcules=" + str(Z4Calcules) + ", DateDebPer='" + DateDebPer + "', DateFinPer='" + DateFinPer + "'"
            Bilan = Bilan + " WHERE LieuCourse='" + LieuCourse + "' and Discipline='" + Discipline + "' and Jeu='mt'"
            cur.execute(Bilan)
            conn.commit()
        cur.close
    # Fonction de traitement d'un cycle complet sur le cube
    def CycleToutesCourses(CoefRub):
        global XBasesX
        global NbCycles
        global LieuCourse
        global Discipline
        global SvNbCoursesGains
        global SvMontantGains
        # Initialisation des zones de calcul pour le bilan financier
        NbCoursesGains = 0
        MontantGains = 0
        MontantJeux = 0
        CptEcartMoyen = 0
        CptEcartMaxi = 0
        Z4Calcules = 0
        EcartMoyen = 0
        EcartMaxi = 0
        Ecart = 0
        t0=time()
        # on boucle sur les courses charges dans XBasesX
        for BasesX in XBasesX:
            # Calcul des points cumulés pour chaque cheval de la course lignes 0 à 19
            for Bases in BasesX:
                # Cumul des 14 rubriques colonnes 3 à 17
                for Rub in range(0,14):
                    Bases[3] += (CoefRub[Rub] * Bases[Rub + 4])
            # Tri préalable sur colonne 8 classement ClaStatsCombi
            BasesX = sorted(BasesX, key=itemgetter(8))
            # Tri sur colonne 3 Cumul des valeurs dégressives de 5 à 1 points pour les 5 premiers des classements de chaque rubriques
            BasesX = sorted(BasesX, key=itemgetter(3), reverse=True)
            # Bilan Z4 combiné 8 chevaux 70€
            MontantJeux += 70
            Z4 = 0
            for i in range(0,9):
                if 0<BasesX[i][2]<5:
                    Z4 += 1
            if Z4 == 4:
                NbCoursesGains += 1
                GZ4 = BasesX[0][19]
                if GZ4 > 0:
                    MontantGains += GZ4
                else:
                    # Gains approximatifs calculés lorsque le rapport du Z4 est inconnu 
                    GA = BasesX[1][19] * BasesX[2][19] * BasesX[3][19]
                    if GA > 0:
                        MontantGains += GA / 10
                    else:
                        # 80 € par défaut si incalculable
                        MontantGains += 80
                    Z4Calcules += 1
                if CptEcartMaxi > EcartMaxi:
                    EcartMaxi = CptEcartMaxi
                EcartMoyen = EcartMoyen + CptEcartMoyen
                if CptEcartMoyen != 0:
                    Ecart += 1
                CptEcartMoyen = 0
                CptEcartMaxi = 0
            else:
                CptEcartMoyen += 1
                CptEcartMaxi += 1
    	# on sort du cycle sous les conditions suivantes =>
    	# Au bout 10 courses analysées, si moins de 2 courses ont été pronostiquées
    	# Au bout 20 courses analysées, si moins de 5 courses ont été pronostiquées 
    	# Au bout 50 courses analysées, si moins de 11 courses ont été pronostiquées 
    	# Au bout 100 courses analysées, si moins de 21 courses ont été pronostiquées 
    	# Si l'écart maximum est supérieur à 10
            if (MontantJeux > 630 and NbCoursesGains < 2) or (MontantJeux > 1330 and NbCoursesGains < 5) or (MontantJeux > 3430 and NbCoursesGains < 11) or (MontantJeux > 6930 and NbCoursesGains < 21) or EcartMaxi > 10:
                break
        # Enregistrement du bilan financier dans Parametres
        # Si le montant des gains ou le nombre de courses gagnées s'est amélioré
        if NbCoursesGains > SvNbCoursesGains or MontantGains > SvMontantGains:
            if EcartMoyen > 0:
                EcartMoyen = round(EcartMoyen / Ecart, 2)
            EnregistreParam(CoefRub,EcartMoyen,EcartMaxi,NbCourses,NbCoursesGains,MontantJeux,MontantGains,Z4Calcules)
            print(NbCycles," - ",strftime("%Y%m%d %H:%M",gmtime())," - ",CoefRub," - NbCG=",NbCoursesGains," - Gains=",MontantGains," - Z4C=",Z4Calcules)
            if NbCoursesGains > SvNbCoursesGains:
                SvNbCoursesGains = NbCoursesGains
            if MontantGains > SvMontantGains:
                SvMontantGains = MontantGains
        else:
            if NbCycles % 50000 == 0:
                print(NbCycles," - ",strftime("%Y%m%d %H:%M",gmtime())," - ",CoefRub," - NbCG=",NbCoursesGains," - Gains=",MontantGains," - Z4C=",Z4Calcules)
        #print("Temps d'execution : ",time()-t0," Sec.")
    #
    # Création du Cube :
    #
    #   Dimension 1 : les courses de l'hippodrome
    #   Dimension 2 : les chevaux de la course
    #   Dimension 3 : les rubriques des chevaux
    #
    # Chargement des données Reunions, Courses et rubriques pour 1 Hippodrome et 1 Discipline
    # Par exemple pour Vincennes en attelé 566 courses pendant le meeting du 1er octobre 2017 au 31 mars 2018 :
    # On divise l'année en 2, meeting (1er octobre au 31 mars) et hors meeting (1er avril au 30 septembre)
    # on charges les x courses dans le tableau XBasesX qui servira de base pour l'étude financières
    # Dans le cas de Vincennes, 
    curR = conn.cursor()
    curR.execute("SELECT NumGeny, LieuCourse, DateReunion, NumReunion FROM Reunions ORDER BY substr(DateReunion, 7, 4)||substr(DateReunion, 4,2)||substr(DateReunion, 1,2) desc")
    NbCourses = 0
    for rowR in curR.fetchall():
        if NbCourses == 0:
            DateDebPer = rowR["DateReunion"]
            LieuCourse = rowR["LieuCourse"]
        # Chargement des données courses
        curC = conn.cursor()
        curC.execute("SELECT NumGeny, NumCourse, Discipline, Partants, Distance, Premier, Deuxieme, Troisieme, Quatrieme, Cinquieme, round(CouplePlace12), round(CouplePlace23), \
        round(CouplePlace13), round(Trio), round(QuarteD) FROM Courses WHERE Partants>9 and NumGeny='" + rowR["NumGeny"] + "' and Discipline='ATTELE' ORDER BY NumCourse")
        for rowC in curC.fetchall():
            NbCourses = NbCourses + 1
            Discipline = rowC["Discipline"]
            # Initialisation du tableau 1 course BasesX de 19 colonnes et 20 lignes 
            BasesX = [[0] * 20 for _ in range(20)]
            # chargement du tableau 1 course avec les numéro des chevaux de 1 à* 20
            for i in range(0,20):
                BasesX[i][0] = i + 1
                # On initialise les classements rubriques à 99 pour les tris croissants
    	    #
                for j in range(4,18):
                    BasesX[i][j] = 99
            # Boucle d'appels à la fonction de chargement des valeurs des 16 rubriques statistiques dans BasesX
            for Rub in range(0,14):
                if Rub==4:
                    # Rubriques combinées
                    SHHippo(rowR["DateReunion"],rowR["NumReunion"],rowC["NumCourse"]) 
                else:
                    # Rubriques classiques
                    AMHippo(rowR["NumGeny"],rowC["NumCourse"],rowC["Partants"],rowC["Distance"],LibRub[Rub],Rub) 
            BasesX[0][19] = int(rowC["round(QuarteD)"])
            BasesX[1][19] = int(rowC["round(CouplePlace12)"])
            BasesX[2][19] = int(rowC["round(CouplePlace13)"])
            BasesX[3][19] = int(rowC["round(CouplePlace23)"])
            XBasesX.append(BasesX)
        curC.close()
        DateFinPer = rowR["DateReunion"]
    curR.close()
    # Boucles de calcul de l'analyse financière
    SvNbCoursesGains = 0
    SvMontantGains = 0
    NbCycles = 0
    print(NbCycles," - ",strftime("%Y%m%d %H:%M",gmtime()))
    # variation de 0 à 3 des coefficients des 14 rubriques
    for CoefRub in numpy.ndindex( (3,)*14 ):
        NbCycles += 1
        CycleToutesCourses(CoefRub)
    Dans cette configuration, j'obtiens les temps de traitement suivants :

    Temps d'execution :  0.002000093460083008  Sec.
    Temps d'execution :  0.002000093460083008  Sec.
    Temps d'execution :  0.002000093460083008  Sec.
    Temps d'execution :  0.0029997825622558594  Sec.
    Temps d'execution :  0.002000093460083008  Sec.
    Temps d'execution :  0.0019998550415039062  Sec.
    Temps d'execution :  0.003000020980834961  Sec.
    Temps d'execution :  0.002000093460083008  Sec.
    Temps d'execution :  0.0019998550415039062  Sec.
    Temps d'execution :  0.0030002593994140625  Sec.
    
    NbCycles - Date et heure       - Coéfficients multiplicateurs ...
    0            -  20181128 14:35
    19684     -  20181128 14:36  -  (0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)  - NbCG= 7  - Gains= 1596  - Z4C= 1
    50000     -  20181128 14:39  -  (0, 0, 0, 0, 2, 1, 1, 2, 1, 2, 0, 2, 1, 1)  - NbCG= 7  - Gains= 1596  - Z4C= 1
    Soit 50000 cycles en 4 minutes environ, 750000 à l'heure, soit environ 6H30.
    Ce qui donne dans le cas des coefficients multiplicateurs de 0 à 2, théoriquement une baisse d'un 1/3 de la durée de traitement.

    Merci lg_53

  5. #5
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Autre optim :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            BasesX = sorted(BasesX, key=itemgetter(8))
            BasesX = sorted(BasesX, key=itemgetter(3), reverse=True)
    1er optim : utiliser la fonction sort qui trie la liste "sur place" plutot que de se réserver une nouvelle place en mémoire et de réaffecter ensuite

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    BasesX.sort(key=itemgetter(8))
    BasesX.sort( key=itemgetter(3), reverse=True)
    2eme optim : plutot que de faire 2 tris, n'en faire qu'un seul avec une clé double (python sait comparer des tuples) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    BasesX.sort( key= lambda x : (-x[3],x[8])))
    L'astuce ici est de lettre un signe - pour imiter un tri dans l'ordre inverse sur cette clé.

    Vous pouvez mettre les time pile autour de cela pour mesurer les gains.


    Vous pouvez également poussez les investigations plus loin en mettant des time un peu partout pour savoir ce qui prend le plus de temps (et ce qu'il serait donc de bon gout d'optimiser). Le module cProfile peut aussi vous faire cela de manière très complète.

    Il nous faudrait un fichier, ou formuler les choses différemment pour qu'on puisse faire tourner qqch si vous voulez qu'on puisse vous aidez plus.

    Bonne soirée

  6. #6
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Bonsoir lg_53,

    un grand merci pour ton aide, j'ai le fichier sqlite en pièce jointe. Je reprendrai le codage demain.
    Fichiers attachés Fichiers attachés

  7. #7
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Chez moi, temps d'exécution :

    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0009999275207519531  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0010001659393310547  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0010001659393310547  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0010001659393310547  Sec.
    Temps d'execution :  0.0010001659393310547  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0009999275207519531  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0010001659393310547  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0010001659393310547  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0009999275207519531  Sec.
    0.0 ne veut pas dire 0, mais c'est trop petit pour que python ait pu mesurer.
    Maintenant si je change les sort comme mentionné précédemment

    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Temps d'execution :  0.0  Sec.
    Si tu es en python version 3.7 ou plus tu peux avoir un time plus précis (à tester, je n'ai jamais essayer) en faisant cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    from time import time_ns as time
    au lieu de
    Tu peux aussi mesurer le temps pour faire 10 000 appels par exemple, et là c'est suffisamment grand pour être mesurable, et avoir une valeur pour toi de comparaison.

  8. #8
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Désolé, je vous ai tutoyé dans l'excitation lors de mon dernier message.
    J'ai mis à jour mon code que je mets en pièce jointe au format zip.
    Je suis en version python 3.7, mais le time_ns n'a pas changé le résultat, j'ai peut-être loupé quelque chose.
    Un grand merci à vous.
    Fichiers attachés Fichiers attachés

  9. #9
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Citation Envoyé par Tchicken Voir le message
    Désolé, je vous ai tutoyé dans l'excitation lors de mon dernier message.
    Non au contraire.

  10. #10
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    J'ai corrigé un problème sur le chargement du cube, les classements utilisés dans les cumuls rubriques et les tri n'était pas chargés !
    Ceci à complètement changé les temps de traitement :
    Avec ce correctif, le batch exécute environ 20.000 cycles à l'heure, alors qu'à vide, il en exécutait 750.000.
    Ce qui devrait donner 10 jours de temps de traitement pour un indice variant de 0 à 3.

    avec time_ns j'obtiens ces données :

    Debut Cycle : 10:27:52
    Temps cumul rubriques = 140400300 NanoSec.
    Temps tri = 31200000 NanoSec.
    Temps bilan = 0 NanoSec.
    Fin Cycle = 171600300 NanoSec.
    Fin Cycle : 10:27:53 - [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2] - NbCG= 232 - Gains= 22354

    1 cycle a une durée inférieure à 1 seconde :
    Temps cumul rubriques = 82 %
    Temps tri = 18 %
    Temps bilan = quasi nul

    Je remet en pièce jointe deux versions du code avec le timing et numpy.
    Je pense qu'il va falloir passer au multiprocessing et certainement avec du partage réseau, j'ai plusieurs pc à ma disposition à la maison.

    Comme ce sont les parties cumul rubrique et tri qui prennent le plus de temps, j'essaie de passer mon cube de list python en array numpy.

    J'ai essayé 2 méthodes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    # On transforme le cube en array numpy
    XnpX = np.array(XBasesX,int)
    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
    # Initialisation du tableau de X courses XBasesX constitué de tableaux de courses BasesX
    #XBasesX = []
    Bnp0 = np.array([1,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp1 = np.array([2,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp2 = np.array([3,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp3 = np.array([4,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp4 = np.array([5,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp5 = np.array([6,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp6 = np.array([7,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp7 = np.array([8,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp8 = np.array([9,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp9 = np.array([10,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp10 = np.array([11,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp11 = np.array([12,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp12 = np.array([13,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp13 = np.array([14,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp14 = np.array([15,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp15 = np.array([16,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp16 = np.array([17,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp17 = np.array([18,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp18 = np.array([19,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    Bnp19 = np.array([20,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    npX = np.array([Bnp0,Bnp1,Bnp2,Bnp3,Bnp4,Bnp5,Bnp6,Bnp7,Bnp8,Bnp9,Bnp10,Bnp11,Bnp12,Bnp13,Bnp14,Bnp15,Bnp16,Bnp17,Bnp18,Bnp19])
    avec ce genre de tri :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    npX = npX[npX[:,7].argsort(kind='mergesort')]
    npX = npX[npX[:,3].argsort(kind='mergesort')]
    Mais il faut que j'étudie encore numpy, car j'ai pas tout compris !
    Fichiers attachés Fichiers attachés

  11. #11
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Citation Envoyé par Tchicken Voir le message
    J'ai corrigé un problème sur le chargement du cube, les classements utilisés dans les cumuls rubriques et les tri n'était pas chargés !
    Ceci à complètement changé les temps de traitement :
    Avec ce correctif, le batch exécute environ 20.000 cycles à l'heure, alors qu'à vide, il en exécutait 750.000.
    Ce qui devrait donner 10 jours de temps de traitement pour un indice variant de 0 à 3.
    Vous voulez dire que vous venez de trouver un bug dans votre algo, et que la correction de ce bug multiplie par 37 le temps d'éxécution ? En effet ca commence à devenir conséquent.

    Citation Envoyé par Tchicken Voir le message
    Je pense qu'il va falloir passer au multiprocessing et certainement avec du partage réseau, j'ai plusieurs pc à ma disposition à la maison.
    Pas si facile que cela. Il y a 2 difficultés. La première c'est d'avoir un algorithme parallélisable. C'est à dire qu'on peut découper l'algorithme en plusieurs sous taches indépendantes. Notamment, prenons comme exemple le tri d'une grosse liste. En multiprocess, vous sentez bien que même si vous avez 2 processeurs et que vous dites à chacun de trier la moitié de la liste, et bien à la fin vous n'avez pas une liste triée. Vous avez 2 sous liste, et chacune d'elle est triée. Faut-il encore les rassembler après coup, en les intercalant judicieusement, ce qui ne pourra se faire que par un seul et unique processeur.
    La seconde difficulté est d'utiliser des CPU de machines différentes. Ca j'avoue n'avoir jamais fait. Mais il faut se méfier de cela, car si vos processeur ont besoin de beaucoup communiqué entre eux, ce que vous allez gagner en temps d'éxécution c'est rien du tout, car le gain que vous allez faire sur le calcul, vous allez remettre ce temps en communication entre les processeurs. Le phénomène existe déjà sur des CPU de la même machine, mais la limite n'est tout de même pas la même.

    Citation Envoyé par Tchicken Voir le message
    Comme ce sont les parties cumul rubrique et tri qui prennent le plus de temps, j'essaie de passer mon cube de list python en array numpy.
    Très bonne idée. J'ai hésité à vous le suggérer, mais j'ai vu pas mal d'append dans le code et je n'ai pas pris le temps de regarder si l'on pouvait s'en passer ou non. Car avec numpy, il faut travailler à taille de tableau fixée, c'est à dire déclarer au départ le tableau en entier, avec les bonnes dimensions (et remplies de valeurs arbitraires, ou de zéros), et ensuite modifier les valeurs qui sont dedans sans toucher à la taille. Garder en tête que tout ce qui modifie la taille d'un array numpy a une performance inférieur à un append sur une liste.

    PS : Lorsque vous affichez vos temps avec précision nanoseconde, multiplier par 1e-9 pour garder un affichage seconde (qui est humainement plus lisible) tout en préservant une précision nanoseconde

  12. #12
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Citation Envoyé par lg_53 Voir le message
    Pas si facile que cela. Il y a 2 difficultés. La première c'est d'avoir un algorithme parallélisable. C'est à dire qu'on peut découper l'algorithme en plusieurs sous taches indépendantes. Notamment, prenons comme exemple le tri d'une grosse liste. En multiprocess, vous sentez bien que même si vous avez 2 processeurs et que vous dites à chacun de trier la moitié de la liste, et bien à la fin vous n'avez pas une liste triée. Vous avez 2 sous liste, et chacune d'elle est triée. Faut-il encore les rassembler après coup, en les intercalant judicieusement, ce qui ne pourra se faire que par un seul et unique processeur.
    La seconde difficulté est d'utiliser des CPU de machines différentes. Ca j'avoue n'avoir jamais fait. Mais il faut se méfier de cela, car si vos processeur ont besoin de beaucoup communiqué entre eux, ce que vous allez gagner en temps d'éxécution c'est rien du tout, car le gain que vous allez faire sur le calcul, vous allez remettre ce temps en communication entre les processeurs. Le phénomène existe déjà sur des CPU de la même machine, mais la limite n'est tout de même pas la même.
    Je pensais paralléliser la fonction CycleToutesCourses, cela vous semble-t-il possible ?


    Citation Envoyé par lg_53 Voir le message
    Très bonne idée. J'ai hésité à vous le suggérer, mais j'ai vu pas mal d'append dans le code et je n'ai pas pris le temps de regarder si l'on pouvait s'en passer ou non. Car avec numpy, il faut travailler à taille de tableau fixée, c'est à dire déclarer au départ le tableau en entier, avec les bonnes dimensions (et remplies de valeurs arbitraires, ou de zéros), et ensuite modifier les valeurs qui sont dedans sans toucher à la taille. Garder en tête que tout ce qui modifie la taille d'un array numpy a une performance inférieur à un append sur une liste.
    je peux déterminer la taille de l'array numpy à la lecture de la base de données, ce n'est pas un problème.

  13. #13
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Citation Envoyé par Tchicken Voir le message
    Je pensais paralléliser la fonction CycleToutesCourses, cela vous semble-t-il possible ?
    Cette fonction en elle même ne prends pas beaucoup de temps. C'est plutot les 5 millions d'appels qui en prennent ! Donc ce serait plutot les appels qu'il faudrait paralléliser. Seul hic : je pense que chaque appel à cette fonction modifie qqch dans vos variables globales, modification qui semble nécéssaire au prochain appel de la fonction dans l'algorithme que vous présentez. Pour en être sûr il faudrait avoir une version plus propre sans variable globale. Et si on ne peut, ou que l'on peut mais de manière pénible et/ou lourde, une petite classe serait peut-être la bienvenue (mais cela fait peut etre aller trop loin pour vous).


    Citation Envoyé par Tchicken Voir le message
    je peux déterminer la taille de l'array numpy à la lecture de la base de données, ce n'est pas un problème.
    Hop, alors on y va !

    N'hésitez pas à poster votre nouveau code

  14. #14
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Bon j'attaque l'array numpy !!!
    j'ai du mal à visualiser la construction d'un array, alors je l'ai décomposé comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    # Chevaux
    Chx = np.array([0,0,0,0,99,99,99,99,99,99,99,99,99,99,99,99,99,99,0,0],int)
    # Une courses est constituée d'un maximum de 20 chevaux
    # Là je souhaiterai incrémenter la colonne 1 avec le numéro du cheval de 1 à 20
    Crs = np.repeat(Chx,20)
    # le Meeting de Vincennes 2017 comportait 560 courses attelées
    Mee = np.repeat(Crs,560)
    Là encore je ne suis pas sûr d'avoir construit mon array correctement, surtout lorsque je l'affiche j'obtiens un truc comme ça :
    0000000000000000000000000000
    0000000000000000000000000000
    0000000000000000000000000000
    ...etc
    Je ne visualise pas mes chevaux qui constituent une course, est-ce normal ?

  15. #15
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Non, avec repeat tu modifies la taille de ton array. Pas besoin de Chx ni de Crs, tout est contenu dans le tableau global !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import numpy as np
     
    ## 1er dim : les courses
    ## 2eme dim : les chevaux dans chaque course
    ## 3eme dim : les stats de chaque chevaux par course
    Mee = np.zeros((560,20,20), dtype=int)  ## On remplit de zéros pour démarrer
    Mee[:,:,0] = np.arange(1,20+1)
    Mee[:,:,4:-2] = 99
     
    print(Mee[0]) ### La premiere course donc
    Une petite digression de quelques jours pour se plonger dans un tuto numpy me parait indispensable. Tu peux commencer par
    https://ghajba.developpez.com/tutori...prendre-numpy/
    https://ghajba.developpez.com/tutori.../numpy-avance/
    https://python-scientific-lecture-no...nnee-polynome/
    dans cet ordre.

  16. #16
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Bonjour,

    Citation Envoyé par lg_53 Voir le message
    Mee[:,:,0] = np.arange(1,20+1)
    sur le np.arange, le paramètre "start" est à 1 et le "Stop" est à 20+1, ce 20+1 est-il écrit de cette manière juste pour la compréhension ou est-ce une nomenclature ?

    Et j'ai une autre question, sur les arrays :
    Dans la fonction "CycleToutesCourses", pour optimiser, je me demande si il ne serait pas plus judicieux d'éclater mon array en 3 :
    - Un array avec les données nécessaire au calcul
    - Un array plus petit où je charge seulement le résultat du calcul et peut-être que cela pourrait accélérer la partie tri
    - Un array avec les rapports pour le calcul financier

  17. #17
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Je l'ai écrit comme cela pour la compréhension. Car start est inclus, mais stop ne l'est pas. Donc si tu veux l'intervalle [1;20] bornes incluses, il faut écrire np.arange(1;21) donc.

    Donc oui on peut écrire 21, et pas 20+1. Ceci dit, tu auras certainement un peu plus tard des variables pour stocker les dimensions, plutôt que d'écrire les dimensions en dur. Et là le +1 prend son sens aussi dans le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Nb_chevaux = 20
    Mee[:,:,0] = np.arange(1,Nb_chevaux+1)

  18. #18
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Est-ce que travailler avec 3 arrays sous cette forme serait plus rapide :
    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
    # ###################################
    # Version 3 arrays #
    # ##################
    # Array BDD #
    # ###########
    Mee = np.zeros((560,20,16), dtype=int)  
    Mee[:,:,0] = np.arange(1,20+1)
    Mee[:,:,2] = 99
    # Mee[:,:,0]  Numéro
    # Mee[:,:,1]  Place à l'arrivée
    # Mee[:,:,2]  Classement ClaCX
    # Mee[:,:,3]  Classement ClaCRIFX
    # Mee[:,:,4]  Classement IndForme
    # Mee[:,:,5]  Classement ClaTMatic
    # Mee[:,:,6]  Classement ClaStatsCombi
    # Mee[:,:,7]  Classement ClasRD
    # Mee[:,:,8]  Classement ClasRE
    # Mee[:,:,9]  Classement ClaCote
    # Mee[:,:,10] Classement ClasCoefReussite
    # Mee[:,:,11] Classement ClaAR
    # Mee[:,:,12] Classement ClaOR
    # Mee[:,:,13] Classement ClaRub
    # Mee[:,:,14] Classement ClaHisto
    # Mee[:,:,15] Classement ClasStats
    # ##############
    # Array Calcul #
    # ##############
    Cal = np.zeros((20,2), dtype=int)  
    # Cal[:,:,0] Numéro
    # Cal[:,:,1] Classements cumulés indicés
    # ################
    # Array Rapports #
    # ################
    Rap= np.zeros((560,4), dtype=int)  
    # Rap[:,0] Z4
    # Rap[:,1] Couplé placé 12
    # Rap[:,2] Couplé placé 13
    # Rap[:,3] Couplé placé 23
    # ###################################
    Les arrays Mee et Rap seraient passées en paramètre à la fonction "CycleToutesCourses" avec les coëfficients
    L'array Cal serait local à la fonction "CycleToutesCourses"
    Je supprime les variables Globales et je passes mes données en paramètres.

  19. #19
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Oui ca peut le faire aussi. Il me semble que tu as des tris sur ton tableau entier et que le fait que tout soit dans XBasesX va te trié toute les colonnes selon 1 colonne (ou 2).

    Donc si tu peux te permettre de ne trier que 16 colonnes sur les 20 ok. Sinon autant tout laisser ensemble, car il faudra que tu récupère la clé de tri pour l'appliquer aussi sur les 2 autres petits tableaux. La problématique est la même avec des listes (Une grosse liste, ou alors une un peu plus petite et 2 petites à côté?) et donc tu peux aborder cette question de stockages sur ce que tu connais mieux.

    PS :
    Si Cal = np.zeros((20,2), dtype=int), cal est de dimension 2, donc tu ne peux pas écrire Cal[:,:,0] qui est une extraction d'un array de dimension 3.

  20. #20
    Membre confirmé Avatar de Tchicken
    Homme Profil pro
    Responsable d'exploitation informatique
    Inscrit en
    Août 2017
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Responsable d'exploitation informatique

    Informations forums :
    Inscription : Août 2017
    Messages : 108
    Par défaut
    Bonjour,

    je n'arrive pas trier mon tableau numpy :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # ##############
    # Array Calcul #
    # ##############
    # Cal = np.zeros((20,4), dtype=int)  
    # Cal[:,0] = np.arange(1,20+1) 
    # Cal[:,0] Numéro
    # Cal[:,1] Place à l'arrivée
    # Cal[:,2] Classements ClaTMatic
    # Cal[:,3] Classements cumulés indicés
    Cal = Cal[:, Cal[3].argsort(kind='mergesort')]
    quicksort est plus rapide dit-on, je cherche à trier ce tableau sur la colonne 3 sur valeur dégressive avec un pré-tri sur le colonne 2 sur valeur progressive.
    Le tri doit-il se faire sur l'array ou en affectant le résultat dans une autre variable ?

    D'avance merci.

Discussions similaires

  1. optimisation requête avec jointures externes
    Par beurtom dans le forum Oracle
    Réponses: 14
    Dernier message: 16/10/2006, 16h50
  2. [Optimisation] Requetes avec agregats et vue
    Par rad_hass dans le forum Décisions SGBD
    Réponses: 1
    Dernier message: 14/01/2006, 13h39
  3. Optimisation requete avec sous-requetes multiples
    Par gege.boubou dans le forum Requêtes
    Réponses: 3
    Dernier message: 08/09/2005, 10h42
  4. optimisation requetes avec base de données
    Par flogreg dans le forum Décisions SGBD
    Réponses: 9
    Dernier message: 05/07/2005, 14h54
  5. Optimisation HTML avec Tomcat
    Par zekey dans le forum Tomcat et TomEE
    Réponses: 2
    Dernier message: 23/03/2005, 12h24

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