Como os blocos / warps / threads CUDA são mapeados para os núcleos CUDA?


142

Uso CUDA há algumas semanas, mas tenho algumas dúvidas sobre a alocação de blocos / warps / threads. Estou estudando a arquitetura do ponto de vista didático (projeto universitário), portanto, atingir o máximo desempenho não é minha preocupação.

Antes de mais, gostaria de entender se entendi esses fatos:

  1. O programador escreve um kernel e organiza sua execução em uma grade de blocos de threads.

  2. Cada bloco é atribuído a um Streaming Multiprocessor (SM). Uma vez atribuído, ele não pode migrar para outra SM.

  3. Cada SM divide seus próprios blocos em Warps (atualmente com um tamanho máximo de 32 threads). Todos os threads em um warp são executados simultaneamente nos recursos do SM.

  4. A execução real de um encadeamento é realizada pelos núcleos CUDA contidos no SM. Não há mapeamento específico entre threads e núcleos.

  5. Se um warp contiver 20 threads, mas atualmente houver apenas 16 núcleos disponíveis, o warp não será executado.

  6. Por outro lado, se um bloco contiver 48 threads, ele será dividido em 2 warps e eles serão executados em paralelo, desde que haja memória suficiente disponível.

  7. Se um encadeamento iniciar em um núcleo, ele será interrompido para acesso à memória ou para uma operação longa de ponto flutuante, sua execução poderá continuar em um núcleo diferente.

Eles estão corretos?

Agora, eu tenho uma GeForce 560 Ti, de acordo com as especificações, ela é equipada com 8 SM, cada uma contendo 48 núcleos CUDA (384 núcleos no total).

Meu objetivo é garantir que todos os núcleos da arquitetura executem as mesmas instruções. Supondo que meu código não exija mais registro do que os disponíveis em cada SM, imaginei abordagens diferentes:

  1. Crio 8 blocos de 48 threads cada, para que cada SM tenha 1 bloco para executar. Nesse caso, os 48 threads serão executados em paralelo no SM (explorando todos os 48 núcleos disponíveis para eles)?

  2. Existe alguma diferença se eu lançar 64 blocos de 6 threads? (Supondo que eles sejam mapeados igualmente entre as SMs)

  3. Se eu "submergir" a GPU no trabalho agendado (criando 1024 blocos de 1024 threads cada, por exemplo), é razoável supor que todos os núcleos serão usados ​​em um determinado ponto e executem os mesmos cálculos (assumindo que os threads nunca parar)?

  4. Existe alguma maneira de verificar essas situações usando o criador de perfil?

  5. Existe alguma referência para essas coisas? Li o guia de programação da CUDA e os capítulos dedicados à arquitetura de hardware em "Programando processadores massivamente paralelos" e "Design e desenvolvimento de aplicativos CUDA"; mas não consegui uma resposta precisa.


Gostaria de acrescentar como comentário o que é "núcleo CUDA". "Núcleo CUDA" ou "Unidade de execução" é um ALU e FPU inteiro totalmente em pipeline que executa uma instrução de instrução aritmética por ciclo de clock em um thread cuda.
bruziuz

Respostas:


123

Duas das melhores referências são

  1. White paper sobre arquitetura de computação da NVIDIA Fermi
  2. Comentários GF104

Vou tentar responder a cada uma das suas perguntas.

O programador divide o trabalho em threads, threads em blocos de threads e blocos de threads em grades. O distribuidor de trabalho de computação aloca blocos de encadeamento para Multiprocessadores de Streaming (SMs). Depois que um bloco de threads é distribuído para uma SM, os recursos para o bloco de threads são alocados (warps e memória compartilhada) e os threads são divididos em grupos de 32 threads chamados warps. Depois que um warp é alocado, ele é chamado de warp ativo. Os dois agendadores de warp selecionam dois warps ativos por ciclo e enviam warps para as unidades de execução. Para mais detalhes sobre unidades de execução e envio de instruções, consulte 1 p.7-10 e 2 .

4 ' . Existe um mapeamento entre laneid (índice de threads em um warp) e um núcleo.

5 ' . Se um warp contiver menos de 32 threads, na maioria dos casos, será executado da mesma forma que se tivesse 32 threads. Os warps podem ter menos de 32 encadeamentos ativos por vários motivos: o número de encadeamentos por bloco não é divisível por 32, o programa executa um bloco divergente para que os encadeamentos que não seguiram o caminho atual sejam marcados como inativos ou um encadeamento no warp encerrado.

6 ' . Um bloco de encadeamento será dividido em WarpsPerBlock = (ThreadsPerBlock + WarpSize - 1) / WarpSize Não é necessário que os planejadores de warp selecionem dois warps do mesmo bloco de encadeamento.

7 ' . Uma unidade de execução não irá parar em uma operação de memória. Se um recurso não estiver disponível quando uma instrução estiver pronta para ser despachada, a instrução será despachada novamente no futuro quando o recurso estiver disponível. Os warps podem parar em barreiras, operações de memória, operações de textura, dependências de dados, ... Um warp parado é inelegível para ser selecionado pelo agendador de warp. No Fermi, é útil ter pelo menos 2 warps elegíveis por ciclo para que o agendador de warp possa emitir uma instrução.

Consulte a referência 2 para obter diferenças entre um GTX480 e um GTX560.

Se você ler o material de referência (alguns minutos), acho que descobrirá que seu objetivo não faz sentido. Vou tentar responder aos seus pontos.

1 ' . Se você iniciar o kernel <<< 8, 48 >>>, obterá 8 blocos cada um com 2 warps de 32 e 16 threads. Não há garantia de que esses 8 blocos sejam atribuídos a diferentes SMs. Se 2 blocos forem alocados para uma SM, é possível que cada agendador de warp possa selecionar um warp e executá-lo. Você usará apenas 32 dos 48 núcleos.

2 ' . Há uma grande diferença entre 8 blocos de 48 threads e 64 blocos de 6 threads. Vamos supor que seu kernel não tenha divergências e cada thread execute 10 instruções.

  • 8 blocos com 48 threads = 16 warps * 10 instruções = 160 instruções
  • 64 blocos com 6 threads = 64 warps * 10 instruções = 640 instruções

Para obter a eficiência ideal, a divisão do trabalho deve estar em múltiplos de 32 threads. O hardware não unirá os threads de diferentes warps.

3 ' . Um GTX560 pode ter 8 blocos SM * 8 = 64 blocos por vez ou 8 warps SM * 48 = 512 warps se o kernel não atingir o máximo de registros ou memória compartilhada. A qualquer momento, parte do trabalho estará ativa nas SMs. Cada SM possui várias unidades de execução (mais de núcleos CUDA). Quais recursos estão em uso em um determinado momento depende dos agendadores de warp e do mix de instruções do aplicativo. Se você não executar operações TEX, as unidades TEX ficarão ociosas. Se você não fizer uma operação especial de ponto flutuante, as unidades SUFU ficarão inativas.

4 ' . Nsight paralelo e o Visual Profiler mostram

uma. IPC executado

b. IPC emitido

c. urdiduras ativas por ciclo ativo

d. urdidões elegíveis por ciclo ativo (apenas Nsight)

e motivos de distorção (apenas no Nsight)

f. threads ativos por instrução executada

O criador de perfil não mostra a porcentagem de utilização de nenhuma das unidades de execução. Para o GTX560, uma estimativa aproximada seria IssuedIPC / MaxIPC. Para o MaxIPC, assuma que GF100 (GTX480) é 2 GF10x (GTX560) é 4, mas o alvo é 3 é um alvo melhor.


1
Obrigado pela sua resposta. Li as referências, mas há algumas coisas que não entendo na sua resposta. Nas perguntas a seguir, estou assumindo que estamos usando uma arquitetura Fermi com 48 núcleos (16 núcleos * 3 "grupos principais"): 1. Você mencionou um mapeamento entre núcleos e laneid. Que tipo de mapeamento é esse? 2. A partir das referências, entendi que cada "grupo principal" executa no máximo um meio-warp (16 threads) por ciclo de clock. Portanto, em teoria, se tivermos 48 threads no mesmo bloco, eles serão organizados em 3 semi-warps e executados em paralelo nos 48 núcleos. Estou certo?
Daedalus

1
Os núcleos CUDA são o número de unidades FP de precisão única. Pensar na execução em termos de núcleos CUDA não está correto. Cada urdidura possui 32 threads. Esses encadeamentos serão emitidos para um grupo de unidades de execução (por exemplo, 16 núcleos cuda). Para emitir para todos os 48 núcleos em um único relógio, um dos dois agendadores de warp precisa selecionar um warp que atenda ao req de um par superescalar e ambas as instruções precisam ser do tipo executada pelos núcleos CUDA. Além disso, o outro agendador de warp precisa escolher um warp cuja próxima instrução será executada pelos núcleos CUDA.
Greg Smith

1
Não é necessário que os warps estejam no mesmo bloco ou que os warps em um bloco tenham o mesmo contador de programa.
Greg Smith

2
No seu exemplo, cada planejador está escolhendo uma distorção e emitindo 1 instrução. Nesse caso, apenas 2 grupos de unidades de execução serão usados. Para usar mais unidades de execução, 1 dos agendadores precisa emitir dois. Conforme indicado nas referências, existem vários tipos de unidades de execução (e não apenas o que é cunhado de núcleos cuda) e existem regras de emparelhamento de instruções (não bem documentadas) que devem ser atendidas para que os planejadores possam emitir duas questões.
Greg Smith

1
@ GregSmith, estou pesquisando em toda a web para descobrir de onde vêm esses 8 blocos ativos por SM na arquitetura Fermi. Nem é mencionado no white paper da fermi. Você tem mais alguma referência sobre isso?
Greg K.

8

"E. Se um warp contiver 20 threads, mas atualmente houver apenas 16 núcleos disponíveis, o warp não será executado."

está incorreto. Você está confundindo núcleos no sentido usual (também usado em CPUs) - o número de "multiprocessadores" em uma GPU, com núcleos no marketing da nVIDIA ("nossa placa possui milhares de núcleos CUDA").

Um warp em si só pode ser agendado em um único núcleo (= multiprocessador) e pode executar até 32 threads ao mesmo tempo; não pode usar mais que um único núcleo.

O número "48 warps" é o número máximo de warps ativos (warps que podem ser escolhidos para serem agendados para trabalhar no próximo ciclo, a qualquer ciclo) por multiprocessador, nas GPUs nVIDIA com Compute Capability 2.x; e esse número corresponde a 1536 = 48 x 32 threads.

Resposta baseada neste webinar


@ GregSmith: Editada a resposta para resolver isso. É bom que você era paciente com ele, mas - tem sido cinco anos ...
einpoklum

núcleo único (= multiprocessador)? Eu acho que pergunta assume terminologia single core = processador e não multiprocessador. Com sua terminologia, sua resposta está correta.
Adarsh
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.