为SIMD分配内存对齐的缓冲区;16如何给出16的奇数倍,为什么? 我正在使用C++函数在内存中分配多个缓冲区。 缓冲区必须是N字节对齐的,因为它们所持有的数据将使用各种类型的SIMD指令集(SSE、AVX、AVX512等)进行处理

为SIMD分配内存对齐的缓冲区;16如何给出16的奇数倍,为什么? 我正在使用C++函数在内存中分配多个缓冲区。 缓冲区必须是N字节对齐的,因为它们所持有的数据将使用各种类型的SIMD指令集(SSE、AVX、AVX512等)进行处理,c++,dynamic-memory-allocation,simd,memory-alignment,micro-optimization,C++,Dynamic Memory Allocation,Simd,Memory Alignment,Micro Optimization,在Apple Core Audio Utility在线课程中,我发现了以下代码: void CABufferList::AllocateBuffers(UInt32 nBytes) { if (nBytes <= GetNumBytes()) return; if (mABL.mNumberBuffers > 1) { // align successive buffers for Altivec and to take alternating

在Apple Core Audio Utility在线课程中,我发现了以下代码:

void CABufferList::AllocateBuffers(UInt32 nBytes)
{
    if (nBytes <= GetNumBytes()) return;

    if (mABL.mNumberBuffers > 1) {
        // align successive buffers for Altivec and to take alternating
        // cache line hits by spacing them by odd multiples of 16
        nBytes = ((nBytes + 15) & ~15) | 16;
    }
    UInt32 memorySize = nBytes * mABL.mNumberBuffers;
    Byte *newMemory = new Byte[memorySize], *p = newMemory;
    memset(newMemory, 0, memorySize);   // get page faults now, not later

    AudioBuffer *buf = mABL.mBuffers;
    for (UInt32 i = mABL.mNumberBuffers; i--; ++buf) {
        if (buf->mData != NULL && buf->mDataByteSize > 0) {
            // preserve existing buffer contents
            memcpy(p, buf->mData, buf->mDataByteSize);
        }
        buf->mDataByteSize = nBytes;
        buf->mData = p;
        p += nBytes;
    }
    Byte *oldMemory = mBufferMemory;
    mBufferMemory = newMemory;
    mBufferCapacity = nBytes;
    delete[] oldMemory;
}
我知道它将字节数对齐/量化为16,但我不明白为什么它在结尾使用按位或16。该评论说:“通过以16的奇数倍间隔进行交替缓存线命中”。请原谅我的厚度,但我还是不明白

所以我有三个问题:

1) 什么是
| 16准确执行,为什么执行

2) 考虑到内存分配和数据访问的环境,
| 16如何以及以什么术语进行改进代码?从代码中的注释中,我可以猜测它与缓存访问有关,但我不理解整个“交替缓存线命中”部分。内存分配地址的奇数倍间隔16如何改善缓存访问

3) 基于新操作符将返回至少16字节对齐内存的假设,我认为上述函数只能正常工作,这对吗?在C++中,新操作符被定义为返回指向存储的指针,该对齐适合于具有基本对齐要求的任何对象,这可能不一定是16字节。 根据对Altivec的评论,这是特定于电源架构的,我不熟悉。此外,代码不完整,但看起来分配的内存被组织在一个或多个相邻的缓冲区中,并且大小调整仅在存在多个缓冲区时有效。我们不知道如何在这些缓冲区中访问数据。这个答案中会有很多假设,甚至可能是完全错误的。我之所以发布它,主要是因为它太大,无法发表评论

答案(有点) 我可以看出尺寸调整的一个可能优势。首先,让我们记住有关电源体系结构的一些细节:

  • Altivec向量大小为16字节(128位)
  • 缓存线大小为128字节
现在,让我们以
AllocateBuffers
为4个缓冲区分配内存为例(即
mABL.mNumberBuffers
为4个),而
nBytes
为256个。让我们看看这些缓冲区在内存中的布局:

| Buffer 1: 256+16=272 bytes | Buffer 2: 272 bytes | Buffer 3: 272 bytes | Buffer 4: 272 bytes |
^                            ^                     ^                     ^
|                            |                     |                     |
offset: 0                    272                   544                   816
请注意偏移值,并将其与缓存线边界进行比较。为简单起见,假设内存分配在缓存线边界。这其实并不重要,如下所示

  • 缓冲区1从偏移量0开始,偏移量0是缓存线的起点
  • 缓冲区2从超过缓存线边界的16个字节开始(偏移量为2*128=256)
  • 缓冲区3开始时超过缓存线边界32字节(偏移量为4*128=512)
  • 缓冲区4开始时超过缓存线边界48字节(偏移量为6*128=768)
请注意距离最近缓存线边界的偏移量是如何增加16字节的。现在,如果我们假设每个缓冲区中的数据将以16字节块的形式在一个循环中向前访问,那么缓存线将以一个相当特定的顺序从内存中获取。让我们考虑循环的中间部分(因为在开始时CPU必须在每个缓冲器的开头取高速缓存行):

  • 迭代5
    • 从缓冲区1的偏移量5*16=80处加载,我们仍然使用在以前的迭代中获取的缓存线
    • 从缓冲区2的偏移量352处加载,我们仍然使用在以前的迭代中获取的缓存线。缓存线边界在偏移量256处,我们在偏移量96处
    • 在偏移量624处从缓冲区3加载时,我们仍然使用在先前迭代中获取的缓存线。缓存线边界在偏移量512处,我们在偏移量112处
    • 从缓冲区4的偏移量896处加载,我们点击一条新的缓存线,并从内存中获取一条新的缓存线
  • 迭代6
    • 从缓冲区1的偏移量6*16=96处加载,我们仍然使用在以前的迭代中获取的缓存线
    • 从缓冲区2的偏移量368处加载,我们仍然使用在以前的迭代中获取的缓存线。缓存线边界在偏移量256处,我们在偏移量112处
    • 从偏移量640处的缓冲区3加载,我们点击一个新的缓存线,并从内存中获取一个新的缓存线
    • 从缓冲区4的偏移量896处加载,我们仍在使用上次迭代中获取的缓存线。缓存线边界在偏移量896处,我们在偏移量16处
  • 迭代7
    • 从缓冲区1的偏移量7*16=112处加载,我们仍在使用先前迭代中获取的缓存线
    • 从缓冲区2的偏移量384处加载,我们点击一个新的缓存线,并从内存中获取一个新的缓存线
    • 从缓冲区3的偏移量656处加载,我们仍在使用上次迭代中获取的缓存线。缓存线边界在偏移量640处,我们在偏移量16处
    • 从缓冲区4的偏移量912处加载,我们仍然使用在以前的迭代中获取的缓存线。缓存线边界在偏移量896处,我们在偏移量32处
  • 迭代8
    • 从缓冲区1的偏移量8*16=128处加载,我们点击一个新的缓存线,并从内存中获取一个新的缓存线
    • 从缓冲区2的偏移量400处加载,我们仍然使用在以前的迭代中获取的缓存线。缓存线边界在偏移量384处,我们在偏移量16处
    • 从缓冲区3的偏移量672处加载,我们仍然使用在以前的迭代中获取的缓存线。缓存线边界处于关闭状态
      | Buffer 1: 256+16=272 bytes | Buffer 2: 272 bytes | Buffer 3: 272 bytes | Buffer 4: 272 bytes |
      ^                            ^                     ^                     ^
      |                            |                     |                     |
      offset: 0                    272                   544                   816