sábado, maio 29, 2010

Blog descontinuado - Agora é o Blog da Especializa

Para quem encontrar esse blog de algum modo, saiba que ele está inativo desde 2006 e que ando escrevendo no Blog da Especializa, empresa a qual sou professor e diretor.

No Blog da Especializa, pretendo falar de PHP e "otras cositas mas".

Abraços

Marcadores: ,

sábado, maio 12, 2007

Abstração de SGBDs - Estudo comparativo - Parte 3 - Mais sobre desempenho

Opa, to de volta!
Primeiramente, obrigado a Adriano Rivolli, Tiago Oliveira, Felipe, Marcos e alguns anônimos pelos posts nos artigos anteriores sobre o assunto. Vou insistir ainda no assunto desempenho devido à polêmica da metodologia adotada.

Minha idéia (minto, não foi minha ...) foi executar uma consulta com pequeno volume de dados que, ao repetida 250 vezes poderia gerar um resultado mais expressivo. Poderia! Como disse Marcos, além de não ser algo que algum faria na prática, pode favorecer bibliotecas que utilizem mecanismos de cache de consultas mais eficientemente. Como ele mesmo questionou, "quem faria uma coisa dessas?", talvez alguém que desenvolveu uma dessas bibliotecas, com intuitos claros de manipular o resultado em seu favor, como sugeriu Manoel Lemos no faq do Metabase.

Bom, resolvi então executar uma única consulta que trouxesse um volume maior de dados. Tá, tudo bem, ainda não é um caso real, quem iria consultar nomes de atores x titulos de filmes e exibiria 57.371 linhas, "vamo paginar essa bagaça aí né"? Mas enfim, se você está aqui é porque está interessado em saber o resultado.

Para conhecer melhor a dinâmica dos testes, leia o artigo1, o artigo2 ou baixe aqui um zipão com os scripts criados e as bibliotecas ADOdb, Metabase e Creole (DBX, PDO, DB e MDB2 foram instaladas através do pear/pecl). Ahh, para aumentar a polêmica, os testes agora foram feitos no PostgreSQL e também no MySQL.

Antes que alguém saia dizendo "tá vendo que o MySQL é mais rápido?" (ou mesmo faria isso se o resultado fosse em favor do PostgreSQL), queria deixar claro que não é o intuito desse estudo, verificar qual SGBD é o mais rápido. A questão é apenas avaliar através de qual biblioteca de abstração uma listagem de dados é feita de maneira mais eficiente. Testar em dois SGBDs torna o resultado mais autêntico. Para avaliar qual SGBD é o mais rápido, seriam necessárias diversas opções de otimização, tanto nos servidores quanto no próprio sistema operacional, o que foge totalmente da minha intenção.

Segue abaixo o script base:

$time_start = microtime(true);
$db = pg_connect("host=localhost dbname=locadora user=postgres password=postgres");

$sql = "select a.nome, f.titulo
from filmes f inner join filmesatores fa using(idfilme)
inner join atores a using (idator)
order by a.nome";
$rs = pg_query($db, $sql);
while($row = pg_fetch_row($rs)) {
echo "$row[0]: $row[1]\n";
}

$time_end = microtime(true);
$time = $time_end - $time_start;
print("\n\nTEMPO: {$time}\n");

echo pg_num_rows($rs);



Os scripts das bibliotecas estão todos no zip.

Os testes foram realizados num Sempron 2600+, 512Mb DDR400, HD 7200RPM, rodando Slackware Linux 10.2, numa partição Reiserfs, em igualdade de condições (num to roubando não, eu juro, a prova é o PostgreSQL ter ficado abaixo, quem me conhece sabe o quanto que eu "gosto" do MySQL =D).
O PHP foi chamado diretamente do shell, pra não haver envolvimento do servidor Web no resultado.

Abaixo segue o resultado médio no PostgreSQL:
Postgresql Pear DB PDO Pear MDB2 DBX ADODB Creole JDBC Creole SPL Metabase
2,48900 3,42284 2,46843 3,78125 2,72751 2,98505 3,30825 2,68212 3,37926
2,49348 3,41937 2,47007 3,78145 2,73322 2,98462 3,30672 2,67480 3,38211
2,49281 3,41792 2,47403 3,77754 2,73643 2,98938 3,30986 2,68533 3,37229
2,48918 3,41881 2,47437 3,77573 2,73429 2,98038 3,31598 2,68195 3,37205
2,49928 3,41092 2,46116 3,77751 2,72606 2,98885 3,30632 2,67427 3,37583
2,50353 3,41355 2,46660 3,77111 2,73749 2,98402 3,30085 2,68511 3,36946
2,49429 3,41590 2,46946 3,77134 2,72822 2,98827 3,29874 2,67426 3,36973
2,49729 3,40895 2,46679 3,77131 2,73000 2,98797 3,30574 2,67872 3,36731
2,49134 3,43569 2,47650 3,77919 2,72292 2,98258 3,31773 2,67193 3,36646
2,48886 3,41745 2,46946 3,77690 2,73127 2,98863 3,31203 2,68006 3,36945









24,939055 34,181396 24,696869 37,763326 27,307407 29,859731 33,082212 26,788560 33,723949
2,493905 3,418140 2,469687 3,776333 2,730741 2,985973 3,308221 2,678856 3,372395
2,503531 3,435693 2,476499 3,781448 2,737492 2,989376 3,317731 2,685334 3,382108
2,488856 3,408949 2,461159 3,771106 2,722916 2,980377 3,298736 2,671928 3,366462









0,00% 37,06% -0,97% 51,42% 9,50% 19,73% 32,65% 7,42% 35,23%
0,00% 37,06% -0,97% 51,42% 9,50% 19,73% 32,65% 7,42% 35,23%
0,00% 37,23% -1,08% 51,04% 9,35% 19,41% 32,52% 7,26% 35,09%
0,00% 36,97% -1,11% 51,52% 9,40% 19,75% 32,54% 7,36% 35,26%

Cada coluna representa os testes realizados em cada biblioteca. Foram executados e colhidos 10 valores para cada biblioteca. Abaixo, seguem respectivamente, a soma dos tempos, a média, o maior e o menor tempo. Por fim, a diferença em termos percentuais entre as bibliotecas e o PostgreSQL direto.

Na ordem:
  1. PDO
  2. PostgreSQL diretamente
  3. Creole, utilizando o método SPL
  4. ADOdb
  5. DBX
  6. Creole, utilizando o método JDBC
  7. Metabase
  8. PEAR::DB
  9. PEAR::MDB2

Destaque para o bom desempenho do PDO, que andou sempre abaixo dos tempos tomados como base e o mau desempenho, novamente, das bibliotecas do PEAR. Sistemas que tenham desempenham como requisito importante, talvez elas não sejam as melhores opções.

Abaixo, seguem os mesmos testes no MySQL:
Mysql Pear DB PDO Pear MDB2 DBX ADODB Creole JDBC Creole SPL Metabase
2,46589 3,24191 2,48229 3,35256 2,64678 2,77412 3,19877 2,93436 118,36037
2,46582 3,22849 2,46531 3,33221 2,63623 2,76856 3,18477 2,93014 118,33984
2,46999 3,22517 2,47153 3,33555 2,62988 2,76712 3,19094 2,93711 118,78771
2,46978 3,23061 2,47483 3,33502 2,63268 2,75994 3,18629 2,94175 118,64869
2,47445 3,22243 2,46748 3,33577 2,62682 2,77076 3,18985 2,93569 118,74402
2,47193 3,22906 2,46340 3,33681 2,62872 2,76505 3,19841 2,92951 118,82050
2,46494 3,22448 2,46289 3,33166 2,63245 2,76516 3,18692 2,93461 118,71466
2,46563 3,23186 2,46532 3,33192 2,63752 2,76717 3,19743 2,92785 118,62763
2,47323 3,22112 2,46734 3,32946 2,63167 2,76253 3,19038 2,93096 118,66344
2,47618 3,22932 2,46527 3,33308 2,63654 2,76481 3,19058 2,93379 118,65430









24,697852 32,284441 24,685658 33,354044 26,339296 27,665220 31,914343 29,335775 1186,361161
2,469785 3,228444 2,468566 3,335404 2,633930 2,766522 3,191434 2,933577 118,636116
2,476184 3,241906 2,482288 3,352563 2,646775 2,774116 3,198772 2,941745 118,820496
2,464941 3,221116 2,462890 3,329457 2,626821 2,759941 3,184767 2,927847 118,339844









0,00% 30,72% -0,05% 35,05% 6,65% 12,01% 29,22% 18,78% 4703,50%
0,00% 30,72% -0,05% 35,05% 6,65% 12,01% 29,22% 18,78% 4703,50%
0,00% 30,92% 0,25% 35,39% 6,89% 12,03% 29,18% 18,80% 4698,53%
0,00% 30,68% -0,08% 35,07% 6,57% 11,97% 29,20% 18,78% 4700,92%

Na ordem:
  1. PDO
  2. MySQL direto
  3. DBX
  4. ADOdb
  5. Creole SPL
  6. PEAR::DB
  7. PEAR::MDB2
  8. Metabase (?)

No geral, elas andaram mais rápido, destaque para a diferença menor em termos percentuais entre elas e a execução direta no MySQL e para um resultado estranho nos número da Metabase. Tão estranho que cheguei a procurar onde estava errando e uma melhor maneira fazer. Como não descobri, fica a deixa para quem quiser desvendar esse mistério. Interessante também o resultado melhor da DBX, que dessa vez andou sempre à frete da Creole SPL e da ADOdb. A Creole SPL também perdeu espaço para a ADOdb.

Bom, espero que dessa vez o estudo pareça mais autêntico. Peço que você teste também em outras plataformas e combinações de configuração. Abraço e até o próximo post.

Marcadores: , , , , , , , , , , ,

domingo, fevereiro 11, 2007

Constantes e as limitações do PHP

Houston, we have a problem! Seriam limitações? Bugs? Sinceramente, ainda não sei, mas como o intuito é falar sobre PHP de maneira descompromissada, largo mão até da corriqueira mania de “evangelizar” (quem sou eu) a linguagem que trabalho há uns 7 anos e sou professor há quase outros tantos, para meter um pouco de pimenta no acarajé nosso de cada dia.

Lembro vagamente do meu professor de matemática em alguma série ginasial dizendo que uma constante é uma maneira de representar algum dado absoluto e imutável. Na verdade não lembro, odiava matemática (putz, por que eu fui odiar logo matemática), mas certamente ele falou quando fez a gente engolir aquele ¶ obscuro. Quando comecei a estudar PHP, logo li que constantes também só admitem valores escalares. Até aí tudo bem, estava iniciando na linguagem e pude sobreviver sem questionar nada.

O PHP5 trouxe alguns avanços na programação orientada a objetos e blábláblá que você, se está aqui, já deve saber. Entre as dádivas da quinta geração está a instrução const. Com ela é possível declarar constantes de classe, ou seja, uma maneira de representar algum dado absoluto e imutável, como dizia meu professor (ou seria professora, ...), agora inerente a uma classe.

Via de regra, constantes de classe são sempre públicas e estáticas (estão vinculadas à própria classe e não às suas instâncias), tanto é que declarar algo como public const ou static const são erros de sintaxe por se tratar de algo redundante. Sobre elas recai a mesma restrição imposta às suas irmãs globais, oriundas de instruções define, no que diz respeito à impossibilidade de assumirem valores como objetos ou arrays.

Perdoai minha insolência, mas por que esta limitação? O que há de mal nas linhas:

define('MEU_ARRAY', array("a" => 1));
echo MEU_ARRAY["a"];

ou

define('MEU_OBJETO', new Objeto());
echo MEU_OBJETO->getQualquerCoisa();

Quando o assunto é array, concordo que pode não ser interessante ter que garantir a constância de uma estrutura complexa na memória. No entanto, ao atribuir um objeto a uma variável no PHP5, seu valor será apenas uma referência ao objeto. Em outras palavras, nenhuma variável no PHP5 possui um valor complexo, mas sim um dado escalar referente ao endereço de memória onde reside de fato o objeto. Por que então as constantes, que só admitem valores escalares, não podem agora referenciar objetos? Note que o que deve ser imutável é o valor da constante, ou seja, a referência ao objeto. Mas o objeto em si pode sofrer alterações em seus atributos sem maiores preocupações.

Só para ilustrar, suponha uma listagem de dados de clientes (dentre eles os seus endereços). A classe Cliente possui um atributo do tipo Endereço que por sua vez, possui um atributo do tipo Uf a fim de representar qual o Estado do cliente. Ao buscar os dados do banco e construir o objeto cliente, uma instrução como essa:

while ( ... ) {
$cliente = ...;
$cliente->endereco->uf = new Uf($registroDoBanco[‘uf_sigla’]);
$lista[] = $cliente;
}

Criaria um objeto Uf para cada cliente, mesmo que a sigla obtida no banco de dados seja a mesma obtida numa linha anterior.

Para evitar essas instâncias desnecessárias, seria possível carregar um array de Ufs obtidas e só criar uma nova quando esta for baseada numa nova sigla. Veja:

while (...) {

$cliente = ...;

$uf = $registroDoBanco[‘uf_sigla’];

if (!in_array($uf, $ufsJaObtidas)) {

$ufsJaObtidas[$uf] = new Uf($uf);

}

$cliente->endereco->uf = $ufsJaObtidas[$uf];

$lista[] = $cliente;

}

No entanto, gostaria de contar com uma saída mais elegante em que eu não me preocupasse em sobrecarregar meu código sempre que fosse listar algo do tipo. Pensei no padrão de projeto Typesafe Enum.

Joshua Bloch, idealizador deste padrão, em seu livro Effective Java Programming Language Guide, recomenda 57 regras para programadores Java. Dentre elas, quatro são focadas em trazer construções interessantes disponíveis na linguagem C. Uma delas (o item 21) são justamente os tipos enumerados (enum). Estes são comuns em diversas linguagens e bancos de dados. São úteis em momentos em que você já conhece todas as variações possíveis que determinado dado pode assumir.

Devido à popularidade deste padrão de projeto, o JCP (Java Community Process – entidade que dita os caminhos dessa tecnologia) resolveu, sob a JSR 161, introduzir a declaração de tipos Enum no Java 5 (tiger), o que já era comum em ambientes .NET. Dizem as más línguas, esse fato foi um dos principais catalisadores desta JSR.

Pois bem, no PHP não temos o tipo Enum, no entanto o padrão Typesafe Enum poderia resolver nosso problema, veja como seria declarada a classe Uf:

class Uf {

const AC = new Uf("AC"); const AL = new Uf("AL");

const AM = new Uf("AM"); const AP = new Uf("AP");

const BA = new Uf("BA"); const CE = new Uf("CE");

const DF = new Uf("DF"); const ES = new Uf("ES");

const GO = new Uf("GO"); const MA = new Uf("MA");

const MG = new Uf("MG"); const MS = new Uf("MS");

const MT = new Uf("MT"); const PA = new Uf("PA");

const PB = new Uf("PB"); const PE = new Uf("PE");

const PI = new Uf("PI"); const PR = new Uf("PR");

const RJ = new Uf("RJ"); const RN = new Uf("RN");

const RO = new Uf("RO"); const RS = new Uf("RS");

const SC = new Uf("SC"); const SE = new Uf("SE");

const SP = new Uf("SP"); const TO = new Uf("TO");

private $sigla;

private $nome;

private function __construct($sigla = "") {

$this->setSigla($sigla);

}

public function getSigla() {

return $this->sigla;

}

private function setSigla($sigla) {

$this->sigla = $sigla;

switch($this->sigla) {

case "AC":

$this->setNome("Acre");

break;

case "AL":

$this->setNome("Alagoas");

break;

// ... E assim por diante

}

}

public function getNome() {

return $this->nome;

}

public function setNome($nome) {

$this->nome = $nome;

}

}

?>

Veja que a classe Uf já declarou uma constante para cada Estado do Brasil e atribuiu a ela uma instância dela mesma, ou seja, cada constante da classe Uf é um objeto Uf, cuja sigla e nome serão configurados de acordo com a string passada como parâmetro ao seu construtor. Assim como no Singleton, o construtor é privado para que ninguém tente construir um objeto Uf, todos eles já serão criados na primeira referência à classe Uf de todo o script.

Nosso script de acesso a dados ficaria assim:

while ( ... ) {

$cliente = ...;

$cliente->endereco->uf = Uf::$registroDoBanco[‘uf_sigla’];

$lista[] = $cliente;

}

No caso da sigla obtida no banco de dados ser PE, seria o mesmo que termos:

$cliente->endereco->uf = Uf::PE;

Simples e eficaz, no entanto é preciso ter cuidado com o uso dos tipos enumerados. Recomendo apenas em casos de objetos simples e relativamente poucos. Não considero 26 objetos compostos por dois atributos de pequenas strings como algo degradante em termos de desempenho, porém não o recomendo em casos onde os objetos sejam muitos ou possuam gráficos extensos, visto que haverá muita alocação desnecessária de memória no momento da primeira referência à classe enum.

Nossa classe Uf não pôde implementar o Typesafe Enum justamente pela limitação de valores escalares para constantes. Tive uma idéia. Por que não declarar as constantes simplesmente como variáveis de classe (static). Ora, eu vou perder a garantia do valor constante (perdão professor (a)), mas posso obter o mesmo resultado:

class Uf {

public static $AC = new Uf("AC"); public static $AL = new Uf("AL");

}

O código de acesso ficaria:

$uf = ::$registroDoBanco[‘uf_sigla’];

$cliente->endereco->uf = Uf::$$uf;

Entrei no ônibus, esbarrei na manivela. O PHP também não suporta chamada a qualquer procedimento no momento da declaração de atributos. O curioso é que se nossas variáveis de classe $AC e companhia fossem arrays, não haveria problemas:

class Uf {

public static $AC = array ("AC");

}

This is Houston. Say again please! Bom, minha conclusão antes que a véia que for passando me leve pro delegado é que constantes não podem referenciar objetos por um triste legado do PHP4 onde estes eram, como li em algum lugar, arrays que comeram espinafre. Procurei algo a respeito no site de bugs do PHP, mas como não encontrei nada, achei que o problema era comigo mesmo. Fessora, suas aulas de matemática eram ótimas, eu juro.

Marcadores: , ,

quinta-feira, janeiro 18, 2007

Design patterns: 1 - O onipresente Singleton e o conceito de aplicação

Desde o lançamento da Zend Engine 2, núcleo do PHP5, há 3 anos, venho presenciando um crescente interesse da comunidade PHP na orientação a objetos. Não é pra menos, afinal, foi nesta área que ela trouxe maiores impactos. Vale frisar que seu novo modelo de objetos já era ansiosamente aguardado. Lembrar da OO no PHP4 hoje, dá vontade de rir - ou chorar se houver necessidade implementar algo nesse sentido - basta lembrar dos incontáveis &'s.

Como um fenômeno inexplicável em qualquer linguagem com suporte maduro à OO, a moda no PHP pós-moderno são os padrões de projeto. Por favor, não há tempo para introduções. Deixaremos o arquiteto Cristopher Alexander, Martin Fowler, Erich Gamma e seus mosqueteiros para o seu professor de faculdade.

O que me chama à atenção é a quase que instantânea explanação acerca do padrão criacional GoF Singleton. Falou em design pattern (para os que não gostam de traduzir certos termos já amplamente conhecidos abaixo da linha do Equador em sua língua pátria), explicou o Singleton. Note que ele foi concebido num livro que tratou de C++, Eiffel, SmallTalk e trouxe diagramas em formatos mais livres anteriores à UML, ou seja, para a maioria dos programadores, definitivamente, não foi cunhado em linguagem cotidiana.

Mas afinal, por que todo mundo fala em Singleton? Bom, se o intuito de se catalogar padrões de projeto for promover o conhecimento de uma solução consistente para determinados problemas corriqueiros e gerar um vocabulário comum de entendimento universal, talvez estejamos diante de uma verdadeira obra-prima. Seria a gangue dos quatro (GoF – Gang of Four) composta por Leonardo, Michelangelo, Donatello e Rafael?

Talvez a resposta esteja exatamente no livro dos livros. Presente há mais de uma década na cabeceira de nove entre dez analistas de sistemas, a obra Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1995) não é de fácil leitura. Seus exemplos não são os mais comerciais (quem leu deve lembrar do labirinto ou do editor de textos), mas atingiu o status cult. É bonito falar dele. O Singleton então faz o papel de padrão de projeto mais trivial e acessível dos 23 catalogados.

Recentemente, li quatro artigos em PHP sobre Singleton. Adler Medrado, no phpavancado.net, Diego Botelho, no phpbrasil.com, Felipe Ribeiro em seu blog, aqui mesmo no Blogger e Alexandre Altair de Melo em seu artigo sobre padrões de projeto elaborado para a primeira edição da revista eletrônica brazuca PHP Magazine (não confunda com sua xará alemã). Registro aqui meus parabéns pela iniciativa. Quando crescer, quero escrever para vocês =).

Numa definição breve, Singleton é uma implementação OO para situações onde é necessário que tenhamos apenas uma única instância (objeto) de determinada classe. Não sei que fixação é essa, mas o que eu mais vi em PHP sobre o assunto trouxe o exemplo de uma classe que criasse objetos para gerenciar conexões a um banco de dados. Vamos a ela então (código retirado do artigo do Alexandre Melo – nossa única ressalva é a declaração static do método getInstancia() que ele esqueceu de colocar):

  1. /**
  2. * Classe para se conectar com o banco de dados.
  3. */
  4. class ConectaBanco {
  5. /**
  6. * Propriedade flag utilizada para verificar se já
  7. * existe uma instância do objeto
  8. */
  9. private static $instancia = null;
  10. /**
  11. * Construtor.
  12. * Aqui está a grande sacada o construtor da classe é privado.
  13. * Assim só um outro método interno pode ter acesso,
  14. * a esse construtor.
  15. *
  16. * @return void
  17. */
  18. private function __construct() {}
  19. /**
  20. * Método que irá fornecer o ponto global para se
  21. * ter acesso a instância.
  22. *
  23. * @return ConectaBanco
  24. */
  25. public static function getInstancia() {
  26. if (is_null(ConectaBanco::$instancia)) {
  27. ConectaBanco::$instancia = new ConectaBanco();
  28. }
  29. return ConectaBanco::$instancia;
  30. }
  31. }
  32. ?>


O primeiro passo para implamentação do padrão Singleton é proibir a criação de objetos da classe em questão a partir de fora dela. Isso ocorre, vedando o acesso externo ao construtor com o modificador private. Assim, new ConectaBanco(), que invoca o construtor, só pode ser feito na própria ConectaBanco. Os membros de classe (static) $instancia e do método getInstancia() independem de objeto, existem a partir do momento em que a classe existir. O método instanciou um objeto ConectaBanco e o atribuiu à variável de classe ConectaBanco::$instancia apenas se esta ainda estiver nula. A partir de chamadas sucessivas, ele apenas retornará o conteúdo já atribuído.

Assim, nas linhas abaixo:

$conn1 = ConectaBanco::getInstancia();

$conn2 = ConectaBanco::getInstancia();

As variáveis $conn1 e $conn2 referenciam o mesmo objeto.

Críticas construtivas

Perdão ao Diego, mas sua classe misturou demais o HTML ao PHP.

Perdão ao Adler, mas seu código fugiu um pouco do conceito de Singleton. O método getInstancia() (no seu caso, singleton()) retorna uma instância de mysqli e não da própria classe, como reza a norma GoFiana forjada em ambientes mais fortemente tipados.

Perdão ao Alexandre, mas se eu me deparasse com esse exemplo como argumento para utilização do Singleton há alguns anos, provavelmente continuaria trabalhando com meus códigos procedurais onde toda página incluía um simples arquivo conexao.php que atribuísse o almejado recurso de conexão com o banco de dados numa simples variável global.

O conceito de aplicação

Conforme bem lembra Felipe Ribeiro (aliás, o povo agradece por sair do lugar comum e elaborar um exemplo diferente da conexão com o banco):

“Em PHP não existe o conceito de Aplicação, ou seja, cada vez que alguém acessa sua página, o que acontece é apenas a execução de um script numa thread do seu webserver, e mesmo que várias pessoas acessam simultaneamente o *mesmo* script, essas execuções são independentes, não há relação nenhuma entre elas.”

Cada nova requisição inicia um novo processo no servidor que é encerrado no momento que não houver mais nada a devolver ao browser.


Estaria o PHP fadado a não suportar o Singleton?

Permita-me concordar não por completo com Felipe. O conceito de aplicação pode sim ser definido pelo desenvolvedor. Recentemente, ao elaborar um sistema e-commerce orientado a objetos pensei da seguinte maneira: Considerei que a aplicação deveria estar disponível para cada cliente, enquanto ele navegar. Começando na sua chegada à página inicial e encerrando no fechamento do browser ou conclusão da compra. Nesse último caso, ela reinicia automaticamente.

Se você pensou em sessões, pensou o mesmo que eu. Observe a definição da classe Carrinho que elaborei (obviamente, não trouxe seus demais atributos e métodos de negócio para não acarretar num código demasiadamente extenso):

  1. class Carrinho {
  2. /**
  3. * Instância única do Carrinho
  4. * @var Carrinho
  5. */
  6. private static $instancia;
  7. /**
  8. * Método estático de acesso ao objeto Carrinho.
  9. * Antes de construir um novo Carrinho, ele verifica
  10. * se há algum já salvo na sessão do cliente.
  11. * Se houver, retorna ela. Só em último caso, retorna uma nova.
  12. *
  13. * @return Carrinho objeto único
  14. */
  15. public static function getCarrinhoAtual() {
  16. if (!isset(Carrinho::$instancia)) {
  17. Carrinho::$instancia = (isset($_SESSION['carrinho'])) ?
  18. $_SESSION['carrinho'] :
  19. new Carrinho();
  20. }
  21. return Carrinho::$instancia;
  22. }
  23. /**
  24. * Construtor privado
  25. */
  26. private function __construct() {}
  27. /**
  28. * Apenas aproveitei o destrutor para lançar o
  29. * Carrinho na sessão.
  30. * É necessário que o script execute um sesssion_start()
  31. */
  32. public function __destruct() {
  33. $_SESSION['carrinho'] = Carrinho::$instancia;
  34. }
  35. }
  36. ?>

Dessa forma, consegui criar um objeto Carrinho que será único até o usuário fechar o browser ou encerrar a sessão de alguma forma. O que vai depender de como o sistema deve gerenciar suas sessões. Para um Singleton único por toda a aplicação, provavelmente seria necessário elaborar algum mecanismo de serialização e acesso concorrente, mas não foi o que precisei neste caso.

O argumento para utilizar o Singleton agora, diferentemente da simples conexão com o banco é que nosso objeto pode ser inserido num contexto OO completo, possuindo associações com outras classes, como o Carrinho possuir uma coleção de objetos Item, constituírem um atributo de outro objeto Compra e assim por diante.

Enfim, a idéia desse artigo não foi exatamente explicar o que é o Singleton, mas mostrar que o propósito dos padrões de projeto é atender às necessidades do desenvolvedor mostrando-o uma saída a um problema que ele já deve ter, e não forçá-lo a programar de uma maneira rígida. Não acho legal ver as pessoas querendo apenas adotar os padrões simplesmente como eles são, estejam eles adequados ou não à sua realidade.


Marcadores: , ,

sábado, novembro 11, 2006

Abstração de SGBDs - Estudo comparativo - Parte 2 Desempenho

Esse post é uma continuação do anterior http://www.especializa.com.br/berardo/2006/11/abstrao-de-sgbds-estudo-comparativo.html. Para saber mais sobre as bibliotecas, visite o link acima.

Estudo comparativo

A partir de agora, apresentaremos um estudo comparativo entre as sete bibliotecas citadas. Não é nosso intuito procurar uma resposta definitiva sobre qual é a melhor, você vai perceber que nos três quesitos que avaliaremos (desempenho, facilidade e portabilidade) nenhuma conseguiu se sobressair completamente. Vale ressaltar aqui que este é um estudo bem inicial, servindo apenas para fomentar a pesquisa antes de tecer qualquer julgamento sobre alguma delas. Sem contar que muitos outros fatores importantes não estão sendo avaliados.

Desempenho
Este certamente é um fator da mais alta importância. Inspirado no blog de Joseph Scott (http://joseph.randomnetworks.com/), que trouxe um comparativo preliminar apenas com três destas bibliotecas, nosso estudo também é inicial.
Avaliaremos o tempo de processamento do PHP para consultar uma base de dados PostgreSQL de cadastro de atores de cinema. Nela, há 542 atores cadastrados e faremos uma consulta para listar seus nomes em ordem alfabética. Primeiramente, não exibiremos seus nomes e colhemos o resultado, depois exibiremos e também coletaremos o resultado. Uma consulta com essa quantidade de registros não é grande o suficiente, portanto, a repetiremos num laço 250 vezes.
Tomaremos como base um simples script procedural com acesso diretamente através das funções nativas do PostgreSQL. Confira no código abaixo:

Código 1 – Benchmark – PostgreSQL direto
1. 2. $time_start = microtime(true);
3. $db = pg_connect("host=localhost dbname=locadora " .
4. "user=postgres password=postgres");
5.
6. $runs = 250;
7. for($i = 0; $i < $runs; $i++) {
8. $sql = "select nome from atores order by nome";
9. $rs = pg_query($db, $sql);
10. while($row = pg_fetch_row($rs)) {
11. echo $row[0];
12. }
13. }
14.
15. $time_end = microtime(true);
16. $time = $time_end - $time_start;
17. print("\n\nTEMPO: {$time}\n");
18. ?>


O primeiro passo foi recuperar o timestamp atual (em $time_start). Depois conectamos ao banco PostgreSQL. A linha que nos interessa é a 11. Nela, exibiremos o resultado carregado em $row. Ao final das 250 consultas idênticas, recuperaremos o timestamp final e exibiremos a diferença.
Os testes foram realizados primeiramente numa máquina modesta. Foram feitas chamadas ao script diretamente da linha de comando (# php teste1.php), ou seja, sem requerer processamento do Apache e envio da resposta ao browser. Invocamos a execução do script omitindo a linha 11, que imprime os nomes. Apesar de não percebermos variações significativas de resultado, colhemos o valor do tempo mais baixo. Depois executamos mais cinco vezes com a linha omitida anteriormente e também colhemos o resultado mais baixo. Para tornar o teste mais real, efetuamos o mesmo procedimento em outra máquina um pouco mais potente. Confira suas configurações:
Máquina 1: Intel Pentium III, 256Mb RAM, SO Linux Slackware 10.2. PHP 5.1.6
Máquina 2: AMD Sempron 64 2800, 512Mb RAM, SO Linux Slackware 10.2. PHP 5.1.6. Confira os resultados na tabela abaixo (valores arredondados em segundos):

Tabela 1 – Benchmark – Resultados PostgreSQL direto


Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560




Agora veja como faríamos na primeira biblioteca citada, Metabase:

Código 2 – Benchmark – Metabase
1. 2. require("metabase/metabase_interface.php");
3. require("metabase/metabase_database.php");
4. ini_set('include_path',
5. ini_get('include_path').':'.
6. dirname(__FILE__).'/metabase');
7. $time_start = microtime(true);
8.
9. MetabaseSetupDatabaseObject(array(
10. "Type" => "pgsql",
11. "User" => "postgres",
12. "Password" => "postgres"),
13. $conn);
14. MetabaseSetDatabase($conn->database, "locadora");
15.
16. $runs = 250;
17. for($i = 0; $i < $runs; $i++) {
18. $sql = "select nome from atores order by nome";
19. $rs = $conn->Query($sql);
20. $rows = $conn->NumberOfRows($rs);
21. for ($x=0; $x<$rows; $x++) {
22. echo $conn->FetchResult($rs, $x, 0);
23. }
24. }
25.
26. $time_end = microtime(true);
27. $time = $time_end - $time_start;
28. print("\n\nTEMPO: {$time}\n");
29. ?>


Aproveitando para explicar o código, inicialmente, incluímos os arquivos necessários e adicionamos o diretório da biblioteca Metabase extraída (sob o próprio diretório dos testes) na include_path. Só a partir daí o script começou a contar o tempo. A função MetabaseSetupDatabaseObject estabeleceu a conexão com o servidor passado como parâmetro e MetabaseSetDatabase definiu a base de dados . Objeto $conn efetuou a consulta ($conn->Query()) e um recuperou o total de linhas ($conn->NumberOfRows()). Dentro do laço, cada linha foi recuperada através de $conn->FetchResult(). No primeiro teste, a linha 22 não foi totalmente omitida. Se fosse, nenhum dado seria carregado na memória, visto que o laço é um simples for de zero até o total de linhas consultadas. O que fizemos foi testar a linha 22 da seguinte forma:

$resultado = $conn->FetchResult($rs, $x, 0);

Trouxemos o resultado para uma variável em vez de imprimir direto. No segundo teste, a linha 22 ficou como está lá. Veja o resultado. Mantivemos os valores do PostgreSQL direto para efeito comparativo. A última linha mostra quanto em valores percentuais os novos tempos excederam os primeiros (valores arredondados):


Tabela 2 – Benchmark – Resultados Metabase

Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560
5,03319599,83236982,30786905,6365590
150,07%131,92%150,89%137,53%




Percebe-se que o PHP passou a sofrer um pouco para recuperar os mesmos dados. Se antes ele precisava de pouco mais de quatro segundos para efetuar uma operação, agora serão necessários mais do dobro. Dando seqüência aos testes, apresentaremos a ADOdb. Confira:


Código 3 – Benchmark ADOdb
1. 2. require_once("adodb/adodb.inc.php");
3. $time_start = microtime(true);
4.
5. $conn = NewADOConnection(
6. "pgsql://postgres:postgres@localhost/locadora");
7. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
8. $runs = 250;
9. for($i = 0; $i < $runs; $i++) {
10. $sql = "select nome from atores order by nome";
11. $rs = $conn->Execute($sql);
12. while(!$rs->EOF) {
13. echo $rs->fields[0];
14. $rs->MoveNext();
15. }
16. }
17.
18. $time_end = microtime(true);
19. $time = $time_end - $time_start;
20. print("\n\nTEMPO: {$time}\n");
21. ?>

Assim como na Metabase, fizemos o download da ADOdb e extraímos num diretório abaixo do nosso teste. Agora, apenas o arquivo adodb.inc.php foi necessário. Não precisamos adicionar este subdiretório da biblioteca na include_path.
A função NewADOConnection() entregou a $conn o objeto que utilizaremos para efetuar a consulta. Neste momento, também foi mais fácil passar os parâmetros necessário para ela saber qual banco conectar. Parâmetros na forma de uma única URL, no lugar de uma array de parâmetros e um método adicional para escolher a base, simplifica bem as coisas.
A variável definida na linha 8 foi necessária para informar à biblioteca que traga apenas arrays indexados, já que o padrão é trazer arrays com valores indexados e associativos (algo como MYSQL_BOTH que você já conhece).
Na linha 12, a instrução $conn->Execute() efetuou a consulta e devolveu o identificador do resultado para $rs. O atributo $rs->EOF (end of file) será verdadeiro quando o ponteiro de leitura do resultado alcançar o final do resultado. Já o atributo $rs->fields é um array (apenas indexado devido à variável $ADODB_FETCH_MODE ser ADODB_FETCH_NUM) com os campos da consulta.
A linha 14 foi comentada no primeiro teste e recolocada no segundo. O método $rs->MoveNext() foi o responsável para avançar ao próximo registro. Sem ele, entraríamos em loop infinito.
Da mesma maneira que fizemos com a Metabase, confira os resultados:

Tabela 3 – Benchmark – Resultados ADOdb

Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560
5,189945910,74717402,23551396,1193878
157,86%153,50%143,03%157,88%



Note que o resultado final foi ligeiramente mais lento do que a Metabase, chegando ser mais rápida apenas no terceiro teste. Podemos concluir que elas tiveram tecnicamente o mesmo desempenho, com vitória da Metabase no critério de desempate =D.
A próxima a ser testada será a extensão DBX. Confira seu código:

Código 4 – Benchmark DBX
1. 2. $time_start = microtime(true);
3.
4. $conn = dbx_connect("pgsql", "localhost",
5. "locadora", "postgres", "postgres");
6. $runs = 250;
7. for($i = 0; $i < $runs; $i++) {
8. $sql = "select nome from atores order by nome";
9. $rs = dbx_query($conn, $sql, DBX_RESULT_INDEX);
10. for ($x = 0; $x < $rs->rows; $x++) {
11. echo $rs->data[$x][0];
12. }
13. }
14. $time_end = microtime(true);
15. $time = $time_end - $time_start;
16. print("\n\nTEMPO: {$time}\n");
17. ?>


Por se tratar de uma extensão, não foi necessário incluir arquivo algum ao script. A função dbx_connect(), conectou ao nosso banco recebendo as informações cada uma como um parâmetro (banco, host, base, usuário e senha). A consulta ficou a cargo da função dbx_query(), que assim como a pg_query(), recebeu no primeiro argumento, a variável que representa a conexão e no segundo a instrução SQL. O terceiro, DBX_RESULT_INDEX, foi para informar que o resultado será recuperado num array apenas indexado. O mesmo que fizemos com a ADOdb.
Após a consulta, a variável $rs recebeu um objeto de stdClass com alguns atributos populados. $rs->rows corresponde ao total de linhas da consulta. Em $rs->data todo o conteúdo foi lançado como uma matriz. O que fizemos foi varrer essa matriz da maneira mais eficiente (através de um laço for - utilizar um foreach nesse caso degradaria o desempenho desnecessariamente). A linha 11 foi simplesmente comentada no primeiro teste e refeita no segundo. Confira o resultado:

Tabela 4 – Benchmark – Resultados DBX


Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560
4,79552419,35048991,95202514,8769801
138,26%120,26%112,21%105,52%



Houve um pequeno ganho de desempenho. Uma coisa interessante de se notar é a queda considerável de percentual nos testes com impressão do resultado. A explicação é bem simples. A DBX carregou os dados numa matriz, num processo semelhante à pg_fetch_row() que recuperou o resultado num array a cada iteração do laço. O ato de leitura em ambas foi apenas imprimir a posição de um array indexado. Assim a ação de exibição dos dados na tela levou o mesmo tempo em ambas. Em suma, a diferença está exclusivamente no momento de envio da consulta ao SGBD e recuperação do resultado em variável.

A próxima da lista é a PEAR::DB. Acompanhe:

Código 5 – Benchmark PEAR::DB
1. 2. require_once("PEAR.php");
3. require_once("DB.php");
4. $time_start = microtime(true);
5.
6. $conn = DB::Connect(
7. "pgsql://postgres:postgres@localhost/locadora");
8. $runs = 250;
9. for($i = 0; $i < $runs; $i++) {
10. $sql = "select nome from atores order by nome";
11. $rs = $conn->query($sql);
12. while($row = $rs->fetchRow()) {
13. echo $row[0];
14. }
15. }
16.
17. $time_end = microtime(true);
18. $time = $time_end - $time_start;
19. print("\n\nTEMPO: {$time}\n");
20. ?>


Incluímos, no início, os arquivos necessários. PEAR.php para todo e qualquer biblioteca do PEAR (ela seria incluída indiretamente se não fizéssemos, como nosso foco é o desempenho apenas da consulta, a incluímos logo de cara, antes de zerar os cronômetros) e DB.php específico para a PEAR::DB.
A conexão foi efetuada pela instrução DB::Connect(). DB é uma classe utilitária, seu método estático Connect() retorna um objeto do tipo DB_common. Esta última é uma classe extendida pelas classes particulares de cada SGBD suportado. Assim, no nosso caso, a variável $conn recebeu uma instância de DB_ pgsql (que herda de DB_common) devido à informação do protocolo (pgsql://) na URL de conexão.
A consulta se deu através do método $conn->query(), declarado em DB_common e redefinido em cada uma de suas subclasses. Num processo semelhante ao do PostgreSQL direto, fizemos um while onde uma variável $row recebeu o resultado do método $rs->fetchRow(), que por padrão já retorna um array indexado.
Comentando e descomentando a linha 13, obtivemos os seguintes resultados:

Tabela 5 – Benchmark – Resultados PEAR::DB


Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560
7,681608016,50980813,48861919,4957421
281,66%289,43%279,25%300,17%



Repetimos esses testes exaustivamente por simplesmente não acreditarmos que o resultado pudesse ter sido tão ruim. O mais estranho foi o aumento percentual nos testes com impressão do resultado. É como se houvesse uma alocação excessiva de memória, suficiente até para que uma simples exibição de dados nas variáveis também se tornasse uma operação mais custosa do que no diretamente nas funções de acesso ao PostgreSQL.
Com a sua sucessora PEAR::MDB2 o resultado curiosamente só piorou. Veja:

Código 6 – Benchmark PEAR::MDB2
1. 2. require_once("PEAR.php");
3. require_once("MDB2.php");
4. $time_start = microtime(true);
5.
6. $conn = MDB2::Connect(
7. "pgsql://postgres:postgres@localhost/locadora");
8. $runs = 250;
9. for($i = 0; $i < $runs; $i++) {
10. $sql = "select nome from atores order by nome";
11. $rs = $conn->query($sql);
12. while($row = $rs->fetchRow()) {
13. echo $row[0];
14. }
15. }
16.
17. $time_end = microtime(true);
18. $time = $time_end - $time_start;
19. print("\n\nTEMPO: {$time}\n");
20. ?>


Note que o script foi praticamente o mesmo. Os métodos de $conn e $rs não mudaram. Confira o resultado:

Tabela 6 – Benchmark – Resultados PEAR::MDB2

Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560
9,353699920,75852394,259263011,3491249
364,74%389,64%363,03%378,27%


Tornamos a falar que nosso teste não é conclusivo. Haveria uma série de outras coisas a se avaliar. No entanto, numa operação bastante corriqueira em qualquer sistema (simples listagem de dados de uma base), a diferença entre as demais soluções e as do PEAR foram expressivas.
A próxima biblioteca analisada foi a Creole, veja como foi:

Código 7 – Benchmark Creole
1. 2. require_once("creole/Creole.php");
3. $time_start = microtime(true);
4.
5. $conn = Creole::getConnection(
6. "pgsql://postgres:postgres@localhost/locadora");
7. $stmt = $conn->createStatement();
8. $runs = 250;
9. for($i = 0; $i < $runs; $i++) {
10. $sql = "select nome from atores order by nome";
11. $rs = $stmt->executeQuery($sql, ResultSet::FETCHMODE_NUM);
12. while ($rs->next()) {
13. echo $rs->getString(1);
14. }
15. }
16.
17. $time_end = microtime(true);
18. $time = $time_end - $time_start;
19. print("\n\nTEMPO: {$time}\n");
20. ?>

Assim como em Java, $conn é uma instância de uma classe que implementa a interface Connection (no JDBC seria java.sql.Connection), no caso, PgSQLConnection. A partir de $conn, obtivemos um objeto Statement (PgSQLStatement) que armazenamos em $stmt. Objetos são os responsável por efetuar as consultas.
O método $stmt->executeQuery() recuperou um objeto ResultSet, de onde iremos colher as informações. Seu método $rs->next() avança o ponteiro e retorna verdadeiro se houver próximo registro, falso caso contrário.
Seguir à risca a sintaxe do JDBC não rendeu tão bem, se você trocar o laço while das linhas 12 a 14 pelo laço abaixo, terá um resultado mais eficiente:

foreach ($rs as $row) {
echo $row[0];
}

A variável $rs é do tipo PgSQLResultSet, que por sua vez, implementa a interface ResultSet, como seria em Java. O que a Creole fez de interessante foi declarar que ResultSet herda da interface SPL IteratorAggregate. Assim, PgSQLResultSet implementou os métodos homônimos ao ResultSet do JDBC, mais o método getIterator(). No caso da PgSQLResult, o método retorna uma instância de PgSQLResultSetIterator. Assim, foi possível passar $rs no foreach, o PHP já vai saber que deve chamar os métodos de PgSQLResultSetIterator para recuperar cada valor colocado em $row. Finalmente, $row será apenas um array indexado, devido ao parâmetro ResultSet::FETCHMODE_NUM passado na consulta.
A tabela abaixo mostra os resultados das duas metodologias:

Tabela 7 – Benchmark – Resultados Creole

Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560
Método JDBC
3,995573011,82725591,72740607,6208939
98,52%178,98%87,79%221,16%
Método SPL
3,62128717,45382501,57320124,2920520
79,92%75,85%71,03%80,87%


O formato SPL garantiu um melhor desempenho em todos os testes, com destaque para o momento em que exibimos o resultado (Com impressão) que, no método JDBC foi bem mais lento em termos percentuais e no SPL chegou a ser mais rápido na máquina 1 e pouco mais lento na máquina 2. Afinal exibir o valor de um array é mais rápido do que chamar um método que pesquisará por esse valor no resultado da consulta.

A última biblioteca testada foi a PDO, confira:

Código 8 – Benchmark PDO
1. 2. $conn = new PDO("pgsql:host=localhost dbname=locadora " .
3. "user=postgres password=postgres");
4. $runs = 250;
5. for($i = 0; $i < $runs; $i++) {
6. $sql = "select nome from atores order by nome";
7. foreach($conn->query($sql, PDO::FETCH_NUM) as $row) {
8. echo $row[0];
9. }
10. }
11.
12. $time_end = microtime(true);
13. $time = $time_end - $time_start;
14. print("\n\nTEMPO: {$time}\n");
15. ?>


A conexão com o SGBD se dá através da instância de um objeto da classe PDO. Passamos como parâmetro, a mesma string que havíamos passado no pg_connect(), adicionando apenas o rótulo pgsql:. Num exemplo de desatenção à portabilidade, a conexão PDO com o MySQL, não seria desta forma, mas de acordo com a sintaxe da função mysql_connect(), ou seja, receberia três parâmetros, como abaixo:

$conn = new PDO("mysql:host=localhost;dbname=locadora", "root", "");

Nesse caso, as diferenças para a mysql_connect() são apenas o rótulo mysql: e a informação da base de dados, no lugar da chamada à infame mysql_select_db().
Como você deve saber que conexões com o banco devem ser declaradas em um único local para serem incluídas quando necessárias (afinal, trocar de banco só em casos especiais, mas a troca de servidor, usuário ou senha é um procedimento bem comum), a diferença nessa declaração de um SGBD para outro não lhe traria maiores problemas.
A consulta foi feita através da instrução $conn->query() que recebeu a intrução SQL como parâmetro e retornou um objeto do tipo PDOStatement, uma classe da API da PDO que implementa a interface SPL IteratorAggregate, por isso, é transpassável por um foreach como fosse um array. Devido ao segundo parâmetro PDO::FETCH_NUM, o resultado de cada iteração do laço será um array indexado.
O resultado final foi surpreendente. Apesar de já esperarmos que um bom desempenho do PDO por ser uma extensão escrita em C, a boa implementação da SPL proporcionou o melhor resultado, longe dos demais. Observe:

Tabela 8 – Benchmark – Resultados PDO

Máquina 1 - Sem impressãoMáquina 1 - Com impressãoMáquina 2 - Sem impressãoMáquina 2 - Com impressão
2,012687924,23952190,91986592,3729560
2,14941794,44300500,91163801,9495799
6,79%4,80%-0,89%-17,84%



Em todos os testes, ela andou bastante próxima das chamadas nativas. E por mais estranho que possa parecer, chegou a superá-las na segunda máquina. Não pense que foi um resultado isolado, durante os testes nesta máquina, a PDO andou regularmente abaixo do PostgreSQL direto.

As grades abaixo mostram o acumulado de resultados catalogados aqui, com as bibliotecas na ordem da média do percentual de tempo:

Tabela 9 – Benchmark – Grade Máquina 1


AcessoSem impressão (s)Com impressão (s)Sem impressão %Com impressão %
PostgreSQL2,012687921524,239521980290,00%0,00%
PDO2,14941787724,443005084996,79%4,80%
Creole SPL3,621287107477,4538249969579,92%75,82%
DBX4,795524120339,35048985481138,26%120,56%
Creole JDBC3,9955730438211,827255964398,52%178,98%
Metabase5,033195972449,83236980438150,07%131,92%
ADOdb5,189945936210,7471740246157,86%153,50%
DB7,6816079616516,5098080635281,66%289,43%
MDB29,3536999225620,758523941364,74%389,64%



Tabela 10 – Benchmark – Grade Máquina 2

AcessoSem impressão (s)Com impressão (s)Sem impressão %Com impressão %
PDO0,9116380214691,94957995415-0,89%-17,84%
PostgreSQL0,9198658466342,372956037520,00%0,00%
Creole SPL1,57320117954,2920520305671,03%80,07%
DBX1,952025175094,8769800663112,21%105,52%
Metabase2,307868957525,63655900955150,89%137,53%
ADOdb2,235513925556,11938786507143,03%157,88%
Creole JDBC1,727406024937,6208939552387,79%221,16%
DB3,488619089139,4957420826279,25%300,17%
MDB24,2592630386411,3491249084363,03%378,27%



Na média geral dos percentuais de tempo, o resultado foi:

Tabela 11 – Grade final – Média percentual dos quatro tempos

AcessoMédia percentual
PDO-1,79%
PostgreSQL0,00%
Creole SPL76,91%
DBX119,14%
Metabase142,61%
Creole JDBC146,61%
ADOdb153,07%
PEAR::DB287,63%
PEAR::MDB2373,92%




No próximo post, entraremos nos próximos quesitos do estudo: facilidade e portabilidade.