2009-10-06 2 views
19

Есть ли какой-либо портативный способ определения максимально возможного выравнивания для любой тип есть?Определение максимально возможного выравнивания в C++

Например, для x86 для инструкций SSE требуется 16-байтовое выравнивание, но, насколько мне известно, никаких инструкций не требуется больше, поэтому любой тип можно безопасно хранить в буфере с выравниванием по 16 байт.

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

Если все остальное терпит неудачу, я знаю, что выделение массив символов с new гарантированно иметь максимальное выравнивание, но с TR1/C++ 0x шаблоны alignment_ofaligned_storage и, мне интересно, если это было бы возможно создать буфера на месте в моем классе буфера, вместо того, чтобы требовать дополнительной указательной ссылки на динамически выделенный массив.

Идеи?

Я понимаю, что существует множество вариантов определения максимального выравнивания для ограниченного набора типов: объединение или просто alignment_of из TR1, но моя проблема заключается в том, что набор типов неограничен. Я заранее не знаю, какие объекты должны храниться в буфере.

+0

портативных в каком отношении, точно? для каждого компилятора? для каждой ОС? для каждой архитектуры? –

+0

Просто портативный, как в «гарантированном стандартом C++ для работы». Конечно, я мог бы легко опираться на собственные знания о целевой архитектуре и жестком кодексе с максимальным выравниванием, но было бы неплохо, если бы сам язык предоставил инструменты для ответа на это. – jalf

+3

Обратите внимание, что параметр шаблона 'Align'' 'std :: aligned_storage ' имеет аргумент по умолчанию «выравнивание по умолчанию», который определяется как «Значение выравнивания по умолчанию должно быть самым строгим требованием выравнивания для любого объекта C++ тип которого не больше, чем «Лен». Я не знаю, считаются ли типы SSE «типами объектов C++», а стандартная библиотека VC10 не имеет аргумента по умолчанию, поэтому я не знаю, что это за значение (у меня нет другой стандартной библиотеки реализации на этой машине). –

ответ

9

В C++ 0x, параметр Align шаблон std::aligned_storage<Len, Align> имеет аргумент по умолчанию "по умолчанию выравнивания", который определяется как (N3225 §20.7.6.6 Таблица 56):

Значение выравнивание по умолчанию должно быть самым строгим требованием выравнивания для любого типа объекта C++, размер которого не превышает Len.

Неясно, будут ли типы SSE рассматриваться как типы объектов C++.

Аргумент по умолчанию не был частью TR1 aligned_storage; он был добавлен для C++ 0x.

5

Короткая часть maximally_aligned_t типа, что все компиляторы обещали добросовестно поддерживать все архитектуры во всем мире, я не вижу, как это можно было бы решить во время компиляции. Как вы говорите, набор потенциальных типов неограничен. Является ли дополнительная ссылка указателем действительно большой сделкой?

+0

Возможно, это не так, но мне любопытно, есть ли решение. C++ 0x добавляет несколько других связанных с выравниванием функций, и реализация уже должна определить максимально возможное выравнивание в других случаях (при динамическом распределении массива char), поэтому я подумал, что может быть какой-то неясный шаблон стандартной библиотеки, который предоставляет это стоимость. – jalf

+0

Да. Это интересный вопрос, и я бы хотел, чтобы у меня был лучший ответ для вас, но я не думаю, что существует какой-либо стандарт-совместимый способ. maximally_aligned_t (или лучше, maximal_alignment) было бы непросто реализовать; возможно, вы должны предложить его для C++ 1x :) –

1

Выделяя выровнен памяти намного сложнее, чем это выглядит - смотрите, например Implementation of aligned memory allocation

+0

Я знаю, что это сложно. Это был не мой вопрос. ;) Но стандарт дает некоторые гарантии, и особенно когда вы принимаете C++ 0x, у вас есть несколько стандартных * инструментов, которые помогут вам справиться. – jalf

+1

Сложность не относится к Jalf, потому что он не делает общий распределитель. Все, что ему нужно, - это дополнительное пространство в буфере и округлить указатель in-buffer к следующему желаемому блоку выравнивания. – Potatoswatter

5

К сожалению, обеспечивая максимальное выравнивание намного сложнее, чем это должно быть, и нет гарантированных решений AFAIK. Из GotW блога (Fast Pimpl article):

union max_align { 
    short  dummy0; 
    long  dummy1; 
    double  dummy2; 
    long double dummy3; 
    void*  dummy4; 
    /*...and pointers to functions, pointers to 
     member functions, pointers to member data, 
     pointers to classes, eye of newt, ...*/ 
}; 

union { 
    max_align m; 
    char x_[sizeofx]; 
}; 

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

Это около ближайшего «взлома». Я знаю для этого.

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

Я использую его для Pimpls в частности. Однако, просто наличие распределителя недостаточно; для того, чтобы такой распределитель работал, мы должны предположить, что память для класса Foo выделяется в конструкторе, та же память также освобождается только в деструкторе, а сам Foo создается в стеке. Чтобы это было безопасно, мне нужна была функция, чтобы увидеть, есть ли этот «указатель» класса в локальном стеке, чтобы определить, можем ли мы использовать наш супер быстрый кучевой распределитель стека.Для этого нам пришлось исследовать решения, специфичные для ОС: я использовал TIBs и TEBs для Win32/Win64, а мои сотрудники нашли решения для Linux и Mac OS X.

Результат после недели исследования конкретных ОС методы определения диапазона стека, требования к выравниванию и множество тестов и профилирования - это распределитель, который мог бы распределять память в 4 тактовых циклах в соответствии с нашими критериями контроля счетчика, а не около 400 циклов для нового malloc/оператора (наш тест thread malloc, вероятно, будет немного быстрее, чем это в однопоточных случаях, возможно, несколько сотен циклов). Мы добавили кучу кучи в потоке и обнаружили, какой поток использовался, что увеличило время до 12 циклов, хотя клиент может отслеживать распределитель потоков, чтобы получить 4 распределения циклов. Он уничтожил горячие точки распределения памяти на карте.

Хотя вам не нужно проходить через все эти проблемы, написание быстрого распределителя может быть проще и более общеприменимым (например: разрешение объема выделения/освобождения памяти для определения во время выполнения), чем что-то вроде max_align Вот. max_align достаточно прост в использовании, но если вы используете скорость для распределения памяти (и предположим, что вы уже профилировали свой код и обнаружили горячие точки в malloc/free/operator new/delete, а основные участники находятся в коде, которым вы управляете) , написание собственного распределителя может действительно изменить ситуацию.

+0

+1. Ничего себе, в 100 раз быстрее распределения. Спасибо, что поделились этой информацией. –

+1

Хм, это очень интересно. Но я не спрашивал о быстром распределении (в моем случае на самом деле это довольно простая проблема, потому что мне не нужно обрабатывать неприятные общие случаи, как вы, и я должен сказать, что я впечатлен вы его работали). Но мой вопрос был просто в том, чтобы обеспечить выделение объектов по правильно выровненным адресам. – jalf

+0

@jalf Ах, извинения, как правило, я думаю, что когда кто-то начинает разбирать проблемы выравнивания и хранить различные типы данных в одном буфере, это часто происходит с помощью распределителя памяти и производительности. Боюсь, я не знаю ни одного портативного способа обеспечения максимального выравнивания для данного типа. Обычно в таких случаях мне приходилось иметь определенную платформу. Чтобы попытаться получить его в безопасности, я часто использовал подходы к выбору (тип, чье выравнивание неизвестно, будет выравниваться по двойным границам квадранта, максимально возможное выравнивание, о котором я знаю). – stinky472

-2

Это то, что я использую. В дополнение к этому, если вы выделяете память, тогда новый массив() 'd char с длиной, большей или равной max_alignment, будет выровнен с max_alignment, чтобы затем вы могли использовать индексы в этом массиве для выравнивания адресов.

enum { 
      max_alignment = boost::mpl::deref< 
       boost::mpl::max_element< 
         boost::mpl::vector< 
          boost::mpl::int_<boost::alignment_of<signed char>::value>::type, 
          boost::mpl::int_<boost::alignment_of<short int>::value>::type, 
          boost::mpl::int_<boost::alignment_of<int>::value>::type,        boost::mpl::int_<boost::alignment_of<long int>::value>::type, 
          boost::mpl::int_<boost::alignment_of<float>::value>::type, 
          boost::mpl::int_<boost::alignment_of<double>::value>::type, 
          boost::mpl::int_<boost::alignment_of<long double>::value>::type, 
          boost::mpl::int_<boost::alignment_of<void*>::value>::type 
         >::type 
        >::type 
       >::type::value 
      }; 
     } 
+0

К сожалению, это не обеспечивает подходящего выравнивания для типов SSE. – jalf

9

В C++ 11 станда :: max_align_t, определенном в заголовочном cstddef представляет собой тип POD, чье выравнивание требования, по крайней мере, как строгие (как большие), как и у каждого скалярного типа.

Использование нового оператора alignof было бы так просто, как alignof(std::max_align_t)

+0

для компиляторов, которые не поддерживают alignof (то есть MSVC11), вы можете использовать std :: alignment_of :: значение –

+0

Это отлично работает, пока не появится [ошибка в стандартной библиотеке] (https: // github .com/cameron314/concurrentqueue/issues/64): - / – Cameron

Смежные вопросы