Blog do Eduardo Costa Meu blog pessoal

12Jan/130

Adeus velha guarda do iOS

Depois de meses longe do desenvolvimento iOS, fui pegar meus projetos que ficaram "na geladeira". Surpresa do dia: a Apple jogou fora o suporte ao armv6. Em português claro, isso significa que não existe mais suporte oficial para iPhones anteriores ao 3GS, iPod Touch anteriores ao 2ª Geração, etc. Enfim, todo mundo que não pode atualizar para o iOS 5 ou superior.

Para quem gosta de sofrer fazendo aplicações que suportem todas as versões do iOS desde o 3, isso pode significar o fim da tortura ou o início de uma tortura maior ainda (regada a várias gambiarras). Para quem gosta de falar mal da Apple, prato cheio.

Eu fico feliz com o fim dessa tortura e dúvida. Poder criar jogos com Shaders, aplicações com Storyboard, suporte a iCloud, Facebook, Twitter, etc. Sem me preocupar em colocar "ifs" para ver se o usuário tem ou não um celular novo. Ótima notícia.

Não encontrei estatísticas oficiais, mas desenvolvedores espertos publicaram gráficos e estatísticas. Os números são deprimentes para quem gosta de sofrer. No gráfico de pizza, a fatia da versão 4 não tem a grossura de um aspargo. Percentual não chega a 4%.

Hora dos números. Digamos que cada versão venda igualmente. Você sabe que isso é mentira, pois alguém que ainda mantém um dispositivo tão antigo realmente não deve gastar muito com aplicativos. Mas, mesmo assim, vamos supor que a venda é proporcional. Se vender dez mil com um aplicativo, parabéns! Mas somente quatrocentos seriam da versão 4. Isso não vai pagar o tempo que levou para adaptar o software para as versões antigas. Sinceramente, esse valor poderia ter sido economizado, ou investido em usar os novos recursos do iOS 5/6.

Eu adorei a notícia. Só fico triste por abandonar meu iPod Touch 2ª geração. Deu muitas alegrias, mas agora está praticamente virando peso de papel.

8Abr/122

Tower Defense no Unity3D

Não sei se o Unity faz tudo ficar tão simples, mas a implementação de um jogo de "Tower Defense" parece ser tão trivial com ele. Não desmerecendo o trabalho de ninguém, mas uma engine poderosa resolve a maior parte do problema. Em menos de um dia, fiz o básico:

Com o Blender, fiz o mapa cor-de-Hulk e o caminho a seguir. Poderia ter feito a torre, a defesa e o inimigo também, mas estava com preguiça (afinal, é só uma "spike solution"). Com o Unity, a parte de detecção de proximidade está pronta. Minha parte foi escrever meia dúzia de scripts que, somados, não alcançam 100 linhas de código.

1Abr/120

Hello Unity3D World

Após algumas semanas estudando o Unity3D, decidi fazer um "Hello World" por conta própria. É um remake de um jogo retro de ping-XXXX cujo nome não posso falar ou a empresa que retém o copyright vai querer me processar. Acredite, pois foi o que aconteceu quando fiz isso na App Store.

O jogo é só uma experiência, de forma que não tem pontuação e deve ter tanto bug que eles já possuem cadeia-de-comando e briga entre gangues. Acredito que nenhum deles vai sair da Matrix para descer a lenha em seu HD numa cena entupida de efeitos especiais, mas vou seguir o contrato AS-IS (aquele que, resumindo, quer dizer: "não garanto nada"). De qualquer forma, clique aqui para jogar!

Próximo "hello world" eu prometo embutir na mesma página para facilitar.

3Nov/110

Santa Ignorância, node.js! – Parte 2

Minhas experiências em casa com o node.js vão andando. Fiz uma pesquisa ingênua no Google para ver se existe algum blog "powered by node.js" e achei o "blog do node.js". Cliquei para ver qual a implementação que eles usam e, claro, era WordPress. Casa de ferreiro, espeto de pau.

A parte interessante foi ver as diferenças entre a versão 0.4 (que eu estou estudando) e a 0.6 (ainda não lançada). Algumas coisas interessantes, não seria nada de outro mundo, exceto pelo novo recurso de clustering.

Sim! Agora o node.js vai suportar usar mais de um core! Refiz os testes de segunda-feira e a velocidade pulou de 13 mil requisições por segundo para mais de 24 mil! O Sr. Ignorância conseguiu superar o Apache! Apesar que esse número mágico (23 mil do httpd versus 24 mil do node.js) me aparenta ser um limite máximo causado por algum outro fator (rede loopback, concorrência com o ab, etc).

Pensei em testar com uma aplicação mais complexa (usando o módulo Express do node.js). Ela aguenta 3 mil requisições por segundo no v0.4, mas o Express não funciona direito na v0.6! Lá se vão meus 5 minutos de prazo para testar - fica para a próxima. Não obstante, facilita muito minha vida saber que o node.js vai ter mais esse recurso.

PS: Aos que não conhecem, o node.js é um "executor" de Javascript. Sim, basicamente ele só executa JS. As principais vantagens dele são: um belo dum engine (o veloz V8 da Google) e sua incrível API (suficientemente completa e massivamente event-driven). Como muitas aplicações web são I/O-bound, ou seja, perdem muito tempo lendo do disco, trafegando na rede, etc, essa "inversão de controle" é altamente benéfica - os números provam isso.

31Out/110

Santa Ignorância, node.js!

Já faz um tempo que estou brincando com o node.js em casa e consegui uns números interessantes (800 requisições por segundo em uma t1.micro na Amazon). Pensei então: quanto atingiria um "hello world" na máquina i7 com oito cores que tenho no trabalho?

Vamos à experiência! O código é assombroso de tão simples:

var http = require("http");
http.createServer(function(request, response) {
    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.end("ok");
}).listen(8000);

Qualquer requisição feita na porta 8000 gera uma resposta "200" com o texto "ok". Vamos executar o "ab" (ApacheBench):

$ ab -n 10000 -c 100 http://localhost:8000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
 
Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
 
Server Software:        
Server Hostname:        localhost
Server Port:            8000
 
Document Path:          /
Document Length:        2 bytes
 
Concurrency Level:      100
Time taken for tests:   0.736 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      660000 bytes
HTML transferred:       20000 bytes
Requests per second:    13587.64 [#/sec] (mean)
Time per request:       7.360 [ms] (mean)
Time per request:       0.074 [ms] (mean, across all concurrent requests)
Transfer rate:          875.77 [Kbytes/sec] received
 
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       3
Processing:     1    7   3.5      7      19
Waiting:        1    7   3.5      7      19
Total:          2    7   3.4      7      19
 
Percentage of the requests served within a certain time (ms)
  50%      7
  66%      9
  75%     10
  80%     10
  90%     11
  95%     13
  98%     16
  99%     17
 100%     19 (longest request)

Repare na quantidade de requisições por segundo. Sim, IT'S OVER NINE THOUSAND! Só para comparar, fiz alguns Hello World's na mesma máquina:

  • Apache (instalação default do ubuntu) gerou 25 mil para o index.html!
  • Apache+PHP com um "echo ok" gerou 21 mil!
  • Apache+Perl gerou 3 mil
  • Rails no WEBrick (trocando o index dele pelo index.html do httpd do ubuntu) aguentou "apenas" 800 requisições por segundo (e ainda tomei alguns muitos "connection reset" no meio dos testes)

Eu pensei em testar com Java e Python também, mas Python está longe de meus conhecimentos para um Hello World "quick-and-dirt" e, em Java é impossível fazer um "index" em menos de cinco minutos sem ter o ambiente pré-pronto (ou seja, preciso instalar o Tomcat, criar um projeto no NB, criar um Servlet, etc etc).

E, antes de mandarem comentários xingando minha metodologia de testes, reparem que todos os exemplos são instalações "vanilla" sem nenhuma configuração. Eu achei os números do node.js interessantes e quis mostrar, comparando com outras tecnologias que permitem um "Hello World" saindo do forno em menos de 5 minutos.

No final, dá a impressão que o Apache é bem mais rápido que o node.js, porém o httpd usa N processos! O node.js, por outro lado, é 100% monothread (event-driven ao extremo) - por isso não escala legal em oito cores. Tentei procurar por uma solução multi-core para o node.js, mas sem sucesso (tanto o módulo "cluster" quanto o "multi-node" não funcionaram).

Mas, vamos ser sinceros: 13 mil requisições por segundo usando um único core é fora de escala! Em um ambiente como na Amazon, posso rapidamente fazer um cluster de nodes usando máquinas t1.micro ou m1.small e um ELB para balancear. E, a parte engraçada é: com 13 mil requisições por segundo, nem precisa de cluster! Isso praticamente aguenta um Twitter (a antiga versão Rails precisou segurar 11 mil)!

O node.js pode ser uma plataforma baixo-nível, mas, com as libraries corretas e frameworks "home made", é uma poderosa ferramenta para a web!

30Out/112

Ponto Flutuante

Estou chegando a conclusão que ninguém nesse mundo lembra como funciona o conceito de "ponto flutuante" na programação. Em uma semana, já tive notícia de uns três ou quatro casos de pessoas falando "não sei porque a soma de decimal às vezes dá um resultado errado".

Vamos começar a aula esclarecendo que conta decimal não pode dar divergência. O que com certeza está acontecendo é que você está usando pontos flutuantes. Tirando COBOL e dBase III, não conheço nenhuma outra linguagem de programação na qual você use pontos decimais numa literal (ex: "83.27") e a linguagem interprete como decimal. Até que alguém prove o contrário, assuma que ela usa pontos flutuantes.

E, antes que alguém diga: "Ah, mas o meu não é float, é double.", vou avisando que "double" quer dizer o dobro de bits de representação, e não o dobro de precisão. Continua sendo ponto flutuante.

Como funciona? Em uma linha: o número é representado como Nx2^M. Lembra de notação científica (Nx10^M)? Mesmo princípio, só que usando base 2 (pois é essa a base que um CPU binário conhece). O número em ponto flutuante consiste de uma "base" N e uma "mantissa" M. Exemplo: N=2 e M=-4 é a representação PF para 2/16 ou 0,125. Como N e M tem uma quantidade finita de bits, claramente vão existir números não-representáveis em PF (ex: 10000000,000000001 - clique aqui e veja).

Se Nx2^M não é preciso, imagine a soma de Nx2^M e Ox2^P! Isso, "computacionalmente falando", significa transformar as mantissas M e P para um valor comum (Q), adaptando as bases N e O, efetuar a soma [Nx2^(M-Q)+Ox2^(P-Q)]x2^Q e normalizar o resultado. Preciso elaborar uma prova matemática para dizer que esse monstro é impreciso? A prova em JavaScript você vê clicando aqui. Mas, por via das dúvidas, se quiser uma prova mais formal, lembre que [Nx2^(M-Q)+Ox2^(P-Q)] vira a nova base, então ela precisa caber nos n bits da base durante a conta!

Resumo da ópera para quem gosta de decorar regra: não use ponto flutuante para valores monetários.

13Out/111

JavaScript para Brucutús

Os Ads do Lomadee estavam me torrando a paciência hoje: a tag <script> não deixava a página do post carregar. Saí de lá com o banco zoado, então pode ter algo relacionado. Solução? Remover o Lomadee? Não. Apelar para ignorância.

Troquei o equivalente a isto aqui:

<script src='urlTravada.js' type='text/javascript'></script>

Por isso aqui:

<div id="ad"></div>
<script type="text/javascript">
(function() {
  var alml = document.createElement('script');
  alml.type = 'text/javascript';
  alml.async = true;
  alml.src = 'urlTravada.js';
  var almlDiv = document.getElementById('ad');
  almlDiv.appendChild(alml);
})();
</script>

Moral da história: a página carrega e depois tenta iniciar o JavaScript. Se travar, travou apenas o Ad!

EDIT: Encontrei dois pequeno pormenores. Primeiro, o javascript do Lomadee usa o malígno "document.write" (que não funciona após o load da página). Fácil resolver: basta criar um novo "document.write". Segundo, isso quebrou o ad do Google, que também usa a mesma função. No fim, ambos estão com o mesmo lazy loading e o novo document.write é algo assim:

document.write = function(x) {
  var id = "";
  if (x.match(/contextAds/)) id = 'adLomadee';
  else id = 'adGoogle';
  document.getElementById(id).innerHTML += x;
};

Pode clicar nos dois! Funcionam como os originais!

10Jul/110

Webdesign ficou mais simples

Lembro que, em 1999, quando eu trabalhei por quatro meses como webdesigner em Brasilia, o trabalho era muito difícil. Mesmo com o Dreamweaver para auxiliar, o Netscape e o IE faziam uma disputa implícita para deixar a Internet compatível apenas consigo mesmo. Era digno de uma verdadeira epopéia fazer o sítio ficar bonito e compatível. Na época ainda era aceitável colocar o ícone "compatível apenas com o IE".

Anos depois, como programador júnior na antiga Unitech (que foi comprada pela Braxis, que se juntou com a CPM e agora foi tudo comprado pela Capgemini - maravilha isso no meu CV), o problema de compatibilidade migrou do HTML para o Javascript/CSS. Não existiam mais "blinks", "marquees" e outras nojeiras inúteis, mas era outro épico conseguir fazer o CSS e o Javascript rodar igual no IE, no Mozilla e no recém-nascido Firefox.

Agora, com o Safari, o Opera, o Chrome, o Firefox, o IE, e alguns outros menores, seria uma volta pelos nove círculos do inferno obter compatibilidade. Graças a Deus alguém pôs juízo na cabeça dos quatro primeiros, pois a padronização é lei para eles.

Ou seja, onze anos depois de digladiar com a falta de padrões, hoje posso deleitar-me aliviado com o design em XHTML e o CSS, seguindo os preceitos Zen, pois apenas poucas - e conhecidas - gambiarras garantem a compatibilidade entre os navegadores.

Aliado ao Wicket e frameworks JS (que nem o jQuery), criar um website pessoal Java ficou muito mais simples e produtivo.

8Mai/112

Teste unitário no iOS

Acho que finalmente consegui pegar o jeito de fazer testes unitários no iOS. Em Java, com o JUnit mais o NetBeans (e talvez o Maven), é super fácil, trivial, o tipo de coisa que faço sem problemas. No iOS, com o XCode, nem tanto.

O primeiro grande problema é saber como gerar testes unitários. Na Internet, você vai encontrar um monte de idéias mirabolantes. Algumas boas, outras, nem tanto. Mas, para o arroz-com-feijão, geralmente são idéias complexas demais. No XCode 4, consegui fazer de um jeito ridículo de simples.

Primeiro, estou separando um target para cada suite de testes. Mando criar um novo target de testing bundle, adiciono somente as classes que vou testar, crio mock para as demais e pronto! Automaticamente, as classes incluídas vão usar as classes mock. Como tenho um target para cada suite, posso criar combinações específicas para cada teste.

A parte triste seria rodar cada target de teste individualmente. Mas, basta adicionar os targets ao "test scheme" do target principal e já era! Você pode rodar o teste (⌘-U) como se fosse um target de teste!

Quase tão simples quanto no NetBeans! Adorei!

30Abr/110

Otimização de cubos

Cá estou eu trabalhando em um pequeno teste envolvendo cubos e OpenGL ES. Gerei alguns números impressionantes e gostaria de compartilhar.

Bem, quem manja de OpenGL sabe que glVertex com GL_TRIANGLES é o pior jeito de gerar objetos 3D (embora seja o mais simples). Usar Vertex Buffer Objects, glDrawElements e GL_TRIANGLE_STRIP é algo bem melhor. Entretanto, faria muito noob se borrar nas calças e dizer que Direct3D é melhor se as pessoas aprendessem primeiro pelo jeito mais otimizado.

Vamos aos números. Em um cubo, temos 6 faces, correto? Cada face é representável com dois triângulos e cada um com três vértices. Dá um total de 36 (6x3x2) pontos ou "chamadas ao glVertex". Para definir cada ponto, temos 3 posições (X, Y, Z), mais uma coordenada constante extra para shaders (W) , mais duas coordenadas de textura (U, V). Se todas forem "float", são 6 dados de 4 bytes, ou seja, 24 bytes por ponto. No total, o cubo tem 864 bytes (36x24).

Como melhorar isso? Em primeiro lugar, usando shorts no lugar de floats para XYZW. Cada ponto fica com 16 bytes (4 shorts de 2 bytes mais 2 floats de 4 bytes). Já reduzimos o tamanho do cubo para 574 bytes (36x24). Melhorou, mas não está bom.

Usando triangle strips, reduzimos a quantidade de vértices. Em virtude das texturas que estou aplicando (uma em cima, uma para os lados e uma para baixo), posso fazer um "strip" para os lados, "casar" com a parte de cima e "pular" para a parte de baixo. Não sei descrever melhor, mas isso me permite descrever o cubo com apenas 22 pontos. Reduz para 352 bytes (16x22). Calma que ainda vem mais!

Com VBOs, ainda mantendo o padrão das texturas, descobre-se que esses 22 pontos tem muito "repeteco", gerando apenas 15 pontos únicos. Isso são 240 bytes (16x15), mas falta o buffer de índices. Criando esse IBO, temos os 22 pontos representados como minúsculos shorts. São mais 44 bytes (22x2). No total, temos 284 bytes (44+240)!

Ou seja, nesse caminho todo, o computador precisava movimentar e processar 864 bytes (quase 1k) por cubo. Otimizando, caiu para 284! Isso representa uma economia de quase 68%! E, nesse caso, também representa uma otimização de processamento, VBOs reduzem as cópias entre memória de CPU e memória de GPU.

A parte legal é que fica tudo intuitivo depois que você entende o porquê.