Abordagem moderna para tornar std :: vector alocar memória alinhada

11

A pergunta a seguir está relacionada, no entanto, as respostas são antigas e os comentários do usuário Marc Glisse sugerem que existem novas abordagens desde o C ++ 17 para esse problema que talvez não sejam discutidas adequadamente.

Estou tentando fazer com que a memória alinhada funcione corretamente para o SIMD, enquanto ainda tenho acesso a todos os dados.

Na Intel, se eu criar um vetor flutuante do tipo __m256e reduzir meu tamanho em um fator de 8, isso me dará uma memória alinhada.

Por exemplo std::vector<__m256> mvec_a((N*M)/8);

De uma maneira um pouco invasiva, posso converter ponteiros em elementos vetoriais para flutuar, o que me permite acessar valores de flutuação individuais.

Em vez disso, eu preferiria ter um std::vector<float>que esteja alinhado corretamente e, portanto, possa ser carregado em __m256e outros tipos de SIMD sem segfaulting.

Eu estive procurando em align_alloc .

Isso pode me dar uma matriz de estilo C que está alinhada corretamente:

auto align_sz = static_cast<std::size_t> (32);
float* marr_a = (float*)aligned_alloc(align_sz, N*M*sizeof(float));

No entanto, não tenho certeza de como fazer isso std::vector<float>. Dar a std::vector<float>propriedade de marr_a não parece ser possível .

Eu já vi algumas sugestões de que eu deveria escrever um alocador personalizado , mas isso parece dar muito trabalho, e talvez com o C ++ moderno haja uma maneira melhor?

Prunus Persica
fonte
11
sem segfaulting ... ou sem possíveis desacelerações das divisões da linha de cache quando você usa _mm256_loadu_ps(&vec[i]). (Embora nota que, com opções de ajuste padrão, GCC divide não-garantida alinhado 256 bits cargas / lojas em vmovups XMM / vinsertf128. Portanto, não é uma vantagem de usar _mm256_loadmais loaduse você se preocupa sobre como seus compila o código no GCC se alguém se esquece de uso -mtune=...ou -march=opções.)
Peter Cordes

Respostas:

1

Todos os contêineres na biblioteca C ++ padrão, incluindo vetores, têm um parâmetro de modelo opcional que especifica o alocador do contêiner e não é muito trabalhoso implementar seu próprio:

class my_awesome_allocator {
};

std::vector<float, my_awesome_allocator> awesomely_allocated_vector;

Você precisará escrever um pouco de código que implementa seu alocador, mas não seria muito mais código do que você já escreveu. Se você não precisar de suporte pré-C ++ 17, precisará implementar apenas os métodos alocar () e desalocar () , é isso.

Sam Varshavchik
fonte
Eles também precisam se especializarallocator_traits
NathanOliver
11
Esse pode ser um bom lugar para uma resposta canônica, com um exemplo em que as pessoas podem copiar / colar para pular os aros irritantes do C ++. (Pontos de bônus se houver uma maneira de permitir que std :: vector tente realocar no lugar, em vez do cabeçalho usual do C ++, sempre aloque + copiar.) Também, é claro, observe que isso vector<float, MAA>não é compatível com o tipo vector<float>(e não pode ser porque qualquer coisa que faça .push_backem uma planilha std::vector<float>compilada sem esse alocador pode fazer uma nova alocação e copiar para a memória minimamente alinhada.E new / delete não é compatível com align_alloc / free)
Peter Cordes
11
Acho que não há garantia de que o ponteiro retornado do alocador seja usado diretamente como o endereço base da std::vectormatriz do. Por exemplo, eu poderia imaginar uma implementação std::vectorusando apenas um ponteiro para a memória alocada que armazena o final / capacidade / alocador na memória antes do intervalo de valores. Isso poderia facilmente frustrar o alinhamento feito pelo alocador.
Dietmar Kühl
11
Exceto que std::vectorgarante isso. É para isso que serve. Talvez você deva revisar o que o padrão C ++ especifica aqui.
Sam Varshavchik
11
> Eles também precisam se especializar allocator_traits- Não, não precisam . Tudo o que é necessário é implementar um alocador compatível.
Andrey Semashev