AVX2: ошибка сегментации при назначении члена класса __m256i - PullRequest
1 голос
/ 21 апреля 2019

В настоящее время я занимаюсь рефакторингом императивной программы на C ++, которая широко использует примитивы AVX2 для хорошо структурированной программы на основе классов. К сожалению, при назначении учащемуся с типом данных AVX2 я сталкиваюсь с segfault.

Я в WSL, используя:

gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)

Компиляция с флагами:

g++ -mavx2 -g minimal.cpp

Минимальный пример кода для воспроизведения segfault:

#include <immintrin.h>

class MyClass
{
    public:
        MyClass(int* arr);
        __m256i value;
};

MyClass::MyClass(int* arr){
    this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
}

int main(){
    int arr[8] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7};
    MyClass* m = new MyClass(arr);
}

Вывод GDB:

Program received signal SIGSEGV, Segmentation fault.
0x00000000080007cf in MyClass::MyClass (this=0x8413e70, arr=0x7ffffffedd90) at minimal.cpp:11
11          this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);

Я уже пытался назначить члена класса после конструктора, тот же segfault.

Обновление: Этот является связанным вопросом, однако он не является дубликатом (здесь: Сосредоточьтесь на учениках, отношение к «новому» стало очевидным только после первоначального вопроса)

1 Ответ

4 голосов
/ 21 апреля 2019

Как уже упоминал Питер Кордес в комментариях выше, проблема в том, что new не учитывает расширенное выравнивание до C ++ 17.(См. [P0035R4] , который был принят в C ++ 17, чтобы сделать new пригодным для использования в памяти с более чем alignof(maxalign_t) выравниванием).

GCC7 и более поздние поддерживает выровненный новый с -std=gnu++17 или -std=c++17 (или просто -faligned-new).Ваш код будет Just Work ™ и автоматически передаст необходимое выравнивание на operator new, если вы включите эти опции .


Но более старый GCC, включая ваш 6.3, делает не , поэтому вам придется вручную убедиться, что память выровнена правильно.Есть несколько способов сделать это.

_mm_alloc уже упоминалось в комментариях.В GCC _mm_alloc, по-видимому, в основном соответствует posix_memalign, так что вы также можете просто использовать это напрямую.Переносимое решение C ++ 11 будет выделять достаточно большой буфер для размещения объекта вашего класса плюс любое пространство, необходимое для заполнения в начале, чтобы обеспечить правильное выравнивание.Затем вы можете использовать std::align и для размещения нового , чтобы построить свой объект по подходящему адресу.

Все это, независимо от того, какой метод распределенияПравильно выровненную память, которую вы выберете, я бы настоятельно рекомендовал инкапсулировать в этом, предоставляя функции выделения и освобождения для вашего класса.Требование выравнивания является свойством самого типа, и пользователь вашего класса не должен знать, что из-за деталей реализации, таких как тот факт, что он имеет член типа __m256i, любой объект типа MyClass имеет расширенные требования выравнивания, которые должны учитываться всякий раз, когда такой объект выделяется с помощью нового выражения.Вам следует либо запретить создание объектов этого типа с помощью нового выражения, либо предоставить необходимые средства для правильной работы типа с новыми выражениями ...

Решение C ++ 11 может выглядеть следующим образом:

#include <cstddef>
#include <memory>

#include <immintrin.h>

class MyClass
{
    __m256i value;

public:
    MyClass(const int* arr)
    {
        this->value = _mm256_set_epi32(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
    }

    void* operator new(std::size_t size)
    {
        return _mm_malloc(size, alignof(MyClass));
    }

    void* operator new[](std::size_t size)
    {
        return _mm_malloc(size, alignof(MyClass));
    }

    void operator delete(void* ptr)
    {
        _mm_free(ptr);
    }

    void operator delete[](void* ptr)
    {
        _mm_free(ptr);
    }
};

int main()
{
    int arr[8] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7};
    auto m = std::unique_ptr<MyClass> { new MyClass(arr) };
}

живой пример здесь

...