当为不同的处理器编译时,程序是否可以使用(显着)更少的内存?
Can programs use (significantly) less memory when compiled for different processors?
我有一个正在为 AMD64 编译的 C++ 程序。当然,不同的处理器,尽管是 AMD64,但支持不同的功能和指令,因为它们实现了不同的微体系结构。为自己的机器优化程序的一种简单方法是仅在 Clang 或 GCC 中使用 -march=native
,但就分发而言,这不是很便携。一种更便携的解决方案是挑选特定的目标功能。
这显然会影响性能(有些处理器支持 AVX-512,有些不支持,有些支持 AVX2,有些不支持,等等),但这会影响内存使用吗(heap/stack,不是代码尺寸)在任何重要方面?
不同的对齐规则或类型宽度是您获得差异的两种主要方式,但 -march=
不会改变这一点,在同一 ISA 上针对同一 ABI 进行编译时不会。 (否则 -march=skylake-avx512
代码无法调用 -march=sandybridge
代码,反之亦然,如果他们不同意结构布局。)
针对不同的 ABI 进行编译可以节省 space,尤其是在指针密集型数据结构中。特别是 ILP32 ABI,例如 Linux x32 有 4 个字节指针而不是 8 个,所以 struct foo { foo *next; int val; };
是 8 个字节而不是 16 个字节(填充后使 sizeof(foo)
成为 alignof(foo)
的倍数它继承自需要 8 字节对齐的指针)。但这不适用于 100GB 数据的用例; 32 位指针将您限制为 4GiB 地址 space.
自动向量化时,-march=
可能会对 stack space 产生一些小的影响。例如为了 spill/reload ZMM 向量,函数可能将堆栈对齐 64。
或者对于较旧的 GCC,即使最终的 asm 实际上没有将任何向量存储或加载到堆栈帧,也要对齐。但这最多是每层函数嵌套额外 56 字节的浪费堆栈 space,而作为调用约定的一部分可以免费获得 16 字节对齐。
GCC / clang 的优化器不会 AFAIK 进行任何更改动态分配大小的优化。 Clang 有时可以完全在函数中优化动态分配,例如创建和销毁 std::vector<float> foo(100);
并且可以优化对它的所有访问。 (例如,将常量存储到向量中然后将它们读回,它可以优化它然后也消除分配。或者甚至没有使用的 std::vector
。)
如果您最终分配了一些内存页面但未完全使用,可能是一个更擅长减少内部碎片的不同分配器库可以节省 space。但这不是 -march=
影响的东西。
我有一个正在为 AMD64 编译的 C++ 程序。当然,不同的处理器,尽管是 AMD64,但支持不同的功能和指令,因为它们实现了不同的微体系结构。为自己的机器优化程序的一种简单方法是仅在 Clang 或 GCC 中使用 -march=native
,但就分发而言,这不是很便携。一种更便携的解决方案是挑选特定的目标功能。
这显然会影响性能(有些处理器支持 AVX-512,有些不支持,有些支持 AVX2,有些不支持,等等),但这会影响内存使用吗(heap/stack,不是代码尺寸)在任何重要方面?
不同的对齐规则或类型宽度是您获得差异的两种主要方式,但 -march=
不会改变这一点,在同一 ISA 上针对同一 ABI 进行编译时不会。 (否则 -march=skylake-avx512
代码无法调用 -march=sandybridge
代码,反之亦然,如果他们不同意结构布局。)
针对不同的 ABI 进行编译可以节省 space,尤其是在指针密集型数据结构中。特别是 ILP32 ABI,例如 Linux x32 有 4 个字节指针而不是 8 个,所以 struct foo { foo *next; int val; };
是 8 个字节而不是 16 个字节(填充后使 sizeof(foo)
成为 alignof(foo)
的倍数它继承自需要 8 字节对齐的指针)。但这不适用于 100GB 数据的用例; 32 位指针将您限制为 4GiB 地址 space.
自动向量化时,
-march=
可能会对 stack space 产生一些小的影响。例如为了 spill/reload ZMM 向量,函数可能将堆栈对齐 64。
或者对于较旧的 GCC,即使最终的 asm 实际上没有将任何向量存储或加载到堆栈帧,也要对齐。但这最多是每层函数嵌套额外 56 字节的浪费堆栈 space,而作为调用约定的一部分可以免费获得 16 字节对齐。
GCC / clang 的优化器不会 AFAIK 进行任何更改动态分配大小的优化。 Clang 有时可以完全在函数中优化动态分配,例如创建和销毁 std::vector<float> foo(100);
并且可以优化对它的所有访问。 (例如,将常量存储到向量中然后将它们读回,它可以优化它然后也消除分配。或者甚至没有使用的 std::vector
。)
如果您最终分配了一些内存页面但未完全使用,可能是一个更擅长减少内部碎片的不同分配器库可以节省 space。但这不是 -march=
影响的东西。