Compartilhar via


Usando objetos accelerator e accelerator_view

Você pode usar as classes accelerator e accelerator_view para especificar o dispositivo ou o emulador no qual executar seu código C++ AMP. Um sistema pode ter vários dispositivos ou emuladores que diferem pela quantidade de memória, suporte de memória compartilhada, suporte de depuração ou o suporte de precisão dupla. O C++ Accelerated Massive Parallelism (C++ AMP) fornece APIs que você pode usar para examinar os aceleradores disponíveis, para definir um como padrão, para especificar várias accelerator_views para chamadas múltiplas ao parallel_for_each, e para executar tarefas de depuração especial.

Usando o acelerador padrão

O tempo de execução de C++ AMP escolhe um acelerador padrão, a menos que você escreva código para escolher um específico. O tempo de execução escolhe o acelerador padrão da seguinte maneira:

  1. Se o aplicativo estiver executado no modo de depuração, um acelerador que oferece suporte à depuração.

  2. Caso contrário, o acelerador que é especificado pela variável de ambiente CPPAMP_DEFAULT_ACCELERATOR, se estiver definido.

  3. Caso contrário, um dispositivo não emulado.

  4. Caso contrário, o dispositivo que tem a maior quantidade de memória disponível.

  5. Caso contrário, um dispositivo que não está anexado à tela.

Além disso, o tempo de execução especifica um access_type de access_type_auto para o acelerador padrão. Isso significa que o acelerador padrão usa a memória compartilhada se ela tiver suporte e se suas características de desempenho (largura de banda e latência) estiverem iguais à memória dedicada (não compartilhada).

Você pode determinar as propriedades dos aceleradores padrão construindo o acelerador padrão e examinando suas propriedades. O exemplo de código a seguir mostra o caminho, a quantidade de memória do acelerador, o suporte de memória compartilhada, o suporte de precisão dupla e o suporte de precisão dupla limitado do acelerador padrão.

void default_properties() {
    accelerator default_acc;
    std::wcout << default_acc.device_path << "\n";
    std::wcout << default_acc.dedicated_memory << "\n";
    std::wcout << (accs[i].supports_cpu_shared_memory ? 
        "CPU shared memory: true" : "CPU shared memory: false") << "\n";
    std::wcout << (accs[i].supports_double_precision ? 
        "double precision: true" : "double precision: false") << "\n";
    std::wcout << (accs[i].supports_limited_double_precision ? 
        "limited double precision: true" : "limited double precision: false") << "\n";
}

Variável de ambiente CPPAMP_DEFAULT_ACCELERATOR

Você pode definir a variável de ambiente CPPAMP_DEFAULT_ACCELERATOR para especificar o accelerator::device_path do acelerador padrão. O caminho é dependente de hardware. O código a seguir usa a função accelerator::get_all para recuperar uma lista de aceleradores disponíveis e, em seguida, exibe o caminho e as características de cada acelerador.

void list_all_accelerators()
{
    std::vector<accelerator> accs = accelerator::get_all();
    for (int i = 0; i < accs.size(); i++) {
        std::wcout << accs[i].device_path << "\n";
        std::wcout << accs[i].dedicated_memory << "\n";
        std::wcout << (accs[i].supports_cpu_shared_memory ? 
            "CPU shared memory: true" : "CPU shared memory: false") << "\n";
        std::wcout << (accs[i].supports_double_precision ? 
            "double precision: true" : "double precision: false") << "\n";
        std::wcout << (accs[i].supports_limited_double_precision ? 
            "limited double precision: true" : "limited double precision: false") << "\n";
    }
}

Selecionando um acelerador

Para selecionar um acelerador, use o método accelerator::get_all para recuperar uma lista de aceleradores disponíveis e então selecione um com base em suas propriedades. Este exemplo mostra como escolher o acelerador que tem a maior memória:

void pick_with_most_memory()
{
    std::vector<accelerator> accs = accelerator::get_all();
    accelerator acc_chosen = accs[0];
    for (int i = 0; i < accs.size(); i++) {
        if (accs[i].dedicated_memory > acc_chosen.dedicated_memory) {
            acc_chosen = accs[i];
        }
    }

    std::wcout << "The accelerator with the most memory is "  
        << acc_chosen.device_path << "\n"
        << acc_chosen.dedicated_memory << ".\n";
}

Dica

Um dos aceleradores que são retornados por accelerator::get_all é o acelerador de CPU.Você não pode executar o código no acelerador CPU.Para filtrar o acelerador de CPU, compare o valor da propriedade device_path do acelerador que é retornado por accelerator::get_all com o valor de accelerator::cpu_accelerator.Para obter mais informações, consulte a seção “Aceleradores Especiais” neste artigo.

Memória compartilhada

A memória compartilhada é a que pode ser acessada pela CPU e pelo acelerador. O uso de memória compartilhada elimina ou reduz significativamente a sobrecarga de copiar dados entre a CPU e o acelerador. Embora a memória seja compartilhada, não pode ser acessada simultaneamente pela CPU e pelo acelerador, e fazendo isso provocaria um comportamento indefinido. A propriedade do acelerador supports_cpu_shared_memory retorna true se o acelerador oferece suporte à memória compartilhada, e a propriedade default_cpu_access_type obtém o access_type padrão da memória alocada no accelerator, por exemplo, o array associada ao accelerator ou a objetos array_view acessados no accelerator.

O tempo de execução de C++ AMP escolhe automaticamente o melhor access_type padrão para cada accelerator, mas as características de desempenho (largura de banda e latência) da memória compartilhada podem ser piores do que as da memória do acelerador (não compartilhada) dedicada ao ler da CPU, gravar da CPU ou ambos. Se a memória compartilhada funcionar tão bem como a memória dedicada para ler e gravar da CPU, o tempo de execução terá como padrão access_type_read_write; caso contrário, o tempo de execução escolherá um access_type padrão mais conservador e permitirá que o aplicativo o substitua caso os padrões de acesso à memória de seus kernels de computação se beneficiem de um access_typediferente.

O exemplo de código a seguir mostra como determinar se o acelerador padrão dá suporte à memória compartilhada e, em seguida, substitui seu tipo de acesso padrão e cria um accelerator_view a partir dele.

#include <amp.h>
#include <iostream>

using namespace Concurrency;

int main()
{
  accelerator acc = accelerator(accelerator::default_accelerator);

  // Early out if the default accelerator doesn’t support shared memory.
  if(!acc.supports_cpu_shared_memory)
  {
    std::cout << "The default accelerator does not support shared memory" << std::endl;
    return 1;
  }

  // Override the default CPU access type.
  acc.set_default_cpu_access_type(access_type_read_write);

  // Create an accelerator_view from the default accelerator. The
  // accelerator_view reflects the default_cpu_access_type of the
  // accelerator it’s associated with.
  accelerator_view acc_v = acc.default_view;
}

Um accelerator_view sempre reflete o default_cpu_access_type do accelerator com o qual está associado, e não fornece nenhuma interface para substituir ou alterar seu access_type.

Alterando o acelerador padrão

Você pode alterar o acelerador padrão chamando o método accelerator::set_default. É possível alterar o acelerador padrão apenas uma vez por execução de aplicativo e você deve alterá-lo antes que qualquer código seja executado no GPU. Quaisquer chamadas de função subsequentes para alterar o acelerador retornam false. Se você deseja usar um acelerador diferente em uma chamada para parallel_for_each leia a seção “Usando múltiplos aceleradores” neste artigo. O exemplo de código a seguir define o acelerador padrão para um que não é emulado, não é conectado a uma exibição e oferece suporte de precisão dupla.

bool pick_accelerator()
{
    std::vector<accelerator> accs = accelerator::get_all();
    accelerator chosen_one;

    auto result = 
        std::find_if(accs.begin(), accs.end(), [] (const accelerator& acc)
    {
        return !acc.is_emulated && 
            acc.supports_double_precision && 
            !acc.has_display;
    });

    if (result != accs.end())
        chosen_one = *(result);

    std::wcout << chosen_one.description << std::endl;

    bool success = accelerator::set_default(chosen_one.device_path);
    return success;
}

Usando múltiplos aceleradores

Há duas maneiras de usar múltiplos aceleradores em seu aplicativo:

  • Você pode passar objetos accelerator_view para chamadas ao método parallel_for_each.

  • Você pode criar um objeto array usando um objeto específico accelerator_view. O tempo de execução C++ AMP pegará o objeto accelerator_view do objeto capturado array na expressão lambda.

Aceleradores especiais

Os caminhos de dispositivo dos três aceleradores especiais estão disponíveis como propriedades da classe accelerator:

  • Membro de dados accelerator::direct3d_ref: Este acelerador de thread único usa software no CPU para emular uma placa gráfica genérica. Usado por padrão para a depuração, mas não é útil para a produção porque é mais lento do que os aceleradores de hardware. Além de isso, está disponível somente em DirectX SDK e o Windows SDK, e é improvável que esteja instalado nos computadores dos seus clientes. Para obter mais informações, consulte Depurando código de GPU.

  • Membro de dados accelerator::direct3d_warp: Este acelerador fornece uma solução de retorno para executar o código de C++ AMP em CPUs multi-core que usam extensões Streaming SIMD (SSE, em inglês).

  • Membro de dados accelerator::cpu_accelerator: Você pode usar esse acelerador para configurar matrizes de teste. Não pode executar o código de C++ AMP. Para obter mais informações, consultea postagem Preparando matrizes no C++ AMP no blog Programação em paralelo em código nativo.

Interoperabilidade

O tempo de execução de C++ AMP suporta a interoperabilidade entre a classe accelerator_view e a interface do Direct3D ID3D11Device. O método create_accelerator_view usa uma interface IUnknown e retorna um objeto accelerator_view. O método get_device utiliza um objeto accelerator_view e retorna uma interface IUknown.

Consulte também

Referência

Classe accelerator

Classe accelerator_view

Outros recursos

C++ AMP (C++ Accelerated Massive Parallelism)

Depurando código de GPU