Introdução à Otimização do Desempenho de CPU e GPU
Em computação de alto desempenho e processamento de tarefas paralelas em larga escala, as GPUs se tornaram aceleradores indispensáveis. Para utilizar plenamente as capacidades computacionais das GPUs, é crucial otimizar as relações entre CPU e GPU, alocando e vinculando razoavelmente os núcleos da CPU às GPUs. Este artigo abordará os conceitos de sockets e NUMA (Non-Uniform Memory Access) e discutirá como implementar a ligação de núcleos de CPU e GPU com base nessas arquiteturas de hardware para garantir o desempenho ideal do sistema.
Conceito de Socket
O que é um Socket?
Um socket geralmente se refere ao slot físico de instalação da CPU na placa-mãe. Cada socket corresponde a uma CPU física, geralmente contendo múltiplos núcleos e um ou mais níveis de cache (por exemplo, caches L1, L2, L3). Em sistemas multi-socket (por exemplo, servidores duplos ou quádruplos), cada socket possui uma CPU física conectada por meio de uma interconexão de alta velocidade (por exemplo, QPI da Intel ou Infinity Fabric da AMD).
Características de Sistemas Multi-Socket
Em sistemas multi-socket, a CPU de cada socket pode acessar sua memória local e também acessar memória de outros sockets. Esse padrão de acesso à memória introduz o conceito de NUMA, com o objetivo de otimizar a eficiência do acesso à memória.
Arquitetura NUMA (Non-Uniform Memory Access)
O que é NUMA?
NUMA significa Non-Uniform Memory Access, ou acesso à memória não uniforme. Diferentemente do acesso uniforme à memória tradicional (UMA), na arquitetura NUMA a memória do sistema é dividida em várias regiões, cada uma associada a uma CPU específica (socket). Acessar a memória local (dentro do mesmo socket) é mais rápido do que acessar a memória remota (de outro socket), resultando em maior latência.
Nós NUMA e Latência de Acesso à Memória
Em sistemas NUMA, cada socket e sua memória diretamente conectada formam um nó NUMA. O acesso à memória dentro do mesmo nó NUMA é mais rápido, enquanto o acesso à memória entre nós incorre em maior latência devido à transmissão adicional do barramento. Otimizar a afinidade entre memória e CPU, garantindo que as tarefas sejam executadas dentro do mesmo nó NUMA, é uma etapa crítica de otimização de desempenho.
Relação Física entre CPU e GPU
Arquitetura de Hardware da GPU
As GPUs normalmente se comunicam com as CPUs por meio de barramentos PCIe (Peripheral Component Interconnect Express). Em sistemas multi-socket, as GPUs geralmente são conectadas a apenas um socket (e seu nó NUMA correspondente) e não entre sockets. Isso significa que, na operação real, a GPU tem maior largura de banda e menor latência com os núcleos da CPU e a memória do socket ao qual está conectada.
Afinidade entre CPU e GPU
A comunicação entre CPU e GPU depende principalmente da transferência de dados. Os dados são transferidos da CPU para a GPU e de volta para a CPU, envolvendo operações de acesso à memória que impactam significativamente o desempenho. Se o núcleo da CPU vinculado à GPU estiver localizado dentro do mesmo nó NUMA que a GPU, a latência de transferência de dados é reduzida significativamente. Portanto, vincular núcleos da CPU às GPUs é uma etapa fundamental de otimização de desempenho.
Estratégia de Ligação de Núcleo de GPU e CPU Baseada em Afinidade
Na implantação em contêineres Docker, implementar a ligação de afinidade entre GPU e CPU é crucial para melhorar o desempenho de tarefas em contêineres. Os recursos de controle de CPU e GPU do Docker permitem um controle preciso sobre os núcleos da CPU e as GPUs usadas pelos contêineres.
Alocação de Recursos de CPU e GPU em Contêineres
Os contêineres Docker permitem um controle preciso sobre os recursos de CPU e GPU alocados. Ao especificar os núcleos da CPU e os dispositivos GPU usados por um contêiner, é possível implementar a ligação de afinidade para otimizar o desempenho.
Configurações de CPU no Docker
No Docker, a alocação de recursos da CPU pode ser controlada usando os seguintes parâmetros:
--cpuset-cpus: Especifica os núcleos físicos da CPU que podem ser usados pelo contêiner. Por exemplo,--cpuset-cpus="0-3"restringe o contêiner a usar os núcleos 0 a 3.--cpu-shares: Controla o peso de uso da CPU do contêiner, mas não restringe o uso de núcleos específicos.--cpus: Limita o número total de núcleos de CPU (em núcleos virtuais) que podem ser usados pelo contêiner.
Configurações de GPU no Docker
A vinculação de dispositivos GPU pode ser alcançada usando os seguintes parâmetros do Docker:
--gpus: Especifica os dispositivos GPU que podem ser acessados pelo contêiner. Por exemplo,--gpus '"device=0"'atribui a GPU 0 ao contêiner.
Implementando a Ligação de Núcleo de GPU e CPU no Docker
Para implementar a ligação de núcleo de GPU e CPU em contêineres Docker, combine as configurações de CPU e GPU. Aqui está um exemplo de como vincular núcleos de CPU específicos e uma GPU ao iniciar um contêiner Docker:
docker run --cpuset-cpus="0-3" --gpus '"device=0"' --memory="8g" my_gpu_container
Neste comando:
--cpuset-cpus="0-3"vincula o contêiner aos núcleos 0 a 3, que devem estar dentro do mesmo nó NUMA que a GPU 0.--gpus '"device=0"'atribui a GPU 0 ao contêiner.--memory="8g"limita o uso de memória do contêiner a 8 GB, garantindo que a alocação de memória também esteja alinhada com a afinidade CPU/GPU.
Alcançando a Ligação Ideal
Para garantir a ligação ideal nos contêineres, primeiro determine a topologia física da máquina. Use nvidia-smi topo -m para visualizar a topologia da máquina:
nvidia-smi topo -m
A partir da saída, você pode determinar a configuração NUMA da máquina e as atribuições de GPU. Por exemplo, se a máquina tiver dois nós NUMA, NUMA 0 com 4 GPUs e NUMA 1 com 4 GPUs, você pode identificar a afinidade GPU e CPU para cada nó NUMA.
Pseudocódigo para Cálculo de Afinidade
Aqui está um pseudocódigo simplificado para calcular a afinidade e determinar os IDs de GPU e CPU:
type Affinity struct {
}
// Calculate whether the affinity is satisfied and return the corresponding GPU and CPU IDs
func calAffinity(affinity *Affinity, cpuUse []int, gpuUse []int, gpuReq int, cpuReq int) (bool, []int, []int) {
return true, []int{}, []int{}
}
Este método pode ser usado para determinar se um nó satisfaz os requisitos ideais de ligação. Quando existem vários nós, este método pode ser usado para avaliar e selecionar o nó mais adequado.A Novita AI desenvolveu um mecanismo de contêiner adaptado para a próxima geração de computação de IA, ajustando algoritmos dinamicamente para monitorar o uso de hardware em tempo real e realizar otimização ponta a ponta. Os usuários podem desfrutar do melhor desempenho computacional sem se preocupar com detalhes técnicos de NUMA.
Os usuários também podem realizar configurações NUMA mais avançadas no painel de controle. Se você tiver mais requisitos relacionados, sinta-se à vontade para entrar em contato conosco no Discord!
Visite Novita AI para mais detalhes!

