为什么将指针强制转换为“void*”会减少二进制大小?

问题描述 投票:0回答:1

由于缓存的使用,二进制大小会对性能产生间接影响。

我编写这段代码是为了调查编译器是否会折叠相同的模板代码(在本例中是指针的排序)。

#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<>
函数具有相同的机器代码并将它们折叠成一个。

但显然,这并没有发生。我想知道为什么?

c++ performance templates g++ clang++
1个回答
0
投票

您看到的行为可能取决于编译器如何处理特定于类型的优化和内联。尽管对指针进行排序的逻辑是相同的,但由于大小或对齐等细微的类型差异,Clang 仍可能为每种类型生成单独的代码,尤其是在内联发生时。转换为 void** 会擦除类型信息,从而允许更通用的版本,从而减少二进制大小。尽管使用 -O3 进行了积极的优化,编译器可能不会对这些实例化进行重复数据删除。链接时优化 (-flto) 可以帮助跨类型折叠相同的代码,这可能解释了为什么在内联未发生时您看不到折叠。此变体还显示了 Clang 处理模板的方式与其他编译器不同,从而导致了这些大小差异。

© www.soinside.com 2019 - 2024. All rights reserved.