ゆとりーなの日記

日記的な事を書いて行くと思はれる

インライン

卒論がそろそろ危うい季節になってきましたが冷静にインライン展開することで凌いでいきましょうということでC++のインライン展開の話です。
取り敢えずこういうコードがあったとすると

#include <cstdio>

template <typename T>
void foo(T t, const char *str) {
  t(str);
}

struct hoge {
  void operator ()(const char *str) const {
    std::puts(str);
  }
};

int main() {
  foo(hoge{}, "313");
}

まあ当然-O3とかを付ければ全部mainにインライン展開されそうです。実際アセンブリ見てもそんな感じがします。

  10              	main:
  11              	.LFB33:
  12              		.cfi_startproc
  13 0000 4883EC08 		sub	rsp, 8
  14              		.cfi_def_cfa_offset 16
  15 0004 BF000000 		mov	edi, OFFSET FLAT:.LC0
  15      00
  16 0009 E8000000 		call	puts
  16      00
  17 000e 31C0     		xor	eax, eax
  18 0010 4883C408 		add	rsp, 8
  19              		.cfi_def_cfa_offset 8
  20 0014 C3       		ret
  21              		.cfi_endproc
  22              	.LFE33:

で、このインライン展開がおいしいから函数ポインタより函数オブジェクトの方が強いという話だったりするわけですが、別に函数ポインタでもインライン展開される時はされるみたいです。

#include <cstdio>

template <typename T>
void foo(T t, const char *str) {
  t(str);
}

void hage(const char *str) {
  std::puts(str);
}

int main() {
  foo(&hage, "313");
}

これのアセンブリ

  21              	main:
  22              	.LFB33:
  23              		.cfi_startproc
  24 0000 4883EC08 		sub	rsp, 8
  25              		.cfi_def_cfa_offset 16
  26 0004 BF000000 		mov	edi, OFFSET FLAT:.LC0
  26      00
  27 0009 E8000000 		call	puts
  27      00
  28 000e 31C0     		xor	eax, eax
  29 0010 4883C408 		add	rsp, 8
  30              		.cfi_def_cfa_offset 8
  31 0014 C3       		ret
  32              		.cfi_endproc
  33              	.LFE33:

と同じでした。
とはいいつつも函数オブジェクト版の方が出来るバイナリサイズが小さくなるというのはあるみたいです。函数オブジェクト版は1592バイトなのに対して函数ポインタ版は1768バイトでした。まあこれは多分hageの実体が入り込んでいるせいでしょう。実はこのバイナリサイズも函数にinlineを付ければあっさり函数オブジェクト版と同じサイズになったりするみたいですけどね。
で、最近驚いたのが前方にある函数でも普通にインライン展開されるということです。昔呼び出す側から見えていない函数はインライン化期待できないよとか習った記憶があったので密かにびっくりしてたりしました。

#include <cstdio>

template <typename T>
void foo(T t, const char *str) {
  t(str);
}

void hage(const char *str);

int main() {
  foo(&hage, "313");
}

void hage(const char *str) {
  std::puts(str);
}
 10              	main:
  11              	.LFB32:
  12              		.cfi_startproc
  13 0000 4883EC08 		sub	rsp, 8
  14              		.cfi_def_cfa_offset 16
  15 0004 BF000000 		mov	edi, OFFSET FLAT:.LC0
  15      00
  16 0009 E8000000 		call	puts
  16      00
  17 000e 31C0     		xor	eax, eax
  18 0010 4883C408 		add	rsp, 8
  19              		.cfi_def_cfa_offset 8
  20 0014 C3       		ret
  21              		.cfi_endproc
  22              	.LFE32:

どうも同じ翻訳単位にあればいいみたいです。流石に翻訳単位が違うところにある函数はインライン化されませんでしたが、この辺りはinlineを付けてみた時の警告メッセージを見ればなんとなく察しが付きそうです。