De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Rubens Queiroz de Almeida
Data de Publicação: 14 de agosto de 2010
Este artigo foi traduzido e adaptado a partir do original em inglês, de autoria de Swayam Prakasha, do artigo Optimizing Linux System Performance, publicado em 6 de julho de 2007, no site LinuxDevCenter.com
Otimização de desempenho em sistemas GNU/Linux nem sempre significa o que podemos pensar. Não é apenas diretamente uma questão de velocidade; às vezes pode envolver ajustar o sistema para fazer com que consiga caber dentro de um sistema com pouca memória. Sem dúvida, é difícil encontrar um programador que não queira fazer seus programas rodarem mais rápido, independentemente da plataforma. Programadores Linux não são exceção; alguns deles chegam às raias do fanatismo na forma como se dedicam à tarefa de otimizar seu código para obter melhor desempenho. A medida que o hardware se torna mais rápido, barato e abundante, alguns argumentam que otimização de performance não é tão algo tão crítico -- com especial destaque para pessoas envolvidas com desenvolvimento de software e que tentam seguir à risca os prazos estabelecidos. Não é bem assim, mesmo o hardware mais avançado dos dias de hoje, combinado com as últimos avanços na tecnologia de otimização de compiladores, não consegue nem chegar perto dos benefícios de melhoria de desempenho que podem ser alcançados através da correção de alguns pequenos programas, ou mesmo chegando ao ponto de adotar um projeto inteiramente diferente e muito mais rápido.
Muitas idéias podem ser empregadas para fazer programas obterem um desempenho melhor. Tendo estas idéias em mente ao escrever (e revisar) o código, podemos esperar que os programas sejam melhores e mais rápidos. Quando falamos sobre performance, precisamos levar em consideração diversas coisas diferentes. Uma é a quantidade absoluta de tempo necessária para que o software finalize uma dada tarefa. Considere um exemplo em que mesmo se um servidor web atende o pedido de um cliente perfeitamente, pode haver um atraso de alguns segundos antes que o servidor comece a enviar as páginas para o cliente. Em tal caso, o servidor web está falhando em obter um desempenho adequado em termos do tempo total exigido para completar a tarefa.
Outro fator a ser considerado é o tempo de CPU requerido por um programa. Tempo de CPU é uma medida do tempo gasto pelos processadores do computador para executar o código. Muitos programas tendem a gastar a maior parte de seu tempo esperando que algo aconteça -- chegada dos dados de entrada, saída dos dados para serem escritos no disco, etc. Enquanto espera, a CPU irá atender outros pedidos e, portanto, o programa não estará usando tempo de CPU. Entretanto, alguns programas podem ser primariamente dependentes de CPU e, para tais programas, uma economia no tempo de CPU necessário pode resultar em uma economia substancial do tempo absoluto.
É importante observar que se o seu programa usa muito tempo de CPU, ele pode
impactar o tempo de execução de todos os processos em seu sistema. O tempo
de CPU podem ser separado em tempo de sistema e tempo do usuário. O tempo do
sistema é o tempo de CPU usado pelo kernel para atender aos seus pedidos.
Isto pode ocorrer quando da invocação de funções tais como open()
e
fork()
. O tempo de usuário (i.e. a quantidade de tempo de CPU usada pelo
seu programa) pode ser usada para manipulação de strings e operações
aritméticas. Um terceiro fator a ser considerado para desempenho é o tempo
gasto realizando I/O. Se considerarmos que alguns programas, tais como
servidores de rede, gastam a maior parte do seu tempo gerenciando solicitações
de I/O. Outros programas gastam pouco tempo em tarefas relacionadas com
I/O. Então, a otimização de operações de I/O pode ser muito crítica em alguns
projetos e completamente irrelevantes em outros.
A otimização de performance consiste basicamente dos seguintes passos:
É importante observar que gargalos ocorrem em diversos pontos em um sistema. Determinar os gargalos é um procedimento passo a passo para se chegar às causas primárias. Otimização de performance é um processo relativamente complexo que requer a correlação de muitos tipos de informação com o código fonte, para localizar e analisar gargalos em problemas de performance.
Ao focar em otimização de performance, um administrador de sistemas precisa
de certas ferramentas para medir e monitorar as situações e para identificar
os gargalos. Em sistemas GNU/Linux, várias ferramentas estão disponíveis
para esta finalidade. top
é um pequeno programa e fornece informação
em tempo real em nível de sistema. O top
é interativo e oferece um
instantâneo do sistema no momento em que está sendo executado. mtop
é
semelhante ao top
, mas faz a monitoração de bancos de dados MySQL. Ele
exibe as consultas que são lentas ou travas (locks) que estão ativos
no momento, mostrando o código SQL que está sendo executado. Sysstat
,
procfs
, sysctl
e sysfs
são ferramentas valiosas e comandos
de configuração que podem ser usados para medir a performance de sistemas
GNU/Linux e ajustes de desempenho. Outra ferramenta, sar
, pode ser usada
para coletar uma grande variedade de informações sobre a atividade do sistema
(como utilização de CPU, uso de memória, rede, uso de buffers, etc.) e pode
ser usado para identificar gargalos potenciais.
Primeiramente, vamos entender os problemas de desempenho causados por laços (loop). Laços amplificam os efeitos de problemas de desempenho que, em outras condições, impactam pouco o sistema. Isto ocorre devido ao fato do código dentro do laço ser executado muitas vezes. Sempre se certifique de mover para fora do laço todo o código que não precise ser executado repetidamente.
Consideremos o seguinte trecho de código:
main() { int five; int cnt; int end; int temp; for (cnt=0; cnt < 2* 100000 * 7/ 15 + 6723; cnt += (5-2)/2 { temp = cnt / 16430; end = cnt; five = 5; } printf("printing values of five=%d and end = %d\n", five, end); }
Se observarmos o código cuidadosamente, podemos ver que muitas coisas podem
ser retiradas do laço. O valor de end
pode ser calculado apenas uma vez,
depois que sairmos do laço. Da mesma forma, a atribuição da variável five
é um código morto e faz muito mais sentido movê-lo para fora do laço.
Quando falamos de otimizar a performance, precisamos nos certificar que,
a menos que haja uma necessidade inescapável, não devemos nunca tentar usar
tipos de dados de ponto flutuante, como float
e double
. Isto se
dá devido ao fato de que estes tipos de dados levam mais tempo para serem
calculados do que seus equivalentes inteiros. Da mesma forma, se temos uma
função que é chamada frequemente, é melhor declará-la como inline
.
Outra maneira de melhorar a performance é aumentar o tamanho do bloco. Como sabemos muitas operações são feitas sobre blocos de dados. Ao aumentar o tamanho do bloco, poderemos transferir mais dados de cada vez. Isto reduzirá a frequencia com que realizamos operações que consomem mais tempo.
É claro que quando estamos interessados em otimizar o código, nós sempre queremos nos livrar de operações "caras" e substituí-las por outras mais "baratas". Chamadas de sistema são operações "caras". Vejamos algumas das chamadas de sistema mais "caras":
fork
: Um fork é muito útil. Não é lento, mas se os usamos com
frequencia, o impacto pode ser significativo. Considere um cenário em que um
servidor web realize um fork a cada novo pedido. Não é uma boa prática,
e select()
pode ser usado para multiplexação.
exec
: Este é usado imediatamente após um fork. Esta chamada pode ser
bastante "cara" pois o novo programa terá que realizar muitas inicializações
como carregar bibliotecas, etc.
system
: Esta chamada invoca uma shell para executar o comando
especificado e invocar uma shell pode ser bastante "caro". Portanto, usar
a chamada system
é definitivamente uma péssima idéia.
Se encontrarmos um trecho de código como system("ls /etc");
nós podemos
ver como é "caro". O programa tem que primeiramente executar um fork
e então executar a shell. A shell precisa realizar a inicialização, faz um
novo fork e então executa o comando ls
. Certamente não é o tipo
de código que se deva usar com frequencia.
O primeiro passo para ajustar o sistema e deixá-lo mais veloz e confiável
é caçar as versões mais recentes dos drivers de dispositivo mais recentes.
Outra dica útil é entender quais são os gargalos e como eles podem ser
tratados. Nós podemos identificar diversos gargalos executando utilitários
de monitoração do sistema, como por exemplo o comando top
.
Sempre vale a pena dar atenção ao acesso aos discos. Existem várias técnicas que podem produzir melhoras significativas na performance dos discos.
Primeiramente, leia sobre o comando hdparm
e você irá notar que ele
define várias diretivas e modos no subsistema IDE do driver disco. Existem
duas opções para as quais precisamos dar atenção - a opção -c
pode definir
32 bits para suporte de I/O e a opção -d
que habilita ou desabilita
o uso do DMA (Direct Memory Access - using_dma
) para o disco. Na maior
parte dos casos, esta diretiva é definida como 1, mas se o seu sistema não
estiver assim, então você irá sofrer com problemas de performance. Tente
alterar este valor colocando o comando
hdparm -d 1 /dev/hda
ao final do arquivo /etc/rc.d/rc.local
.
De forma similar, adicione a linha
hdparm -c 1 /dev/hda
ao final do arquivo /etc/rc.d/rc.local/
para definir o suporte de 32 bits a operações de I/O.
Nota da tradução: Em engenharia de software, profiling significa analisar o comportamento de um programa de forma dinâmica, usando informação coletada durante sua execução. O objetivo é determinar quais partes de um programa devem ser otimizadas. Não sei de uma tradução adequada para este termo e, por esta razão, usarei o termo no original em inglês nas próximas duas seções.
Depois de termos tomada as medidas necessárias para otimizar nosso código, o compilar também pode ser útil na otimização. Uma ferramenta que podemos usar para analisar a execução de nosso programa é o GNU profiler (gprof). Com esta ferramenta, nós podemos descobrir onde o programa está gastando a maior parte do seu tempo. Com esta informação nós podemos determinar quais partes do programa são mais lentas do que o esperado. Estas seções são definitivamente boas candidatas a serem reescritas para que o programa seja executado mais rapidamente. O profiler coleta dados durante a execução do programa. Profiling é uma outra forma de conhecer melhor o código fonte.
Relacionamos a seguir os requisitos para analisar um programa usando o comando gprof:
Para que você use o utilitário gprof, o pacote precisa ser instalado em seu sistema.
Para analisar um programa com gprof, nós precisamos compilá-lo com uma opção especial.
Considerando que nós tenhamos um programa chamado sample_2007.c
, o seguinte comando
pode ser usado para compilá-lo:
$ gcc -a -p -pg -o sample_2007 sample_2007.c
Observe que a opção -pg
habilita o suporte básico para profiling no gcc.
O programa irá executar um pouco mais lentamente quando profiling estiver
habilitado. Isto se dá devido ao fato de que ele, além de executar suas instruções,
também coleta dados. O suporte ao profiling no programa cria um arquivo
chamado gmon.out
no diretório corrente. Este arquivo é utilizado mais tarde
pelo gprof para analisar o código.
Podemos executar o seguinte comando para obter a saída (que redirecionamos para um arquivo):
$ gprof sample_2007 gmon.out > output.txt
gprof é útil não apenas para determinar quanto tempo é gasto em diversas rotinas, mas ele também nos diz quais rotinas invocam outras rotinas. Usando gprof, nós podemos identificar quais seções de nosso código estão causando os atrasos maiores. A análise do código fonte com gprof é considerada como uma maneira eficiente de determinar qual função está usando a maior parte do tempo total gasto na execução do programa.
Kprof é uma ferramenta gráfica que exibe as informações de profiling geradas pelo comando gprof. Kprof é bastante útil pois exibe a informação gerada em formato de lista ou árvore e torna a informação mais fácil de ser entendida.
Kprof tem os seguites recursos:
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you are declaring that you agree with these rules:
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comentários