由于缓存的使用,二进制大小会对性能产生间接影响。
我编写这段代码是为了调查编译器是否会折叠相同的模板代码(在本例中是指针的排序)。
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define CAST 1
template<typename T>
void f() {
std::size_t n = 1024;
T** v = (T**)std::malloc(n * sizeof(T*));
for(std::size_t i = 0; i < n; ++i) {
v[i] = (T*)std::malloc(sizeof(T));
*v[i] = T{};
}
#if CAST
std::sort((void**)v, (void**)(v + n));
#else
std::sort(v, v + n);
#endif
std::printf("%p, %p\n", v[0], v[n-1]);
for(std::size_t i = 0; i < n; ++i) {
std::free(v[i]);
}
std::free(v);
}
int main() {
std::printf("CAST = %d\n", CAST);
f<double>();
f<float>();
f<int>();
f<short>();
f<char>();
f<long>();
f<unsigned>();
f<unsigned long>();
}
我用
-O3
编译它,然后用 strip
编译二进制文件。有趣的是,生成的二进制大小确实取决于在排序之前指针是否转换为 void*
。特别是对于 Clang++
,二进制文件要小得多。
演员表 | G++ 12 | 叮当++ 15 |
---|---|---|
假 | 22KB | 35KB |
真实 | 18KB | 19KB |
这很奇怪,因为人们会认为如果
sort
是内联的,则生成的机器代码不应该依赖于 CAST
。但如果它不是内联的,那么编译器应该注意到一堆 sort<>
函数具有相同的机器代码并将它们折叠成一个。
但显然,这并没有发生。我想知道为什么?
您看到的行为可能取决于编译器如何处理特定于类型的优化和内联。尽管对指针进行排序的逻辑是相同的,但由于大小或对齐等细微的类型差异,Clang 仍可能为每种类型生成单独的代码,尤其是在内联发生时。转换为 void** 会擦除类型信息,从而允许更通用的版本,从而减少二进制大小。尽管使用 -O3 进行了积极的优化,编译器可能不会对这些实例化进行重复数据删除。链接时优化 (-flto) 可以帮助跨类型折叠相同的代码,这可能解释了为什么在内联未发生时您看不到折叠。此变体还显示了 Clang 处理模板的方式与其他编译器不同,从而导致了这些大小差异。