web-dev-qa-db-fra.com

Fonction inline v. Macro en C - Quelle est la surcharge (mémoire / vitesse)?

J'ai recherché Stack Overflow pour les avantages/inconvénients des macros fonctionnelles par rapport aux fonctions en ligne.

J'ai trouvé la discussion suivante: Avantages et inconvénients de différentes fonctions macro/méthodes en ligne en C

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

À savoir, quelle est la surcharge en c de l'utilisation d'une fonction macro (avec des variables, éventuellement d'autres appels de fonction) v. Une fonction en ligne, en termes d'utilisation de la 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:

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

Ma raison de le transformer en une fonction/macro en ligne est que je puisse le déposer dans un fichier c puis compiler 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);
EnergyContribution += 
   4 * Epsilon * (RepulsiveTerm - AttractiveTerm);

(notez la différence sur la deuxième ligne ...)

Cette fonction est un élément central de mon code et est appelée des milliers de fois par étape dans mon programme et mon programme effectue des millions d'étapes. Ainsi, je veux avoir le MOINS frais généraux possible, d'où la raison pour laquelle je perds du temps à m'inquiéter des frais généraux liés à 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 de type et erreurs en résultant) des macros ... mais ce que je veux savoir le plus, et je ne sais pas actuellement, c'est la PERFORMANCE.

Je sais que certains d'entre vous, vétérans C, auront une grande perspicacité pour moi !!

35
Jason R. Mick

L'appel d'une fonction en ligne peut ou non générer un appel de fonction, ce qui entraîne généralement une très petite quantité de surcharge. Les situations exactes dans lesquelles une fonction inline est réellement intégrée varient en fonction du compilateur; la plupart font un effort de bonne foi pour incorporer de petites fonctions (au moins lorsque l'optimisation est activée), mais il n'y a aucune obligation de le faire (C99, §6.7.4):

Faire d'une fonction une fonction en ligne suggère que les appels à la 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 d'engendrer une telle surcharge (bien que là encore, rien n'empêche un compilateur de faire quelque chose; la norme ne définit pas les programmes de code machine à développer, uniquement le comportement observable d'un programme compilé).

Utilisez ce qui est plus propre. Profil. Si c'est important, faites quelque chose de différent.

Aussi, ce que fizzer a dit; les appels à la puissance (et à la division) sont généralement plus chers que les frais généraux d'appel de fonction. Les minimiser est un bon début:

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

EnergyContribution est-il composé uniquement de termes qui ressemblent à ceci? Si oui, tirez sur le 4 * Epsilon out, et enregistrez deux multiplications par itération:

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

Une macro n'est pas vraiment une fonction. tout ce que vous définissez comme macro est publié textuellement dans votre code, avant que le compilateur ne le voie, par le préprocesseur. Le préprocesseur n'est qu'un outil d'ingénieur logiciel qui permet à diverses abstractions de mieux structurer votre code.

Une fonction en ligne ou autre que le compilateur connaît et peut décider quoi en faire. Un mot clé inline supplanté par l'utilisateur n'est qu'une suggestion et le compilateur peut la contourner. C'est ce dépassement qui, dans la plupart des cas, aboutirait à un meilleur code.

Un autre effet secondaire de la connaissance des fonctions par le compilateur est que vous pourriez potentiellement forcer le compilateur à prendre certaines décisions - par exemple, désactiver l'inclusion 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 cependant extrêmement puissantes, et pour sauvegarder cela, je citerais google test et google mock. Il existe de nombreuses raisons d'utiliser des macros: D.

Les opérations mathématiques simples qui sont chaînées ensemble à l'aide de fonctions sont souvent intégrées par le compilateur, surtout si la fonction n'est appelée qu'une seule fois dans l'étape de traduction. Donc, je ne serais pas surpris que le compilateur prenne des décisions en ligne pour vous, quelle que soit la météo fournie ou non.

Cependant, si le compilateur ne fonctionne pas, vous pouvez manuellement aplatir des segments de votre code. Si vous l'aplatissez, les macros serviront peut-être de bonne abstraction, après tout, elles présentent une sémantique similaire à une fonction "réelle".

Le Crux

Donc, voulez-vous que le compilateur soit conscient de certaines limites logiques afin qu'il puisse produire un meilleur code physique, ou voulez-vous forcer les décisions sur le compilateur en l'aplatissant manuellement ou en utilisant des macros. L'industrie penche vers la première.

Je pencherais pour l'utilisation de macros dans ce cas, simplement parce que c'est rapide et sale, sans avoir à en apprendre beaucoup plus. Cependant, comme les macros sont une abstraction de l'ingénierie logicielle, et parce que vous êtes concerné par le code généré par le compilateur, si le problème devenait légèrement plus avancé, j'utiliserais des modèles C++, car ils ont été conçus pour répondre aux préoccupations que vous réfléchissez.

10
Hassan Syed

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

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

est la seule chose qui fera une différence significative dans vos performances ici.

8
fizzer

Veuillez consulter la norme de codage CERT Secure en ce qui concerne les macros et les fonctions en ligne en termes de sécurité et de déclenchement de bogues, je n'encourage pas à utiliser des macros de type fonction, car: - Moins de profilage - Moins traçable - Plus difficile à déboguer - Peut entraîner des bogues graves

3

Les macros, y compris les macros fonctionnelles, sont de simples substitutions de texte, et en tant que telles peuvent vous mordre dans le cul si vous n'êtes pas vraiment prudent avec vos paramètres. Par exemple, la macro SQUARE toujours aussi populaire:

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

peut être un désastre en attente de se produire si vous l'appelez comme SQUARE(i++). De plus, les macros de type fonction n'ont aucune notion de portée et ne prennent pas en charge 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'attribution d'un résultat comme x = MACRO(a,b);.

Le meilleur pari d'un point de vue exactitude et maintenabilité est d'en faire une fonction et de 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 trouvez où se trouve un goulot d'étranglement réel avant de le pirater (l'appel à pow serait certainement un candidat pour la rationalisation).

2
John Bode

La meilleure façon de répondre à votre question est de comparer les deux approches pour voir laquelle est réellement plus rapide dans l'application votre, en utilisant les données de test votre. Les prédictions sur les performances sont notoirement peu fiables, sauf aux niveaux les plus grossiers.

Cela dit, je m'attendrais à ce qu'il n'y ait pas de différence significative entre une macro et un appel de fonction en ligne vraiment. Dans les deux cas, vous devriez vous retrouver avec le même code d'assemblage sous le capot.

2
Eric Melski

Si vous random-pause ceci, ce que vous allez probablement voir, c'est que 100% (moins epsilon) du temps est à l'intérieur de la fonction pow, donc comment cela est arrivé fait essentiellement 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 pile. (En général, il prend le log du premier argument, le multiplie par le deuxième argument et exp de celui-ci, ou quelque chose qui fait la même chose. Le log et exp pourrait très bien être fait par une sorte de série impliquant beaucoup d'arithmétique. Elle recherche des cas spéciaux, bien sûr, mais cela va encore prendre plus de temps que vous ne le feriez.) Cela seul devrait donner vous autour d'un ordre de grandeur d'accélération.

Faites ensuite à nouveau la pause aléatoire. Maintenant, vous allez voir autre chose prendre beaucoup de temps. Je ne peux pas deviner ce que ce sera, et personne d'autre non plus, mais vous pouvez probablement réduire cela aussi. Continuez à le faire jusqu'à ce que vous ne puissiez plus.

Cela peut se produire le long du chemin que vous choisissez d'utiliser une macro, et cela peut être légèrement plus rapide qu'une fonction en ligne. C'est à vous de juger quand vous y arrivez.

2
Mike Dunlavey

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

Je parie que "pow" vous coûte plus cher que n'importe quel inlining ou macro vous sauvera :)

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

la mise en cache et le pipelining sont vraiment là où vous obtiendrez de bons gains si vous exécutez cela sur un processeur moderne. c'est à dire. supprimer les instructions de branchement comme "si" font d'énormes différences (cela peut être fait par un certain nombre d'astuces)

0
Keith Nicholas

Si je comprends bien certains gars qui écrivent des compilateurs, une fois que vous appelez une fonction de l'intérieur, il est peu probable que votre code soit de toute façon en ligne. Mais c'est pourquoi vous ne devez pas utiliser de macro. Les macros suppriment les informations et laissent au compilateur 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 branche échouée ou un échec de cache ou toute autre magie noire oblige les processeurs modernes à aller vite. Je pense que tout le monde a raison de souligner que le code ci-dessus n'est pas optimal de toute façon, c'est donc là que l'accent devrait être mis.

0
Tavison