Sábado, Fevereiro 12, 2011

Arquitetura de Software: Performance

Como garantir que seu software tenha Performance?

As pessoas da área de exatas normalmente se esquecem, mas a comunicação verbal não é uma coisa lá muito certinha, grande parte das palavras não possui definição exata e depende muito do contexto. Então, antes de começar, vamos evitar a confusão e a baderna e vamos definir direitinho o que é "Performance" no contexto desse artigo.

Quando digo "Perfomance" estou falando do tempo que leva entre o usuário disparar uma operação e o sistema retornar o resultado desejado. Quanto menor o tempo, mais "performance" [SAIP].

Outro conceito que quero deixar claro é o de "Tempo de Resposta", que defino aqui como o tempo que leva entre o usuário disparar uma operação e o sistema responder algo de volta. Quanto menor o tempo, menos "tempo de resposta". [WKRT]

Repare na importante diferença: "retornar o resultado" vs "responder algo".

Não é a mesma coisa? Não, não é. Eu posso ter uma operação extremamente lenta (baixa performance), mas se ela for respondendo de tempos em tempos "estou trabalhando" (responder algo), o usuário não vai ter a impressão que meu sistema está travado. O exemplo mais típico é o de cópia de arquivos, sem aquela barra de progresso, qualquer usuário acharia que a máquina travou.

Por que essa diferença é tão importante? Porque, dependendo do sistema que você está envolvido, "performance" não é o que você quer, e sim "tempo de resposta". Um dos casos mais típicos é o da aplicação web que demora para responder, o usuário pensa que travou e clica no botão "refresh", disparando a operação uma segunda vez. Encontre um usuário com o dedo nervoso e você terá dezenas de operações iguais concorrendo no servidor e um usuário cada vez mais frustrado do outro lado.

Então, antes de fazer a pergunta "como garantir que meu software tenha Performance", você deve responder "preciso de Performance ou de Tempo de Resposta?"

Um boa "thumb rule" é que sistemas para o público em geral devem perseguir primeiro Tempo de Resposta, já sistemas que são suporte a outros sistemas (APIs, Frameworks e Linguagens de Programação, por exemplo), devem perseguir Performance antes de qualquer coisa.

Esclarecido o ponto, vamos ver o que podemos fazer para garantir Tempo de Resposta e o que podemos fazer para garantir Performance.

Ambas são qualidades de software que podem ser garantidas com "táticas arquiteturais" [SAIP]. Uma "tática" é uma decisão de design, ou seja, uma diretiva de como o software vai ser feito.


Táticas para Tempo de Resposta

O importante do tempo de resposta passar para o usuário percepção de que o sistema não travou e continua trabalhando.

Segundo Jakob Nielsen [JNRT], o comportamento do usuário varia com o tempo de resposta:

  • até 0,1 segundos;
  • entre 0,1 e 1 segundo;
  • entre 1 e 10 segundos;
  • acima de 10 segundos.


Um tempo de resposta de até 0,1 segundo é o que o usuário espera de coisas em que ele está no controle, tais como bater uma tecla e ver a letra aparecer na tela.

De 0,1 até 1,0 segundo é o tempo que o usuário espera que o sistema esteja "fazendo algo". Operações como salvar ou apagar devem ter mais de 0,1 segundo, caso contrário o usuário vai pensar que nada aconteceu e tentar novamente. Em algumas operações AJAX, por exemplo, a resposta pode ser imediata (salvar em background ao clicar, por exemplo), então, uma tática interessante é adicionar um pequeno delay nessas operações se elas não ultrapassarem 0,1 segundo.

De 1 até 10 segundo, o usuário começa a perder o foco que você precisa indicar que está trabalhando. Grande parte das páginas web possui carregamento entre 1 e 3 segundos [MTBC], em sistemas web, esse tempo tende a ser maior devido a operações mais complexas. Ou seja, se você está trabalhando em sistemas web, conte com o "loading" do browser para ajudar, mas se você está usando AJAX, a tática recomendada é sempre colocar alguma dica visual ("loading" animado, por exemplo) de que o sistema está trabalhando, pois é quase certo que várias requisições levarão mais de 1 segundo para responder.

Acima de 10 segundos, o usuário pára de esperar a operação finalizar e começa a fazer outra coisa. A tática recomendada nesse caso é  apresentar uma barra de progresso ou ao menos um "loading" animado. Uma tática interessante apresentada por Matt Kelly, é colocar algo divertido, como uma pequena animação não convencional ou algo que o usuário possa interagir enquanto espera.

Se a operação puder levar minutos para completar, a tática recomendada é dispará-la em background e avisar ao usuário para buscar o resultado mais tarde. Em tempos de internet, isso pode significar algo do tipo "mandaremos um e-mail (sms/twitter/IM) com o link para os resultados quando o processo estiver concluído". Essa é tática usada pela Pragmatic Programmer Bookshelf, por exemplo, quando se pede para baixar um e-book e ele ainda não foi gerado.

A apresentação de  Matt Kelly da ZURB na jQuery Conference de Boston possui outras táticas muito interessantes de como lidar com esses tempos de resposta em sistemas web [MTBC]. Só tenha certeza de pegar os slides para assistir junto com o vídeo.

Táticas para Performance

 "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" - Donald Knuth

Um bom tempo de resposta pode não bastar para as operações mais frequentes no sistema. Como elas são muito utilizadas, o usuário pode ficar irritado se ficar esperando sempre até que a operação seja concluída. Em casos como esse, não há outra alternativa senão tentar melhorar a performance.

Performance ruim é frequentemente causada não pelo sistema como um todo, mas sim por pequenos trechos e operações. Esses são os famigerados "gargalos". O grande trabalho para a arquitetura de software não é evitar esses gargalos, mas sim providenciar maneiras de identificá-los rapidamente.

As táticas, nesse caso, serão focadas em sistemas web para o público em geral, e não APIs ou Frameworks.

Em primeiro lugar, uma boa parte dos sistemas web costumam seguir o modelo arquitetural de repositório [SAIN]. Ou seja, um banco de dados (o repositório) é o centro do sistema e a maior interação é entre banco e a página web (a parte) ao invés das páginas interagirem fortemente entre si.

Esse é um excelente modelo, pois é bastante desacoplado. É possível trocar a partes com relativa facilidade, desde que não se  modifique muito o repositório. Ele possui um problema com o rastreio das informações, pois é difícil descobrir qual parte alterou um dado que outra parte precisa, ou seja, existe uma dependência indireta via dados.

Nesse tipo de sistema, os maiores gargalos de performance inevitavelmente se concentram ao se movimentar grandes massas de dados no repositório, seja criando, recuperando ou alterando. Em miúdos: o gargalo de performance está normalmente em operações pesadas de banco de dados.

Uma tática NÃO recomendada é tentar aliviar o banco de dados passando o processamento para a aplicação. Isso normalmente diminui a performance, pois toda a operação que seria feita diretamente  agora precisa ser passada pela rede até a aplicação, carregada em memória (novamente, pois se ela foi recuperada do banco, teve que ser carregada na memória do banco também), processada e novamente serializada e enviada de volta para o banco de dados.

Outra tática NÃO recomendada é tentar otimizar o sistema enquanto não se sabe onde no sistema a performance é pior [DKSP]. Essa "tática" é  conhecida como "otimização prematura" e aumenta a complexidade do sistema sem garantias de resultado.

A melhor das abordagens para problemas de performance é monitorar a execução do sistema para identificar quais operações são mais lentas e então remover o gargalo.

A primeira tática recomendada é monitorar o tempo de todas operações de banco de dados da aplicação, isso pode ser feito facilmente, bastando logar seu tempo de execução. Para isso, é preciso que todas as operações de bancos de dados (consultas, inserções, alterações), sejam executadas por um único mecanismo, dessa maneira, registrar o tempo de execução não custa mais quem meia dúzia de linhas de código.

Além de monitorar o tempo de execução da operações de banco de dados, é interessante monitor o tempo de execução das páginas (tempo entre a "request" recebida e "response" enviada). Assim, mesmo quando o gargalo não está no banco, ainda é possível identificá-lo. Ao se monitorar os ambos, é possível saber não somente em que parte do sistema está o gargalo, mas se ele é causado pela aplicação ou pelo banco de dados.

Uma prática controversa, mas que me deu excelentes resultados, foi logar a pilha de execução (stack trace) junto com o tempo de execução do banco de dados. Dessa forma, consegui identificar rapidamente em que parte do sistema o SQL estava sendo gerado, por vezes, o sistema gerava partes do SQL em lugares diferentes. Recomendo essa prática para os primeiros meses de vida do sistema e principalmente se seu sistema possuir muitas consultas dinâmicas.

Outra tática é usar "timeouts", para garantir que as operações de banco de dados não ultrapassem um limite tolerável [MNRI][SAIP]. Apesar dessa ser uma tática recomendada para "Estabilidade", ela é muito útil para impedir que uma operação pesada deixe o sistema lento para todos os outros usuários.

Ambas as táticas devem ser usadas não somente com o banco de dados, mas também com qualquer dependência que o software tenha com sistemas externos, tais como web APIs.

Uma tática extra ao se lidar com sistemas externos é o uso do padrão "Circuit Break"[MNRI][TRCB]. Apesar de não estar diretamente relacionado com performance, ele garante que o software não sofra com instabilidade de sistemas externos.

Uma vez identificado o gargalo, basta aplicar uma série de técnicas para tentar removê-lo. Coisas como "memoization", "buffers", "índices e planos de execução forçados" e principalmente melhoria de algoritmos. Infelizmente essas técnicas estão além do escopo desse (já longo) artigo. Por outro lado, recomendo muito que a equipe tenha pessoas bem versadas nessas técnicas para que os gargalos sejam removidos rapidamente assim que identificados.

Espero que tanto texto tenha sido de alguma utilidade!

[SAIP] - Software Architecture In Practice - http://www.amazon.com/Software-Architecture-Practice-2nd-Bass/dp/0321154959
[JNRT] - http://www.useit.com/papers/responsetime.html
[MTBC] - http://events.jquery.org/2010/boston/video/video.php?talk=matt-kelly
[SAIN] - http://www.cs.cmu.edu/afs/cs/project/vit/ftp/pdf/intro_softarch.pdf
[DKSP] - http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf

Sábado, Outubro 30, 2010

Compilando Vim 7.3 para Ubuntu

Direto ao ponto:
  1. Instale xorg-dev e libgtk2.0-dev. Sem isso, não rola o gVim.
  2. ./configure --enable-gui=gtk2 --enable-multibyte --with-features=huge --enable-rubyinterp
  3. make
  4. sudo make install
Os executáveis vão estar em /usr/local/bin

Se quiser suporte ao Python, ao invés de usar --enable-rubyinterp, use --enable-pythoninterp. De qualquer maneira, você vai precisar dos headers de ambas as linguagens.

Como instalei o Ruby via RVM, ele encontrou o source do Ruby facilmente. Mas um aviso: o interpretador do ruby dentro do Vim vai ser sempre aquele em que o RVM estava quando você compilou, mudar a versão do Ruby via RVM não tem efeito nenhum no interpretador usado pelo Vim.

O tiuzão metaleiro TaQ apontou esse instalador que ele fez: https://github.com/taq/viminstall que é muito bom. Mesmo que você não queira usá-lo e quiser instalar tudo na mão, o script é uma ótima referência!

---
Editado:
1 - A opção para interpretador do Ruby estava errada: --with-rubyinterp deve ser --enable-rubyinterp.
2 - Adicionada referência ao instalador do TaQ.

Sexta-feira, Outubro 01, 2010

Qual o real papel de um Arquiteto de Software

A questão apareceu em uma discussão da Tectura.

Comecei a escreve uma resposta, mas a coisa ficou tão longa que resolvi postar aqui =\

Versão para apressados:

0 - IMHO, acho que estamos respondendo na discussão como o arquiteto de agir, mas não o o quê (papel) ele deve fazer...
1 - Simplificando absurdamente um arquiteto de software cuida da arquitetura de sofware (duh, momento Homer)
2 - Infelizmente, Arquitetura de software NÃO é um conceito aceito universalmente, nem é um conceito bem definido, muito pelo contrário. Cada um fala o que quer sobre o assunto e ninguém está errado :)
3 - Um dos melhores trabalhos sobre o tema é de Bass et al.
4 - Segundo ele, uma das principais funções do arquiteto é cuidar das "ilities" do software e documentá-lo.
5 - Pessoalmente, acho que documentação de software é só uma tática para atingir "modificabilidade".
6 - Conclusão: se o raciocínio desses caras estiver correto e se a minha opinião estiver correta, o papel do arquiteto é garantir as "ilities" de software, e para isso, precisamos nos meter em tudo qto é buraco do desenvolvimento de software, da negociação ao código.
7 - Mas todos nós podemos estar errados ;)

Versão para pacientes:

Acho q estamos confundindo a resposta, ou talvez eu não tenha entendido bem a pergunta. IMHO, boa parte das nossas respostas estão indo no caminho "como um arquiteto de software deve se comportar" e não "o que um arquiteto de software deve fazer".

Digo, se ele é um ditador, um comunicador, um líder nato, se é up front, "ágil" ou whatever, isso é COMO ele faz, e não O QUÊ ele faz. (tá correto isso?).

Tentando colocar a questão de outra maneira: "O que (papel) um Arquiteto de Software faz?"

Resposta óbvia: "Cuida da arquitetura" (duh)

E aí caímos na questão do Guilherme Silveira: "Uma questão anterior ao papel do arquiteto é o que é arquitetura"

Consultando a wikipedia (http://en.wikipedia.org/wiki/Software_architecture e http://en.wikipedia.org/wiki/Software_architect) e mais algumas fontes (http://goo.gl/ucNC, http://www.sei.cmu.edu/architecture/start/community.cfm, por exemplo, mas vc pode procurar por mais) dá pra perceber rapidamente que não existe um conceito uniformizado sobre o que é a bagaça. Ou seja, cada um de nós tem uma concepção diferente do que é arquitetura, e nenhum de nós está errado, simplesmente pq não existe resposta certa aceita por todos.

Um trabalho sobre o assunto que é sempre citado é o de Bass et al, mesmo assim, se vc lê-lo atentamente, vai perceber alguns conceitos que vêm "from thin air" (ou seja, sem referência a outros trabalhos), embora sejam caras experientes com vários projetos de sucesso na bagagem.

Segundo esse trabalho, uma das preocupações do arquiteto é garantir as "ilities" de um software (http://en.wikipedia.org/wiki/Software_quality) e documentar a coisa toda.

Opinião pessoal, eu concordo com a parte de garantir as "ilities", mas acho que a documentação é só uma outra tática para se atingir "modificability" ou "manutenability".

E se isso está certo, desde "Time to Market" até "Performance" e "Usabilidade" são responsabilidade do arquiteto. Para poder garantir isso, então, o arquiteto deveria se meter desde a negociação do software até programação e interações com o usuário.

Por outro lado, podemos estar todos redondamente errados. Por quê? Bom, "arquitetura" de software e "arquiteto" de software são papéis que foram criados por similaridade metafórica com construção civil. Se você já trabalhou desenvolvendo software, sabe que essa metáfora é muito frágil. Ou seja, podemos estar tentando criar um papel que não faz sentido existir. Por outro lado, existe algo realmente de alto nível que precisa ser cuidado, e que está muito mais alto nível do que estamos acostumados a pensar.


Segunda-feira, Setembro 20, 2010

Duvidando do óbvio: Gravidade?

Não sou nenhuma sumidade no assunto, aliás, sou uma porta de leigo nessa bagaça... mas... e se ao invés de vivermos um mundo redondo, estivermos na verdade em um mundo chato, mas com a gravidade torcendo o espaço sobre si mesmo?

Sábado, Setembro 18, 2010

Ruby 1.9.2 - diferenças com expressões regulares

O Ruby 1.9.2 veio com algumas poucas incompatibilidades com as versões anteriores. Duas delas me parecem um pouco "inconvenientes", a primeira comentei no post anterior. A segunda tem a ver com mudanças em expressões regulares.

Do ruby-lang.org (http://svn.ruby-lang.org/repos/ruby/tags/v1_9_2_0/NEWS)

* \d, \s, and \w are now ASCII only; use POSIX bracket classes and \p{} for Unicode semantics

O que significa que no Ruby 1.8.7, isso aqui funciona:

> irb -Ku
> "você" =~ /\w$/
=> 3

Mas não no Ruby 1.9.2:

> irb
> "você" =~ /\w$/
=> nil

Para que a coisa funcione corretamento no 1.9.2 é preciso usar "\p{Alpha}":

> irb
> "você" =~ /\p{Alpha}$/
=> 3

Perfeito, mas se você tenta a mesma solução no Ruby 1.8.7:

> irb -Ku
> "você" =~ /\p{Alpha}$/
(irb):1: warning: regexp has invalid interval
(irb):1: warning: regexp has `}' without escape
=> nil

Okay... nada que seja um absurdo, mas para quem trabalha com a língua portuguesa isso fica um pouco chato.

Terça-feira, Setembro 14, 2010

Ruby 1.9.2 primeiras experiências

A versão final do Ruby 1.9.2 foi liberada mês passado (agosto/2010), e só agora comecei a meter a mão na massa nele pra valer.

Logo de cara, já tive alguns problemas menores, mas importantes:

O primeiro foi no "require", a partir dessa versão o diretório atual não está mais incluído no $LOAD_PATH. O que significa que essa construção passa a ter problemas:

require File.dirname(__FILE__) + '/algum_subdiretorio/algum_ruby'
Para resolver isso, duas soluções:

A primeira é expandir o caminho:
require File.expand_path(File.dirname(__FILE__)) + '/algum_subdiretorio/algum_ruby'
A segunda, mais sensata, é adicionar especificamente o diretório "raiz" da aplicação ao $LOAD_PATH, como sugerido aqui)
$LOAD_PATH << File.expand_path(File.dirname(__FILE__)) + '/../lib'
require 'algum_subdiretorio/algum_ruby'
require 'outro_subdir_dentro_de_lib/outro_arquivo_ruby'
Com algum esforço, dá até pra criar um script pra arrumar isso automaticamente ;-)

Segunda-feira, Junho 21, 2010

Eu tenho todo o conhecimento do mundo...

Ontem estava no mercado com minha esposa e ela disse: "Queria fazer um molho de maracujá, como será que faz?"

Dei de ombros e continuamos pegando os refrigerantes, de repente parei e disse: "Peraí, eu SEI como fazer esse molho!".

Saquei o celular, abri o browser, Google, "molho de maracujá" e... bingo! Primeiro resultado já tinha a receita com todos os ingredientes. Compramos tudo e almocei um pelo pernil regado a molho agridoce.


Eu tenho todo o conhecimento do mundo...

...eu, e qualquer pessoa com acesso a internet. :)


Desde que comprei esse celular, e acesso a internet com ele, me pareceu que consigo fazer ou saber qualquer coisa que eu precise. Nós de gravata? Fácil! Receitas? Imediatamente! O que significa "nó cego"? Tá na mão! Algoritmos? Pra qualquer coisa que vc deseje...

Assinei o Safari (da O'Reilly), coloquei o Stanza e comecei a baixar livros gratuitos e comprar outros da Pragmatic Bookshelf (fazem meses que não leio um livro "físico"). E posso consultar toda minha biblioteca de conhecimento a qualquer momento.

Isso começou a me dar a impressão que consigo saber qualquer coisa que eu queira, de qualquer assunto, a qualquer momento.

Não bastasse isso, esse nossos pequenos e maravilhosos "gadgets" nos dão "super-poderes" (hmm... essa palavra ainda tem hífen?)

Memória fotográfica? Bom... o celular tem uma câmera e um gravador de som. Também tem uma agenda "buzina" meus compromissos.

Senso de direção? Viva o Google Maps e o TomTom.

Telepatia? Experimente SMS (boooommm... dá pra simplesmente fazer uma ligação :-p)

Pare um momento e pense como uma pessoa de 1980... "tudo isso é ficção científica!" =\

Acho que estou ficando velho...