2016-11-23 12 views
2

Скажем, у меня есть что-то вроде:Как я могу обеспечить выравнивание по 16 байт для определенных членов класса?

class C 
{ 
private: 
    __m128i m0; 
    __m128i m1; 
    __m128i cm0; 

    int s0; 

... 

} 

Это может вызвать проблемы, потому что m0, m1 и cm0 не гарантируется 16 байт выровнены, что позволяет за нарушения доступа выравнивания связанных. Если бы я абсолютно хотел иметь частных членов класса simd, чье выравнивание я мог бы гарантировать, как я должен это делать?

Редактировать: конкретно в моем случае, я использую std :: vector класса, в котором есть члены __m128i (в дополнение к 32 и 64 битным целям). Можно ли принудительно выполнить выравнивание по 16 байт моих членов класса ?. внутри станд :: вектор

+1

У членов класса гарантируются требования к их выравниванию в локальных или статических переменных, а также в локальных или статических массивах. Если вы динамически выделяете память, вы можете столкнуться с проблемами. Я предполагаю, что вы пытаетесь использовать 'std :: vector ' или аналогичный? Если да, скажите так. –

+0

Вы правы, я пытаюсь использовать вектор такого класса, как этот. Любые рекомендации? – MNagy

+1

Вы не можете «выровнять элементы», не выровняв весь класс с одинаковым значением. В противном случае вы можете просто вытеснить весь объект с помощью разрешенного выравнивания класса и сломать выравнивание элемента. (Разметка класса, конечно, исправлена ​​и не определена для каждого объекта.) –

ответ

1

Это решение, которое я нашел, чтобы решить свои собственные проблемы

то, что я сделал, было в два раза:

Первоначально я работал с чем-то вроде:

std::vector<C> myVector(mySize); 

Я решил сделать вместо:

C* myVector = _aligned_malloc(sizeof(C) * mySize, 16); 

Во-вторых, я добавил переменную отступы к классу С, что его размер в байтах будет кратен 16. Я не увлечен неиспользуемого пространства, что влечет за собой, поэтому я могу переделать вещи в будущем (возможно, один блок памяти для переменных simd и отдельный блок для других).

+0

Пусть компилятор подходит для вас. Вы можете увидеть это в действии, поместив в свой код 'int size = sizeof (C)' и посмотрите на выход asm, например, на Godbolt (https://godbolt.org/g/p1RckS). (Обратите внимание, что Godbolt нацелен на System V ABI, а не на Windows ABI, и они могут не всегда соглашаться на структуру структуры, но они всегда будут разбиты на несколько кратных требуемого выравнивания, чтобы все элементы массива имели требуемое выравнивание.) –

+0

Но да, если вам не нужны функции 'std :: vector', его легче избежать, чем передать пользовательский распределитель, который выделяет выровненную память. IDK почему 'std :: vector' не использует распределитель, который по умолчанию удовлетворяет требованиям к выравниванию. :( –

1

Если вы не против потратить немного ОЗУ, вы можете сделать класс оболочки aligned_holder, который гарантирует, что объект, который он удерживает, будет выровнен с любой границей выравнивания, которую вы укажете. (Обратите внимание, что количество неиспользуемого ОЗУ равно заданной границе выравнивания)

#include <stdio.h> 
#include <stddef.h> 
#include <stdint.h> 
#include <new> 

// Makes sure that its held object is always aligned to the specified alignment-value, 
// at the cost of using up some extra RAM. 
template<class T, int AlignTo=16> class aligned_holder 
{ 
public: 
    aligned_holder() 
    { 
     new (getAlignedPointer()) T(); 
    } 

    ~aligned_holder() 
    { 
     getAlignedObject().~T(); 
    } 

    T & getAlignedObject() {return *reinterpret_cast<T*>(getAlignedPointer());} 
    const T & getAlignedObjectConst() const {return *reinterpret_cast<const T*>(getAlignedPointerConst());} 

private: 
    char * getAlignedPointer() 
    { 
     const ptrdiff_t offset = intptr_t(_buf)%AlignTo; 
     return &_buf[(intptr_t)(AlignTo-offset)]; 
    } 

    const char * getAlignedPointerConst() const 
    { 
     const ptrdiff_t offset = intptr_t(_buf)%AlignTo; 
     return &_buf[(intptr_t)(AlignTo-offset)]; 
    } 

    char _buf[AlignTo+sizeof(T)]; 
}; 

... и модульное тестирование, к (надеюсь) убедиться, что я не испортит пример:

class IWannaBeAligned 
{ 
public: 
    IWannaBeAligned() 
    { 
     const intptr_t iThis = (intptr_t)(this); 
     if ((iThis % 16) != 0) printf("Error! object %p is not 16-byte aligned! (offset=%ld)\n", this, iThis%16); 
         else printf("Good, object %p is 16-byte aligned.\n", this); 
    } 

    ~IWannaBeAligned() 
    { 
     printf("Deleting object %p\n", this); 
    } 

    char buf[32]; // just to give it a more realistic object size than one byte 
}; 

int main() 
{ 
    { 
     printf("First, let's try it without the aligned_holder:\n"); 
     IWannaBeAligned notAligned; 
    } 

    printf("\n"); 

    { 
     printf("Now, we'll try it with the aligned_holder:\n"); 
     aligned_holder<IWannaBeAligned> isAligned; 
    } 

    return 0; 
}