Otimização de métodos que retornam arrays em PHP

Artigo que explica como é possível otimizar métodos do PHP que retornam arrays muito grandes.

performance

Existem métodos que servem apenas para retornar arrays. Em alguns casos, estes arrays são muito grandes e ocupam muito espaço no próprio arquivo da classe onde o método se encontra. Arquivos muito grandes acarretam em uso de memória para carregá-los. Por este motivo pode ser útil considerar uma estratégia de otimização deste array, para que ele só seja carregado caso necessário (consulta sob demanda).

A alternativa simples é criar um outro arquivo PHP que apenas cria um array chamado $array, por exemplo, e este arquivo é incluído pelo método que retorna o array. Desta forma, o arquivo original (que tem o método) fica enchuto e o array só é carregado quando o método é invocado. Veja, abaixo, um exemplo de como era a classe antes e como ela ficou depois.

Antes:

<?php
// Arquivo teste.class.php
class teste {

    // Funcao que retorna um array grande
    public static function get_tipos() {
        return array(
            1 => 'Abacate',
            2 => 'Abacaxi',
            3 => 'Amora',
            ...
            100000 => 'Banana',
            100001 => 'Maçã',
            100002 => 'Pêra'
        );
    }

    // Outros metodos da classe "teste"
    ...
}

Depois:

<?php
// Arquivo teste.class.php
class teste {

    // Funcao que retorna um array grande
    public static function get_tipos() {
        include(dirname(__FILE__).'/array.php');
        return $array;
    }

    // Outros metodos da classe "teste"
    ...
}
<?php
// Arquivo array.php
$array = array(
    1 => 'Abacate',
    2 => 'Abacaxi',
    3 => 'Amora',
    ...
    100000 => 'Banana',
    100001 => 'Maçã',
    100002 => 'Pêra'
);

Uma solução híbrida, é criar um array estático dentro do método e preenchê-lo sob demanda. Desta forma, o array será carregado apenas na primeira vez que o método é executado e, nas demais, ele é apenas retornado. Veja o exemplo:

<?php
// Arquivo teste.class.php
class teste {

    // Funcao que retorna um array grande
    public static function get_tipos() {
        static $array = null;
        if ($array === null) {
            include(dirname(__FILE__).'/array.php');
        }
        return $array;
    }

    // Outros metodos da classe "teste"
    ...
}

Fazendo um teste com um array de 1000 posições com valores aleatórios, obtive o seguinte resultado:

Tabela comparativa de uso de uma classe com método otimizado e classe com método não otimizado
Chamadas ao método get_array Script Tempo (segundos) Memória (bytes)
0 não otimizado 0.00270 429.336
otimizado 0.00023 332.052
híbrido 0.00024 332.324
1 não otimizado 0.00385 514784
otimizado 0.00392 516.220
híbrido 0.00435 516.464
1000 não otimizado 0.20694 611.356
otimizado 1.11245 630.140
híbrido 0.14143 536.780

Ou seja, quando o método não é utilizado, o carregamento da classe otimizada e da classe híbrida é muito mais rápido que a classe não otimizada. Quando o método é utilizado uma única vez, as três classes têm performance muito próxima. Entretanto, quando o método é utilizado muitas vezes, a classe otimizada demora muito mais tempo para carregar o array, já a classe não otimizada e a classe híbrida obtem uma boa performance.

Portanto, leve em consideração a quantidade de vezes que o método será invocado em uma execução.

Note que a mesma ideia também vale para biblioteca de funções. Caso uma função retorne um array muito grande, você pode separar o array em outro arquivo.

2 comentários

Anônimo disse...

como voce faz para obter esses resultados de performance? é algum benchmark ou alguma artimanha com o proprio php?

rubS (autor do blog) disse...

É utilizando recursos do próprio PHP.
Para obter o tempo gasto, utilizei a função microtime.
Para obter a quantidade de memória, usei memory_get_usage.
Executei um script de teste algumas vezes e obtive a média (que é o valor apresentado).