【发布时间】:2017-03-21 10:31:44
【问题描述】:
class Wrapper {
public:
// some functions operating on the value_
__m128i value_;
};
int main() {
std::vector<Wrapper> a;
a.resize(100);
}
vector a 中的Wrapper 对象的value_ 属性是否总是占用连续内存而__m128i values 之间没有任何间隙?
我的意思是:
[128 bit for 1st Wrapper][no gap here][128bit for 2nd Wrapper] ...
到目前为止,g++ 和我正在使用的 Intel cpu 以及 gcc godbolt 似乎都是如此。
由于Wrapper 对象中只有一个 __m128i 属性,这是否意味着编译器总是不需要在内存中添加任何类型的填充? (Memory layout of vector of POD objects)
测试代码1:
#include <iostream>
#include <vector>
#include <x86intrin.h>
int main()
{
static constexpr size_t N = 1000;
std::vector<__m128i> a;
a.resize(1000);
//__m128i a[1000];
uint32_t* ptr_a = reinterpret_cast<uint32_t*>(a.data());
for (size_t i = 0; i < 4*N; ++i)
ptr_a[i] = i;
for (size_t i = 1; i < N; ++i){
a[i-1] = _mm_and_si128 (a[i], a[i-1]);
}
for (size_t i = 0; i < 4*N; ++i)
std::cout << ptr_a[i];
}
警告:
warning: ignoring attributes on template argument
'__m128i {aka __vector(2) long long int}'
[-Wignored-attributes]
大会 (gcc god bolt):
.L9:
add rax, 16
movdqa xmm1, XMMWORD PTR [rax]
pand xmm0, xmm1
movaps XMMWORD PTR [rax-16], xmm0
cmp rax, rdx
movdqa xmm0, xmm1
jne .L9
我猜这意味着数据是连续的,因为循环只是将 16 个字节添加到它在循环的每个循环中读取的内存地址。它使用pand 进行按位与。
测试代码2:
#include <iostream>
#include <vector>
#include <x86intrin.h>
class Wrapper {
public:
__m128i value_;
inline Wrapper& operator &= (const Wrapper& rhs)
{
value_ = _mm_and_si128(value_, rhs.value_);
}
}; // Wrapper
int main()
{
static constexpr size_t N = 1000;
std::vector<Wrapper> a;
a.resize(N);
//__m128i a[1000];
uint32_t* ptr_a = reinterpret_cast<uint32_t*>(a.data());
for (size_t i = 0; i < 4*N; ++i) ptr_a[i] = i;
for (size_t i = 1; i < N; ++i){
a[i-1] &=a[i];
//std::cout << ptr_a[i];
}
for (size_t i = 0; i < 4*N; ++i)
std::cout << ptr_a[i];
}
组装 (gcc god bolt)
.L9:
add rdx, 2
add rax, 32
movdqa xmm1, XMMWORD PTR [rax-16]
pand xmm0, xmm1
movaps XMMWORD PTR [rax-32], xmm0
movdqa xmm0, XMMWORD PTR [rax]
pand xmm1, xmm0
movaps XMMWORD PTR [rax-16], xmm1
cmp rdx, 999
jne .L9
看起来也没有填充。 rax 每一步增加 32,即 2 x 16。额外的 add rdx,2 肯定不如测试代码 1 中的循环。
测试自动矢量化
#include <iostream>
#include <vector>
#include <x86intrin.h>
int main()
{
static constexpr size_t N = 1000;
std::vector<__m128i> a;
a.resize(1000);
//__m128i a[1000];
uint32_t* ptr_a = reinterpret_cast<uint32_t*>(a.data());
for (size_t i = 0; i < 4*N; ++i)
ptr_a[i] = i;
for (size_t i = 1; i < N; ++i){
a[i-1] = _mm_and_si128 (a[i], a[i-1]);
}
for (size_t i = 0; i < 4*N; ++i)
std::cout << ptr_a[i];
}
大会 (god bolt):
.L21:
movdqu xmm0, XMMWORD PTR [r10+rax]
add rdi, 1
pand xmm0, XMMWORD PTR [r8+rax]
movaps XMMWORD PTR [r8+rax], xmm0
add rax, 16
cmp rsi, rdi
ja .L21
...我只是不知道这对于 intel cpu 和 g++/intel c++ 编译器是否总是如此/(在此处插入编译器名称)...
【问题讨论】:
-
可以,但不保证对齐正确
-
它是 __m128i 元素。所以我希望这意味着向量的每个元素都有 128 位对齐。
-
那是另一个问题。保证连续存储,不保证过度对齐。
-
不是答案,但您可以(并且应该)静态地断言这些事情。然后,如果这些断言失败,您可以使用特定于实现(或标准,如果存在)的方法来修复结构。
-
@marshalcraft,标准保证向量是连续的。