如何确定内存是否对齐?

时间:2009-12-13 23:15:33

标签: c optimization memory sse simd

我是使用SSE / SSE2指令优化代码的新手,直到现在我还没有走得太远。据我所知,一个常见的SSE优化函数如下所示:

void sse_func(const float* const ptr, int len){
    if( ptr is aligned )
    {
        for( ... ){
            // unroll loop by 4 or 2 elements
        }
        for( ....){
            // handle the rest
            // (non-optimized code)
        }
    } else {
        for( ....){
            // regular C code to handle non-aligned memory
        }
    }
}

但是,如何正确确定内存ptr是否指向对齐,例如16字节?我认为我必须包含非对齐内存的常规C代码路径,因为我无法确保传递给此函数的每个内存都将对齐。使用内在函数将未对齐内存中的数据加载到SSE寄存器中似乎非常慢(甚至比常规C代码慢)。

提前谢谢你......

8 个答案:

答案 0 :(得分:44)

#define is_aligned(POINTER, BYTE_COUNT) \
    (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)

转换为void *(或等效,char *)是必要的,因为标准只保证uintptr_t void *的{​​{1}}可转换。

如果您想要类型安全,请考虑使用内联函数:

static inline _Bool is_aligned(const void *restrict pointer, size_t byte_count)
{ return (uintptr_t)pointer % byte_count == 0; }

并希望编译器优化,如果byte_count是编译时常量。

为什么我们需要转换为 void *

C语言允许对不同的指针类型进行不同的表示,例如,您可以使用64位void *类型(整个地址空间)和32位foo *类型(段)。

转化foo * - > void *可能涉及实际计算,例如添加偏移量。该标准还将实现在将(任意)指针转换为整数时会发生什么,但我怀疑它通常是作为noop实现的。

对于此类实施,foo * - > uintptr_t - > foo *可行,但foo * - > uintptr_t - > void *void * - > uintptr_t - > foo *不会。对齐计算也不能可靠地工作,因为您只检查相对于段偏移的对齐,这可能是您想要的,也可能不是。

总之:始终使用void *来实现与实现无关的行为。

答案 1 :(得分:25)

编辑:转换为long是一种廉价的方式来保护自己免受int和指针现在不同大小的可能性。

正如下面的评论中所指出的,如果您愿意包含标题,则有更好的解决方案......

指针p在16字节边界上对齐iff ((unsigned long)p & 15) == 0

答案 2 :(得分:20)

其他答案建议设置低位的AND运算,并与零进行比较。

但更直接的测试是使用所需的对齐值进行MOD,并与零进行比较。

#define ALIGNMENT_VALUE     16u

if (((uintptr_t)ptr % ALIGNMENT_VALUE) == 0)
{
    // ptr is aligned
}

答案 3 :(得分:6)

使用像

这样的功能模板
#include <type_traits>

template< typename T >
bool is_aligned(T* p){
    return !(reinterpret_cast<uintptr_t>(p) % std::alignment_of<T>::value);
}

您可以通过调用类似

的内容来检查运行时的对齐方式
struct foo_type{ int bar; }foo;
assert(is_aligned(&foo)); // passes

要检查错误的路线是否失败,您可以

// would almost certainly fail
assert(is_aligned((foo_type*)(1 + (uintptr_t)(&foo)));

答案 4 :(得分:3)

这基本上就是我所使用的。通过使整数成为模板,我确保它扩展了编译时间,因此无论我做什么,我都不会以慢速模运算结束。

我总是喜欢检查我的输入,因此编译时断言。如果您的对齐值错误,那么它就不会编译......

template <unsigned int alignment>
struct IsAligned
{
    static_assert((alignment & (alignment - 1)) == 0, "Alignment must be a power of 2");

    static inline bool Value(const void * ptr)
    {
        return (((uintptr_t)ptr) & (alignment - 1)) == 0;
    }
};

要查看正在进行的操作,您可以使用此功能:

// 1 of them is aligned...
int* ptr = new int[8];
for (int i = 0; i < 8; ++i)
    std::cout << IsAligned<32>::Value(ptr + i) << std::endl;

// Should give '1'
int* ptr2 = (int*)_aligned_malloc(32, 32);
std::cout << IsAligned<32>::Value(ptr2) << std::endl;

答案 5 :(得分:2)

你可以'和'ptr与0x03(在4s上对齐),0x07(在8s上对齐)或0x0f(在16s上对齐)来查看是否设置了任何最低位?

答案 6 :(得分:1)

将其留给专业人士

https://www.boost.org/doc/libs/1_65_1/doc/html/align/reference.html#align.reference.functions.is_aligned

bool is_aligned(const void* ptr, std::size_t alignment) noexcept; 

示例:

        char D[1];
        assert( boost::alignment::is_aligned(&D[0], alignof(double)) ); //  might fail, sometimes

答案 7 :(得分:-3)

怎么样:

void *mem = malloc(1024+15); 
void *ptr =( (*(char*)mem) - (*(char *)mem % 16) );
相关问题