Partage
  • Partager sur Facebook
  • Partager sur Twitter

Une bonne gestion de la mémoire ?

    24 mars 2021 à 11:36:24

    Bonjour à tous !

    Cela fait bientôt une petite dizaine d'années que je me fais la main en programmation avec les langages.NET ; jusqu'ici, je ne m'étais réellement attardé que sur l'aspect logique de la programmation, une approche très calculatoire, en somme. Cependant, il y peu, j'ai réalisé qu'il était plus que capital d'également l'aborder sous un angle technique (vaut mieux tard que jamais). Écrire un programme qui fonctionne, c'est bien, le rendre efficace, rapide, et peu gourmand en ressources, c'est encore mieux, sachant que les machines ont toutes des besoins, des architectures et spécificités différentes.

    Par exemple, je n'ai jamais vraiment saisi l'intérêt pratique des buffers. Comme j'en utilise moi-même, je sais parfaitement l'utilisation qu'on en fait, à savoir stocker temporairement un bloc de données, mais pourquoi ?

    Admet-on que j'ai deux flux de données et que je veuille copier une certaine portion de l'un dans l'autre. Je peux utiliser un buffer mais je pourrais aussi naïvement faire transiter un octet à la fois directement d'un flux vers l'autre en utilisant une boucle. Pourquoi cette dernière méthode n'est-elle pas la plus efficace ? Allouer de la mémoire à un buffer en utilise forcément, et l'étape supplémentaire de la mise en mémoire tampon devrait également être coûteuse en terme de temps.

    Alors, bien sûr, je suppose très largement qu'en réalité, le buffer est la solution la plus efficace, ce sont simplement les raisons qui m’échappent ! J'ai vu quelque part qu'ils pouvaient s'avérer très utiles lorsqu'il est fait état d'une différence de rythme de travail entre les flux ; ici encore, pourquoi ? Et quid de deux flux travailleraient à la même cadence ?

    J'ai une autre interrogation concernant le passage par référence. Imaginons d'abord que mon programme contienne une classe, bien goulue, qui me pompe beaucoup de mémoire. Ma classe comprend une propriété d’accès pour venir bidouiller une de ses variables lorsqu'elle est instanciée. Si, dans une méthode, j'entre en paramètre "classe.propriété" en passant par valeur, qu'est-ce qui sera copié dans la mémoire ? La variable de la classe, ou la classe ENTIÈRE ?

    Maintenant, si j’effectue le passage par référence, quand, exactement, ma classe est elle susceptible d'être modifiée ? Uniquement lorsque je lui attribut une valeur, ou également lorsque j'essaye d'y avoir accès et/ou d'effectuer des opérations de comparaison ? Bien sûr, je ne parle ici que du .NET, je sais qu'en C++ par exemple, il faut faire gaffe lors d'un passage par référence, mais je suis infoutu de savoir si l'utilisation est sensiblement la même ou si elle diffère d'un langage à l'autre. Du coup, j'évite au maximum le passage par référence par peur de faire une bourde, mais j'ai aussi l'impression que l’efficacité de mes programme en pâtit...

    Voilà voilà, merci par avance pour votre temps. J'ai beau chercher, j'avoue que la situation ne s’éclaircit pas beaucoup :lol:

    • Partager sur Facebook
    • Partager sur Twitter
      24 mars 2021 à 21:05:54

      L'utilisation de .NET incite à ne pas trop prendre en compte les problématiques d'optimisation mémoire car le framework .NET fait un fabuleux travail (la plupart du temps) pour que cela ne soit pas un problème.

      Donc, dans le contexte .NET, travailler ces problématiques est encore plus ardu qu'en C++ natifs ou en C où vous avez à faire ce travail vous-même, de zéro (ou presque).

      Donc, réfléchissez vraiment à 2 fois avant d'incriminer une mauvaise gestion de la mémoire pour des problèmes de performance.

      >à savoir stocker temporairement un bloc de données, mais pourquoi ?

      Bin, parce qu'on peut avoir à stocker temporairement des données, en fonction d'un algorithme.

      C'est un peu comme "Pourquoi la fourchette ?", il n'y pas de pourquoi, c'est juste plus pratique qu'une cuillère ou ces doigts, pour certain type de plats.

      >j'ai deux flux de données et que je veuille copier une certaine portion de l'un dans l'autre.

      Cela veut dire qu'il y a déjà un bon paquet de "buffer" dans la boucle. Un flux de données ne se génère pas ex-nihilo.

      Mais un simple appuie sur une touche utilise déjà un ou plusieurs buffers d'au moins un octet (il y a au moins 120 "scan code" de touches donc encodé sur un octet), plus le mask des modificateurs de touches (Maj, Alt, ...), ça fait au moins un deuxième octet. Et ces 2 octets minimum vont remoter la pile des drivers de claviers pour déclencher une cascade d'autres événements (transcodage d'un scan code en un code ASCII, déclenchement des hooks de claviers). Les modules à l'écoute de ces événements vont stockés ces informations dans leur propre buffer, pour, par exemple, convertir une combinaison de touche comme "Alt+des chiffres" en un caractère, etc...

      En bref, votre "Flux" en entré est déjà un bon  gros putain de buffer sa mère.

      >Je peux utiliser un buffer mais je pourrais aussi naïvement faire transiter un octet à la fois directement d'un flux vers l'autre en utilisant une boucle.

      Le flux en sortie, s'il n'est pas programmer avec des moufles est déjà un buffer.

      Il existe en programmation systèmes, des flux spécifiques et des options spécifiques, pour bufferiser ou pas les flux.

      Donc, votre boucle, à par gâcher des ressources CPU car vous faites des copies octet par octet plutôt que d'utiliser tous les mécanismes d'optimisation de transfert de mémoire, comme les lignes de caches, les instructions vectorisées du CPU, du DMA (Direct Memory Access) en cas de dialogue avec un périphérique, etc..., elle vous fait "gagner" quoi ? De la lisibilité du code, c'est assez peu probable, et vous devez faire confiance à l'optimiseur/compilateur pour que "bouboucle" soit transformée en un truc qui plombe pas trop les performances ? Vous avez votre brevet de cornac de compilateur ?

      >Pourquoi cette dernière méthode n'est-elle pas la plus efficace ?

      Souvent, parce que vous ne permettez pas aux outils que vous utilisez d'avoir la latitude pour prendre la meilleur option.

      Mais en terme de performance, il faut toujours mesurer. Mais il est assez peu probable qu'une boucle soit la meilleur des solutions, quand on n'a le choix.

      Par exemple, si, dans votre boucle, vous faites un appel système, vous allez "payer" plusieurs milliers de cycles CPU à chaque itération alors que si vous passer un buffer dans un seul appel système, vous ne payerez ces plusieurs milliers de cycles CPU qu'une seule fois.

      >Allouer de la mémoire à un buffer en utilise forcément,

      Utilise quoi ?

      Si c'est de la mémoire, oui, mais vous avez le contrôle de l'espace que cela occupe. En .NET, c'est un peu complexe de maîtriser la chose, mais si vous suivez bien les recommandations de codage (utilisation de l'instruction using, gestion strict de l'enregistrement sur des event statics, ...), le garbage-collector et le runtime .NET en général fait du très bon boulot. Allouer un "gros" bloc pour votre buffer prend moins de place et utilise moins de CPU que plein de petits "blocs" que votre "bouboucle" utilisera implicitement par passage de paramètre ou instanciation de variable locale à votre boucle.

      Si c'est en terme de CPU, il y a tellement de techniques d'optimisation des allocations et autres instanciations que ce n'est pas des buffers qui feront des dégâts, bien au contraire. Une boucle, avec ces variables locales de boucle est bien plus solicitatrice qu'un buffer utilisable dans tous les tours de boucle, etc...

      Un buffer, ça fait pas gagner à tous les coups des performances, mais c'est extrêmement rare que ça en face perdre, par rapport à une "boubucle".

      Sur architecture x86, l'allocation de toutes les variables locales "simple" peut se faire en un cycle CPU, car c'est la pile d'appel qui est utilisé. Donc 1 buffer de taille fixe de 64Ko ou 1 variable locale de 1 octet, c'est le même "prix".

      >et l'étape supplémentaire de la mise en mémoire tampon devrait également être coûteuse en terme de temps.

      Bin non, car on le fait en une fois plutôt que de le faire, le refaire et encore le refaire ..., à chaque tour de boucle, etc...

      >le buffer est la solution la plus efficace

      Très souvent, oui. Mais en termes de performance, rien ne vaut la mesure.

      >ce sont simplement les raisons qui m’échappent !

      Donnez-nous un cas concret, qu'on vous explique.

      >lorsqu'il est fait état d'une différence de rythme de travail entre les flux ; ici encore, pourquoi ?

      Pouvez-vous être plus précis ?

      C'est plus une obligation qu'un choix de performance.

      Si vous avez 2 flux à vitesse différents, vous DEVEZ bufferiser.

      >Et quid de deux flux travailleraient à la même cadence ?

      Cela n'arrive que si c'est le même flux, donc, il y a un flux de trop.

      >mon programme contienne une classe

      Un "programme", un exécutable ou un processus ne contient pas de classe. C'est juste le compilateur qui se sert de ce modèle pour générer du code. Après compilation, il n'existe plus de "classe".

      >bien goulue, qui me pompe beaucoup de mémoire.

      On va plutôt dire qu'une instance utilise beaucoup de mémoire ( mais laquelle, registre ? cache CPU ? mémoire centrale ? mémoire de masse ? le tas des objets volumineux .NET ? etc...).

      >"classe.propriété" en passant par valeur, qu'est-ce qui sera copié dans la mémoire ?

      C'est fonction du type de la propriété, si c'est un type "ValueType" ou un type "ReferenceType", le comportement ne sera pas le même.

      >La variable de la classe

      Cela ne veut absolument rien dire.

      Ne confondez-vous pas classe et instance ???

      >ou la classe ENTIÈRE ?

      Si c'est une variable de type "classe" et non une "struct", c'est généralement des type "ReferenceType" et c'est donc une copie de la référence qui est "copiée" et non pas l'objet lui-même. Et une référence, c'est 4 ou 8 bits, c'est pas très "goulue".

      >ma classe est elle susceptible d'être modifiée ?

      Classe ??? C'est pas plutôt instance ?

      Pourquoi voulez-vous que votre instance soit modifiée ? Copier une référence ne modifie pas l'objet.

      >Uniquement lorsque je lui attribut une valeur

      A qui ? A l'instance ? à la propriété de l'instance ? à la référence que vous avez passée en paramètre ?

      >ou également lorsque j'essaye d'y avoir accès et/ou d'effectuer des opérations de comparaison ?

      Si des opérateurs d'accès ou de comparaison modifient des trucs dans les instances, faudrait revoir votre Design. (Si c'est un mécanisme de "Lazy Loading", c'est fait exprès et c'est justement fait pour réduire la charge CPU et mémoire du bidule)

      >je sais qu'en C++ par exemple, il faut faire gaffe lors d'un passage par référence

      Dans les faits, il y a au moins une dizaine de manière de passer des paramètres et LES passages par références font partie, sans aucun doute, des plus "safe". Mais en C++, faut réfléchir un peu au type de passage de paramètre. Sinon, ça se paye "cash'. .NET est plus "souple" mais c'est les performances qui s'en ressent (boxing, encombrement mémoire, etc...).

      >si l'utilisation est sensiblement la même ou si elle diffère d'un langage à l'autre.

      Chaque framework (et pas langage) est différent. En C++/CLI, c'est différent du C++ natif et c'est aussi différent des langages .NET (qui sont plutôt comparables entre eux, C++/CLI n'est pas un langage ".NET CLS").

      >Du coup, j'évite au maximum le passage par référence par peur de faire une bourde

      Ridicule, vous devez maîtriser ces notions FONDAMENTALES.

      >mais j'ai aussi l'impression que l’efficacité de mes programme en pâtit

      Moi, je m’inquiéterai plutôt de la maintenabilité d'un machin qui fait nimportnawak en terme de passages de paramètre.

      • Partager sur Facebook
      • Partager sur Twitter
      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.

      Une bonne gestion de la mémoire ?

      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
      × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
      • Editeur
      • Markdown