Bonjour,
J'ai développé un outil pour générer des jeux de donnés fictives en python, l'objectif étant d'avoir des bases avec des données proches de ce qu'on pourrait trouver en prod en terme de volumétrie et respect de règles fonctionnelles mais avec des données complètement bidon pour ne pas avoir à me préoccuper des questions de confidentialité extrêmement présents dans ma branche. En outre, je n'ai pas besoin d'existant. Je peux créer des bases pour une application complètement nouvelle.
J'ai construit cet outil au fur et à mesure de mes propres besoins et il commence à devenir suffisamment intéressant pour que j'essaie de l'optimiser un peu.
L'idée de départ était d'avoir une moulinette très hautement paramétrable pour permettre une adaptation à toutes sortes de situations.
Présentation rapide du fonctionnement
Je paramètre la structure d'alimentation de la base sous forme d'une arborescence yaml et, pour chaque table, je crée un fichier texte qui contient (en gros) des fonctions que le moteur appelle. Au démarrage de l'application, le mapping entre les champs et les fonctions est stocké dans des objets/dictionnaires (correspondant aux entités) puis le programme consiste en une imbrication de boucles qui décrivent l'arborescence paramétrée et appels des fonctions d'alimentation des champs.
Les données de référence (valeurs et probabilités, principalement) sont stockées dans une ou plusieurs bases SQLite et les données de sorties sont également écrites dans une ou plusieurs bases SQLite (il est possible d'avoir des bases de travail pour écrire des données intermédiaires mais non souhaitées dans le résultat).
Je peux facilement intégrer du hasard et des probabilités d'apparitions de valeurs respectant éventuellement des distributions plus ou moins gaussiennes.
Tout cela fonctionne pas mal et, au final, pour créer une nouvelle base, c'est relativement rapide. Le gros du travail est dans le paramétrage. Je n'ai pas ou très peu de code à écrire (juste les fonctions qui manquent pour des cas spécifiques non encore rencontrés) et ça tourne bien.
Sauf qu'en terme de performance, je pense qu'il y a pas mal de marge de progression (je génère environ 100 Mo/h).
La question
Je serais intéressé par quelques avis d'orientation pour améliorer tout cela le plus efficacement possible avant de me lancer dans des expérimentations éventuellement très lourdes en terme d'investissement pour un retour nul ou quasi nul.
Quelques réflexions :
- Serait-il pertinent d'essayer de passer cette base de code en Cython. J'ai peur que ce soit difficile pour un résultat incertain car l'application est très dynamique : les fonctions appelées pour alimenter les champs des tables sont écrites dans les fichiers de paramétrage et les branchements se font à l'initialisation et à l'exécution, avec parfois un peu d'"exec" (la sécurité n'est pas une priorité : cette moulinette n'est pas censée être utilisée autrement que sur mon poste local). D'un autre côté, certaines fonctions ont des variables qui pourraient sans doute être typées de façon statiques. Je ne connais pas cython et en particulier le niveau de modification de code que cela implique pour un gain intéressant et j'ai donc du mal à mesurer l'intérêt de cette solution qui me semble la plus réaliste en terme de réécriture (j'avais pensé à tout réécrire en Rust mais là, le réalisme m'amène à penser que la marche est vraiment haute)
- multiprocessing ? multithreading ? Ces options sont certainement prometteuses... La génération est un enchevêtrement de boucles qui décrit une arborescence. Les boucles filles sont, bien sûr, dépendantes de leurs parents mais à la racine, chaque tour est indépendant du précédent. On pourrait tout à fait scinder la génération en autant de morceaux à traiter en parallèle. Les questions sont relatives au fait que la (ou les) base(s) de référence (qui contient les valeurs et les probabilités à respecter), et la base de sortie sont, du coup, à partager entre les processus ou les threads. Est-ce une limite ? Faut-il prévoir une écriture dans autant de bases que de process/thread lancés avec réconciliation à la fin ? Quel est le plus adapté ? Intuitivement, je dirais que le multiprocessing serait bien pour éclater la boucle racine et le multithreading pour les écritures en base mais je ne vois pas trop comment concilier les deux. En fait, je ne vois pas très bien comment utiliser le multithreading dans ce cas...
Merci pour vos éclairages.







Répondre avec citation
Partager