< é mais rápido que <= em C++? Desvendando mitos com exemplos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: O mito que vive há décadas

\n\n

Se você já deu uma olhada no Stack Overflow na seção de C++, provavelmente já viu a pergunta: «Is < faster than <=?». Esta pergunta acumulou milhares de visualizações e dezenas de páginas de discussões. E não é surpresa — o tópico de otimização de código agita as mentes de programadores de todos os níveis.

\n\n

A lógica parece de concreto armado: o operador < compara dois números e retorna o resultado, enquanto <= faz o mesmo, mas verifica adicionalmente a igualdade. Então, < deve ser mais rápido, certo? Isso é especialmente importante para loops, onde a operação de comparação é executada milhões de vezes.

\n\n

Hoje vamos desvendar esse mito de uma vez por todas. Você descobrirá:

\n
    \n
  • Como os operadores realmente funcionam no nível do processador.
  • \n
  • Por que o compilador é seu melhor amigo (ou inimigo, se você não confiar nele).
  • \n
  • Quando a micro-otimização faz sentido e quando é perda de tempo.
  • \n
\n\n

Como os operadores < e <= funcionam no nível do assembly?

\n\n

Para entender se há diferença, precisamos olhar "sob o capô" — no que o compilador transforma seu código C++. Vamos considerar duas funções simples:

\n\n
bool less_than(int a, int b) {\n    return a < b;\n}\n\nbool less_or_equal(int a, int b) {\n    return a <= b;\n}
\n\n

Vamos compilá-las com g++ (sem otimizações, flag -O0). Aqui está o que vemos na listagem assembly (x86-64):

\n\n
less_than(int, int):\n    cmp    edi, esi      ; comparar a e b\n    setl   al            ; definir al=1 se a < b (menor que)\n    ret\n\nless_or_equal(int, int):\n    cmp    edi, esi      ; comparar a e b\n    setle  al            ; definir al=1 se a <= b (menor ou igual)\n    ret
\n\n

Viu? A única diferença são as instruções setl (set if less) e setle (set if less or equal). Ambas as instruções são executadas em um ciclo do processador. Em CPUs modernas (Intel Core i7, AMD Ryzen), elas têm latência idêntica — 1 ciclo.

\n\n

Conclusão nº 1: No nível das instruções de máquina, não há diferença de desempenho entre < e <=. Ponto final.

\n\n

E quanto aos loops? Deve haver diferença aqui!

\n\n

Na maioria das vezes, a discussão gira em torno de loops. Compare duas abordagens:

\n\n
// Opção 1: com <\nfor (int i = 0; i < n; ++i) {\n    // faz algo\n}\n\n// Opção 2: com <=\nfor (int i = 0; i <= n - 1; ++i) {\n    // faz a mesma coisa\n}
\n\n

A intuição sugere: a segunda opção faz um cálculo extra n - 1. Mas vamos ver o que acontece após a otimização do compilador.

\n\n

Vamos executar um benchmark com g++ -O2 para n = 1 000 000 000:

\n\n
#include <iostream>\n#include <chrono>\n\nint main() {\n    const long long n = 1'000'000'000;\n    volatile long long sink = 0; // para o compilador não eliminar o loop\n\n    auto start = std::chrono::high_resolution_clock::now();\n    \n    for (long long i = 0; i < n; ++i) {\n        sink += i;\n    }\n    \n    auto end = std::chrono::high_resolution_clock::now();\n    std::cout << "Tempo (<): " \n              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n              << " ms\\n";\n\n    start = std::chrono::high_resolution_clock::now();\n    \n    for (long long i = 0; i <= n - 1; ++i) {\n        sink += i;\n    }\n    \n    end = std::chrono::high_resolution_clock::now();\n    std::cout << "Tempo (<=): " \n              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n              << " ms\\n";\n\n    return 0;\n}
\n\n

Resultados na minha máquina (i7-107

Blogs

Book Recommendations