A la question des décors

Alice: Pourquoi cet endroit est-il un endroit TRÈS bizarre?
Dodo: Mais parce que tous les autres endroits ne sont pas très étranges. Il doit y avoir au moins un endroit TRÈS étrange.



Ainsi, nous allons considérer le texte de la classe de modèle BitSet afin de l'adapter aux exigences de MK, les principaux domaines d'optimisation ont été identifiés précédemment. Vous pouvez bien sûr écrire votre propre classe à partir de zéro, mais ne négligeons pas l'occasion de vous familiariser avec de bonnes solutions, car la bibliothèque STL (à ne pas confondre avec spl) est connue depuis longtemps et est utilisée partout. Vous devez d'abord trouver le code source, après un court voyage sur Internet, je viens d'ouvrir le répertoire avec mon MinGW et j'ai cherché le fichier requis, que j'ai l'intention de discuter plus avant.

Au début, nous voyons un peu plus qu'une page avec le droit d'auteur et le texte de la licence, ainsi qu'une variété d'avertissements - eh bien, vous pouvez ignorer cela, c'est pour les avocats. La prochaine étape comprend et je remercie les auteurs - chacun est accompagné d'un commentaire sur ce que nous voulons obtenir de chaque fichier - tout le monde le ferait, le travail du chercheur d' Indiana Jones serait plus facile. Définit venir ensuite, je ne suis pas un fanatique de la propreté du code et ne discuterai pas - laissez-les et voyez immédiatement la définition du nombre de bits dans l'unité de stockage, qui est une longue non signée. Poursuivant sur le chemin glissant de Defines, je cherche ma propre définition d'unité de stockage, qui est égale à uint8_fast, bien que je comprenne que ce ne sera clairement pas suffisant. Soit dit en passant, je remercie immédiatement les auteurs pour le style - à la fin du module, ils nettoient après eux-mêmes, détruisant les définitions internes - je n'espérais même pas que je verrais une telle chose dans la production moderne.

Et ici, de légers malentendus commencent - au début, ils définissent le modèle de structure auxiliaire struct _Base_bitset (pourquoi exactement la structure, et non la classe, car ils sont interchangeables), dans lequel ils déterminent le type de stockage des données et il est à nouveau spécifié directement - nous le changeons pour le nôtre. Ensuite, deux instances de cette structure auxiliaire sont déterminées - pour une unité de stockage (enfin, cela est compréhensible, pour augmenter l'efficacité du code) et pour un exemple dégénéré sans stockage du tout (probablement pour la gestion des erreurs), jusqu'à ce que nous touchions ce code.

Ensuite, nous trouvons le jeu de bits de la classe principale (maintenant la classe), dérivé de la structure du modèle (oui, en C, c'est possible, il n'est pas clair pourquoi faire cela, mais c'est possible), dans lequel le type de stockage est à nouveau déterminé et changé à nouveau pour le nôtre. Mais ici, je me suis demandé - pourquoi le type n'était pas hérité de la structure parente et était étonné de constater (oui avec étonnement, les classes de modèles - ce n'est pas ce que je fais dans la vie quotidienne) que l'héritage des classes de modèles est quelque peu différent des classes ordinaires. Autrement dit, l'héritage est exactement le même et toutes les entités du parent sont disponibles dans la classe fille (j'espère que personne ne me prendra pour un chauviniste de genre), mais elles doivent être indiquées avec un nom complet. Eh bien, cela peut encore être compris, sinon le compilateur ne devinera pas laquelle des options instanciées appliquer, mais si vous utilisez les types définis dans la classe, vous devez toujours ajouter le mot clé typename. Ce n'est pas une solution évidente, j'étais particulièrement satisfait des diagnostics du compilateur, ce qui signifie "peut-être que vous aviez un type de la classe parent" - oui, merci, capitaine, c'est ce que je voulais dire, je suis content que nous nous comprenions, mais ça ne devient pas plus facile pour moi. Cependant, un tel diagnostic a lieu dans l'ancienne version de GCC, dans le message actuel, il devient plus compréhensible "vous avez peut-être oublié le mot-clé du nom de type". La compréhension des messages de diagnostic du compilateur pour les classes de modèles est généralement un sujet pour une chanson distincte, et cette chanson n'est en aucun cas joyeuse.

Donc, si nous ne voulons pas utiliser constamment la construction dans la classe enfant

std::_Base_bitset<Nb>::_WordT 

(et nous ne voulons pas, neg?) alors nous avons trois façons

1) trivial

 #define _WordT typename _Base_bitset<_Nb>::_WordT 

2) évidente - définition du type au niveau mondial et je l'ai aimé plus

3) pour les fans d'étranges

 typedef typename _Base_bitset<_Nb>::_WordT _WordT; 

(sensation persistante d'une magnifique béquille chromée). Personnellement, dans les trois cas, je ne suis pas satisfait de l'indication directe de la classe parent, car l'implémentation ne doit pas prendre en compte la chaîne d'héritage, mais peut-être que je ne comprends pas quelque chose.

Il existe également une méthode 4)

  using _WordT = std::_Base_bitset<Nb>::_WordT; 

mais dans ma version du compilateur, cela ne fonctionne pas, ce n'est donc pas le cas.

Note sur les marges (PNP) - dans le merveilleux livre "C ++ pour les vrais programmeurs" (je l'ai téléchargé par erreur à l'époque, décidant qu'il était consacré au C ++ dans les systèmes en temps réel), il dit à propos du langage "Son élégance n'est en aucun cas dans la simplicité (les mots C ++ et la simplicité coupent l'oreille avec son apparente contradiction), mais dans ses capacités potentielles. Pour chaque vilain problème, il y a une sorte d'idiome intelligent, une feinte de langage élégante, grâce à laquelle le problème fond juste sous nos yeux. » Étant donné que le livre est vraiment merveilleux, cela signifie que la citation ci-dessus est correcte (je comprends qu'il y a un certain tronçon logique ici), mais personnellement, malheureusement, je vois le problème, mais il y a une souche avec un idiome intelligent.

Malgré le sentiment de perplexité qui subsiste, le problème est résolu et vous pouvez apprécier le résultat - mais il n’était pas là. Lors du redimensionnement de l'ensemble de test de 15 à 5, une erreur de compilation s'est produite de manière complètement inattendue. Non, tout est clair, une instanciation non modifiée avec le paramètre de modèle 1 a fonctionné, mais de l'extérieur, cela semble très étrange - nous changeons la constante et le programme cesse de compiler.

Vous pouvez, bien sûr, modifier cette implémentation et une autre, mais une telle approche ressemble à une violation flagrante du principe DRY. Il existe des solutions possibles, et il y en a plusieurs 1) l'évidence - encore une fois la définition et 2) la trivialité - encore une fois la définition de type au niveau global, mais dans ce cas, elle sortira du module, ce qu'elle fait cependant dans la mise en œuvre considérée, dépassant de la structure de base , mais aussi le qualificatif n'aura pas besoin d'être écrit.

Par conséquent, je suis enclin à l'option 3) la classe de base pour déterminer le type et l'héritage de toutes les instances. De plus, les entités de la classe parente sont protégées, là encore, afin qu'elles ne dépassent pas. Ensuite, je découvre une propriété amusante, C ++ devrait probablement être loué pour sa flexibilité - pour une variante sans stockage, le type n'est pas nécessaire et le langage permet à l'implémentation d'être utilisée sans héritage, bien que ce ne soit pas particulièrement nécessaire dans ce cas particulier. Immédiatement, je découvre un autre inconvénient de la bibliothèque - dans les trois variantes, les opérations de calcul du numéro d'unité de stockage sont définies à chaque fois

 static size_t _S_whichword 

et les masques de bits qu'il contient _S_maskbit et ils sont complètement identiques - nous les déplaçons également vers la classe de base. Dans ce cas, un code "mort" est détecté - la méthode _S_whichbyte, je ne sais même pas quoi faire - d'une part, de bonnes règles de tonalité nécessitent sa suppression, d'autre part - dans le cas d'un modèle, cela n'affecte pas le code résultant. J'utiliserai la règle - "ne comprends pas quelque chose - ne touche pas" et laisse cette méthode.

En principe, les modifications en termes de type de stockage sont terminées et vous pouvez commencer les tests. Et immédiatement, je découvre un manque d'implémentation - pour l'architecture MSP430, pour une raison quelconque, des mots de deux octets sont alloués au lieu d'octets. Bien sûr, pas des mots doubles, comme avant, mais nous nous battons toujours pour le code minimum (dans tous les sens). Il s'avère que le compilateur est sûr que dans cette architecture, le type uint_fast8_t a la taille 2, bien que le système de commande ait des opérations d'octets et que le compilateur lui-même les utilise complètement. L'idée d'utiliser ce type est compromise et vous devez définir directement le type de données uint8_t. Eh bien, s'il existe une architecture dans laquelle le travail avec des octets échoue et que la taille du type uint_fast8_t est différente de 1 (aucune de celles disponibles dans le compilateur), elle devra souffrir de la vitesse de tous les autres.

Le test de la version corrigée montre son bon fonctionnement sur diverses architectures et avec différents paramètres, mais il y a toujours une question avec le calcul des masques de bits pour MK sans décalage développé, dans notre cas, c'est MSP430 et AVR. En principe, vous pouvez simplement faire de la lecture du masque binaire à partir du tableau une méthode pour tous les cas, quelle que soit l'architecture MK. La solution fonctionne plutôt bien, dans les architectures développées avec indexation, tout va bien, mais il y aura toujours une perte de temps par rapport aux changements rapides, et je ne voudrais pas me fourrer les doigts avec les mots "la classe sera plus rapide, dit-il, nous allons optimiser, dit-il. "

Par conséquent, nous avons besoin d'une implémentation différente pour nos deux architectures faibles, qui diffèrent de toutes les autres par la taille du type uint_fast16_t - c'est 2 contre 4, ou 8 pour les versions 64 bits. La compilation conditionnelle ne nous aidera pas, mais fixer la condition dans l'espoir d'une optimisation n'est pas la façon du samouraï, les schémas restent. J'essaie de créer une méthode de modèle de la classe, mais une spécialisation partielle n'est pas disponible pour elle - il s'avère que cela devrait être le cas et même trouver un article qui explique pourquoi c'est une bonne solution. Honnêtement, je ne comprenais toujours pas pourquoi cette décision est bonne, très probablement, cela est dû à des considérations religieuses. Néanmoins, on ne nous a pas demandé, et ce qui reste à faire - vous pouvez faire une fonction de modèle amicale (il s'est avéré que c'est impossible, et pour la même raison - la spécialisation partielle est interdite), il existe une autre solution, ça a l'air drôle - la spécialisation partielle de la méthode en dehors du corps de la classe. Vous pouvez même créer une fonction de modèle distincte et y accéder à partir des méthodes de classe, je ne sais pas quelle méthode est la plus correcte, j'ai choisi celle-ci. Oui, elle n’a pas accès aux entités internes de la classe, mais dans ce cas particulier il n’est pas nécessaire, que faire si nécessaire, je ne sais pas encore, "nous allons résoudre les problèmes au fur et à mesure de leur apparition".

Maintenant que nous avons tout ce dont nous avons besoin, nous collectons les fragments testés ensemble et obtenons le code qui résout le problème d'optimisation initial. Dans le même temps, nous avons rendu le code plus compact (ce qui signifie plus compréhensible, bien que ce dernier soit discutable), supprimé de nombreuses répétitions et éliminé les entités redondantes qui dépassaient. La seule chose qui n'est pas fixe est un mélange de structures et de classes, mais ici je ne comprends pas les raisons d'une telle implémentation, donc, au cas où, je ne toucherai pas à cette partie.

Un deuxième article sera consacré à la mise en œuvre de la deuxième version de l'ensemble, et sans cela quelque chose a beaucoup fonctionné.

Source: https://habr.com/ru/post/fr450176/


All Articles