Вчера вечером я работал над этим классом как надёжная оболочка для объектов, выровненных по памяти. У меня есть байтовый массив и математика для доступа к памяти байтового массива для чтения и записи как T
, Мне любопытно, однако, как я могу обеспечить наиболее эффективный доступ к согласованным T
,
Я пытался использовать публичный T &
называется Value
который я бы инициализировал к выровненным T
в списке инициализатора конструктора. Как это:
template <typename T, size_t alignment = 64>
struct Aligned {
private:
std::uint8_t bytes[sizeof(T) + alignment - 1];
public:
T & Value;
Aligned(T const & value = T()) : Value(*reinterpret_cast<T *>((intptr_t)bytes + (alignment - 1) & ~intptr_t(alignment - 1))) {
Value = value;
}
};
Это увеличивает размер класса на sizeof(T *)
поскольку T & Value
нужно хранить адрес выровненного T
,
Другой мой подход — не хранить адрес, а вычислять его каждый раз, когда требуется доступ, с помощью методов доступа …
#include <array>
#include <cstdint>
template <typename T, size_t alignment = 64>
struct Aligned {
private:
std::array<uint8_t, sizeof(T) + alignment - 1> const bytes;
public:
T const & value() const {
return *reinterpret_cast<T *>((intptr_t)bytes.data() + (alignment - 1) & ~intptr_t(alignment - 1));
}
void value(T const & x) {
*reinterpret_cast<T *>((intptr_t)bytes.data() + (alignment - 1) & ~intptr_t(alignment - 1)) = x;
}
Aligned(T const & x = T()) {
value(x);
}
};
Этот подход потребует арифметику указателя и разыменование указателя (я думаю?) Для каждого доступа, но ничего не добавляет к размеру класса.
Есть ли другие подходы или приемы, чтобы получить оба преимущества?
Я думаю, что вариант 1 выглядит аккуратнее, и я не думаю, что есть какая-то польза от варианта 2.
Однако, если вам нужно знать, что дает вам лучшую производительность, вам действительно нужно выполнить код таким образом, чтобы можно было измерить производительность. Я или кто-то еще, глядя на код и говоря: «А выглядит лучше, чем В», не годится — компиляторы не предсказуемы на 100%, и иногда выбор «Хорошо выглядит» не лучший выбор. Это то, что я говорю о ВСЕХ постах производительности, и для этого есть веская причина. Я лично испытал это, когда вы смотрели на два фрагмента кода, говоря: «Ну, они будут проходить одно и то же время, они почти идентичны», но, поскольку есть небольшая разница, производительность в случае А заметно выше, чем в случае B (или наоборот).
Убедитесь, что вы не просто тестируете здесь тривиальный случай, вам нужно несколько различных вариантов, таких как структура с большим количеством элементов, большой и маленький массив, а также простой int
, long long
, double
, так далее.
Если у вас есть доступ к C ++ 11, вы можете использовать новое ключевое слово alignas, чтобы компилятор выровнял тип или переменную для вас.
alignas(64) classA myA;