Fonctions virtuelles dans les microcontrôleurs - le côté obscur

La situation est-elle familière lorsque la place sur le flash est épuisée et que vous devez pousser le non comestible, sacrifier quelque chose dont vous avez besoin? Essayons plutôt de sacrifier inutile, il se cache dans des endroits plutôt inattendus.

Je voulais faire un serveur telnet pour contrôler divers équipements sur le module WIZnet W5500 populaire et peu coûteux. Tout ce qui est nécessaire pour cela fait partie de la bibliothèque standard Arduino, le résultat peut être trouvé ici . Mais ce n'est pas à propos de lui. La première chose qui m'a vraiment surpris, c'est que ce code de fonctionnalité simple occupait plus de la moitié du flash ATmega328P. Bien sûr, il y a beaucoup de code dans la bibliothèque Ethernet, mais tout n'est pas utilisé, le compilateur doit jeter le code inutilisé du micrologiciel assemblé. Vérifiez si tel est le cas.

Nous allons dans le répertoire où l'assemblage a lieu - le chemin d'accès à celui-ci peut être vu dans les messages de compilation, et faisons objdump -t <fichier de firmware elf> pour obtenir la table des caractères. Nous y voyons beaucoup de fonctions liées à Ethernet, y compris celles dont le besoin n'est pas évident, par exemple, des fonctions pour travailler avec UDP. Autrement dit, il semble que la suppression des fonctions inutiles ne s'est pas produite. Quelle est la question?

La réponse peut sembler inattendue - le tout est l'héritage de classes qui implémentent Ethernet à partir de classes de base avec de nombreuses fonctions virtuelles. Le compilateur considère qu'une fonction (ou méthode de classe) est utilisée lorsqu'il existe des liens vers elle à d'autres endroits du code. Mais pour créer un tel lien, il n'est pas nécessaire d'appeler la fonction. Il suffit de sauvegarder son adresse. Même si nous ne le faisons pas explicitement, C ++ le fait pour nous en plaçant un pointeur sur une fonction dans la table des fonctions virtuelles. Même si nous n'utilisons jamais cette fonction virtuelle, elle sera présente dans le firmware. Si une fonction est définie dans la classe de base comme pure virtuelle (sans implémentation), alors nous n'avons pas d'autre option que de l'implémenter, même si nous n'en avons pas du tout besoin, augmentant ainsi la taille du code du firmware.

Nous vérifions l'exactitude de notre hypothèse. Prenez la bibliothèque Ethernet du github, par exemple ici , afin de ne pas toucher à la norme, et modifiez-la. Nous supprimons l'héritage et nous créons des fonctions virtuelles simplement par des méthodes. Comment le faire avec soin, de manière réversible, vous pouvez voir ici . Résultat: la taille du code a diminué de 4460 octets, soit plus du quart de la taille d'origine.

Bien sûr, l'héritage et les fonctions virtuelles sont utiles. Cependant, la création d'une classe de base avec de simples fonctions virtuelles juste pour définir une interface pour les implémentations suivantes n'est pas toujours justifiée. Vous devez d'abord vous assurer que vous utiliserez vraiment cette interface avec des objets de différents types, sinon les fonctionnalités implémentées dans la classe de base (comme dans la classe Print) vous seront utiles.

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


All Articles