Por que C++ lê linhas do stdin mais lentamente que Python? Análise e solução

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 paradoxo da velocidade que explodiu o Stack Overflow

\n\n

Imagine a situação: você está resolvendo um problema de competição ou processando um arquivo de log de gigabytes. O script em Python termina em 2 segundos. Você o reescreve em C++, esperando uma aceleração de dez vezes, mas… obtém 10 segundos! Uma dor familiar? Foi exatamente essa pergunta que acumulou milhares de visualizações no Stack Overflow e gerou a discussão lendária: «Why is reading lines from stdin much slower in C++ than Python?».

\n\n

Aparentemente, como o C++ «rápido» pode perder para o Python «lento» em uma operação tão trivial quanto ler linhas? A resposta não está na velocidade da linguagem em si, mas nas configurações padrão e na arquitetura do iostream. Neste artigo, faremos nossa própria investigação, escreveremos código de teste em C++ e Python, mediremos o tempo e, principalmente, aprenderemos a tornar a entrada/saída em C++ verdadeiramente rápida.

\n\n

Este artigo não é teoria seca, mas um guia prático para todos que programam em C++ e enfrentam problemas de desempenho de entrada/saída. Vamos nessa!

\n\n

O cerne do problema: Por que o iostream «engasga»?

\n\n

Quando você escreve std::cin >> data ou std::getline(std::cin, line), o C++ por padrão faz algo que muitos nem imaginam. Ele sincroniza seus fluxos de entrada/saída com os fluxos padrão do C (stdin, stdout). Para quê? Para compatibilidade: você pode usar printf e std::cout simultaneamente no mesmo programa, e a saída não se misturará.

\n\n

Essa sincronização tem um custo alto. Cada chamada a cin ou cout verifica o estado dos fluxos C, transformando uma operação rápida e bufferizada em uma operação síncrona lenta.

\n\n

Em Python, é diferente. A função input() e sys.stdin.readline() usam seu próprio buffer, bem otimizado em nível C. Nenhuma sincronização extra — apenas leitura de um bloco de dados do descritor de arquivo. É por isso que a leitura do stdin em Python pode ser mais rápida que em C++ «de fábrica».

\n\n

Experimento: Teste de velocidade

\n\n

Vamos verificar isso na prática. Escreveremos um programa simples que lê 1.000.000 de linhas do stdin e não faz nada com elas (apenas as lê). Primeiro em C++ «como está»:

\n\n
// slow_cin.cpp\n#include <iostream>\n#include <string>\n\nint main() {\n    std::ios::sync_with_stdio(false); // Por enquanto, comentado\n    std::cin.tie(nullptr);           // E isso também\n\n    std::string line;\n    while (std::getline(std::cin, line)) {\n        // Não faz nada\n    }\n    return 0;\n}
\n\n

E agora em Python:

\n\n
# fast_python.py\nimport sys\n\ndef main():\n    for line in sys.stdin:\n        pass\n\nif __name__ == "__main__":\n    main()
\n\n

Vamos executar o teste com seq 1000000 | ./slow_cin e seq 1000000 | python3 fast_python.py. O resultado será chocante: C++ sem otimizações pode ser de 5 a 10 vezes mais lento que Python! Mas não se apresse em enterrar o C++ — o problema é resolvido com duas linhas de código.

\n\n

Solução: Duas linhas mágicas

\n\n

Tudo o que é necessário para acelerar a entrada/saída em C++ é desabilitar a sincronização e o vínculo dos fluxos. Adicione no início da main():

\n\n
std::ios::sync_with_stdio(false);\nstd::cin.tie(nullptr);
\n\n

A primeira linha desabilita a sincronização entre iostream e stdio. A segunda desvincula cin de cout (por padrão, antes de cada operação de entrada, cout descarrega o buffer, o que também retarda o processo). Após isso, seu código C++ começará a funcionar no mesmo nível do Python, e frequentemente mais rápido.

\n\n

Verificando o resultado

\n\n

Agora, descomente essas linhas em nosso teste e execute novamente. Você verá que o desempenho do C++ dispara. Em alguns benchmarks, a diferença chega a ser 10 vezes maior.

Blogs

Book Recommendations