stamatios
← Voltar ao feed
Desvio ferroviário libera trilho principal para o trem expresso, metáfora do macro unlikely otimizando caminhos raros do código
Dev & Engenharia

Macro unlikely mostra otimização por caminho improvável de código

resumo de ~3 min

O que faz a macro unlikely

O padrão unlikely aparece com frequência no código do Linux Kernel para indicar ao compilador que determinada condição raramente será verdadeira. Sua definição é #define unlikely(e) __builtin_expect(!!(e), 0): o operando e é enviado a __builtin_expect junto do valor 0, fornecendo uma informação prévia sobre o ramo que o programa provavelmente seguirá. A macro oposta, likely, faz a indicação inversa.

Essa informação pode influenciar a previsão de desvios do processador e a forma como o compilador organiza o código. Quando a indicação corresponde ao comportamento real, a execução pode ser mais eficiente; quando é usada de modo inadequado, pode produzir o efeito contrário.

O experimento com os dois ramos

Para observar o efeito, o texto compara duas funções quase idênticas, unlikely_simple e likely_simple. Em ambas, um laço soma valores e testa uma condição. O primeiro ramo ocorre uma fração do tempo, enquanto o else é o caminho comum. O divisor usado no teste é alterado para produzir diferentes probabilidades de entrada no primeiro ramo.

Os tempos médios de execução, em microssegundos, foram:

Função Primeiro ramo Tempo médio
unlikely_simple 50% 48.899
unlikely_simple 10% 51.736
unlikely_simple 1% 58.496
unlikely_simple 0,1% 53.637
likely_simple 50% 48.643
likely_simple 10% 75.979
likely_simple 1% 79.594
likely_simple 0,1% 72.342

Com probabilidades equilibradas, a diferença é pequena. À medida que o primeiro ramo se torna menos frequente, porém, a versão que usa likely para favorecê-lo fica mais lenta, porque a dica do compilador contradiz o comportamento observado durante a execução. A versão com unlikely apresenta tempos menores nesses cenários do experimento, embora os resultados variem conforme a proporção escolhida.

O que aparece no código de máquina

A análise do assembly gerado com gcc -O2 mostra que a principal diferença está na ordem dos ramos. Na função com unlikely, a instrução correspondente ao caminho comum - total += i + 1 - vem logo depois da verificação da condição; um desvio leva ao trecho raro, que faz total += 2. Na função com likely, essa ordem é invertida: o caminho indicado como provável aparece primeiro e o caminho comum fica depois.

A ordem importa porque o processador normalmente organiza e antecipa a execução de várias instruções seguindo a disposição delas na memória. Se o fluxo real acompanha essa sequência, o trabalho antecipado tende a ser aproveitado. Se o programa segue repetidamente o ramo oposto ao priorizado, parte desse trabalho pode ser desperdiçada, prejudicando o desempenho.

O texto ressalta que likely e unlikely envolvem conceitos de baixo nível que talvez não sejam relevantes para a maioria dos programas. Ainda assim, considera útil entender esse mecanismo ao estudar código aberto e otimização: em situações específicas, uma escolha correta de caminho pode produzir um ganho muito grande, embora a macro também possa causar perda quando a previsão estiver errada.