C++ 11引入了alignas说明符来指定变量的对齐方式,以及alignof运算符来查询类型的默认对齐方式.但是,我没有看到任何方法来获得特定变量的对齐方式.让我们采取以下简单的例子:
alignas(16) float* array;
Run Code Online (Sandbox Code Playgroud)
以下是我们可以做的事情:
alignof(float*) 返回8,这显然不是我们想要的.alignof(array)返回16,这正是我们想要的,但这是一个编译器扩展; alignof由标准指定不能用于特定变量.alignof(decltype(array)) 返回8,这是非常期待但不是我们想要的.std::alignment_of是实施的alignof,所以它没有多大帮助.我想要一种机制来确认特定变量array是否在16字节边界上对齐.标准中是否有任何内容可以执行此类查询?
我一直在阅读一篇关于MSDN中的无锁编程的文章.它说 :
在所有现代处理器上,您可以假设自然对齐的本机类型的读取和写入 是原子的.只要存储器总线至少与读取或写入的类型一样宽,CPU就会在单个总线事务中读取和写入这些类型,这使得其他线程无法在半完成状态下看到它们.
它给出了一些例子:
// This write is not atomic because it is not natively aligned.
DWORD* pData = (DWORD*)(pChar + 1);
*pData = 0;
// This is not atomic because it is three separate operations.
++g_globalCounter;
// This write is atomic.
g_alignedGlobal = 0;
// This read is atomic.
DWORD local = g_alignedGlobal;
Run Code Online (Sandbox Code Playgroud)
我读了很多答案和评论说,没有什么能保证在C++中是原子的,甚至在标准中都没有提到,在SO中,现在我有点困惑.我误解了这篇文章吗?或者文章作者是否谈论了非标准和特定于MSVC++编译器的内容?
所以根据这篇文章,下面的作业必须是原子的,对吧?
struct Data
{
char ID;
char pad1[3];
short Number;
char pad2[2];
char Name[5];
char pad3[3];
int Number2;
double …Run Code Online (Sandbox Code Playgroud) 我一直在使用Howard Hinnant的堆栈分配器,它就像一个魅力,但实现的一些细节对我来说有点不清楚.
new和delete使用?的allocate()和deallocate()成员函数使用::operator new和::operator delete分别.同样,成员函数construct()使用全局布局new.为什么不允许任何用户定义的全局或类特定的重载?std::alignment_of<T>?max_size有throw()异常规范?这不是劝阻(参见例如更有效的C++第14项)?在分配器中发生异常时,是否真的有必要终止和中止?这是否随新的C++ 11 noexcept关键字而改变?construct()成员函数将是完美转发(在构造函数被调用)的理想选择.这是编写符合C++ 11标准的分配器的方法吗?c++ memory-alignment exception-specification allocator c++11
给定void *一些存储,如何检查它是否指向正确对齐的存储而没有任何实现定义的行为?
我们当然有std::align,但是有更有效的方法吗?
template <std::size_t alignment>
inline bool is_aligned(void * ptr) noexcept {
std::size_t max = 1u;
return std::align(alignment, 1u, ptr, max);
}
Run Code Online (Sandbox Code Playgroud)
PS:我需要以兼容C++标准的方式执行此操作,而不依赖于任何特定于平台的(实现定义的)黑客攻击.
PPS:我为我(理解)英语而道歉,而不是我的母语.
EDIT(2018.08.24):从标题中删除了"有效",添加了更多的措辞,强调我不希望任何实现定义或特定于平台的行为.
在我的第一个例子中,我使用了两个位域int64_t.当我编译并获得类的大小时,我得到8.
class Test
{
int64_t first : 40;
int64_t second : 24;
};
int main()
{
std::cout << sizeof(Test); // 8
}
Run Code Online (Sandbox Code Playgroud)
但是,当我将第二个bitfeild更改int32_t为类的大小时,会增加到16:
class Test
{
int64_t first : 40;
int32_t second : 24;
};
int main()
{
std::cout << sizeof(Test); // 16
}
Run Code Online (Sandbox Code Playgroud)
这种情况发生在GCC 5.3.0和MSVC 2015上.但为什么呢?
这是我通常用于通过Visual Studio和GCC获得对齐内存的代码
inline void* aligned_malloc(size_t size, size_t align) {
void *result;
#ifdef _MSC_VER
result = _aligned_malloc(size, align);
#else
if(posix_memalign(&result, align, size)) result = 0;
#endif
return result;
}
inline void aligned_free(void *ptr) {
#ifdef _MSC_VER
_aligned_free(ptr);
#else
free(ptr);
#endif
}
Run Code Online (Sandbox Code Playgroud)
这个代码一般是好的吗?我也见过人们用_mm_malloc,_mm_free.在大多数情况下,我想要对齐内存,使用SSE/AVX.我可以一般使用这些功能吗?它会使我的代码更简单.
最后,创建我自己的函数来对齐内存很容易(见下文).为什么会有这么多不同的常用函数来获得对齐的内存(其中许多只能在一个平台上运行)?
此代码执行16字节对齐.
float* array = (float*)malloc(SIZE*sizeof(float)+15);
// find the aligned position
// and use this pointer to read or write data into array
float* alignedArray = (float*)(((unsigned long)array + 15) & (~0x0F));
// dellocate memory …Run Code Online (Sandbox Code Playgroud) 请考虑以下包含一些环境值的结构:
struct environment_values {
uint16_t humidity;
uint16_t temperature;
uint16_t charging;
};
Run Code Online (Sandbox Code Playgroud)
我想为这些带有幻像类型*的值添加一些额外的信息,并同时使它们的类型不同:
template <typename T, typename P>
struct Tagged {
T value;
};
// Actual implementation will contain some more features
struct Celsius{};
struct Power{};
struct Percent{};
struct Environment {
Tagged<uint16_t,Percent> humidity;
Tagged<uint16_t,Celsius> temperature;
Tagged<uint16_t,Power> charging;
};
Run Code Online (Sandbox Code Playgroud)
内存布局是Environment一样的environment_values吗?这是否也适用于混合类型布局,例如:
struct foo {
uint16_t value1;
uint8_t value2;
uint64_t value3;
}
struct Foo {
Tagged<uint16_t, Foo> Value1;
Tagged<uint8_t , Bar> Value2;
Tagged<uint64_t, Quux> Value3;
}
Run Code Online (Sandbox Code Playgroud)
对于我迄今为止尝试过的所有类型,以下断言都是:
template <typename T, …Run Code Online (Sandbox Code Playgroud) 以下是GCC 6和7在使用时无法优化的一些代码std::array:
#include <array>
static constexpr size_t my_elements = 8;
class Foo
{
public:
#ifdef C_ARRAY
typedef double Vec[my_elements] alignas(32);
#else
typedef std::array<double, my_elements> Vec alignas(32);
#endif
void fun1(const Vec&);
Vec v1{{}};
};
void Foo::fun1(const Vec& __restrict__ v2)
{
for (unsigned i = 0; i < my_elements; ++i)
{
v1[i] += v2[i];
}
}
Run Code Online (Sandbox Code Playgroud)
编译上面的g++ -std=c++14 -O3 -march=haswell -S -DC_ARRAY代码会产生很好的代码:
vmovapd ymm0, YMMWORD PTR [rdi]
vaddpd ymm0, ymm0, YMMWORD PTR [rsi]
vmovapd YMMWORD PTR [rdi], ymm0 …Run Code Online (Sandbox Code Playgroud) 我读过这个 什么时候应该担心对齐?但我仍然不知道我是否要担心放置新运算符返回的对齐指针 - 就像在这个例子中:
class A {
public:
long double a;
long long b;
A() : a(1.3), b(1234) {}
};
char buffer[64];
int main() {
// (buffer + 1) used intentionally to have wrong alignment
A* a = new (buffer + 1) A();
a->~A();
}
Run Code Online (Sandbox Code Playgroud)
__alignof(A) == 4,(buffer + 1)是不对齐的4.但一切正常 - 这里有完整的例子:http://ideone.com/jBrk8
如果这取决于架构,那么我正在使用:linux/powerpc/g ++ 4.xx
[更新]发布此问题后,我读了这篇文章:http://virtrev.blogspot.de/2010/09/memory-alignment-theory-and-c-examples.html.也许在我的情况下唯一的缺点是性能损失,我的意思是未对齐的访问成本超过对齐?
我搜索了关于未对齐访问的标准,但没有找到任何东西(也许我是无意的).
是不确定的行为?它是实现定义的吗?
由于许多当前的CPU支持未对齐访问,因此未对齐的内存访问是实现定义的,这是明智的.是这样的吗?
通过未对齐访问,我的意思是例如:
alignas(int) char buffer[sizeof(int)+1];
int &x = *new(buffer+1) int;
x = 42;
Run Code Online (Sandbox Code Playgroud) c++ ×10
memory-alignment ×10
c++11 ×3
c++17 ×2
allocator ×1
atomic ×1
bit-fields ×1
c ×1
c++14 ×1
g++ ×1
gcc ×1
optimization ×1
performance ×1
powerpc ×1
simd ×1
sse ×1
struct ×1
types ×1