предотвращение невыровненных данных по "куче"

Я создаю иерархию классов, которая использует SSE intrinsics функции, и таким образом некоторые члены класса должны быть выровненных 16 байтов. Для экземпляров стека я могу использовать __declspec(align(#)), как так:

typedef __declspec(align(16)) float Vector[4];
class MyClass{
...
private:
Vector v;
};

Теперь, с тех пор __declspec(align(#)) директива компиляции, следующий код может привести к невыровненному экземпляру Вектора на "куче":

MyClass *myclass = new MyClass;

Это также, я знаю, что могу легко решить путем перегрузки новых и операторов delete для использования _aligned_malloc и _aligned_free соответственно. Как так:

//inside MyClass:
public:
void* operator new (size_t size) throw (std::bad_alloc){
    void * p = _aligned_malloc(size, 16);
    if (p == 0)  throw std::bad_alloc()
    return p; 
}

void operator delete (void *p){
    MyClass* pc = static_cast(p); 
    _aligned_free(p);
}
...

Пока все хорошо.. но вот моя проблема. Рассмотрите следующий код:

class NotMyClass{ //Not my code, which I have little or no influence over
...
MyClass myclass;
...
};
int main(){
    ...
    NotMyClass *nmc = new NotMyClass;
    ...
}

Начиная с myclass экземпляра MyClass создается статически на динамическом экземпляре NotMyClass, myclass составит 16 байтов, выровненных относительно к началу nmc из-за Вектора __declspec(align(16)) директива. Но это бесполезно, так как nmc динамично выделяется на "куче" с новым оператором NotMyClass, который не обязательно гарантирует (и определенно вероятно, НЕ) 16-байтовое выравнивание.

До сих пор я могу только думать о 2 подходах к тому, как иметь дело с этой проблемой:

  1. Предотвращение пользователей MyClass от способности скомпилировать следующий код:

    MyClass myclass;
    

    при значении экземпляры MyClass могут только быть созданы динамично, с помощью нового оператора, таким образом гарантировав, что все экземпляры MyClass действительно динамично выделяются с MyClass, перегруженным новый. Я консультировался относительно другого потока о том, как выполнить это и получил несколько больших ответов: C++, препятствуя тому, чтобы экземпляр класса был создан на стеке (во время compiltaion)

  2. Вернитесь от наличия Векторных участников в моем Классе и только имейте указатели на Вектор как участники, которые я выделю и буду освобождать использование _aligned_malloc и _aligned_free в ctor и dtor соответственно. Этот methos кажется сырым и подверженным ошибке, так как я не единственный программист, пишущий эти Классы (MyClass происходит из Базового класса, и многие из этих классов используют SSE).

Однако, так как оба решения были осуждены в моей команде, я приезжаю к Вам для предложений другого решения.

9
задан Community 23 May 2017 в 02:31
поделиться

3 ответа

Если вы настроены против выделения кучи, другая идея - перераспределить в стеке и выровнять вручную (ручное выравнивание обсуждается в этой публикации SO ). Идея состоит в том, чтобы выделить байтовые данные ( unsigned char ) с размером, гарантированно содержащим выровненную область необходимого размера ( +15 ), а затем найти выровненную позицию путем округления в меньшую сторону от наиболее смещенная область ( x + 15 - (x + 15)% 16 или x + 15 & ~ 0x0F ). Я разместил рабочий пример этого подхода с векторными операциями на кодовой панели (для g ++ -O2 -msse2 ). Вот важные биты:

class MyClass{
   ...
   unsigned char dPtr[sizeof(float)*4+15]; //over-allocated data
   float* vPtr;                            //float ptr to be aligned

   public:
      MyClass(void) : 
         vPtr( reinterpret_cast<float*>( 
            (reinterpret_cast<uintptr_t>(dPtr)+15) & ~ 0x0F
         ) ) 
      {}
   ...
};
...

Конструктор обеспечивает выравнивание vPtr (обратите внимание, что порядок членов в объявлении класса важен).

Этот подход работает (выделение в куче / стеке содержащих классов не имеет отношения к выравниванию), является переносимым (я думаю, что большинство компиляторов предоставляют uint размером с указатель uintptr_t ), и не будет утечки памяти.Но это не особенно безопасно (убедитесь, что выровненный указатель действителен при копировании и т. Д.), Тратит (почти) столько памяти, сколько он использует, а некоторые могут найти reinterpret_casts неприятным.

Риски проблем с выровненной операцией / невыровненными данными можно было бы в основном исключить, инкапсулируя эту логику в объект Vector,тем самым контролируя доступ к выровненному указателю и гарантируя, что он будет выровнен при построении и останется действительным.

4
ответ дан 3 November 2019 в 07:12
поделиться

Вы можете использовать "placement new."

void* operator new(size_t, void* p) { return p; }

int main() {
    void* p = aligned_alloc(sizeof(NotMyClass));
    NotMyClass* nmc = new (p) NotMyClass;
    // ...

    nmc->~NotMyClass();
    aligned_free(p);
}

Конечно, вы должны быть осторожны при уничтожении объекта, вызывая деструктор и затем освобождая место. Вы не можете просто вызвать delete. Вы можете использовать shared_ptr<> с другой функцией, чтобы справиться с этим автоматически; это зависит от того, являются ли накладные расходы на работу с shared_ptr (или другой оберткой указателя) проблемой для вас.

1
ответ дан 3 November 2019 в 07:12
поделиться

Грядущий стандарт C ++ 0x предлагает средства для работы с необработанной памятью. Они уже включены в VC ++ 2010 (в пространстве имен tr1 ).

std::tr1::alignment_of // get the alignment
std::tr1::aligned_storage // get aligned storage of required dimension

Это типы, вы можете использовать их так:

static const floatalign = std::tr1::alignment_of<float>::value; // demo only

typedef std::tr1::aligned_storage<sizeof(float)*4, 16>::type raw_vector;
        // first parameter is size, second is desired alignment

Затем вы можете объявить свой класс:

class MyClass
{
public:

private:
  raw_vector mVector; // alignment guaranteed
};

И, наконец, вам понадобится приведение типов для управления им (до сих пор это необработанная память):

float* MyClass::AccessVector()
{
  return reinterpret_cast<float*>((void*)&mVector));
}
0
ответ дан 3 November 2019 в 07:12
поделиться
Другие вопросы по тегам:

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