Я хотел бы выделить несколько char буферов 0 для передачи во внешнюю функцию, не относящуюся к C ++, которые имеют специфическое требование выравнивания.

Требуется, чтобы буфер был выровнен по границе N - байта 1 , но не по границе 2N. Например, если N равен 64, указатель на этот буфер p должен удовлетворять ((uintptr_t)p) % 64 == 0 и ((uintptr_t)p) % 128 != 0 - по крайней мере на платформах, где указатели имеют обычную интерпретацию как простой адрес при приведении к uintptr_t.

Есть ли разумный способ сделать это с помощью стандартных средств C ++ 11?

2

Буфер будет передан внешней подпрограмме (в соответствии с C ABI, но записан в asm). Требуемое выравнивание обычно будет больше 16, но меньше 8192.

Перераспределение или любые другие незначительные проблемы с неиспользованными ресурсами вполне устраивают. Меня больше интересует правильность и переносимость, чем потеря нескольких байтов или миллисекунд.

То, что работает как в куче, так и в стеке, идеально, но все, что работает в любом из них, все еще довольно хорошо (с предпочтением в распределении кучи).


0 Это может быть operator new[] или malloc, или, возможно, какой-то другой метод, который учитывает выравнивание: все, что имеет смысл.

1 Как обычно, N является степенью двойки.

2

1
BeeOnRope 29 Май 2017 в 01:55

2 ответа

Лучший ответ

Логически, чтобы удовлетворить «выравнивание по N, но не по 2N», мы выравниваем по 2N, а затем добавляем N к указателю. Обратите внимание, что это приведет к чрезмерному выделению N байтов.

Итак, если мы хотим выделить B байт, если вам нужно только место в стеке, alignas возможно сработает.

alignas(N*2) char buffer[B+N];
char *p = buffer + N;

Если вам нужно пространство кучи, std::aligned_storage может сделать следующее:

typedef std::aligned_storage<B+N,N*2>::type ALIGNED_CHAR;
ALIGNED_CHAR buffer;
char *p = reinterpret_cast<char *>(&buffer) + N;

Я не проверял, но документация предполагает, что все должно быть в порядке.

5
Ken Y-N 28 Май 2017 в 23:16

Вы можете использовать _aligned_malloc(nbytes,alignment) (в MSVC) или < a href = "https://software.intel.com/en-us/node/523368" rel = "nofollow noreferrer"> _mm_malloc(nbytes,alignment) (в других компиляторах) для выделения (в куче) nbytes памяти выровнен по байту alignment, который должен быть целым числом от двух.

Затем вы можете использовать прием из ответа Кена, чтобы избежать выравнивания с 2N:

void*ptr_alloc = _mm_malloc(nbytes+N,2*N);
void*ptr = static_cast<void*>(static_cast<char*>(ptr_alloc) + N);

/* do your number crunching */

_mm_free(ptr_alloc);

Мы должны сохранить указатель, возвращаемый _mm_malloc() для последующего перераспределения, что должно быть сделано через _mm_free().

3
Cody Gray 29 Май 2017 в 10:10