Inline-Funktion vs. Makro in C — Was ist der Overhead (Speicher/Geschwindigkeit)?

Lesezeit: 8 Minuten

Benutzeravatar von Jason R. Mick
Jason R.Mick

Ich habe Stack Overflow nach den Vor- und Nachteilen von funktionsähnlichen Makros im Vergleich zu Inline-Funktionen durchsucht.

Ich habe die folgende Diskussion gefunden: Vor- und Nachteile verschiedener Makrofunktionen / Inline-Methoden in C

…aber es hat meine primäre brennende Frage nicht beantwortet.

Was ist nämlich der Overhead in c bei der Verwendung einer Makrofunktion (mit Variablen, möglicherweise anderen Funktionsaufrufen) gegenüber einer Inline-Funktion in Bezug auf Speicherverbrauch und Ausführungsgeschwindigkeit?

Gibt es Compiler-abhängige Unterschiede im Overhead? Ich habe sowohl icc als auch gcc zur Verfügung.

Mein Code-Snippet, das ich modularisiere, ist:

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

Mein Grund, es in eine Inline-Funktion/Makro zu verwandeln, ist, dass ich es in eine AC-Datei ziehen und dann andere ähnliche, aber leicht unterschiedliche Funktionen/Makros kompilieren kann.

z.B:

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

(beachten Sie den Unterschied in der zweiten Zeile …)

Diese Funktion ist zentral in meinem Code und wird in meinem Programm tausende Male pro Schritt aufgerufen, und mein Programm führt Millionen von Schritten aus. Daher möchte ich den GERINGSTEN Overhead haben, weshalb ich Zeit damit verschwende, mir Gedanken über den Overhead des Inlinings zu machen und den Code in ein Makro umzuwandeln.

Basierend auf der vorherigen Diskussion erkenne ich bereits andere Vor- und Nachteile (Typunabhängigkeit und daraus resultierende Fehler) von Makros … aber was ich am meisten wissen möchte und derzeit nicht weiß, ist die PERFORMANCE.

Ich weiß, dass einige von euch C-Veteranen einige großartige Einblicke für mich haben werden!!

  • Verwenden Sie einen Profiler und entscheiden Sie dann.

    – Erich

    7. März 2011 um 23:58 Uhr

  • Es gibt keine allgemeingültige Antwort?

    – Jason R. Mick

    7. März 2011 um 23:58 Uhr

  • +1 für den Link zur Stackoverflow-Homepage 🙂 LOL

    – 0x90

    28. Mai 2013 um 13:03 Uhr

  • Mehr als 100 % Leistungssteigerung durch Konvertierung einiger Zeichenvalidierungsfunktionen in MACRO() Varianten in einem Parser, wo diese Funktionen sehr oft aufgerufen werden. inline und __forceinline hatte aber nur 50% Boost vs. normale Funktionsaufrufe MACROs 100% geliefert. (verfolgte Leistung in Zyklen) – Ich würde also sagen, wenn Inline-Funktionen kurz und einfach sind, probieren Sie ein Makro und einen Benchmark aus.

    – CodeWütend

    5. April 2014 um 5:13 Uhr

  • Mögliches Duplikat von Inline-Funktionen im Vergleich zu Präprozessor-Makros

    – Jim fiel

    9. August 2017 um 18:17 Uhr

Benutzeravatar von Stephen Canon
Stefan Kanon

Der Aufruf einer Inline-Funktion kann einen Funktionsaufruf generieren oder auch nicht, der normalerweise einen sehr geringen Overhead verursacht. Die genauen Situationen, unter denen ein inline Die Funktion wird tatsächlich inliniert, je nach Compiler; Die meisten bemühen sich nach Treu und Glauben, kleine Funktionen einzubetten (zumindest wenn die Optimierung aktiviert ist), aber es besteht keine Verpflichtung dazu (C99, §6.7.4):

Wenn Sie eine Funktion zu einer Inline-Funktion machen, sollten Aufrufe der Funktion so schnell wie möglich erfolgen. Inwieweit solche Vorschläge wirksam sind, wird von der Umsetzung bestimmt.

Ein Makro verursacht mit geringerer Wahrscheinlichkeit einen solchen Overhead (obwohl es wiederum wenig gibt, was einen Compiler daran hindern könnte, etwas zu tun; der Standard definiert nicht, worauf Maschinencodeprogramme erweitert werden müssen, sondern nur das beobachtbare Verhalten eines kompilierten Programms).

Verwenden Sie, was sauberer ist. Profil. Wenn es darauf ankommt, mach etwas anderes.

Also was Fizzer sagte; Aufrufe von pow (und Division) sind in der Regel beide teurer als der Overhead von Funktionsaufrufen. Diese zu minimieren ist ein guter Anfang:

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

Ist EnergyContribution nur aus Begriffen besteht, die so aussehen? Wenn ja, ziehen Sie die 4 * Epsilon out, und speichern Sie zwei Multiplikationen pro Iteration:

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;

Benutzeravatar von Hassan Syed
Hassan Sydney

Ein Makro ist nicht wirklich eine Funktion. Was auch immer Sie als Makro definieren, wird vom Präprozessor wörtlich in Ihren Code geschrieben, bevor der Compiler es sieht. Der Präprozessor ist nur ein Softwareentwickler-Tool, das verschiedene Abstraktionen ermöglicht, um Ihren Code besser zu strukturieren.

Eine Funktion inline oder anderweitig, die der Compiler kennt und Entscheidungen darüber treffen kann, was damit zu tun ist. Ein Benutzer bereitgestellt inline Schlüsselwort ist nur ein Vorschlag und der Compiler kann es überschreiben. Dieses Überschreiben würde in den meisten Fällen zu besserem Code führen.

Ein weiterer Nebeneffekt davon, dass der Compiler sich der Funktionen bewusst ist, besteht darin, dass Sie den Compiler möglicherweise zwingen könnten, bestimmte Entscheidungen zu treffen, z. B. das Deaktivieren des Inlinings Ihres Codes, wodurch Sie Ihren Code besser debuggen oder profilieren können. Es gibt wahrscheinlich viele andere Anwendungsfälle, die Inline-Funktionen im Vergleich zu Makros ermöglichen.

Makros sind jedoch extrem leistungsfähig, und um dies zu untermauern, würde ich Google Test und Google Mock zitieren. Es gibt viele Gründe, Makros zu verwenden :D.

Einfache mathematische Operationen, die über Funktionen verkettet werden, werden oft vom Compiler inliniert, insbesondere wenn die Funktion nur einmal im Übersetzungsschritt aufgerufen wird. Ich wäre also nicht überrascht, dass der Compiler Inlining-Entscheidungen für Sie trifft, unabhängig davon, ob das Schlüsselwort bereitgestellt wird oder nicht.

Wenn der Compiler dies jedoch nicht tut, können Sie Segmente Ihres Codes manuell glätten. Wenn Sie es glätten, dienen Makros vielleicht als gute Abstraktion, schließlich bieten sie eine ähnliche Semantik wie eine “echte” Funktion.

Das Kreuz

Möchten Sie also, dass der Compiler bestimmte logische Grenzen kennt, damit er besseren physischen Code erzeugen kann, oder möchten Sie dem Compiler Entscheidungen aufzwingen, indem Sie ihn manuell oder mithilfe von Makros glätten. Die Branche tendiert zu Ersterem.

Ich würde in diesem Fall zur Verwendung von Makros tendieren, nur weil es schnell und schmutzig ist, ohne viel mehr lernen zu müssen. Da Makros jedoch eine Software-Engineering-Abstraktion sind und Sie sich mit dem Code befassen, den der Compiler generiert, würde ich C++-Vorlagen verwenden, wenn das Problem etwas fortgeschrittener würde, da sie für die Bedenken entwickelt wurden, über die Sie nachdenken.

Benutzeravatar von fizzer
Fizzer

Es sind die Aufrufe von pow(), die Sie eliminieren möchten. Diese Funktion nimmt allgemeine Fließkomma-Exponenten und ist für die Erhöhung auf ganzzahlige Exponenten ineffizient. Das Ersetzen dieser Aufrufe durch zB

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

ist das einzige, was hier einen signifikanten Unterschied zu Ihrer Leistung ausmachen wird.

  • Wird mit einem modernen C++-Compiler ein Aufruf wie pow(x, integer) nicht zu dem vereinfacht, was Sie beschreiben, anstatt den teureren zu verwenden?

    – Tyler Shellberg

    1. November 2019 um 21:29 Uhr

Makros, einschließlich funktionsähnlicher Makros, sind einfache Textersetzungen und können Sie als solche in den Arsch beißen, wenn Sie es nicht sind Ja wirklich vorsichtig mit Ihren Parametern. Zum Beispiel das allseits beliebte SQUARE-Makro:

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

kann eine Katastrophe sein, die darauf wartet, passiert zu werden, wenn Sie es so nennen SQUARE(i++). Außerdem haben funktionsähnliche Makros kein Geltungsbereichskonzept und unterstützen keine lokalen Variablen; Der beliebteste Hack ist so etwas wie

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

was es natürlich schwierig macht, ein Ergebnis wie zuzuordnen x = MACRO(a,b);.

Die beste Wette von a Richtigkeit und Wartbarkeit Standpunkt ist, es zu einer Funktion zu machen und zu spezifizieren inline. Makros sind keine Funktionen und sollten nicht mit ihnen verwechselt werden.

Wenn Sie das getan haben, messen Sie die Leistung und finden Sie heraus, wo welche vorhanden sind tatsächlich Engpass ist vor dem Hacken (der Aufruf von pow wäre sicherlich ein Kandidat für eine Rationalisierung).

Benutzeravatar von Muhammed Abdul Galeil
Muhammed Abdul Galeil

Bitte überprüfen Sie den CERT Secure-Codierungsstandard, in dem über Makros und Inline-Funktionen in Bezug auf Sicherheit und Fehlerauslösung gesprochen wird. Ich ermutige nicht zur Verwendung funktionsähnlicher Makros, weil: – Weniger Profilerstellung – Weniger rückverfolgbar – Schwieriger zu debuggen – Könnte zu schwerwiegenden Fehlern führen

Benutzeravatar von Eric Melski
Erich Melski

Der beste Weg, Ihre Frage zu beantworten, besteht darin, beide Ansätze zu vergleichen, um zu sehen, welcher tatsächlich schneller ist dein Anwendung, Verwendung dein Testdaten. Vorhersagen über die Leistung sind notorisch unzuverlässig, außer auf den gröbsten Ebenen.

Allerdings würde ich erwarten, dass es keinen signifikanten Unterschied zwischen einem Makro und einem gibt wirklich Inline-Funktionsaufruf. In beiden Fällen sollten Sie am Ende denselben Assemblercode unter der Haube haben.

Benutzeravatar der Community
Gemeinschaft

Wenn Sie dies zufällig anhalten, werden Sie wahrscheinlich sehen, dass 100 % (minus Epsilon) der Zeit innerhalb von liegen pow funktionieren, also wie man es dort hinbekommt im Grunde macht nein Unterschied.

Angenommen, Sie finden das, ist das erste, was Sie tun müssen, die Anrufe nach loszuwerden pow die Sie auf dem Stapel gefunden haben. (Im Allgemeinen nimmt es die log des ersten Arguments, multipliziere es mit dem zweiten Argument, und exp davon, oder etwas, das dasselbe tut. Das log und exp könnte gut durch eine Art Reihe erfolgen, die viel Arithmetik erfordert. Es sucht natürlich nach Sonderfällen, aber es dauert immer noch länger als Sie es tun würden.) Das allein sollte Ihnen eine Beschleunigung um eine Größenordnung bringen.

Machen Sie dann die zufällige Pause erneut. Jetzt werden Sie sehen, dass etwas anderes viel Zeit in Anspruch nimmt. Ich kann nicht erraten, was es sein wird, und auch niemand sonst, aber Sie können das wahrscheinlich auch reduzieren. Machen Sie einfach so lange weiter, bis Sie nicht mehr können.

Es kann vorkommen, dass Sie sich für die Verwendung eines Makros entscheiden, und es kann etwas schneller sein als eine Inline-Funktion. Das müssen Sie beurteilen, wenn Sie dort ankommen.

1395150cookie-checkInline-Funktion vs. Makro in C — Was ist der Overhead (Speicher/Geschwindigkeit)?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy