【发布时间】:2020-05-25 18:35:59
【问题描述】:
我正在开发一个 C++ 函数来在内存中分配多个缓冲区。 缓冲区必须是 N 字节对齐的,因为它们保存的数据将使用各种类型的 SIMD 指令集(SSE、AVX、AVX512 等...)进行处理
在 Apple Core Audio Utility Classes online 中,我找到了这段代码:
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;
}
代码非常简单,但是有一行我没有完全理解:
nBytes = ((nBytes + 15) & ~15) | 16;
我知道它将字节数对齐/量化为 16,但我不明白为什么它在末尾使用按位 OR 16。评论说:“通过将它们间隔为 16 的奇数倍来进行交替缓存行命中”。请原谅我的厚度,但我还是不明白。
所以我有三个问题:
1) | 16; 究竟做了什么,为什么这样做?
2) 考虑到内存分配和数据访问的上下文,| 16; 如何以及在哪些方面改进了代码?从代码中的 cmets 我可以猜到它与缓存访问有关,但我不理解整个“交替缓存行命中”位。将内存分配地址间隔为 16 的奇数倍如何提高缓存访问?
3) 我是否认为上述函数只能在新运算符将返回至少 16 字节对齐内存的假设下正常工作?在 C++ 中,new 运算符被定义为返回一个指向存储的指针,该指针具有适合任何具有基本对齐要求的对象的对齐方式,可能不一定是 16 字节。
【问题讨论】:
-
@JesperJuhl:如果/当
alignas为动态 分配做任何事情,它只适用于C++17。较早的 C++ 版本很难在 new/delete 之上对齐内存。 -
@PeterCordes 由于没有指定具体标准,我假设是当前标准(C++17 ATM)。我认为这是合理的。
-
对于问题 (1),
| 16只是使 nBytes 成为 16 的奇数倍,根据代码中此行上方的注释。 -
@JesperJuhl:当然,但它总是在 C++17 中工作吗?或者如果您真正想要的是
float的对齐缓冲区,您是否需要new的过度对齐类型?
标签: c++ dynamic-memory-allocation simd memory-alignment micro-optimization