Explorando o Wide Finder

Eu tenho sentimentos decididamente mistos sobre o livro Beautiful Codemas um dos melhores capítulos é o “Finding Things” de Tim Bray. Nele, ele descreve a criação de um pequeno programa Ruby:

counts = {}
counts.default = 0
ARGF.each_line do |line|
if line =~ %r{GET /ongoing/When/dddx/(dddd/dd/dd/[^ .]+) }
counts[$1] += 1
end
end
keys_by_count = counts.keys.sort { |a, b| counts[b] <=> counts[a] }
keys_by_count[0 .. 9].each do |key|
puts "#{counts[key]}: #{key}"
end

Tim chama Ruby de “a mais legível das linguagens”; acho que isso é um pouco exagerado, mas provavelmente sou a pessoa errada para perguntar, porque Aprendi a desconfiar da beleza:

Parece que a paixão por um design leva inevitavelmente a um desgosto, à medida que realidades feias e negligenciadas se intrometem. O amor é cego, mas os computadores não são. Um relacionamento de longo prazo – manter um sistema por anos – ensina a apreciar virtudes mais domésticas, como a simplicidade e a convencionalidade. A beleza é uma fantasia idealista: o que realmente importa é a qualidade da conversa interminável entre o programador e o código, pois cada um aprende com o outro e se adapta a ele. A beleza não é uma base suficiente para um casamento feliz.

Mas estou divagando. Mesmo que o senhor não tenha ideia do que é Ruby é que esse pequeno programa simples não é muito difícil de decifrar. Ajuda se o senhor souber que Blog de Tim Bray Os URLs são assim:

http://www.tbray.org/ongoing/When/200x/2007/09/20/Wide-Finder

Este é um programa para contar as entradas de URL HTTP GET mais comuns em um arquivo de registro do servidor da Web. Percorremos todo o arquivo de registro, criando um par chave-valor desses URLs, em que a chave é a parte exclusiva do URL e o valor é o número de vezes que o URL foi recuperado.

Talvez seja apenas o desenvolvedor do Windows em mim, mas podemos nos perguntar por que o senhor se daria ao trabalho de escrever esse código diante dos inúmeros zilhões de pacotes de software gratuitos e comerciais de estatísticas de arquivos de logs da Web. Afinal de contas, o melhor código é não ter código algum.

Bem, talvez haja uma razão para a existência desse código, afinal de contas. Tim acabou transformando esse trecho de código em um exercício de benchmarking. O projeto Wide Finder.

É um exemplo clássico da cultura, nascida no Awk e aperfeiçoada no Perl, de realizar trabalhos úteis combinando expressões regulares e tabelas de hash. Eu quero descobrir como escrever um programa equivalente que seja executado rapidamente em CPUs modernas com taxas de clock baixas, mas com muitos núcleos; este é o projeto Wide Finder.

Um experimento nobre, de fato. Os benchmarks foram realizados no seguinte hardware:

  • Sun T5120
  • 8 núcleos de CPU UltraSPARC T2 a 1,4 GHz
  • 64 GB DE RAM

Os dados de entrada são os seguintes:

  • O conjunto completo de logfiles da Web do Tim de março de 2007
  • 926 MB
  • 4.625.236 linhas

Os resultados são mais ou menos… bem, em todo o mapa. Farei um resumo com as piores e melhores pontuações para cada idioma:

Estou simplificando um pouco aqui e omitindo idiomas com apenas uma submissão. vá para a página de resultados reais para obter mais detalhes.

Enquanto o senhor estiver lá, sugiro também que leia A análise dos resultados feita por Timna qual ele argumenta que algumas das otimizações de código que “venceram” os benchmarks deveriam ser automáticas e quase transparentes para o programador. Ele propõe que, em um mundo perfeito, um personagem único no programa Ruby original seria tudo o que seria necessário para habilitar todas as otimizações multicore necessárias:

ARGF.each_line* do |line|

Concordo plenamente. Pessoalmente, acho que esse é o resultado mais importante do experimento Wide Finder. Quando se trata de desempenho multicore, a escolha da linguagem não é uma bala de prata. De que outra forma podemos explicar a maciço disparidade entre as versões mais rápidas e mais lentas do código em cada linguagem?

No que se refere a experimentos, o Wide Finder foi razoavelmente bem-sucedido, embora um pouco incompleto e talvez pequeno demais. Tim abordou essas duas críticas e reiniciou com o O projeto Wide Finder 2. Ele é maior, mais forte e mais robusto, mas o objetivo continua o mesmo:

O problema é que o muitas operações básicas e simples de processamento de dados, no meu caso um simples script Ruby, são executadas como lixo em processadores modernos de muitos núcleos. Como o mundo inteiro está caminhando na direção de processadores mais lentos e de muitos núcleos, essa é uma situação insatisfatória.

Se o senhor observar os resultados da última vez, é óbvio que existem soluções, mas as que vimos até agora impõem um custo de complexidade terrível ao programador. O Santo Graal seria algo que maximizasse a relação entre o aumento de desempenho por núcleo e o esforço do programador. Minha opinião: Qualquer coisa que exija mais do que o dobro do código-fonte para tirar proveito de muitos núcleos é altamente suspeita.

Verifique o Wiki do Projeto Wide Finder 2 para obter todos os detalhes importantes. O implementação ingênua do Ruby leva atualmente 25 horas — sim, horas — para ser concluído. Alguns programadores inteligentes já superaram esse resultado em quase duas ordens de magnitude, de acordo com o página wiki de resultados.

Localizador amplo não é um experimento perfeito, mas é um resumo relativamente simples e facilmente compreensível dos problemas enfrentados por todos os desenvolvedores de software do futuro no mundo massivamente multicore que se aproxima. O senhor pode se sair melhor no tempo? sem explodir o tamanho do código, a complexidade do código ou a cabeça do programador médio?