taskset: Configurando Afinidade de CPU no Linux

taskset: Configurando Afinidade de CPU no Linux

O Que Você Vai Aprender

  • Como fixar um processo em núcleos de CPU específicos com taskset
  • A diferença entre fixação no início (-c) e reatribuição de um processo em execução (-p)
  • Padrões práticos para benchmarking, isolamento de núcleos e NUMA

Resumo Rápido

  • Fixar no início -> taskset -c 0,1 ./program
  • Fixar um processo em execução -> taskset -cp 0-3 <PID>
  • Verificar estado atual -> taskset -cp <PID>
  • A forma de lista (-c) é legível; a máscara hexadecimal serve para automação

Pré-requisitos

  • taskset vem com o pacote util-linux (pré-instalado na maioria das distros)
  • Números de núcleos começam em 0. Conte-os com nproc
  • Alterar um processo em execução requer ser dono do processo (ou root)

O que é afinidade de CPU?

Conclusão: Afinidade de CPU restringe em quais núcleos de CPU um processo ou thread pode executar. taskset é o comando para visualizar e alterar essa configuração.

Por padrão, o escalonador do Linux pode mover um processo para qualquer núcleo ocioso. Configurar afinidade de CPU significa que o processo só pode executar nos núcleos que você especificar.

As principais razões para fixar são:

  • Localidade de cache: evitar invalidação de cache L1/L2 causada por migração de núcleo
  • Isolamento de núcleo: dar a um processo sensível a latência um núcleo dedicado, livre de interferência
  • Benchmarks reprodutíveis: executar nos mesmos núcleos sempre para reduzir ruído nas medições

Afinidade especifica um conjunto de núcleos permitidos. Restrinja a um núcleo para fixação rigida, ou permita vários e deixe o escalonador escolher dentro desse conjunto.

Verificar contagem e numeração de núcleos

Conclusão: Antes de decidir onde fixar, confirme a contagem de núcleos disponíveis com nproc. Números de núcleos começam em 0.

$ nproc
8

Se imprimir 8, números de núcleos válidos são 0-7. Especificar um núcleo inexistente como taskset -c 8 ... retorna um erro.

Use lscpu quando também precisar do layout físico/lógico.

$ lscpu

Fixar núcleos no início (-c)

Conclusão: Para fixar ao iniciar um comando, use taskset -c <lista-nucleos> <comando>. A forma de lista aceita 0,2 (individual) e 0-3 (intervalo).

A forma básica: iniciar um novo processo fixado em núcleos específicos desde o início.

# Iniciar programa apenas nos nucleos 0 e 1
$ taskset -c 0,1 ./program

# Iniciar no intervalo de nucleos 0 a 3
$ taskset -c 0-3 ./program

# Fixar rigidamente em um unico nucleo (nucleo 2)
$ taskset -c 2 ./program

-c (--cpu-list) permite listar números de núcleos diretamente, facilitando a leitura. Use , para núcleos individuais, - para intervalos, e combine ambos (por exemplo 0,2,4-7).

Passe opções ao comando iniciado adicionando-as normalmente. taskset -c 0,1 stress-ng --cpu 2 funciona sem necessidade de separador --.

Visualizar e alterar um processo em execução (-p)

Conclusão: Para um processo já em execução, use -p (--pid): taskset -cp <PID> para verificar, taskset -cp <lista-nucleos> <PID> para alterar.

Verificar a afinidade atual

$ taskset -cp 1234
pid 1234's current affinity list: 0-7

0-7 significa "pode executar em todos os núcleos" (o padrão). Com -c você obtém a forma de lista; sem ele, uma máscara hexadecimal.

Reatribuir um processo em execução

# Reatribuir PID 1234 para nucleos 0 e 1
$ taskset -cp 0,1 1234
pid 1234's current affinity list: 0-7
pid 1234's new affinity list: 0,1

Encontre o PID com ps ou pgrep.

$ pgrep -f program

Atenção a ordem dos argumentos

No modo -p, a lista de núcleos vem primeiro, o PID depois: taskset -cp 0,1 1234, não taskset -cp 1234 0,1. Invertendo, taskset trata o PID como máscara e falha.

Máscara hexadecimal vs forma de lista

Conclusão: Com -c você especifica uma lista de núcleos; sem ele, uma máscara hexadecimal. Na máscara, cada bit mapeia um núcleo (bit 0 = núcleo 0).

Nativamente, taskset especifica núcleos como uma máscara hexadecimal. -c é a opção que troca isso por uma lista legível.

Núcleos alvo Forma de lista (-c) Máscara hex
Núcleo 0 0 0x1
Núcleos 0,1 0,1 0x3
Apenas nucl 1 1 0x2
Núcleos 0-3 0-3 0xf
Apenas nucl 3 3 0x8

A máscara é mais fácil de ler em binário. O bit menos significativo (mais à direita) é o núcleo 0. 0x3 é 0b0011 (núcleos 0 e 1); 0x8 é 0b1000 (núcleo 3).

# Iniciar com mascara (nucleos 0,1 = 0x3)
$ taskset 0x3 ./program

# Verificar com mascara (sem -c)
$ taskset -p 1234
pid 1234's current affinity mask: ff

Para comandos digitados manualmente, a forma de lista (-c) é mais segura. Um único dígito hexadecimal errado aponta para um núcleo diferente. Recorra à máscara apenas quando um script a gera.

Aplicar a todas as threads (-a)

Conclusão: Para fixar todas as threads de um processo multithread, adicione -a (--all-tasks). Sem ele, apenas a thread principal e afetada.

Ao operar em um processo em execução com -p, por padrão a afinidade se aplica apenas ao PID dado (a thread principal) e não se propaga para threads filhas existentes. Use -a para fixar todas as threads do processo de uma vez.

# Fixar todas as threads do PID 1234 nos nucleos 0-3
$ taskset -acp 0-3 1234

Mesmo com -a, threads criadas após a mudança herdam a afinidade do processo. -a se aplica a threads existentes; threads futuras herdam de qualquer forma. Não confunda os dois.

Casos de uso reais

Conclusão: Os casos clássicos são benchmarks reprodutíveis, isolamento de processos sensíveis a latência e manter acesso a memória local em NUMA.

Tornar benchmarks reprodutíveis

Medir nos mesmos núcleos toda vez remove a variância de cache-miss causada por migração de núcleo.

$ taskset -c 2,3 ./benchmark

Isolar processos sensíveis a latência

Combine com o parâmetro de boot do kernel isolcpus, que remove núcleos do escalonador do SO, e então coloque apenas seu processo alvo neles com taskset.

Manter memória local em NUMA

Em sistemas NUMA, fixar em núcleos no mesmo nó que a memória evita acesso remoto a memória. Quando também precisar controlar alocação de memória, numactl é mais adequado (taskset só lida com posicionamento de CPU).

A armadilha do excesso de fixação

Fixe de forma muito agressiva e um processo não poderá usar outros núcleos ociosos, o que pode torná-lo mais lento. Fixe apenas após medir e confirmar o benefício. Não fixe cegamente.

Erros comuns e soluções

Conclusão: A maioria envolve um número de núcleo inexistente, permissões insuficientes ou ordem errada de argumentos.

sched_setaffinity: Invalid argument

Você especificou um número de núcleo que não existe. Verifique o intervalo com nproc.

# Apenas 8 nucleos (0-7) existem mas nucleo 8 foi solicitado -> erro
$ taskset -c 8 ./program

Operation not permitted

Você está tentando alterar o processo de outro usuário. Execute como o dono do processo ou use sudo.

$ sudo taskset -cp 0,1 1234

command not found: taskset

util-linux está ausente (por exemplo, um container mínimo). No Debian/Ubuntu, instale:

$ sudo apt install util-linux

Próximas Leituras