如何分配与C中特定边界对齐的内存(例如,缓存行边界)?我正在寻找malloc/free类似的实现,理想情况下尽可能便携 - 至少在32位和64位架构之间.
编辑添加:换句话说,我正在寻找一些表现得像(现在过时的?)memalign函数的东西,它可以免费使用.
我希望我将问题简化为一个简单且可重复的测试用例.源(在此处)包含10个相同的简单循环副本.每个循环的形式如下:
#define COUNT (1000 * 1000 * 1000)
volatile uint64_t counter = 0;
void loopN(void) {
for (int j = COUNT; j != 0; j--) {
uint64_t val = counter;
val = val + 1;
counter = val;
}
return;
}
Run Code Online (Sandbox Code Playgroud)
变量的'volatile'很重要,因为它强制值在每次迭代时从内存中读取和写入.使用'-falign-loops = 64'将每个循环对齐到64个字节,并生成相同的程序集,除了对全局的相对偏移量:
400880: 48 8b 15 c1 07 20 00 mov 0x2007c1(%rip),%rdx # 601048 <counter>
400887: 48 83 c2 01 add $0x1,%rdx
40088b: 83 e8 01 sub $0x1,%eax
40088e: 48 89 15 b3 07 20 00 …Run Code Online (Sandbox Code Playgroud) 所以我有以下最小化的C11代码,它定义了一个包含uint16_t的结构(这意味着它应该与2个字节对齐的结构)并且我想将一个char缓冲区转换为指向该结构的指针.
随着警告全部上升,clang正确地抱怨结构的对齐要求未得到满足.所以我alignas在缓冲区中添加了一个C11 说明符,以确保缓冲区已经充分对齐,但是没有关闭clang.
我的问题是:我做错了alignas什么?或者只是-Wcast-align诊断只查看参数的类型而不是手动指定的对齐?(我意识到我可以只是为了void*使诊断静音,但由于这段代码应该是可移植的,所以除非我确定它是误报,否则我不想侧面执行诊断.)
#include <stdint.h>
#include <stdalign.h>
struct foo {
uint16_t field1;
};
int main(void) {
alignas(struct foo) char buffer[122] = {0};
struct foo *foo = (struct foo*)buffer;
return foo->field1;
}
Run Code Online (Sandbox Code Playgroud)
编译器选项和错误消息:
$ clang -ggdb -O3 foo.c -Weverything -Werror -Wno-c++98-compat -Wno-c11-extensions
foo.c:11:23: error: cast from 'char *' to 'struct foo *' increases required alignment from 1 to 2 [-Werror,-Wcast-align]
struct foo *foo = (struct foo*)buffer;
^~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
编译器版本:
$ clang -v
clang version 3.5.1 …Run Code Online (Sandbox Code Playgroud) 有人解释我如何在类中的成员声明的顺序决定该类的大小.
例如 :
class temp
{
public:
int i;
short s;
char c;
};
Run Code Online (Sandbox Code Playgroud)
上面这个类的大小是8个字节.
但是当成员声明的顺序改变如下
class temp
{
public:
char c;
int i;
short s;
};
Run Code Online (Sandbox Code Playgroud)
那么类的大小是12个字节.
怎么样?
我有一个结构,我想计算它的大小:
#pragma pack(push,4)
struct MyStruct
{
uint32_t i1; /* size=4, offset=0. */
uint32_t i2; /* size =4 offset =4 */
uint16_t s1; /* size =2 offset=8 */
unsigned char c[8]; /* size=8 offset=12*/
uint16_t s2; /* size=2 offset=20. */
uint16_t s3; /* size=2 offset=24. */
} ; // total size is 26
static_assert(sizeof(MyStruct) == 24, "size of MyStruct incorrect");
#pragma pack(pop)
Run Code Online (Sandbox Code Playgroud)
静态断言显示大小为24,但我的计算表明它应该是26.
为什么尺寸为24?
我正在使用visual studio 2012处理Windows 7,32位应用程序
我经常使用基于编译器的矢量化,例如,用于AVX.我试图#pragma vector aligned通过依赖C++ 11对齐功能,在不依赖基于编译器的扩展(例如Intel )的情况下提出一种更清晰的方法.如果您考虑下面的代码,例如,aligned::array<double,48> my_array;允许我在堆栈中声明一个具有正确对齐的数组,并且如果它在相同的转换单元中使用,则编译器似乎认识到这一点.
我现在的问题是如何声明具有对齐参数的函数.我最成功的尝试是,例如,aligned::ptr<double>在f()下面的函数中使用.
gcc在没有警告(使用-std=c++0x -O3)的情况下编译它,并且循环被矢量化.icc然而,英特尔发出警告并且没有正确地向量化(warning #3463: alignas does not apply here; using type alignas(64) = T;).
谁是对的?我使用alignas有什么问题吗?有没有更好的方法来实现这一目标?
namespace aligned {
template <class T, int N>
using array alignas(64) = T[N];
template <class T>
using type alignas(64) = T;
template <class T>
using ptr = type<T> *;
}
#ifdef __ICC
#define IVDEP "ivdep"
#else
#define IVDEP "GCC ivdep"
#endif
void f(aligned::ptr<double> x, …Run Code Online (Sandbox Code Playgroud) 对齐和未对齐的内存访问有什么区别?
我在TMS320C64x DSP上工作,我想使用内部函数(汇编指令的C函数),它有
ushort & _amem2(void *ptr);
ushort & _mem2(void *ptr);
Run Code Online (Sandbox Code Playgroud)
_amem22字节的对齐访问在哪里进行_mem2未对齐访问.
我什么时候应该使用哪个?
阅读Eigen库文档,我注意到某些对象无法通过值传递.C++ 11或计划开发中是否有任何进展可以安全地按价值传递这些对象?
另外,为什么按值返回这些对象没有问题?
过去,ARM处理器无法正确处理未对齐的内存访问(ARMv5及更低版本).喜欢的东西u32 var32 = *(u32*)ptr;也只是失败(引发异常),如果ptr没有正确的4字节对齐.
编写这样的语句对于x86/x64可以正常工作,因为这些CPU总是非常有效地处理这种情况.但根据C标准,这不是一种"正确"的写作方式.u32显然等同于4个字节的结构,必须在4个字节上对齐.
在保持正统正确性并确保与任何cpu完全兼容的同时获得相同结果的正确方法是:
u32 read32(const void* ptr)
{
u32 result;
memcpy(&result, ptr, 4);
return result;
}
Run Code Online (Sandbox Code Playgroud)
这个是正确的,将为任何能够或不在未对齐位置读取的CPU生成适当的代码.更好的是,在x86/x64上,它已针对单个读取操作进行了适当优化,因此具有与第一个语句相同的性能.它便携,安全,快速.谁能问更多?
好吧,问题是,在ARM上,我们没有那么幸运.
编写memcpy版本确实是安全的,但似乎导致系统谨慎的操作,这对于ARMv6和ARMv7(基本上是任何智能手机)来说都非常慢.
在一个严重依赖读取操作的性能导向应用程序中,可以测量第一版和第二版之间的差异:它在设置时 大于5倍gcc -O2.这太过不容忽视了.
试图找到一种使用ARMv6/v7功能的方法,我寻找了几个示例代码的指导.不幸的是,他们似乎选择了第一个声明(直接u32访问),这不应该是正确的.
这还不是全部:新的GCC版本现在正在尝试实现自动矢量化.在x64上,这意味着SSE/AVX,在ARMv7上意味着NEON.ARMv7还支持一些新的"加载多个"(LDM)和"存储多个"(STM)操作码,这些操作码需要指针对齐.
那是什么意思 ?好吧,编译器可以自由地使用这些高级指令,即使它们没有从C代码中特别调用(没有内在的).为了做出这样的决定,它使用a u32* pointer应该在4个字节上对齐的事实.如果不是,那么所有的赌注都是关闭的:未定义的行为,崩溃.
这意味着即使在支持未对齐内存访问的CPU上,使用直接u32访问也是危险的,因为它可能导致在高优化设置下生成错误的代码(-O3).
那么现在,这是一个困境:如何在未对齐内存访问的情况下访问ARMv6/v7的本机性能而无需编写错误的版本u32访问权限?
PS:我也试过__packed()说明,从性能的角度看,它们似乎与memcpy方法完全一样.
[编辑]:感谢迄今为止收到的优秀元素.
看看生成的程序集,我可以确认@Notlikethat发现该memcpy版本确实生成了正确的ldr操作码(未对齐的加载).但是,我还发现生成的程序集无用地调用str(命令).因此,完整的操作现在是一个未对齐的加载,一个对齐的存储,然后是一个最终对齐的加载.这比必要的工作要多得多.
回答@haneefmubarak,是的,代码正确内联.而且,不是,memcpy提供最佳速度是非常远的,因为强制代码接受直接u32 …
我总是听说未对齐的访问很糟糕,因为它们会导致运行时错误并导致程序崩溃或减慢内存访问速度.但是我找不到任何关于它们会减慢速度的实际数据.
假设我在x86上并且有一些(但未知)未对齐访问的共享 - 实际可能的最差减速是什么?如何在不消除所有未对齐访问和比较两个版本代码的运行时间的情况下估算它?