Domine o Comando `time` no Linux: Medição de Performance e Otimização de Código

Gostaria de saber por quanto tempo um processo está em execução e obter informações detalhadas sobre o uso de recursos? O comando ‘time’ no Linux é a ferramenta ideal para isso. Ele fornece estatísticas de tempo que oferecem uma visão clara de como seus programas estão utilizando os recursos do sistema.

A Diversidade de ‘time’ nos Ambientes Unix

O ecossistema Linux, juntamente com outros sistemas operacionais baseados em Unix, apresenta uma variedade de shells de comando. O shell bash é o mais comum nas distribuições Linux modernas, mas existem alternativas como o zsh e o ksh. É importante notar que cada um desses shells possui seu próprio comando ‘time’, que pode ser uma função interna ou uma palavra reservada.

Ao digitar ‘time’ em um terminal, o shell pode executar sua versão interna do comando, em vez de utilizar o binário GNU ‘time’ fornecido com a distribuição Linux. No entanto, para uma análise mais completa e flexível, a versão GNU é preferível por oferecer mais opções.

Determinando Qual Versão do ‘time’ Está Sendo Utilizada

O comando ‘type’ permite verificar qual versão do ‘time’ será executada. Ele informa se o shell tratará a instrução internamente ou se a encaminhará para o binário GNU.

Para descobrir, digite ‘type time’ no terminal.

type time

Em shells como bash, o ‘time’ é uma palavra reservada, indicando que o shell usará sua implementação interna por padrão.

type time

O mesmo ocorre no zsh, onde ‘time’ é uma palavra reservada, e as rotinas internas do shell serão usadas por padrão.

type time

No shell Korn, ‘time’ também é uma palavra-chave, o que significa que uma rotina interna será usada no lugar do comando GNU ‘time’.

Executando o Comando GNU ‘time’

Se o seu shell possui uma rotina interna para o comando ‘time’, será necessário especificar que deseja usar o binário GNU. Isso pode ser feito de três maneiras:

  • Utilizando o caminho completo para o binário (ex: /usr/bin/time). Para descobrir o caminho, utilize o comando ‘which time’.
  • Usando o comando ‘command time’.
  • Precedendo o comando ‘time’ com uma barra invertida (ex: \time).

O comando ‘which time’ revela o caminho para o binário.

Testamos o binário GNU utilizando ‘/usr/bin/time’. A resposta indica que não foram fornecidos parâmetros na linha de comando. Similarmente, o comando ‘command time’ também funciona, retornando as mesmas informações de uso. O comando ‘command’ instrui o shell a ignorar a interpretação do próximo comando e processá-lo externamente.

Preceder o nome do comando com uma barra invertida tem o mesmo efeito que usar ‘command’. A forma mais direta de garantir o uso do binário GNU ‘time’ é utilizar essa barra invertida.

time
time

‘time’ invoca a versão do shell, enquanto ‘\time’ utiliza o binário do time.

Exemplos Práticos com o Comando ‘time’

Vamos usar o comando ‘time’ para medir a performance de dois programas, ‘loop1’ e ‘loop2’. Eles foram criados a partir de ‘loop1.c’ e ‘loop2.c’ e são projetados para demonstrar diferenças na eficiência de codificação.

O código de ‘loop1.c’ calcula o comprimento de uma string uma única vez, fora dos loops aninhados:

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

int main (int argc, char* argv[])
{
 int i, j, len, count=0;
 char szString[]="how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek";

 // get length of string once, outside of loops
 len = strlen( szString );  

 for (j=0; j

Já o código de 'loop2.c' recalcula o comprimento da string em cada iteração do loop externo, demonstrando ineficiência:

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

int main (int argc, char* argv[])
{
 int i, j, count=0;
 char szString[]="how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek-how-to-geek";

 for (j=0; j

Vamos analisar o desempenho do 'loop1' usando 'time'.

time ./loop1

Agora vamos repetir o processo para o 'loop2'.

time ./loop2

Os resultados são exibidos de forma padrão. Ao analisá-los, é importante entender que os processos alternam entre dois modos de execução: modo usuário e modo kernel.

Em resumo, um processo no modo usuário não pode acessar diretamente o hardware ou memória fora de sua alocação. Para isso, ele deve fazer solicitações ao kernel. Após aprovação, o processo entra no modo kernel para executar a solicitação, retornando ao modo usuário em seguida.

No caso do 'loop1', o tempo gasto no modo usuário foi de 0,09 segundos, praticamente nenhum no modo kernel, e um tempo total de 0,1 segundos. Ele obteve em média 89% do tempo de CPU durante a execução.

O 'loop2', por outro lado, levou três vezes mais tempo para ser executado, com um tempo total de 0,3 segundos e 0,29 segundos no modo usuário. A média de uso da CPU foi de 96%.

Personalizando a Saída do Comando 'time'

A saída do 'time' pode ser personalizada através de strings de formato. Essas strings podem incluir texto e especificadores de formato, encontrados na página de manual do comando. Cada especificador representa uma informação específica.

Ao exibir a string, os especificadores são substituídos pelos valores correspondentes. Por exemplo, o especificador para a porcentagem de CPU é %P. O sinal de porcentagem indica ao 'time' que se trata de um especificador e não de um caractere comum.

A opção '-f' (formato) é usada para indicar ao 'time' que a string seguinte é um formato.

Nossa string de formato irá exibir "Programa:", seguido pelo nome do programa e seus parâmetros (%C). \n força a saída para a próxima linha.

Existem muitos especificadores de formato, que são sensíveis a maiúsculas e minúsculas, portanto, é necessário ter atenção ao usá-los.

Em seguida, exibiremos "Tempo total:", seguido pelo valor do tempo total de execução (%E).

Usaremos \n novamente, seguido por "Modo(s) de usuário" e o tempo de CPU gasto nesse modo (%U).

Novamente, \n para "Modo(s) Kernel" e o tempo de CPU gasto no modo kernel (%S).

Finalmente, "nCPU:" para exibir a porcentagem média de tempo de CPU usada (%P).

A string de formato é colocada entre aspas. Poderíamos também incluir caracteres \t para adicionar tabulações na saída.

time -f "Program: %CnTotal time: %EnUser Mode (s) %UnKernel Mode (s) %SnCPU: %P" ./loop1

Redirecionando a Saída para um Arquivo

Para manter um histórico dos resultados, a saída do 'time' pode ser redirecionada para um arquivo. Utilize a opção '-o' (saída). A saída do seu programa será exibida no terminal normalmente, mas a saída do 'time' será direcionada ao arquivo.

Podemos salvar a saída no arquivo 'test_results.txt' da seguinte forma:

time -o test_results.txt -f "Program: %CnTotal time: %EnUser Mode (s) %UnKernel Mode (s) %SnCPU: %P" ./loop1
cat test_results.txt

A saída de 'loop1' continua sendo mostrada no terminal e os resultados do 'time' são gravados em 'test_results.txt'.

Se desejar adicionar novos resultados ao mesmo arquivo, use a opção '-a' (anexar):

time -o test_results.txt -a -f "Program: %CnTotal time: %EnUser Mode (s) %UnKernel Mode (s) %SnCPU: %P" ./loop2
cat test_results.txt

O uso do especificador %C para incluir o nome do programa na saída da string de formato torna-se evidente.

Considerações Finais Sobre o Comando 'time'

O comando 'time' é uma ferramenta valiosa para programadores e desenvolvedores que buscam otimizar o desempenho de seu código, mas também para quem deseja entender melhor o que acontece nos bastidores ao executar um programa.