更新:新的更快算法
不要使用模数,因为在 x86 上它需要数百个时钟周期,因为除法令人讨厌,而在其他系统上则更多。我想出了一个比 GCC 和 Visual-C++ 更快的 std::align 版本。 Visual-C++ 的实现最慢,它实际上使用了一个业余的条件语句。 GCC 与我的算法非常相似,但我的做法与他们的做法相反,但我的算法快了 13.3%,因为它有 13 条而不是 15 条单周期指令。 See here is the research paper with dissassembly。如果您使用掩码而不是 pow_2,则该算法实际上快了一条指令。
/* Quickly aligns the given pointer to a power of two boundaries.
@return An aligned pointer of typename T.
@desc Algorithm is a 2's compliment trick that works by masking off
the desired number in 2's compliment and adding them to the
pointer. Please note how I took the horizontal comment whitespace back.
@param pointer The pointer to align.
@param mask Mask for the lower LSb, which is one less than the power of
2 you wish to align too. */
template <typename T = char>
inline T* AlignUp(void* pointer, uintptr_t mask) {
intptr_t value = reinterpret_cast<intptr_t>(pointer);
value += (-value) & mask;
return reinterpret_cast<T*>(value);
}
这是你的称呼:
enum { kSize = 256 };
char buffer[kSize + 16];
char* aligned_to_16_byte_boundary = AlignUp<> (buffer, 15); //< 16 - 1 = 15
char16_t* aligned_to_64_byte_boundary = AlignUp<char16_t> (buffer, 63);
这是 3 位的快速逐位证明,它适用于所有位计数:
~000 = 111 => 000 + 111 + 1 = 0x1000
~001 = 110 => 001 + 110 + 1 = 0x1000
~010 = 101 => 010 + 101 + 1 = 0x1000
~011 = 100 => 011 + 100 + 1 = 0x1000
~100 = 011 => 100 + 011 + 1 = 0x1000
~101 = 010 => 101 + 010 + 1 = 0x1000
~110 = 001 => 110 + 001 + 1 = 0x1000
~111 = 000 => 111 + 000 + 1 = 0x1000
如果您在这里学习如何在 C++11 中将对象与缓存行对齐,请使用in-place constructor:
struct Foo { Foo () {} };
Foo* foo = new (AlignUp<Foo> (buffer, 63)) Foo ();
这是 std::align 实现,它使用 24 条指令,而 GCC 实现使用 31 条指令,但可以通过将最低有效位的 (--align) 转换为 mask 来调整它以消除递减指令,但是这在功能上与 std::align 不同。
inline void* align(size_t align, size_t size, void*& ptr,
size_t& space) noexcept {
intptr_t int_ptr = reinterpret_cast<intptr_t>(ptr),
offset = (-int_ptr) & (--align);
if ((space -= offset) < size) {
space += offset;
return nullptr;
}
return reinterpret_cast<void*>(int_ptr + offset);
}
使用掩码比使用 pow_2 更快
这里是使用掩码而不是 pow_2(它是 2 的偶数幂)进行对齐的代码。这比 GCC 算法要胖 20%,但需要您存储掩码而不是 pow_2,因此它不可互换。
inline void* AlignMask(size_t mask, size_t size, void*& ptr,
size_t& space) noexcept {
intptr_t int_ptr = reinterpret_cast<intptr_t>(ptr),
offset = (-int_ptr) & mask;
if ((space -= offset) < size) {
space += offset;
return nullptr;
}
return reinterpret_cast<void*>(int_ptr + offset);
}