関数をインライン化しないようにgccに指示するにはどうすればよいですか?


126

ソースファイルにこの小さな関数があるとしましょう

static void foo() {}

そして、私は自分のバイナリの最適化されたバージョンを構築していますが、この関数をインライン化したくありません(最適化のため)。インライン化を防ぐためにソースコードに追加できるマクロはありますか?


この質問をありがとう!関数が表示されないときにoprofileを使用してプロファイリングしていましたが、ここでの回答によりこれが修正されました。
Simon A. Eugster、2011年

回答:


149

gcc-specific noinline属性が必要です。

この関数属性は、関数がインライン化の対象と見なされないようにします。関数に副作用がない場合、関数呼び出しはライブですが、関数呼び出しが最適化される原因となるインライン化以外の最適化があります。このような呼び出しが最適化されないようにするには、 asm ("");

次のように使用します。

void __attribute__ ((noinline)) foo() 
{
  ...
}

32
Arch Linuxでgcc 4.4.3を使用すると、上記のように配置された属性で構文エラーが発生します。関数(たとえば、属性((noinline))void foo(){})の前にある場合、正しく機能します
mrkj

2
Arduinoはまた、関数の前に配置したかったのです。
Peter N Lewis

2
属性構文を修正するために編集されました。
Quuxplusone 2013年

1
asm( "")構造は実際にはかなりクロスプラットフォームであり、仕事を成し遂げました。私はx86 Linuxでそれを行いましたが、PowerPC AIXでビルドの問題を引き起こしませんでした。この便利な提案をありがとう!
Marty、

1
あらゆる場所でコードの変更を必要とするアプローチは、容認できる回答とは言えません。
ajeh 2018年

31

GCCには、

-fno-inline-small-functions

したがって、gccを呼び出すときにそれを使用します。しかし、副作用として、他のすべての小さな関数もインライン化されていません。


コンパイラーレベルでは機能しませんでした。gcc 5.2.1 20150902(Red Hat 5.2.1-2)を使用していた
John Greene

現在のGCC 6.4が壊れているか、これより単純なもの-fno-inlineはまったく機能しません。gdbまだステップオーバー時にメソッドに入ります。何かが壊れています、そして私はそれが疑いgdbます。
ajeh 2018年

指定された関数だけでなく、すべてのインライン最適化をオフにします。
where23

@ajeh関数をインライン化しないということは、それらが通常に呼び出されることを意味しますね?
Melebius

21

これを行う移植可能な方法は、ポインタを介して関数を呼び出すことです。

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

これは分岐するための異なる命令を生成しますが、それはあなたの目標ではないかもしれません。これは良い点をもたらします:ここあなたの目標何ですか?


2
ポインターが静的ではなくファイルスコープで定義されている場合、コンパイラーは使用時に初期値を持っていると想定できないため、ポインターは機能します。ローカルの場合(示されているように)、ほぼ確実にfoo()と同じように扱われます。(「この10年間」、彼は日付を見て追加しました)
greggo

16

質問がGCCに関するものであることはわかっていますが、他のコンパイラについても、コンパイラに関する情報があると役立つと思いました。

GCCの noinline 関数属性は、他のコンパイラーでも人気があります。それは少なくとも以下によってサポートされます:

  • Clang(で確認__has_attribute(noinline)
  • Intel C / C ++ Compiler(ドキュメントはひどいですが、16.0以降で動作するはずです)
  • Oracle Solaris Studioが少なくとも12.2に戻る
  • ARM C / C ++コンパイラを少なくとも4.1に戻す
  • IBM XL C / C ++が10.1以上に戻る
  • TI 8.0以降(または7.3以降-を定義する--gcc __TI_GNU_ATTRIBUTE_SUPPORT__

さらに、MSVCは__declspec(noinline) Visual Studio 7.1までをサポートし ています。Intelもおそらくサポートしています(GCCとMSVCの両方との互換性を確保しようとします)が、それを確認する必要はありません。構文は基本的に同じです:

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

PGI 10.2+(およびおそらくより古い)はnoinline、次の関数に適用されるプラグマをサポートしています。

#pragma noinline
static void foo(void) { }

TI 6.0以降は、FUNC_CANNOT_INLINE CとC ++で(厄介なことに)異なる動作をするプラグマをサポートしてい ます。C ++では、PGIに似ています。

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

ただし、Cでは関数名が必要です。

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

Cray 6.4以降(およびおそらく以前のバージョン)も同様のアプローチをとっており、関数名が必要です。

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

Oracle Developer Studioは、少なくともForte Developer 6までさかのぼって関数名をとるプラグマもサポートしますが、最近のバージョンでも、宣言のに置く必要があることに注意してください。

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

どれだけ専念しているかによって、どこでも機能するマクロを作成できますが、関数名と宣言を引数として持つ必要があります。

OTOH、ほとんどの人にうまく機能するもので大丈夫であれば、もう少し美的で満足のいくもので、何度も繰り返す必要がないもので済ますことができます。これが、私がHedleyに採用したアプローチです。現在のHEDLEY_NEVER_INLINEのバージョンは 次のようになります。

#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

Hedley(単一のパブリックドメイン/ CC0ヘッダーです)を使用したくない場合は、それほど多くの労力をかけずにバージョンチェックマクロを変換できますが、私がtoに入れようと思っている以上のものです。


プロジェクト@nemequへのリンクをありがとう。他の開発者に私たちが使用するために評価するように依頼しました。さまざまなアーキテクチャがあります。
荒巻大輔

特に彼らが興味がない場合、私は彼らが何を言っているかを知るために非常に興味があります。そしてもちろん、私は質問に回答します(GitHubの課題追跡、電子メールなど...)。
nemequ

14

のコンパイラエラーが発生した__attribute__((noinline))場合は、次のことを試してください。

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

10
static __attribute__ ((noinline))  void foo()
{

}

これは私のために働いたものです。


8

次のnoinline 属性を使用します

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

外部で使用する関数を宣言するときと、関数を記述するときの両方で使用する必要があります。


2

私はgcc 7.2を使用しています。ライブラリにインスタンス化する必要があるため、関数をインライン化しないようにする必要がありました。__attribute__((noinline))答えだけでなく、答えも試しましたasm("")。どちらも問題を解決しなかった。

最後に、関数内で静的変数を定義すると、静的変数ブロックに静的変数を割り当てるスペースがコンパイラーに強制され、関数が最初に呼び出されたときに初期化が発行されることを理解しました。

これはちょっと汚いトリックですが、うまくいきます。


inline void foo(void) { ... }ヘッダーで関数を定義extern inline void foo(void);し、ライブラリソースファイルで宣言できます。C99のセマンティクスに従って、ライブラリでオブジェクトコードを許可し、オブジェクトコードを発行すると、コンパイラーは関数をインライン化できます。「静的」または「extern」なしの「インライン」がC99で役立つことを参照してください
ダイアピル2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.