Paralelização automática e vetorização automática

A paralelização automática e a vetorização automática foram projetadas para fornecer ganhos automáticos de desempenho para loops no seu código.

Paralelizador automático

O comutador do compilador /Qpar permite a paralelização automática de loops no seu código. Quando você especifica esse sinalizador sem alterar o código existente, o compilador avalia o código para localizar loops que podem se beneficiar da paralelização. Como ele pode encontrar loops que não têm muita atividade e, portanto, não se beneficiarão da paralelização, e como toda paralelização desnecessária pode provocar a geração de um pool de threads, sincronização extra ou outro processamento que tende a diminuir o desempenho em vez de melhorá-lo, o compilador é conservador ao selecionar os loops que paraleliza. Por exemplo, considere o exemplo a seguir no qual o limite superior do loop não é conhecido no tempo de compilação:

void loop_test(int u) {
   for (int i=0; i<u; ++i)
      A[i] = B[i] * C[i];
}

Como u pode ser um valor pequeno, o compilador não paralelizará automaticamente esse loop. No entanto, talvez você ainda queira paralelizar porque sabe que u sempre será grande. Para habilitar a paralelização automática, especifique #pragma loop(hint_parallel(n)), em que n é o número de threads a serem paralelizados. No exemplo a seguir, o compilador tentará paralelizar o loop em 8 threads.

void loop_test(int u) {
#pragma loop(hint_parallel(8))
   for (int i=0; i<u; ++i)
      A[i] = B[i] * C[i];
}

Assim como acontece com todas as diretivas de pragma, também há suporte para a sintaxe __pragma(loop(hint_parallel(n))) de pragma alternativa.

Existem alguns loops que o compilador não pode paralelizar, mesmo se você quiser. Veja um exemplo:

#pragma loop(hint_parallel(8))
for (int i=0; i<upper_bound(); ++i)
    A[i] = B[i] * C[i];

A função upper_bound() pode mudar sempre que for chamada. Como o limite superior não pode ser conhecido, o compilador pode emitir uma mensagem de diagnóstico que explica por que ele não pode paralelizar esse loop. O exemplo a seguir demonstra um loop que pode ser paralelizado, um loop que não pode ser paralelizado, a sintaxe do compilador a ser usada no prompt de comando e a saída do compilador para cada opção de linha de comando:

int A[1000];
void test() {
#pragma loop(hint_parallel(0))
    for (int i=0; i<1000; ++i) {
        A[i] = A[i] + 1;
    }

    for (int i=1000; i<2000; ++i) {
        A[i] = A[i] + 1;
    }
}

Compilar usando este comando:

cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:1

produz esta saída:

--- Analyzing function: void __cdecl test(void)
d:\myproject\mytest.cpp(4) : loop parallelized

Compilar usando este comando:

cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:2

produz esta saída:

--- Analyzing function: void __cdecl test(void)
d:\myproject\mytest.cpp(4) : loop parallelized
d:\myproject\mytest.cpp(4) : loop not parallelized due to reason '1008'

Observe a diferença na saída entre as duas opções diferentes de /relatório Qpar (Nível de relatório do paralelizador automático). /Qpar-report:1 gera mensagens do paralelizador somente para loops que são paralelizados com êxito. /Qpar-report:2 gera mensagens paralelizadores para paralelizações de loop bem-sucedidas e malsucedidas.

Para obter mais informações sobre códigos de motivos e mensagens, confira Mensagens do vetorizador e paralelizador.

Vetorizador automático

O vetorizador automático analisa loops no seu código e usa os registros e instruções de vetor no computador de destino para executá-los, se possível. Isso pode ajudar a melhorar o desempenho do código. O compilador tem como alvo as instruções SSE2, AVX e AVX2 em processadores Intel ou AMD, ou as instruções NEON em processadores ARM, de acordo com o comutador /arch.

O vetorizador automático pode gerar instruções diferentes das especificadas pelo comutador /arch. Essas instruções são protegidas por uma verificação de runtime para garantir que o código ainda seja executado corretamente. Por exemplo, quando você compila /arch:SSE2, as instruções SSE4.2 podem ser emitidas. Uma verificação de runtime verifica se o SSE4.2 está disponível no processador de destino e salta para uma versão não SSE4.2 do loop se o processador não oferecer suporte a essas instruções.

Por padrão, o vetorizador automático está habilitado. Se quiser comparar o desempenho do código sob vetorização, você pode usar #pragma loop(no_vector) para desabilitar a vetorização de algum loop específico.

#pragma loop(no_vector)
for (int i = 0; i < 1000; ++i)
   A[i] = B[i] + C[i];

Assim como acontece com todas as diretivas de pragma, também há suporte para a sintaxe __pragma(loop(no_vector)) de pragma alternativa.

Assim como acontece com o paralelizador automático, você pode especificar a opção de linha de comando Relatório /Qvec (Nível de relatório do vetorizador automático) para relatar somente loops vetorizados com êxito /Qvec-report:1, ou loops vetorizados com e sem sucesso /Qvec-report:2.

Para obter mais informações sobre códigos de motivos e mensagens, confira Mensagens do vetorizador e paralelizador.

Para obter um exemplo mostrando como o vetorizador funciona na prática, consulte Projeto Austin Parte 2 de 6: Enrolamento de página

Confira também

loop
Programação paralela em código nativo
/Qpar (paralelizador automático)
/Qpar-report (nível de relatórios do paralelizador automático)
/Qvec-report (nível de relatórios do vetorizador automático)
Mensagens do vetorizador e do paralelizador