数组结构和内存访问模式

use*_*950 5 c arrays struct simd cpu-cache

这是对这个原始问题的跟进,增加了一些新信息.如果您感兴趣,请参见第一部分:数组结构,结构数组和内存使用模式

我第一次尝试为简单类设置数组结构时似乎存在很多问题.主要是针对指针的过多内存分配以及在上一个问题中从vec3_b分配这些指针可能导致的内存泄漏.

我虽然关于如何使用指针重新排列数据,但这需要我首先为我的数据桶的大小设置一些const变量,因此没有像指针这样的无界值,但也会将内存量减少到固定的数量.

    const size_t batch_size = 100;
    struct vec3_c
    {
    size_t x[batch_size];
    size_t y[batch_size];
    size_t z[batch_size];
    };

    struct vec3_c vec3_c(size_t x, size_t y, size_t z, size_t index)
    {
        struct vec3_c v;
        v.x[index] = x;
        v.y[index] = y;
        v.z[index] = z;
        return v;
    }

        struct vec3_c vc3;        
        for(int i = 0; i < batch_size; i++)
        {
            vc3 = vec3_c(i+1, i*i, i*10, i);
            //printf("vec3c x:%zu, y:%zu, z:%zu\n",vc3.x[i], vc3.y[i], vc3.z[i]);
            printf("vec3c x:%p, y:%p, z:%p\n",(void*)&vc3.x[i], (void*)&vc3.y[i], (void*)&vc3.z[i]);
        }

      ---------------x-----------------|----------------y-----------------|----------------z-----------------|

0|      0x7fff57489f40 : 140734657765184 | 0x7fff5748a260 : 140734657765984 | 0x7fff5748a580 : 140734657766784
1|      0x7fff57489f48 : 140734657765192 | 0x7fff5748a268 : 140734657765992 | 0x7fff5748a588 : 140734657766792
2|      0x7fff57489f50 : 140734657765200 | 0x7fff5748a270 : 140734657766000 | 0x7fff5748a590 : 140734657766800
Run Code Online (Sandbox Code Playgroud)

使用此更新的代码,我必须有一个固定的桶大小,所以我将它设置为100的batch_size只是为了简单的数字.用一些数据填充vec3c并进行类似的测试,这次似乎每个值都以8字节块的形式对齐.

例如:

size of vec3      : 24 bytes
size of vec3a     : 24 bytes
size of vec3b     : 24 bytes
size of vec3c     : 2400 bytes
size of size_t    : 8 bytes
size of int       : 4 bytes
size of 16 int    : 64 bytes
vec3c x:0x7fff592d2f40, y:0x7fff592d3260, z:0x7fff592d3580
vec3c x:0x7fff592d2f48, y:0x7fff592d3268, z:0x7fff592d3588
vec3c x:0x7fff592d2f50, y:0x7fff592d3270, z:0x7fff592d3590
vec3c x:0x7fff592d2f58, y:0x7fff592d3278, z:0x7fff592d3598
vec3c x:0x7fff592d2f60, y:0x7fff592d3280, z:0x7fff592d35a0
vec3c x:0x7fff592d2f68, y:0x7fff592d3288, z:0x7fff592d35a8
vec3c x:0x7fff592d2f70, y:0x7fff592d3290, z:0x7fff592d35b0
vec3c x:0x7fff592d2f78, y:0x7fff592d3298, z:0x7fff592d35b8
vec3c x:0x7fff592d2f80, y:0x7fff592d32a0, z:0x7fff592d35c0
vec3c x:0x7fff592d2f88, y:0x7fff592d32a8, z:0x7fff592d35c8
Run Code Online (Sandbox Code Playgroud)

全部由8个字节分隔.

这应该摆脱内存泄漏和指针所需的多余内存的问题.

这是新的布局,像sizeof(vc3 [0] .x)将返回8个字节.

回到原来的问题:

  1. 我的实现是否struct vec3_c正确设置数组结构?

  2. 如果vec_3c批量大小为100,它显示2400字节大,但每个单独的元素只有8个字节并且正确对齐,所以我现在实际上可以在1个现代cpu缓存行上容纳8个元素?

  3. 会改变传递给我的数据,比如说结构数组的典型格式超过了处于缓存友好状态并且能够在每个指令调用的多个数据点上操作的性能优势吗?这是警告,第1点和第2点都是正确的.

ex做两个向量的点积:这意味着我可以在每个指令周期得到2 vec3_c的点积?

编辑另外 一个问题,最好添加额外的8个字节的数据,使这个结构成为32个字节的倍数,并且可以使用额外的8个字节作为临时空间或者只留空?

编辑 有人向我指出,我的初始初始化函数只是搞乱了.我将其更新为此表单:

struct vec3_c* vec3_c()
{
    struct vec3_c *v = (struct vec3_c*)malloc(sizeof(struct vec3_c));
    v->index = 0;
    return v;
}

struct vec3_c* v3 = vec3_c();
    for(size_t i = 0; i < batch_size; i++)
    {
        v3->x[i] = i + 1;
        v3->y[i] = i * i;
        v3->z[i] = i * 10;
        printf("index:%d\tvec3c x:%zu, y:%zu, z:%zu\n",i,v3->x[i], v3->y[i], v3->z[i]);
        printf("index:%zu\tvec3c x:%p, y:%p, z:%p\n",i,(void*)&v3->x[i], (void*)&v3->y[i], (void*)&v3->z[i]);
    }   
Run Code Online (Sandbox Code Playgroud)

Bri*_*n L 3

如果您将有很多这样的 xyz 点,并且您希望能够同时对所有 x 执行操作,那么将所有 x 放在一起更有意义:

struct PointBatch {
    size_t x[batchsize];
    size_t y[batchsize];
    size_t z[batchsize];
}

// More efficient for things like
// - find the point with the largest X
// - find the sum of all the points as [xsum, ysum, zsum]
Run Code Online (Sandbox Code Playgroud)

如果您通常对单个数据点的 x、y 和 z 进行操作,那么将每个点作为一个结构体放在一起更有意义。

struct Point {
    size_t x;
    size_t y;
    size_t z;
}

struct Point pointBatch[batchsize];

// Better for things like
// - plot all the points on a graph
// - determine which points satisfy the equation: x^2 + y^2 < z^2
Run Code Online (Sandbox Code Playgroud)

注意
,在性能不是问题的情况下,您可能会发现Point/pointBatch方法使您的代码更易于编写且更具可读性,因为struct PointBatch没有提供方便的方法来引用或传递单个点。