Introdução: Por que o computador "pensa" mais rápido que o programador?
\n\nImagine: você passou a noite escrevendo no papel a sequência de Collatz para o número 27. E então seu colega escreveu um loop em C++ que, em um segundo, processou um bilhão de números. Você está furioso? Espere, isso é só o começo.
\n\nEm 2025, no Stack Overflow, uma pergunta antiga, mas muito reveladora, explodiu com nova força: "Why does C++ code for testing the Collatz conjecture run faster than hand-written assembly?". À primeira vista, Assembly é um diálogo puro com o processador, sem intermediários. Como é possível que um C++ de alto nível supere o Assembly escrito por um humano?
\n\nVamos analisar esse paradoxo usando o exemplo da hipótese de Collatz (também conhecida como problema 3n+1). Você descobrirá por que os compiladores C++ modernos são verdadeiras máquinas do tempo e como, em 2025, escrever código que será mais rápido do que o "controle manual" dos registradores.
\n\nO que é a hipótese de Collatz e o que o C++ tem a ver com isso?
\n\nVamos relembrar a essência: pegue qualquer número natural n. Se for par — divida por 2. Se for ímpar — multiplique por 3 e some 1. A hipótese afirma que qualquer número, mais cedo ou mais tarde, chegará a 1. A tarefa do programador é verificar isso para a maior faixa de números possível.
A implementação clássica em C++ é assim:
\n\n#include <iostream>\n\nint collatz_steps(long long n) {\n int steps = 0;\n while (n != 1) {\n if (n % 2 == 0) {\n n /= 2;\n } else {\n n = 3 * n + 1;\n }\n ++steps;\n }\n return steps;\n}\n\nint main() {\n long long max_steps = 0;\n long long best_num = 0;\n for (long long i = 1; i <= 1000000; ++i) {\n int s = collatz_steps(i);\n if (s > max_steps) {\n max_steps = s;\n best_num = i;\n }\n }\n std::cout << "Número: " << best_num << " passos: " << max_steps;\n}\n\n\nParece simples. Mas foi esse código simples que gerou a lendária disputa com o Assembly.
\n\nPor que o Assembly perde? Principais erros de 2025
\n\nQuando um desenvolvedor escreve em Assembly, ele frequentemente pensa: "Eu sei como o processador funciona, vou escrever perfeitamente". Mas os processadores modernos não são apenas executores de instruções. Eles são pipelines superescalares complexos com previsão de desvios, execução especulativa e memória cache. O compilador C++ (GCC, Clang, MSVC) conhece tudo sobre sua arquitetura alvo.
\n\nErro nº1: Ignorar a otimização de desvios
\n\nNo Assembly manual, o programador frequentemente escreve desvios "diretos":
\n; Pseudo-Assembly\ncheck_even:\n test rax, 1\n jnz is_odd\n shr rax, 1\n jmp next\nis_odd:\n mov rbx, rax\n shl rax, 1\n add rax, rbx\n inc rax\nnext:\n ...\n\nO compilador C++ com a flag -O3 faz de forma mais inteligente. Ele usa CMOV (conditional move) e mágica de bits. Em vez de desvios, usa aritmética:
; GCC 14 gerará algo como:\n mov rcx, rax\n shr rcx, 1 ; divide por 2 (se par)\n and eax, 1 ; verifica o bit menos significativo\n lea rdx, [rax + rcx*2 + 1] ; cálculo esperto para ímpar\n test eax, eax\n cmove rax, rcx ; se par - pega a divisão\n cmovne rax, rdx ; se ímpar - pega 3n+1\n\n\nResultado: sem stalls no pipeline devido a desvios previstos incorretamente. Em 2025, isso é crítico, pois o preditor de desvios na CPU ficou mais inteligente, mas ainda não é ideal para números aleatórios.
\n\nErro nº2: Esquecer a vetorização (AVX-512)
\n\nOs compiladores C++ modernos (especialmente Clang 18+ e GCC 14+) vetorizam loops automaticamente. Se você escrever um loop de verificação simples, o compilador pode processar 8 números por ciclo, usan