Programa SIGNIFICATIVAMENTE mais lento quando usado no TTY

Programa SIGNIFICATIVAMENTE mais lento quando usado no TTY

Então eu tenho um programa escrito em C++.

Ele pode me dizer quanto tempo levou para fazer todos os cálculos e faz muitos cálculos pesados ​​e multithread.

Acabei de notar que, se eu executar o programa exatamente na mesma máquina, levará cerca de 20 a 21 segundos para fazer todos os cálculos, se iniciado no TTY, e apenas cerca de 0,2 segundos se eu iniciar no terminal GNOME.

O que está causando isso? É literalmente exatamente o mesmo arquivo na mesma máquina.

Responder1

Alguma teoria de fundo

Bem, tanto o que você trabalha depois do CTRL+ ALT+ F1quanto o Terminal GNOME são implementações diferentes do mesmo conceito:emulandoum chamado terminal de tela cheia.

O primeiro é chamado de terminal virtual (VT) no Linux, ou geralmente apenas “console”. Ele usa um modo de vídeo especial "somente texto" ainda fornecido pelas placas de vídeo de hardware nas plataformas compatíveis com x86 (isto é, aquelas da herança "IBM PC"). O último é um aplicativo GUI.

Ambos fornecem aos aplicativos executados com sua ajuda um conjunto de recursos que tal aplicativo espera de "um dispositivo terminal" (mais detalhes e dicas adicionais—aqui).

O problema em questão

OK, agora vamos passar para a lentidão percebida.

Tenho certeza de que o cerne do seu problema é que seu programa faz o chamado "bloqueio" de E/S. Ou seja, cada vez que você faz algo como

std::cout << "Hello, world" << endl;

no seu código, primeiro o código da biblioteca padrão C++ vinculada ao seu aplicativo entra em ação e trata da saída do material enviado para o local indicadofluxo.

Após determinado processamento (e geralmente algum buffer), esses dados precisam realmente sair do processo em execução do seu programa e obter saída para qualquer mídia para a qual seus programas enviam sua saída. No Linux (e em outros sistemas compatíveis com Unix), isso requer uma chamada ao kernel – por meio de um servidor dedicado.chamada de sistema(ouchamada de sistemapara abreviar) nomeado write().

Portanto, o stdlib C++ eventualmente faz esse write()syscall e espera que ele seja concluído - ou seja, espera que o kernel diga de volta "OK, o receptor dos dados disse que os adquiriu".

Como você pode deduzir, o receptor dos dados gerados pelo seu programa é o terminal (emulador) que executa o seu programa - seja um Linux VT ou uma instância do Terminal GNOME em seus testes. (O quadro completo é mais complicado porque o kernel não envia os dados diretamente para um emulador de terminal em execução, mas não vamos complicar a descrição.)

E assim a velocidade com que esse write()syscall é concluído depende muito da rapidez com que o receptor dos dados o processa! No seu caso, o Terminal GNOME é muito mais rápido.

Minha opinião sobre a diferença é que o driver VT renderiza obedientemente todos os dados que estão sendo enviados para ele, rola-os, etc., enquanto o Terminal GNOME otimiza rajadas de dados recebidos, renderizando apenas a parte final deles (o que se ajusta ao tamanho da tela do terminal) e coloca o descanse no chamado "buffer de rolagem" que a maioria dos emuladores de terminal GUI possui.

As lições a fazer

O ponto crucial a ser levado em conta é que, assim que seu programa executa qualquer E/S junto com os cálculos, e você mede a velocidade de cálculo do programa usando o cronômetro de "relógio de parede", normalmente você pode medir a velocidade dessa E/S. Ah, não a velocidade dos cálculos.

Observe que a E/S é complicada: seu processo pode serantecipado(parado com seus recursos entregues a outro processo) pelo sistema operacional sempre que estiver prestes a aguardar que algum recurso de E/S fique disponível para gravação - como uma unidade de disco rígido.

Portanto, a maneira segura de medir o desempenho "bruto" dos cálculos é ter algum recurso em seu programa para desabilitar todas as E/S. Se isso não for possível ou for muito feio de implementar, pelo menos tente direcionar toda a saída para um chamado "dispositivo nulo", /dev/nullexecutando seu programa como

$ ./program >/dev/null

O dispositivo nulo simplesmente descarta todos os dados passados ​​para ele. Então, sim, cada rodada de E/S executada pelo stdlib C++ atingirá o kernel, mas pelo menos você terá velocidade de gravação quase constante (e quase instantânea).

Se você precisar das duas medidaseos dados gerados, considere criar um chamado disco RAM e redirecionar a saída para um arquivo localizado lá.

Mais uma medição: observe que mesmo em um sistema aparentemente ocioso executando um sistema operacional comum (como o Ubuntu ou qualquer outro), a CPU nunca dorme – sempre há algumas tarefas fazendo coisas em segundo plano. Isso significa que medir o desempenho da computação mesmo sem qualquer E/S ou com uma espécie de E/S "desativada" (conforme explicado acima) ainda produzirá resultados diferentes em cada execução.

Para compensar isso, um bom benchmarking significa executar seu cálculo com os mesmos dados de entrada milhares de vezes e calcular a média dos resultados sobre o número de execuções.

informação relacionada