我正在观察带有GCC标志-flto
和jemalloc
/ tcmalloc
的意外行为(至少我找不到解释)。一旦使用了-flto
,并且我与上述库malloc / calloc链接,并且没有用je/tc malloc
实现替换好友,则调用glibc实现。删除-flto
标志后,一切都会按预期进行。我试图将-fno-builtin
/ -fno-builtin-*
与-flto
一起使用,但仍然没有选择je/tc malloc
的实现。
-flto
机制如何工作?为什么二进制文件不选择新的实现?
-fno-builtin
无法解析的外部失败的情况下,它如何与printf
链接?
EDIT001:
GCC 7.3
示例代码
int main()
{
auto p = malloc(1024);
free(p);
return 0;
}
编译:
/ usr / bin / c ++ -O2 -g -DNDEBUG -flto -std = gnu ++ 14 -o CMakeFiles / flto.dir / main.cpp.o -c /home/user/Development/CPPJunk/flto/main.cpp
链接:
/ usr / bin / c ++ -O2 -g -DNDEBUG -flto CMakeFiles / flto.dir / main.cpp.o -o flto -L / home / user / Development / jemalloc -Wl,-rpath,/ home / user / Development / jemalloc -ljemalloc
EDIT002:
更合适的示例代码
#include <cstdlib>
int main()
{
auto p = malloc(1024);
if (p) {
free(p);
}
auto p1 = new int;
if (p1) {
delete p1;
}
auto p2 = new int[32];
if (p2) {
delete[] p2;
}
return 0;
}
答案 0 :(得分:3)
首先,您的示例代码是错误的。仔细阅读C11标准n1570。当您想使用 standard malloc
时,应该使用#include <stdlib.h>
。
在C ++ 11中(读为n3337)malloc
被皱了皱眉,不应该被使用(首选new
)。如果您仍想在C ++中使用std::malloc
,则应该使用#include <cstdlib>
(在GCC中内部包含<stdlib.h>
)
然后,您的示例代码几乎是C代码(一旦将auto
替换为void*
),而不是C ++。根据{{3}},可能是optimized(一旦包含<stdlib.h>
,甚至是不 -flto
但只有-O3
规则,将其更改为空的main
。 (我什至写了一份公共报告as-if,其中有第1.4.2节在几页中解释了优化是如何发生的。)
为了优化malloc
和free
,GCC在__attribute__(malloc)
的声明(在<stdlib.h>
内)中使用了一些malloc
bismon-chariot-doc.pdf。 / p>
-flto机械如何工作?
通过在“编译”和“链接”时使用实际上(类似GCC internals §25和/或类似GIMPLE的代码)的内部表示(实际上,链接步骤变为另一个具有整个程序优化功能的编译,因此您的代码在实践中会被“编译”两次。
在实践中, LTO 始终应该(在实践中)与一些优化标志(例如-O2
甚至是-O3
)一起使用,同时在链接时使用。因此,您应该使用g++ -flto -O2
编译并链接(在至少-flto
和<<的情况下使用-O2
应当在编译时和链接时使用完全相同的优化标记。
更准确地说,-flto
还在目标文件中嵌入了源代码的某些内部(类似于SSA)表示,并且也“在链接时”使用(特别是对于{{3} }和GIMPLE在重新链接您的 entire 程序并重新使用其GIMPLE时再次发生。实际上,GCC包含一些LTO前端和称为lto1
的编译器(除了C ++前端和称为cc1plus
的编译器),而lto1
是(当您 link 链接时使用了g++ -flto -O2
的em>来重新处理这些GIMPLE表示形式。
libjemalloc
可能有其自己的标头,并且可能具有inline
(或不可插入)功能。然后,当从其源代码编译该库时,还需要使用-flto -O2
(以便其Gimple存储在该库中)
最后,通常的malloc
被调用的事实独立于-flto
。这是一个链接器问题,而不是编译器问题。您可以尝试静态链接-ljemalloc
(然后最好使用gcc -flto -O2
来构建该库;如果不这样构建,就不会在{{1}上获得LTO优化) })。
您也可以将malloc
传递给编译和链接命令,以了解-v
在做什么。您甚至可以通过g++
来要求-Wl,--verbose
(以ld
开头)冗长。
请注意,LTO(及其使用的内部表示形式)是特定于编译器和版本的。 optimization和inlining之间的内部(Gimple&SSA)表示稍有不同(在Clang中,非常有所不同,因此当然不兼容)。动态链接器GCC 7不了解LTO。
PS。您可以安装g++
软件包并在代码中添加libjemalloc-dev
。另请参见GCC 8手册页。可能已配置或修补了#include <jemalloc/jemalloc.h>
,以定义一些libjemalloc
符号来代替je_malloc
。这样一来,对于LTO来说,在代码中使用malloc
会更简单(避免几个je_malloc
ld-linux(8)符号之间的冲突)。要了解有关共享库中符号的更多信息,请阅读Drepper的jemalloc(3)论文。当然,您应该期望LTO改变链接的行为!