提问人:resu 提问时间:11/4/2023 最后编辑:resu 更新时间:11/4/2023 访问量:106
C++ 参数传递优化普通类大小
C++ parameter passing optimization of trivial classes size
问:
我有一个微不足道的,它代表了 std::int8_t 的类型安全包装器。 已简化为表示最小的可重现问题。struct A
A
出于性能原因,在此示例中将 struct 作为参数传递时,结构应遵循与 but 相同的优化:-O3
std::int8_t
#include <cstdint>
using in = std::int8_t;
template<typename T>
struct A {
T value;
};
template <typename T>
inline constexpr auto operator +( A <T> lhs, A <T> rhs)
noexcept -> decltype(lhs.value + rhs.value)
{ return lhs.value + rhs.value; }
template <typename T>
inline constexpr auto operator -( A <T> lhs, A <T> rhs)
noexcept -> decltype(lhs.value - rhs.value)
{ return lhs.value - rhs.value; }
template <typename T>
inline constexpr auto operator *( A <T> lhs, A <T> rhs)
noexcept -> decltype(lhs.value * rhs.value)
{ return lhs.value * rhs.value; }
auto h(A<in> a, A<in> b){
return (a*b)*(a-b);
}
auto g(in a, in b){
return (a*b)*(a-b);
}
该函数(与函数相比,我调用的函数包含两个开销:h(A<std::int8_t>, A<std::int8_t>)
movsx
g(std::int8_t, std::int8_t)
h(A<signed char>, A<signed char>):
movsx eax, dil <-- diff
movsx ecx, sil <-- diff
mov edx, ecx
imul edx, eax
sub eax, ecx
imul eax, edx
ret
g(signed char, signed char):
mov eax, esi
imul eax, edi
sub edi, esi
imul eax, edi
ret
问题是否与标准 C++ 的某些结构空间约束有关?请解释一下背后的理论
提供(如果存在)一种可移植/特定于编译器的优化方式。
A
注意:h == g 对于每个 std::int*_t != (std::int8_t 或 std::int16_t) (即 std::int32_t, std::int64_t)
答:
Clang 使用对 x86-64 System V 调用约定的未记录扩展,其中窄整数参数扩展到 32 位。显然,这只适用于原始类型,而不适用于结构包装器。GCC 使函数调用与此兼容,但不依赖于它来表示传入的参数:它将用于这两个函数。请参阅向 x86-64 ABI 的指针添加 32 位偏移量时是否需要符号或零扩展?movsx
https://godbolt.org/z/ssYqrhr1n 显示 AArch64 clang 必须用于两个版本,因此此调用约定扩展似乎是特定于目标的。sxtb
函数返回 int
(因为您使用返回类型,并且 narrow 将隐式整数提升为 )。如果尚未保证输入的符号扩展为宽度,则必须对输入进行符号扩展,以便从对提升值的操作中获得正确的结果。auto
T
int
int8_t
int
int
如果你 make 或 return ,clang 足够聪明,可以优化符号扩展,因为它知道像 // 这样的低位操作不依赖于高位的临时;只有像除法和右移这样的东西才能做到这一点。(向 x86-64 ABI 的指针添加 32 位偏移量时是否需要符号或零扩展名?h()
g()
int8_t
add
sub
mul
)
int8_t h(A<in> a, A<in> b){
return (a*b)*(a-b);
}
// x86-64 clang 17 -O3
h(A<signed char>, A<signed char>):
mov eax, esi
mul dil
sub dil, sil
mul dil
ret
// AArch64 clang
h(A<signed char>, A<signed char>):
mul w8, w1, w0
sub w9, w0, w1
mul w0, w8, w9
ret
正如 @463035818_is_not_an_ai 所指出的,您实际上可能希望模板函数返回 A<T>
而不仅仅是 or ;这也将避免对符号扩展的需要。T
int
评论
h
int
T
A<T>
h()
union { int8_t; A<int8_t>; };
int
union { int; A<int8_t>; }
评论
-E
movsx
A<T> + A<T>
T
A<T>