
Macro unlikely mostra otimização por caminho improvável de código
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.