К сожалению, обеспечивая максимальное выравнивание намного сложнее, чем это должно быть, и нет гарантированных решений 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, а основные участники находятся в коде, которым вы управляете) , написание собственного распределителя может действительно изменить ситуацию.
портативных в каком отношении, точно? для каждого компилятора? для каждой ОС? для каждой архитектуры? –
Просто портативный, как в «гарантированном стандартом C++ для работы». Конечно, я мог бы легко опираться на собственные знания о целевой архитектуре и жестком кодексе с максимальным выравниванием, но было бы неплохо, если бы сам язык предоставил инструменты для ответа на это. – jalf
Обратите внимание, что параметр шаблона 'Align'' 'std :: aligned_storage' имеет аргумент по умолчанию «выравнивание по умолчанию», который определяется как «Значение выравнивания по умолчанию должно быть самым строгим требованием выравнивания для любого объекта C++ тип которого не больше, чем «Лен». Я не знаю, считаются ли типы SSE «типами объектов C++», а стандартная библиотека VC10 не имеет аргумента по умолчанию, поэтому я не знаю, что это за значение (у меня нет другой стандартной библиотеки реализации на этой машине). –