Parfois, vous avez juste des algorithmes qui ne peuvent pas être meilleurs que le temps linéaire pour lesquels il existe toujours une forte demande de performances.
Un exemple est le traitement vidéo où vous ne pouvez pas rendre une image / image plus claire en tant qu'exemple de base sans parcourir en boucle chaque pixel (eh bien, je suppose que vous pouvez utiliser une sorte de structure hiérarchique indiquant les propriétés héritées par les enfants qui finissent par descendre dans des mosaïques d'image. pour les nœuds feuilles, mais vous reporteriez alors un coût plus élevé de boucle sur chaque pixel pour le rendu et le code serait probablement plus difficile à maintenir que même le filtre d'image le plus micro-optimisé).
Il y a beaucoup de cas comme ça dans mon domaine. J'ai tendance à faire plus de boucles de complexité linéaire qui doivent tout toucher ou tout lire que celles qui bénéficient de tout type de structure de données ou d'algorithme sophistiqué. Il n'y a pas de travail qui peut être sauté quand tout doit être touché. Donc, à ce stade, si vous êtes inévitablement confronté à une complexité linéaire, vous devez rendre le travail effectué par itération moins cher et moins cher.
Donc, dans mon cas, les optimisations les plus importantes et les plus courantes sont souvent les représentations de données et les dispositions de mémoire, le multithreading et le SIMD (généralement dans cet ordre, la représentation des données étant la plus importante, car elle affecte la possibilité d'effectuer les deux dernières). Je ne rencontre pas tant de problèmes qui sont résolus par des arbres, des tables de hachage, des algorithmes de tri, etc. Mon code quotidien est plus dans la veine de "pour chaque chose, faire quelque chose."
Bien sûr, c’est un autre cas à prendre en compte lorsque des optimisations sont nécessaires (et plus important encore, lorsqu'elles ne le sont pas), micro ou algorithmiques. Mais dans mon cas particulier, si un chemin d’exécution critique doit être optimisé, les gains de vitesse 10x + sont souvent obtenus par des optimisations au niveau micro, telles que le multithreading, le SIMD et la réorganisation des configurations de mémoire et des modèles d’accès pour améliorer la localité de référence. Par exemple, il m'est moins fréquent de remplacer un type de bulle par un type introsort, un type de base ou une détection de collision à complexité quadratique par un BVH, que de rechercher des points chauds bénéficiant, par exemple, du découpage en champs chaud / froid.
Maintenant, dans mon cas, mon domaine est tellement critique en termes de performances (lancer de rayons, moteurs physiques, etc.) qu’un traceur de rayons lent mais parfaitement correct nécessitant 10 heures pour restituer une image est souvent considéré comme inutile ou plus qu'un rapide qui est complètement interactif, mais génère les images les plus laides avec des rayons qui fuient partout en raison de l'absence d'intersection rayon / tri étanche. La vitesse est sans doute la principale mesure de qualité d'un tel logiciel, sans doute même plus que la correction à un moment donné (puisque la "correction" est une idée floue avec le lancer de rayons puisque tout se rapproche, du moment qu'il ne s'écrase pas ou quelque chose comme ça). Et lorsque c'est le cas, si je ne pense pas à l'efficacité dès le départ, je me trouve dans l'obligation de modifier le code au niveau de conception le plus coûteux pour gérer des conceptions plus efficaces. Donc si je ne le fais pas
Le jeu est un autre domaine similaire au mien. Peu importe si votre logique de jeu est correcte ou si votre base de code est maintenable et brillamment conçue, si votre jeu fonctionne à 1 image par seconde, comme un diaporama. Dans certains domaines, le manque de rapidité pourrait en réalité rendre l'application inutilisable pour ses utilisateurs. Contrairement aux jeux, il n'existe pas de métrique "assez bonne" dans des domaines tels que le lancer de rayons. Les utilisateurs veulent toujours plus de vitesse et la concurrence industrielle cherche principalement à trouver des solutions plus rapides. Ce ne sera jamais assez bon jusqu'à ce que ce soit en temps réel, à quel moment les jeux utiliseront des traceurs de chemins. Et puis, cela ne sera probablement toujours pas suffisant pour les effets spéciaux, car les artistes voudront peut-être charger des milliards de polygones et effectuer des simulations de particules avec auto-collision entre des milliards de particules à plus de 30 FPS.
Maintenant, si cela peut vous rassurer, malgré tout, j’écris environ 90% du code dans un langage de script (Lua) sans me soucier de la performance. Mais j'ai une quantité inhabituellement importante de code qui doit parcourir des millions, voire des milliards de choses, et lorsque vous passez en revue des millions, voire des milliards de choses, vous commencez à remarquer une différence épique entre le code naïf à un seul thread: invoque un cache cache à chaque itération, par opposition à un code vectorisé s'exécutant en parallèle et accédant à des blocs contigus dans lesquels aucune donnée non pertinente n'est chargée dans une ligne de cache.