Как заставить выравнивание объектов в куче?

0

В Как использовать выравнивание для выравнивания силы для выделения кучи? , Я спросил, как использовать alignof для объектов, выделенных кучей. Теперь я обобщаю свой вопрос для любого способа принудительного выравнивания объектов, выделенных кучей, потому что я не могу найти способ сделать это с помощью alignof, std::aligned_storage или std::align. Следующий код компилируется, но ни VC12, ни Clang 3.2 не производят последовательность нулей в качестве вывода, которая указывает, что мой запрос выравнивания соблюдается. (gcc 4.8.1 не хватает std::align, но если вы прокомментируете эту часть, код компилируется под gcc 4.8.1, а также выводит вывод, указывающий, что мои запросы выравнивания игнорируются.)

#include <iostream>
#include <memory>

int main()
{
  // Try using alignas on the allocation
  {
    class Widget {};

    for (int i = 0; i < 5; ++i)
      {
        auto ptr = new alignas(64) Widget;
        std::cout << (long)ptr % 64 << '\n';    // should be 0
      }
  }

  // Try using alignas on the type
  {
    class alignas(64) Widget {};

    for (int i = 0; i < 5; ++i)
      {
        auto ptr = new Widget;
        std::cout << (long)ptr % 64 << '\n';    // should be 0
      }
  }

  // Try using std::aligned_storage
  {
    class Widget {};

    using WidgetStorage_t = 
      std::aligned_storage<sizeof(Widget), 64>::type;

    for (int i = 0; i < 5; ++i)
      {
        auto ptr = new WidgetStorage_t;         // get buffer
        new (ptr) Widget;                       // construct Widget
        std::cout << (long)ptr % 64 << '\n';    // should be 0
      }
  }

  // Try using operator new + std::align
  {
    class Widget {};

    for (int i = 0; i < 5; ++i)
      {
        auto requestSize =      // double space to ensure that a
          2 * sizeof(Widget);   // Widget fits after ptr adjustment

        auto ptr = operator new(requestSize);   // get buffer

        std::align(64, sizeof(Widget),          // align ptr 
                   ptr, requestSize);           // inside buffer

        new (ptr) Widget;                       // construct Widget

        std::cout << (long)ptr % 64 << '\n';    // should be 0
      }
  }
}

UPDATE: мой код для тестирования std::align неверно. Следующий код замены работает:

 // Try using operator new + std::align
  {
    class Widget {};

    for (int i = 0; i < 5; ++i)
      {
        auto requestSize =       // ensure that a
          sizeof(Widget) + 64;   // Widget fits after ptr adjustment

        auto ptr = operator new(requestSize);   // get buffer

        std::align(64, sizeof(Widget),          // align ptr 
                   ptr, requestSize);           // inside buffer

        new (ptr) Widget;                       // construct Widget

        std::cout << (long)ptr % 64 << '\n';    // should be 0
      }
  }

Это единственный способ получить работу? Это намного сложнее, чем хотелось бы, потому что теперь нужно отслеживать два указателя: скорректированный, который указывает на объект (необходимый для уничтожения построенного объекта) и нескорректированный, который указывает на буфер памяти (необходимый для освобождения память).

  • 0
    Что это за странно выглядящий код, у вас есть class alignas(64) Widget {}; ?
  • 0
    Это способ сказать, что объекты Widget имеют 64-байтовое выравнивание, хотя, похоже, это не имеет никакого эффекта для объектов кучи. Пустые фигурные скобки - это тело класса. Для этого примера мне все равно, что в классе.
Показать ещё 3 комментария
Теги:
c++11
memory-alignment

2 ответа

0

Один из вариантов, который я видел на практике, это перераспределение, а затем выравнивание вещей для себя.

Например, предположим, что вы хотите выделить N байтов, согласованных с границами байтов M. Вы можете продолжить, выделив N + M-1 байт, а затем найдите первый согласованный по M-байту адрес в выделенном пространстве. Например, вы можете сделать что-то вроде:

const size_t N = sizeof(Widget);
const size_t M = 64;
char *pc = new char[N+M-1];
char *pc_aligned = (pc+M-1)&(~(M-1));
Widget *p = new (pc_aligned) Widget();    // placement new
// ...
p->~Widget();                             // explicitly call destructor
delete [] pc;                             // delete array allocated with new
  • 1
    Насколько я понимаю, это то, для чего предназначен std::align .
  • 0
    @KnowItAllWannabe Правильно, но все еще довольно часто пропускает std::align прямо сейчас.
0

MS четко документирует, что С++ 11 alignas по адресу http://msdn.microsoft.com/en-us/library/hh567368.aspx

Для GNU - кажется, он работает, если вы положили его в нужное место - откуда я могу использовать alignas() в С++ 11? просто вкус - см. этот ответ для многих других примеров и обсуждений:

typedef float aligned_block alignas(16) [4];
typedef float aligned_block [4] alignas(16);
  • 0
    Я не вижу примеров в stackoverflow.com/questions/15788947/…, которые относятся к объектам кучи. Я пропускаю их?
  • 0
    @KnowItAllWannabe Если вы сообщаете компилятору, что необходимо конкретное выравнивание объекта, new должен учитывать это вплоть до alignof(std::max_align_t) , после которого «Если выравнивание, связанное с конкретным переопределенным типом, не поддерживается распределителем , создание экземпляра распределителя для этого типа может завершиться неудачей. Распределитель также может молча игнорировать запрошенное выравнивание. ", что в основном означает, что вы вернулись к его организации вручную согласно ответу SchighSchagh. Суть этого ответа заключается в описании поддержки компилятора и нотации для alignas .
Показать ещё 8 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню