C ++: как запретить конструктору по умолчанию использовать AVX для инициализации - PullRequest
3 голосов
/ 22 марта 2019

Примите во внимание следующее:

// foo.h
class Foo
{
   public:
      int x = 2;
      int y = 3;
      void DoSomething_SSE();
      void DoSomething_AVX();
   // ( Implicit default constructor is generated "inline" here )
};

// Foo_AVX.cpp, compiled with -mavx or /arch:AVX
void Foo::DoSomething_AVX()
{
   // AVX optimised implementation here
}

// Foo_SSE.cpp, compiled with -msse2 or /arch:SSE2
void Foo::DoSomething_SSE()
{
   // SSE optimised implementation here
}

Вот проблема: компилятор сгенерирует подразумеваемый конструктор по умолчанию с семантикой «inline» (примечание: встроенная семантика не означает, что функция будетобязательно должен быть встроен) в каждом модуле перевода, и - в тех случаях, когда конструктор не встроен - компоновщик затем выберет одну реализацию и откажется от другой.

Если компоновщик выбирает конструктор, сгенерированный в модуле компиляции AVXэтот код будет аварийно завершен с недопустимой инструкцией на компьютере, который не поддерживает AVX.

Можно остановить аварийное завершение, добавив явный конструктор по умолчанию, либо __forceinline (чтобы убедиться, что он встроен один разза единицу компиляции) или объявлено в заголовке и определено в единице компиляции, которая компилируется с набором команд с наименьшим общим знаменателем.

Однако, безусловно, есть способ заставить язык справиться с этим лучше, чем необходимостьнаписать фиктивные функции ..?

(llvm-clang ++ 9.xx / x64 в Mac OS X)

Ответы [ 3 ]

2 голосов
/ 22 марта 2019

Скомпилируйте блоки перевода AVX с помощью gcc или clang -mavx -fno-implement-inlines; компоновщик должен будет найти символ из единиц перевода SSE, если функции не просто встроены.


Из руководства GCC:

-fno-implement-inlines
Для экономии места не создавайте внеплановых копий встроенных функций, управляемых #pragma implementation. Это вызывает ошибки компоновщика, если эти функции не указываются везде, где они вызываются.

Clang также поддерживает эту опцию.

Это не отключает встраивание чего-либо, оно только запрещает выдачу отдельного определения функций, объявленных как inline или в определении класса.

При включенной оптимизации небольшой встроенный конструктор по умолчанию, как в вопросе, должен быть встроенным (и использовать целевые параметры ISA текущей функции / модуля компиляции), что делает его неактуальным большую часть времени. Но это обеспечит правильную работу неоптимизированных сборок на машинах, отличных от AVX.

1 голос
/ 22 марта 2019

Похоже, что другой вариант - не использовать флаги компилятора для установки набора команд - оставьте их по умолчанию и оберните только те функции, которые требуют расширенного набора команд:

#include Foo.h

// Switch on AVX optimisations for the function where they're needed
#pragma clang attribute push (__attribute__((target("arch=sandybridge"))), apply_to = function)

void Foo::DoSomething_AVX()
{
   // AVX optimised implementation here
}
#pragma clang attribute pop

Использование #pragmaАтрибут clang push (...), хотя и немного длиннее, чем простой [[]] или __attribute__(()), похоже, обладает тем преимуществом, что атрибут автоматически применяется к любому шаблонному коду и т. д., создаваемому из области действия прагмы.

0 голосов
/ 22 марта 2019

Поместите реализацию в отдельный файл .cpp и вуаля все работает. Другой способ - сделать эти функции / методы / конструкторы встроенными. Третий способ (зависит от компилятора) - установить для них атрибут «слабая ссылка».

...