Comment puis-je dire à gcc de ne pas insérer une fonction?

126

Disons que j'ai cette petite fonction dans un fichier source

static void foo() {}

et je construis une version optimisée de mon binaire mais je ne veux pas que cette fonction soit intégrée (à des fins d'optimisation). y a-t-il une macro que je peux ajouter dans un code source pour empêcher l'inlining?

vehomzzz
la source
Merci pour cette question! Je profilais avec oprofile lorsqu'une fonction n'apparaissait pas, les réponses ici ont corrigé le problème.
Simon A. Eugster
c ++: stackoverflow.com/questions/3329214/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

149

Vous voulez l' attribut gcc-specific noinline.

Cet attribut de fonction empêche qu'une fonction soit prise en compte pour l'inlining. Si la fonction n'a pas d'effets secondaires, il existe des optimisations autres que l'inlining qui entraînent l'optimisation des appels de fonction, bien que l'appel de fonction soit actif. Pour éviter que ces appels ne soient optimisés, mettez asm ("");

Utilisez-le comme ceci:

void __attribute__ ((noinline)) foo() 
{
  ...
}
alex tingle
la source
32
En utilisant gcc 4.4.3 sur Arch Linux, j'obtiens une erreur de syntaxe avec l'attribut placé comme ci-dessus. Cela fonctionne correctement quand il précède la fonction (par exemple, attribut ((noinline)) void foo () {})
mrkj
2
Arduino voulait également qu'il soit placé avant la fonction.
Peter N Lewis
2
Modifié pour corriger la syntaxe des attributs.
Quuxplusone
1
La construction asm ("") est en fait assez multiplateforme et fait le travail. Je l'ai fait pour Linux x86 et cela n'a pas causé de problème de construction sur PowerPC AIX. Merci pour cette suggestion utile!
Marty
1
L'approche qui exige des changements de code partout ne peut être raisonnablement considérée comme une réponse acceptable.
ajeh
31

GCC a un commutateur appelé

-fno-inline-small-functions

Utilisez-le donc lors de l'appel de gcc. Mais l'effet secondaire est que toutes les autres petites fonctions sont également non intégrées.

Lukmac
la source
N'a pas fonctionné au niveau du compilateur. Utilisait gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
John Greene
Soit le GCC 6.4 actuel est cassé, soit ceci et plus simple -fno-inlinene fonctionne pas du tout. gdbentre toujours des méthodes lors du passage au pas Quelque chose est cassé, et j'en doute gdb.
ajeh
Cela désactivera l'optimisation en ligne pour tous, pas seulement pour une fonction spécifiée.
où23
@ajeh Ne pas insérer de fonctions signifie qu'elles sont appelées normalement, n'est-ce pas?
Melebius
21

Un moyen portable de le faire est d'appeler la fonction via un pointeur:

void (*foo_ptr)() = foo;
foo_ptr();

Bien que cela produise des instructions différentes pour créer une branche, ce n'est peut-être pas votre objectif. Ce qui soulève un bon point: quel est votre objectif ici?

Andy Ross
la source
2
Si le pointeur est défini au niveau de la portée du fichier, et non statique, il devrait fonctionner car le compilateur ne peut alors pas supposer qu'il a sa valeur initiale au moment de l'utilisation. Si c'est un local (comme indiqué), il est presque certainement traité de la même manière que foo (). ("Dans cette décennie", a-t-il ajouté, en regardant les dates)
greggo
16

Je sais que la question concerne GCC, mais j'ai pensé qu'il pourrait être utile d'avoir des informations sur les compilateurs d'autres compilateurs également.

L' noinline attribut function de GCC est également très populaire auprès d'autres compilateurs. Il est soutenu par au moins:

  • Clang (vérifier avec __has_attribute(noinline))
  • Compilateur Intel C / C ++ (leur documentation est terrible, mais je suis certain que cela fonctionne sur 16.0+)
  • Oracle Solaris Studio de retour à au moins 12.2
  • Compilateur ARM C / C ++ de retour à au moins 4.1
  • IBM XL C / C ++ de retour à au moins 10.1
  • TI 8.0+ (ou 7.3+ avec --gcc, qui définira __TI_GNU_ATTRIBUTE_SUPPORT__)

En outre, MSVC prend __declspec(noinline) en charge le retour à Visual Studio 7.1. Intel le supporte probablement aussi (ils essaient d'être compatibles avec GCC et MSVC), mais je n'ai pas pris la peine de le vérifier. La syntaxe est fondamentalement la même:

__declspec(noinline)
static void foo(void) { }

PGI 10.2+ (et probablement plus ancien) prend en charge un noinlinepragma qui s'applique à la fonction suivante:

#pragma noinline
static void foo(void) { }

TI 6.0+ prend en charge un FUNC_CANNOT_INLINE pragma qui (ennuyeusement) fonctionne différemment en C et C ++. En C ++, c'est similaire à PGI:

#pragma FUNC_CANNOT_INLINE;
static void foo(void) { }

En C, cependant, le nom de la fonction est obligatoire:

#pragma FUNC_CANNOT_INLINE(foo);
static void foo(void) { }

Cray 6.4+ (et peut-être plus tôt) adopte une approche similaire, nécessitant le nom de la fonction:

#pragma _CRI inline_never foo
static void foo(void) { }

Oracle Developer Studio prend également en charge un pragma qui prend le nom de la fonction, remontant au moins à Forte Developer 6 , mais notez qu'il doit venir après la déclaration, même dans les versions récentes:

static void foo(void);
#pragma no_inline(foo)

En fonction de votre dévouement, vous pouvez créer une macro qui fonctionnerait partout, mais vous auriez besoin d'avoir le nom de la fonction ainsi que la déclaration comme arguments.

Si, OTOH, vous êtes d'accord avec quelque chose qui fonctionne juste pour la plupart des gens, vous pouvez vous en tirer avec quelque chose qui est un peu plus esthétique et ne nécessite pas de vous répéter. C'est l'approche que j'ai adoptée pour Hedley , où la version actuelle de HEDLEY_NEVER_INLINE ressemble à:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(noinline,4,0,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
#  define HEDLEY_NEVER_INLINE __attribute__((__noinline__))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
#  define HEDLEY_NEVER_INLINE __declspec(noinline)
#elif HEDLEY_PGI_VERSION_CHECK(10,2,0)
#  define HEDLEY_NEVER_INLINE _Pragma("noinline")
#elif HEDLEY_TI_VERSION_CHECK(6,0,0)
#  define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
#else
#  define HEDLEY_NEVER_INLINE HEDLEY_INLINE
#endif

Si vous ne voulez pas utiliser Hedley (c'est un seul en-tête de domaine public / CC0), vous pouvez convertir les macros de vérification de version sans trop d'effort, mais plus que je ne suis prêt à en mettre ☺.

nemequ
la source
Merci pour le lien vers votre projet @nemequ. J'ai demandé à nos autres développeurs de l'évaluer pour notre utilisation. Nous avons des architectures diverses.
Daisuke Aramaki
Je serais très intéressé de savoir ce qu'ils disent, surtout s'ils ne sont pas intéressés. Et, bien sûr, je suis là pour répondre aux questions (suivi des problèmes GitHub, e-mail, peu importe…).
nemequ
14

Si vous obtenez une erreur de compilation pour __attribute__((noinline)), vous pouvez simplement essayer:

noinline int func(int arg)
{
    ....
}
Sam Liao
la source
10
static __attribute__ ((noinline))  void foo()
{

}

C'est ce qui a fonctionné pour moi.

KenBee
la source
8

Utilisez l' noinline attribut :

int func(int arg) __attribute__((noinline))
{
}

Vous devriez probablement l'utiliser à la fois lorsque vous déclarez la fonction pour un usage externe et lorsque vous écrivez la fonction.

Chris Lutz
la source
2

Je travaille avec gcc 7.2. J'avais spécifiquement besoin qu'une fonction ne soit pas intégrée, car elle devait être instanciée dans une bibliothèque. J'ai essayé la __attribute__((noinline))réponse, ainsi que la asm("")réponse. Aucun des deux n'a résolu le problème.

Enfin, j'ai pensé que définir une variable statique à l'intérieur de la fonction forcerait le compilateur à lui allouer de l'espace dans le bloc de variable statique et à émettre une initialisation pour celle-ci lorsque la fonction est appelée pour la première fois.

C'est une sorte de sale tour, mais ça marche.

Ofri Sadowsky
la source
Vous pouvez définir votre fonction inline void foo(void) { ... }dans un en-tête et la déclarer extern inline void foo(void);dans un fichier source de bibliothèque. Suivant la sémantique C99, le compilateur serait autorisé à incorporer la fonction quand bon lui semble ET à émettre du code objet dans votre bibliothèque. Voir "inline" sans "static" ou "extern" est-il jamais utile dans C99? .
diapir