是否有关于元组伪成员的布局和内存对齐的正式规范?
反正有没有修改元组中类型的内存对齐?它是否受#pragma pack()指令的影响?
例如:
typedef std::tuple<uint8_t, uint32_t> myTuple;
Run Code Online (Sandbox Code Playgroud)
是否有任何规范说这将在内存中与以下相同:
#pragma pack() // Default packing
struct myStruct
{
uint8_t first;
uint32_t second;
}
Run Code Online (Sandbox Code Playgroud)
抱歉,如果这是一个愚蠢的问题,但我不完全理解模板的对齐.
编辑:我正在努力完成的例子
目前我有一些东西......
#pragma pack(push)
#pragma pack(4)
struct cTriangle
{
uint32 Index[3];
};
#pragma pack(pop)
template <class T>
inline bool Read(cFileStream& fStream, std::vector<T>& vec)
{
if (!vec.size())
return true;
// fStream.Read(void* pBuffer, size_t Size)
// Just a wrapper around a binary ifstream really
return fStream.Read(&vec[0], sizeof(T) * vec.size());
}
std::vector<cVector3> vPoint;
vPoint.resize(Verticies);
bool result = Read(FileStream, vPoint);
Run Code Online (Sandbox Code Playgroud)
如果我想为元编程目的使用typedef …
在C++ 03代码中,我如何可移植地实现unsigned char[sizeof(T)]与给定类型具有相同大小和对齐的缓冲区T?
例如:
template<class T>
void test()
{
unsigned char buffer[sizeof(T)]; // <----- how do I ensure this is aligned?
if (some_condition())
{
T *const obj = new(buffer) T();
// ...
obj->~T();
}
else { /* use 'buffer' for something else */ }
}
Run Code Online (Sandbox Code Playgroud)
这甚至是可能的,还是你被迫使用编译器扩展来实现它?
在考虑这个问题的反例时,我提出了:
struct A
{
alignas(2) char byte;
};
Run Code Online (Sandbox Code Playgroud)
但如果这是合法的和标准的布局,它是否与布局兼容struct B?
struct B
{
char byte;
};
Run Code Online (Sandbox Code Playgroud)
此外,如果我们有
struct A
{
alignas(2) char x;
alignas(4) char y;
};
// possible alignment, - is padding
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
// x - - - y - - - x - - - y - - -
struct B
{
char x;
char y;
}; // …Run Code Online (Sandbox Code Playgroud) 在我们的嵌入式架构中,我们有一个64位IAB(指令对齐缓冲器).为了优化获取序列,需要循环体开始与8字节边界对齐.
使用该.balign指令很容易在汇编中实现这一点,但我找不到一个会暗示C编译器对齐代码的语法.
尝试在for循环与内联汇编之前使用该.balign指令不起作用,因为它对齐for循环prolog(设置)而不是循环体本身.
asm()在循环内部执行相同操作时,将nop-s 添加到循环体中,这需要花费宝贵的周期.
编辑1:假设代码:
__asm__ volatile("nop");
__asm__ volatile("nop");
for (j0=0; j0<N; j0+=4)
{
c[j0+ 0] = a[j0+ 0] + b[j0+ 0];
c[j0+ 1] = a[j0+ 1] + b[j0+ 1];
c[j0+ 2] = a[j0+ 2] + b[j0+ 2];
c[j0+ 3] = a[j0+ 3] + b[j0+ 3];
}
Run Code Online (Sandbox Code Playgroud)
我希望第一个c=a+b与8字节地址对齐.我可以nop在初步编译后添加类似上面的-s,但这是一个特殊的解决方案,它会破坏第一个代码更改.
编辑2:感谢@R ..,解决方案是使用-falign-loops=8编译器选项.
为了理解如何确保对齐要求得到满足,我多次阅读以下文章,从OpenCL p.no:157中的异构计算一书.这显示了如何为图像卷积中的问题填充填充(假设工作组大小为16 x 16).
对齐记忆访问
NVIDIA和AMD GPU的性能均受益于全局内存中的数据对齐.特别是对于NVIDIA,在128字节边界上对齐访问和访问128字节段将理想地映射到存储器硬件.但是,在这个例子中,16宽工作组将只访问64字节段,因此数据应该对齐到64字节的地址.这意味着每个工作组访问的第一列应该从64字节对齐的地址开始.在此示例中,使边框像素不生成值的选择确定所有工作组的偏移量将是工作组维度的倍数(即,对于16 x 16工作组,工作组将开始访问列N*16处的数据) .为确保每个工作组正确对齐,
1 - 任何人都可以帮助我理解填充每个工作组访问的第一列后是如何从64字节对齐的地址开始的(上述段落中提到的要求,对吧?)?
2 - 该图也是正确的声明:对于16 x 16工作组,工作组将开始访问N*16列的数据.
如果正确,图中所示的工作组1,2应该开始访问第1x16列的数据,这与图中所示的相反.我完全糊涂了!! :(
更新: Q-2现在对我来说很清楚. 实际上图中显示的工作组是2,1(在opencl惯例中,第一列),所以它完全正确:2x16 = 32而不是我想的1x16.
但问题不是.1仍然没有答案.

" C编程语言 "一书在第8.7节" 实例 - 存储分配器 "中讨论了"限制性最强的类型" :
虽然机器各不相同,但每台机器都有一个限制性最强的类型:如果限制性最强的类型可以存储在特定地址,则所有其他类型也可以.在某些机器上,限制最多的类型是a
double; 在别人身上,int或是long足够的.
在他们的代码中,union header使用类型对齐long.
限制性最强的是什么意思?它可能是最大的类型(例如double),还是有另一种方法?
我经常需要将动态数组的开头与用于矢量化的16,32或64字节边界对齐,例如,对于SSE,AVX,AVX-512.我正在寻找一种透明和安全的方式,特别是与智能指针一起使用它std::unique_ptr.
比如分配和释放例程的实现
template<class T>
T * allocate_aligned(int alignment, int length)
{
// omitted: check minimum alignment, check error
T * raw = 0;
// using posix_memalign as an example, could be made platform dependent...
int error = posix_memalign((void **)&raw, alignment, sizeof(T)*length);
return raw;
}
template<class T>
struct DeleteAligned
{
void operator()(T * data) const
{
free(data);
}
};
Run Code Online (Sandbox Code Playgroud)
我想做这样的事情
std::unique_ptr<float[]> data(allocate_aligned<float>(alignment, length));
Run Code Online (Sandbox Code Playgroud)
但我无法弄清楚如何unique_ptr使用正确Deleter而无需用户指定它(这是导致错误的潜在原因).我找到的替代方法是使用模板别名
template<class T>
using aligned_unique_ptr = std::unique_ptr<T[], DeleteAligned<T>>;
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用
aligned_unique_ptr<float> data(allocate_aligned<float>(alignment, length)); …Run Code Online (Sandbox Code Playgroud) 根据https://isocpp.org/wiki/faq/dtors#placement-new ,传递给placement-new的地址必须正确对齐.但它给出的例子似乎与此相矛盾.
char memory[sizeof(Fred)];
Run Code Online (Sandbox Code Playgroud)
这个缓冲区很可能不会与Fred对齐,因为它是一个愚蠢的char[],所以memory可以指向几乎任何地方.然后它在这个地址上做了一个新的位置.
这个例子是否与DANGER脚注中的对齐要求相矛盾?
这导致了一个相关的问题:
如何创建一个为类型对齐的缓冲区(堆栈或堆)T(用于一个或多个T对象的placement-new)?
缓冲区我的意思是一个char[]或void*一些大小的缓冲区,而不是T[]因为那将是对象分配,这会破坏后续进行放置的点.
谢谢.
这些结构体align1andalign2包含相同的数据,但align1由于嵌套布局而具有更多填充。我怎样才能获得align2while的内存节省对齐,同时也使用像 in 这样的嵌套结构align1?
int main() {
struct align1 {
struct {
double d; // 8 bytes
bool b1; //+1 byte (+ 7 bytes padding) = 16 bytes
} subStruct;
bool b2; //+1 byte (+ 7 bytes padding) = 24 bytes
};
struct align2 {
double d; // 8 bytes
bool b1, b2; //+2 byte (+ 6 bytes padding) = 16 bytes
};
std::cout << "align1: " << sizeof(align1) << " …Run Code Online (Sandbox Code Playgroud) c++ padding inner-classes memory-alignment compiler-optimization
编辑:感谢大家的回答和回复。Language Lawyer 的答案在技术上是正确的,因此被接受,但 Human-Compiler 的答案是唯一符合赏金标准(获得 2+ 分)的答案,或者对问题的特定主题进行了充分阐述。
将对象b
置于协程状态是否定义了行为(例如,将其作为参数,或将其保留在暂停点上),在哪里alignof(b) > __STDCPP_DEFAULT_NEW_ALIGNMENT__?
例子:
inline constexpr size_t large_alignment =
__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2;
struct alignas(large_alignment) behemoth {
void attack();
unsigned char data[large_alignment];
};
task<void> invade(task_queue &q) {
behemoth b{};
co_await submit_to(q);
b.attack();
}
Run Code Online (Sandbox Code Playgroud)
当调用协程时,协程状态的堆内存通过operator new.
此调用operator new
可能采用以下形式之一:
无论调用采用哪种形式,请注意它不使用接受 a 的重载std::align_val_t,这是分配必须对齐多于 的内存所必需的__STDCPP_DEFAULT_NEW_ALIGNMENT__。因此,如果__STDCPP_DEFAULT_NEW_ALIGNMENT__
必须将对齐大于 的对象保存在协程状态中,则应该无法保证该对象最终会在内存中正确对齐。
async f(): Assertion `reinterpret_cast<uintptr_t>(&b) % 32ull == 0' failed.
Run Code Online (Sandbox Code Playgroud)
所以它绝对不能在 GCC …
memory-alignment ×10
c++ ×7
c++11 ×3
c ×2
alignment ×1
architecture ×1
c++20 ×1
cuda ×1
for-loop ×1
gcc ×1
gpgpu ×1
long-integer ×1
malloc ×1
opencl ×1
padding ×1
tuples ×1
unions ×1
unique-ptr ×1