Archive for October, 2008

JRuby 1.1.4 vs Ruby 1.8 no Rails 2.2

Continuando a série de benchmarks começada ao comparar o Rails 2.1 com o Rails 2.2 em cima do Ruby 1.8, agora é a vez de comparar o JRuby e Ruby.
Se você não leu a explicação das diferenças do modelo de threading entre Ruby 1.8, Ruby 1.9 e JRuby, leia antes de prosseguir.
Estou rodando os testes no meu computador pessoal, que é um Pentium 4 3.0 HT, com 1Gb de memória rodando Ubuntu Linux. Já é um PC um tanto quanto ultrapassado, portanto o tempo de resposta acaba sendo um pouco alto, além de que ao mesmo tempo em que rodam os testes rodam também outros programas, como o Firefox por exemplo.
Vamos ao que interessa:

Ruby 1.8

$ ruby script/server -p 3001 -e production
$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   3.903964 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      573554 bytes
HTML transferred:       547350 bytes
Requests per second:    12.81 [#/sec] (mean)
Time per request:       390.396 [ms] (mean)
Time per request:       78.079 [ms] (mean, across all concurrent requests)
Transfer rate:          143.44 [Kbytes/sec] received

JRuby 1.1.4

$ jruby -J-Djruby.objectspace.enabled=false -J-Xmn128m -J-Xms512m -J-Xmx512m -J-server -J-Djruby.thread.pooling=true script/server -e production
$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   7.976448 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      573600 bytes
HTML transferred:       547350 bytes
Requests per second:    6.27 [#/sec] (mean)
Time per request:       797.645 [ms] (mean)
Time per request:       159.529 [ms] (mean, across all concurrent requests)
Transfer rate:          70.21 [Kbytes/sec] received

Mesmo utilizando todas as flags de performance tunning da wiki do JRuby, ele ainda se mostrou mais lento. Isso significa que o JRuby não vai tirar proveito do threadsafeting do Rails 2.2? Não!
Pelo que eu entendi pesquisando brevemente, ainda é preciso fazer alterações no código do JRuby pra que ele ofereça a melhor performance possível. Mesmo a última barreira para que o Rails possa ser executado em modo multi-threaded tenha sido removida, ainda é preciso implementar essa função no core do JRuby. Me corrijam se eu estiver errado.

Tempo de carga de uma única requisição no JRuby:

Processing IndexController#index (for 127.0.0.1 at 2008-10-30 18:44:58) [GET]
Completed in 186ms (View: 116, DB: 7) | 200 OK [http://localhost/]

…e no Ruby:

Processing IndexController#index (for 127.0.0.1 at 2008-10-30 18:45:13) [GET]
Completed in 131ms (View: 60, DB: 20) | 200 OK [http://localhost/]

O Ruby é 2x mais rápido que o JRuby ao renderizar a view, mas quase 3x mais lento em consultas ao banco.
Alguém já brincou com o JRuby e Rails 2.2? Obteve resultados melhores? Comentem.

No post passado eu falei sobre os benefícios do Rails 2.2 em termos de performance que obtive na minha aplicação. Porém, não deixei bem claro que a intenção dos meus testes eram comparar a diferença entre as duas versões do Rails apenas, sem tocar no interpretador Ruby. Então pra deixar as coisas mais claras, vou analizar aqui as diferenças entre os interpretadores Ruby 1.8 (MRI), Ruby 1.9 (YARV) e JRuby (JVM).

Ruby 1.8

É o Ruby estável atual. Suporta apenas Green Threads.
Isso significa que, não importa quantas threads você rode no seu programa, haverá apenas uma Thread alocada no kernel do sistema operacional. O efeito colateral disso é a necessidade de esperar que uma Thread termine a sua execução para começar outra Thread.
Até pouco tempo a solução para esse problema eram clusters de servidores. Com vários servidores rodando, cada um fica responsável por uma requisição, permitindo a execução de mais de um processo ao mesmo tempo (não concorrente). Mas gerava um outro problema: cada servidor precisava carregar na memória todas as bibliotecas, classes do framework, classes da aplicação, etc, o que gera um overload de memória.
Então chegaram os Phusion Guys com o Passenger e o Ruby Enterprise Edition, que juntos otimizaram esse processo. O que o Ruby Enterprise Edition fez foi compartilhar os objetos iguais entre as instâncias de Rails, diminuindo o overload. Contúdo você ainda tem conexões de banco, código de aplicação e dados em memória criados depois da inicialização duplicados.

Ruby 1.9

O Ruby 1.9 implementa o YARV (desenvolvido pelo Matz assim como o MRI) como virtual machine, o qual suporta Native Threads. Isso significa que cada Thread Ruby terá sua própria Thread no kernel do sistema operacional. Porém, isso não significa que haverá paralelismo entre as execuções das Threads. Somente um processo rodará por vez, independente de quantos cores tenha o servidor. Isso acontece devido ao GIL (Global Interpreter Lock), que também está presente no MRI.
O GIL nada mais é que uma trava gerada quando são solicitados processos de IO, para impedir que seja compartilhado código que não é Thread Safe entre as Threads.
Remover o GIL beneficiaria a utilização de mais de 1 core no servidor, pois possibilitaria a execução paralela de Threads. O problema é que muitas extensões feitas em C precisariam ser reescritas, o que atrasaria o lançamento. Talvez isso esteja nos planos para a versão 2.0.

JRuby

O JRuby usa JVM, a máquina virtual Java, e que suporta Native Threads e execução simultânea.
Isso significa dizer que várias requisições poderão ser executadas paralelamente, dependendo únicamente da quantidade de cores do seu servidor.

Então por que houve melhora na performance sem trocar o interpretador?

O que acontecia com o Rails até a versão 2.1.1 é que havia um lock único sobre cada requisição, e que fora substituido na versão 2.2 por locks menores, sobre os recursos compartilhados apenas. Porém, ao meu ver, mais importante que isso para a melhora de performance é o pool de conexões. Na versão 2.2, você pode configurar um pool de conexões com o banco de dados que serão acessados ao mesmo tempo.
No seu blog o Carlos Brando explicou como o pool de conexões funciona. Vale o acesso.

Hoje resolvi testar a minha aplicação de várias formas, pra ver se a nova versão do Rails traria algum benefício de fato para mim.

Primeiro rodei o Apache Benchmark na aplicação do jeito que ela estava, como Rails 2.1.1:

$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   39.938685 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      1036850 bytes
HTML transferred:       1010700 bytes
Requests per second:    1.25 [#/sec] (mean)
Time per request:       3993.869 [ms] (mean)
Time per request:       798.774 [ms] (mean, across all concurrent requests)
Transfer rate:          25.34 [Kbytes/sec] received

Hummm.. lento.. muito lento. Já estava preocupado com a hora de colocar isso em produção.

Atualizando o Rails para a versão 2.2 e rodando o benchmark:

$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   32.228686 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      590650 bytes
HTML transferred:       564400 bytes
Requests per second:    1.55 [#/sec] (mean)
Time per request:       3222.869 [ms] (mean)
Time per request:       644.574 [ms] (mean, across all concurrent requests)
Transfer rate:          17.87 [Kbytes/sec] received

What?? Cadê a melhora de performance? Rails não é Threadsafe agora?
É! Mas você tem que habilitar o recurso nas configurações pequeno Padawan:

No seu arquivo config/development.rb adicione no final:

config.threadsafe!

Rodando o Benchmark:

$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   6.807438 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      590620 bytes
HTML transferred:       564400 bytes
Requests per second:    7.34 [#/sec] (mean)
Time per request:       680.744 [ms] (mean)
Time per request:       136.149 [ms] (mean, across all concurrent requests)
Transfer rate:          84.61 [Kbytes/sec] received

Muito melhor não?! Uma melhora de quase 500%! Mas e se rodassemos em modo de produção?

$ ab -n 50 -c 5 http://localhost:3000/
 
Concurrency Level:      5
Time taken for tests:   3.693437 seconds
Complete requests:      50
Failed requests:        0
Write errors:           0
Total transferred:      573555 bytes
HTML transferred:       547350 bytes
Requests per second:    13.54 [#/sec] (mean)
Time per request:       369.344 [ms] (mean)
Time per request:       73.869 [ms] (mean, across all concurrent requests)
Transfer rate:          151.62 [Kbytes/sec] received

Impressionado pequeno Padawan? Cuidado! Nada disso aqui significa que sua aplicação vai ficar tão melhor quanto a minha somente com a nova versão do Rails. A aplicação em questão aqui envolve muitos cálculos complexos, e por isso tira vantagem do threading.
Tentei fazer alguns testes com o mysqlplus, driver de mysql pra ruby que promete acesso assíncrono ao banco de dados. O tutorial que eu segui simplesmente não funcionou no meu ambiente. Li em algum dos posts que pesquisei que eles estavam concentrando esforços pra fazer funcionar com o Rails 2.2 e corrigir alguns bugs.
Rails escala? Pra minha aplicação sim. E pra sua?

GTBox - Controle de caixa

UPDATE (03/11/08): Salvando apropriadamente agora. O problema era que eu mandava o Gazpacho salvar meus campos como String no data-type, mas ele não salvava nada. Solução: abra os arquivos salvos com o gazpacho em um editor de texto qualquer e sete a opção data-type como str, int ou whatever.



A avaliação de uma das matérias do meu curso é a construção de um sistema em Python ou em Java que se comunique com o banco de dados. Muito simples mesmo.

Então comecei um projetinho usando Python + Kiwi + SQLObject+ Gazpacho, que na breve pesquisa que eu fiz acreditei ser a melhor opção. Mas aí me deparei com um grande problema: a falta de documentação do Kiwi. Vários probleminhas que concerteza são muito simples, mas que sem nenhuma referência são difíceis de resolver apareceram.

Tudo está aparentemente certo. Mas quando eu mando salvar as alterações para o banco, todos os campos são salvos como Null.

Pra quem quiser dar uma olhada no projeto ou até mesmo contribuir eu o coloquei no github:

git clone git://github.com/mauriciozaffari/gtbox.git

A intenção não é de forma alguma fazer algo muito sofisticado e qualquer ajuda será muito bem vinda.

Semana passada tivemos o Rails Summit, primeiro grande evento de Rails no Brasil. O Ozéias organizou uma van para levar o pessoal do Paraná, e uns perdidos de Santa Catarina também, e eu embarquei nessa.

Galera da Van

Galera da Van

Foram 13 horas de viagem não muito confortável, mas também não insuportável. Não fosse alguém que não parava de falar um minuto a viagem teria sido menos cansativa. Chegamos antes das 7 horas da manhã e fomos direto para o evento, nem aberto tava ainda. Fomos os primeiros a chegar!

Primeiros a Chegar

Chegamos pra abrir as portas do evento

A organização estava ótima! Desde a organização das palestras aos coffee breaks e almoços. Parabéns ao Fabio Akita e para todo o pessoal da Locaweb que se esforçaram muito para fazer desse um evento muito bom.

As palestras foram muito boas mesmo, mas o ponto alto do evento foi conhecer o pessoal. Nerds de todos os tipos estavam presentes.. nunca vi tanto MacBook no mesmo lugar. Conheci muita gente legal, mas ao mesmo tempo não tive tempo de conhecer melhor muita gente. A galera twittou o tempo inteiro, foi aberto um canal no irc e até foi criado um belo projeto no github! Graças ao livestream do BlogBlogs foi possível acompanhar tudo que era comentado sobre o evento em vários serviços.

Um dos clones do Dr. Nic estava presente

Um dos clones do Dr. Nic estava presente

Nesse evento foi possível ver como a comunidade ruby é uma comunidade unida. O pessoal sempre prestativo e atencioso, até nos FAILcasts da vida! Ao pessoal que não pôde estar presente, dêem um jeito de aparecer ano que vêm. Satisfação garantida ou seu dinheiro de volta.

Hello World!

Como todo blog que fale sobre tecnologia e programação que se preze, este também tem que começar com um post “Hello World!”. Ou no caso deste, recomeçar.

Eu acabei deixando o blog morrer da primeira vez, mas isso não vai acontecer denovo não.

Aqui eu vou falar tudo que me der na telha, provavelmente sobre o assunto que mais me interessar no momento. Ruby, Rails, Desenvolvimento em geral, Metodologias ágeis, são apenas alguns dos assuntos abordados aqui.

Então é isso ai… let’s get it started!

1
2
3
def blog
  @papodenerd = Blog.new :about => "Ruby on Rails"
end