Fonction en ligne v. Macro en C – Quel est le surcoût (mémoire / vitesse)?

J’ai cherché Stack Overflow pour connaître les avantages / inconvénients des macros de type fonction par rapport aux fonctions inline.

J’ai trouvé la discussion suivante: Avantages et inconvénients de différentes méthodes de fonction macro / inline en C

… mais cela n’a pas répondu à ma principale question brûlante.

À savoir, quel est le surcoût en c d’utiliser une fonction macro (avec des variables, éventuellement d’autres appels de fonction) v. Une fonction en ligne, en termes d’utilisation de mémoire et de vitesse d’exécution?

Existe-t-il des différences de surcharge liées au compilateur? J’ai à la fois icc et gcc à ma disposition.

Mon extrait de code que je modularise est le suivant:

double AttractiveTerm = pow(SigmaSquared/RadialDistanceSquared,3); double RepulsiveTerm = AttractiveTerm * AttractiveTerm; EnergyConsortingbution += 4 * Epsilon * (RepulsiveTerm - AttractiveTerm); 

La raison pour laquelle je l’ai convertie en fonction / macro en ligne est que je peux le déposer dans un fichier ac, puis comstackr conditionnellement d’autres fonctions / macros similaires, mais légèrement différentes.

par exemple:

 double AttractiveTerm = pow(SigmaSquared/RadialDistanceSquared,3); double RepulsiveTerm = pow(SigmaSquared/RadialDistanceSquared,9); EnergyConsortingbution += 4 * Epsilon * (RepulsiveTerm - AttractiveTerm); 

(notez la différence dans la deuxième ligne …)

Cette fonction est centrale dans mon code et est appelée des milliers de fois par étape dans mon programme et mon programme effectue des millions d’étapes. Par conséquent, je souhaite avoir le moins de temps système possible, d’où la raison pour laquelle je perds mon temps à me préoccuper du temps système d’inline v et de la transformation du code en macro.

Sur la base de la discussion précédente, je réalise déjà d’autres avantages / inconvénients (indépendance du type et erreurs qui en résultent) des macros … mais ce que je veux savoir le plus et que je ne connais pas actuellement, c’est la PERFORMANCE.

Je sais que certains d’entre vous, anciens combattants, auront de grandes idées pour moi !!

L’appel d’une fonction en ligne peut générer ou non un appel de fonction, qui entraîne généralement une très petite quantité de temps système. Les situations exactes dans lesquelles une fonction inline est réellement en inline varient en fonction du compilateur; la plupart font un effort de bonne foi pour intégrer de petites fonctions en ligne (du moins lorsque l’optimisation est activée), mais rien ne les oblige à le faire (C99, § 6.7.4):

Faire d’une fonction une fonction en ligne suggère que les appels à cette fonction soient aussi rapides que possible. La mesure dans laquelle ces suggestions sont efficaces est définie par la mise en œuvre.

Une macro est moins susceptible de générer une telle surcharge (encore une fois, rien n’empêche un compilateur de faire quelque chose; le standard ne définit pas les programmes de code machine à développer, mais uniquement le comportement observable d’un programme compilé).

Utilisez ce qui est plus propre. Profil. Si cela compte, faites quelque chose de différent.

Aussi, ce que le fizzer a dit; les appels à pow (et à la division) sont généralement plus coûteux que les frais généraux d’appel de fonction. Minimiser ceux-ci est un bon début:

 double ratio = SigmaSquared/RadialDistanceSquared; double AttractiveTerm = ratio*ratio*ratio; EnergyConsortingbution += 4 * Epsilon * AttractiveTerm * (AttractiveTerm - 1.0); 

EnergyConsortingbution est- EnergyConsortingbution composé uniquement de termes qui ressemblent à ceci? Si c’est le cas, sortez le 4 * Epsilon et économisez deux fois par itération:

 double ratio = SigmaSquared/RadialDistanceSquared; double AttractiveTerm = ratio*ratio*ratio; EnergyConsortingbution += AttractiveTerm * (AttractiveTerm - 1.0); // later, once you've done all of those terms... EnergyConsortingbution *= 4 * Epsilon; 

Une macro n’est pas vraiment une fonction. tout ce que vous définissez comme une macro est publié textuellement dans votre code, avant que le compilateur ne la voie, par le pré-processeur. Le préprocesseur est simplement un outil d’ingénierie logiciel qui permet à différentes abstractions de mieux structurer votre code.

Une fonction en ligne ou autrement, que le compilateur sait, et peut décider de quoi faire avec. Un mot clé en inline fourni par l’utilisateur n’est qu’une suggestion et le compilateur peut le remplacer. C’est cette tendance générale qui, dans la plupart des cas, résulterait en un meilleur code.

Un autre effet secondaire de la connaissance des fonctions par le compilateur est que vous pouvez éventuellement le forcer à prendre certaines décisions, par exemple, désactiver l’inline de votre code, ce qui pourrait vous permettre de mieux déboguer ou profiler votre code. Il existe probablement de nombreux autres cas d’utilisation que les fonctions en ligne activent par rapport aux macros.

Les macros sont extrêmement puissantes, et pour les sauvegarder, je citerais google test et google mock. Il existe de nombreuses raisons d’utiliser des macros: D.

Les opérations mathématiques simples chaînées à l’aide de fonctions sont souvent intégrées par le compilateur, en particulier si la fonction n’est appelée qu’une fois dans l’étape de traduction. Donc, je ne serais pas surpris que le compilateur prenne des décisions en ligne pour vous, peu importe la météo, le mot clé est fourni ou non.

Cependant, si le compilateur ne le fait pas, vous pouvez manuellement aplatir des segments de votre code. Si vous l’aplatissez, les macros serviront peut-être d’abstraction, car après tout, elles présentent une sémantique similaire à une “vraie” fonction.

Le noeud

Donc, voulez-vous que le compilateur soit conscient de certaines limites logiques afin de produire un meilleur code physique, ou voulez-vous imposer des décisions au compilateur en l’aplatissant manuellement ou en utilisant des macros. L’indussortinge se penche vers l’ancien.

Dans ce cas, je préférerais utiliser des macros, simplement parce que c’est rapide et sale, sans avoir à apprendre beaucoup plus. Cependant, comme les macros sont une abstraction de l’ingénierie logicielle et que le compilateur génère le code qui vous intéresse, si le problème devenait un peu plus complexe, j’utiliserais les modèles C ++, car ils ont été conçus pour répondre à vos préoccupations.

Ce sont les appels à pow () que vous souhaitez éliminer. Cette fonction prend les exposants généraux en virgule flottante et est inefficace pour les exposants intégraux. Remplacer ces appels avec par exemple

 inline double cube(double x) { return x * x * x; } 

est la seule chose qui fera une différence significative pour votre performance ici.

Veuillez consulter la norme de codage CERT Secure concernant les macros et les fonctions en ligne en termes de sécurité et de détection de bogues. Je ne vous encourage pas à utiliser des macros de type fonction, car:

La meilleure façon de répondre à votre question consiste à parsingr les deux approches afin de déterminer celle qui est réellement la plus rapide dans votre application, à l’aide de vos données de test. Les prévisions concernant les performances sont notoirement peu fiables, sauf aux niveaux les plus grossiers.

Cela dit, je m’attendrais à ce qu’il n’y ait aucune différence significative entre une macro et un appel de fonction véritablement intégré. Dans les deux cas, vous devriez vous retrouver avec le même code d’assemblage sous le capot.

Les macros, y compris les macros de type fonction, sont de simples substitutions de texte et peuvent donc vous mordre à la gorge si vous ne faites pas très attention à vos parameters. Par exemple, la très populaire macro SQUARE:

 #define SQUARE(x) ((x)*(x)) 

peut être un désastre en attente si vous l’appelez comme SQUARE(i++) . De plus, les macros de type fonction n’ont pas de concept de scope et ne supportent pas les variables locales; le hack le plus populaire est quelque chose comme

 #define MACRO(S,R,E,C) \ do \ { \ double AttractiveTerm = pow((S)/(R),3); \ double RepulsiveTerm = AttractiveTerm * AttractiveTerm; \ (C) = 4 * (E) * (RepulsiveTerm - AttractiveTerm); \ } while(0) 

ce qui, bien sûr, rend difficile l’atsortingbution d’un résultat comme x = MACRO(a,b); .

Le meilleur choix en termes de correction et de maintenabilité consiste à en faire une fonction et à spécifier inline . Les macros ne sont pas des fonctions et ne doivent pas être confondues avec elles.

Une fois que vous avez fait cela, mesurez les performances et repérez tout goulot d’étranglement avant de le pirater (l’appel à pow serait certainement un candidat pour la rationalisation).

Si vous mettez cela en pause de manière aléatoire , vous constaterez probablement que 100% (moins epsilon) du temps se situe à l’intérieur de la fonction pow , donc la façon dont il y est arrivé ne fait pratiquement aucune différence.

En supposant que vous trouviez cela, la première chose à faire est de vous débarrasser des appels à pow que vous avez trouvés sur la stack. (En général, il prend le log du premier argument, le multiplie par le deuxième argument, et exp , ou quelque chose qui fait la même chose. Le log et exp pourrait bien être fait par une sorte de série impliquant On recherche des cas particuliers, bien sûr, mais cela prendra encore plus de temps que ce ne serait le cas.) Cela seul devrait vous donner une accélération de l’ordre de grandeur.

Puis faites à nouveau la pause aléatoire. Maintenant, vous allez voir quelque chose d’autre qui prend beaucoup de temps. Je ne peux pas deviner ce que ce sera, ni personne d’autre, mais vous pouvez probablement le réduire aussi. Continuez simplement jusqu’à ce que vous n’en puissiez plus.

Il se peut que vous choisissiez d’utiliser une macro en cours de route et qu’elle soit légèrement plus rapide qu’une fonction en ligne. C’est à vous de juger quand vous y arriverez.

comme d’autres l’ont dit, cela dépend principalement du compilateur.

Je parie que “pow” vous coûte plus cher qu’aucune macro ou macro ne vous fera économiser 🙂

Je pense que son plus propre si c’est une fonction en ligne plutôt qu’une macro.

la mise en cache et le traitement en pipeline sont vraiment des domaines dans lesquels vous obtiendrez de bons gains si vous utilisez cette technologie sur un processeur moderne. c’est à dire. supprimer les instructions de twigment telles que ‘if’ fait d’énormes différences (peut être fait en utilisant plusieurs astuces)

Si j’ai bien compris certains auteurs de compilateurs, une fois que vous appelez une fonction de l’intérieur, il est peu probable que votre code soit en ligne de toute façon. Mais c’est pourquoi vous ne devriez pas utiliser une macro. Les macros suppriment les informations et laissent le compilateur avec beaucoup moins d’options à optimiser. Avec les compilateurs multi-passes et les optimisations de programmes entiers, ils sauront que l’inclusion de votre code entraînera une prédiction de twig défaillante, un cache cache ou une autre magie noire obligeant les processeurs modernes à aller vite. Je pense que tout le monde a raison de souligner que le code ci-dessus n’est de toute façon pas optimal.