Как определить, ли память выровненная?

Я плохо знаком с оптимизацией кода с инструкциями 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 для неприсоединившейся памяти, поскольку я не могу удостовериться, что каждая память, переданная этой функции, будет выровненная. И использование intrinsics для загрузки данных из невыровненной памяти в регистры SSE, кажется, ужасно медленный (Еще медленнее, чем обычный код C).

Заранее спасибо...

40
задан jww 24 August 2018 в 14:05
поделиться

4 ответа

РЕДАКТИРОВАТЬ: приведение к long - дешевый способ защитить себя от наиболее вероятной возможности того, что int и указатели будут разных размеров в настоящее время.

Как указано в комментариях ниже, есть лучшие решения, если вы хотите включить заголовок ...

Указатель p выравнивается по 16-байтовой границе iff ((длинное без знака) p & 15) == 0 .

27
ответ дан 27 November 2019 в 01:12
поделиться

В других ответах предлагается операция И с установленными младшими битами и сравнение с нулем.

Но более простой тест - выполнить MOD с желаемым значением выравнивания. , и сравните с нулем.

#define ALIGNMENT_VALUE     16u

if (((uintptr_t)ptr % ALIGNMENT_VALUE) == 0)
{
    // ptr is aligned
}
22
ответ дан 27 November 2019 в 01:12
поделиться
#define is_aligned(POINTER, BYTE_COUNT) \
    (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)

The cast to void * (or, equivalenty, char *) is necessary because the standard only guarantees an invertible conversion to uintptr_t for void *.

If you want type safety, consider using an inline function:

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

and hope for compiler optimizations if byte_count is a compile-time constant.

Why do we need to convert to void * ?

The C language allows different representations for different pointer types, eg you could have a 64-bit void * type (the whole address space) and a 32-bit foo * type (a segment).

The conversion foo * -> void * might involve an actual computation, eg adding an offset. The standard also leaves it up to the implementation what happens when converting (arbitrary) pointers to integers, but I suspect that it is often implemented as a noop.

For such an implementation, foo * -> uintptr_t -> foo * would work, but foo * -> uintptr_t -> void * and void * -> uintptr_t -> foo * wouldn't. The alignment computation would also not work reliably because you only check alignment relative to the segment offset, which might or might not be what you want.

In conclusion: Always use void * to get implementation-independant behaviour.

49
ответ дан 27 November 2019 в 01:12
поделиться

Можете ли вы просто «и» указать ptr с 0x03 (выровнен по 4s), 0x07 (выровнен по 8s) или 0x0f (выровнен по 16s), чтобы увидеть, установлен ли какой-либо из младших битов?

2
ответ дан 27 November 2019 в 01:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: