对齐、总尺寸和SSE

Alignment, total size and SSE

本文关键字:SSE 对齐      更新时间:2023-10-16

我正在尝试为PCL库定义一个自定义点类型。在那篇教程中,他们谈论的是记忆对齐,所以我一开始就试图了解它是如何工作的。

在本页中,他们提出了一种计算结构总对齐度的相当简单的方法。例如,这种结构

// Alignment requirements
// (typical 32 bit machine)
// char         1 byte
// short int    2 bytes
// int          4 bytes
// double       8 bytes
// structure C
typedef struct structc_tag
{
  char        c;
  double      d;
  int         s;
} structc_t;

将具有24:的大小

1 byte for the char + 7 bytes of padding + 8 bytes for the double + 4 bytes for the int + 4 bytes of padding

对于g++ 4.8.1sizeof返回24。到目前为止,一切都很好。

现在,在PCL中,他们用这个方案定义了SSE对齐的点类型(这里是最简单的点,它在每个轴上保持位置)。

union
{
  float data[4];
  struct
  {
    float x;
    float y;
    float z;
  };
};

sizeof返回16。使用并集可以确保point type是SSE对齐的(我在这里读到的是16字节对齐),并且使用结构可以访问轴值。

引用PCL文档:

用户可以访问的points[i].data[0]或points[i].x访问例如x坐标。

我的推理在此之前有效吗?


在我的情况下,我想更改双打的浮动,以便在XY轴上有更高的精度。

那么,将点类型声明为:就足够了吗

union {
  float data[4];
  struct {
    double x;
    double y;
    float z;
  };
};

sizeof返回24,它不是16的倍数(所以我知道它不是SSE对齐的),但它是"双对齐的"。

我的问题是,我如何定义我的点类型,以便能够将XY坐标存储为双坐标,并且仍然是SSE aligned

PS:此外,如果你们中有人知道这方面的好资源,请告诉我。我想更好地理解这个话题。

PS 2:我忘了说,我正在尝试的平台是64位的。

PS 3:如果可能的话,我对pre-C++11解决方案感兴趣。像g++ 4.4(及其对应的MinGW)这样旧的编译器必须能够构建新的点类型。

对象的大小和对齐不是一回事。如果结构的大小是16字节或一些倍数,并不意味着它必须是16字节对齐的。

在您的情况下,由于代码是以64位模式编译的,您只需要将结构填充到32字节。在64位模式下,堆栈在Windows和Linux/Unix中是16字节对齐的。

在32位模式中,它不必是16字节对齐的。你可以测试一下。如果您在32位模式下在MSVC中运行下面的代码,您可能会发现数组中每个元素的地址都不是16字节对齐的(您可能需要运行几次)。因此,即使结构的大小是16字节的倍数,它也不一定是16字节对齐的。

#include <stdio.h>
int main() { 
    union a {
        float data[4];
        struct {
            double x;
            double y;
            float z;
            float pad[3];
    };
    a b[10];
    for(int i=0; i<10; i++) {
        printf("%dn", ((int)&b[i])%16);
    }
}

如果您希望您的代码也能在32位模式下工作,那么您应该对齐内存。如果你在Windows或Linux上以32位模式运行下面的代码,你会发现它总是16字节对齐的。

#include <stdio.h>
#ifdef _MSC_VER // If Microsoft compiler
#define Alignd(X) __declspec(align(16)) X
#else // Gnu compiler, etc.
#define Alignd(X) X __attribute__((aligned(16)))
#endif
int main() {
    union a {
        float data[4];
        struct {
            double x;
            double y;
            float z;
            float pad[3];
    };
    a Alignd(b[10]);
    for(int i=0; i<10; i++) {
        printf("%dn", ((int)&b[i])%16);
    }
}

为了有一个具有2个双精度和一个浮点的结构,并且是SSE对齐的(16字节),请使用:

#pragma pack(1)
struct T
{
 double x,y;   // 16 bytes
 float z;      // 4 bytes
 char gap[12]; // 12 bytes
};

sizeof(T)将是32,所以如果第一个点是16字节对齐的,那么整个向量将被对齐。

为了使第一个点对齐,应该对堆栈变量使用__attribute((aligned(16)),或对堆内存使用aligned_alloc。

但是,PCL的大多数算法都是为浮点而非双精度编写和硬编码的,所以它们不起作用。。。

参考:pcl用户链接