Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Marcos Vilela Alves

foreach demorando muito

Recommended Posts

Boa tarde. estou com um foreach que está demorando muito pra terminar a execução, e nao está retornando erro nenhum.

 

eu faco uma busca na base de dados. a busca esta correta e demora pouco mais de um segundo pra ser executada. O problema é que ela retorna muitas linhas. mas de 2500 linhas.

 

tudo bem.. quando eu percorro o resultado da busca para criar um arquivo txt está demorando muito, e por essa razao, o servidor está interrompendo a execução.

 

minha dúvida, é porque bem acima dessa busca eu crio um outro arquivo parecido com um resultado de mais de 7200 linhas, e essa criacao nao demora nem 20 segundos. e nessa execução bem sucedida, eu tenho muitas outras funcoes e ifs que sao executados. tecnicamente, era pra dar problema nessa nao?

 

segue o foreach que nao está sendo executado.

 

 
/* Coloca os dados de cada fatura em cada linha do arquivo. */
        $linha_mestre_fiscal = "";
        
        $conta_canceladas = $total_nota = $total_bc_icms = $total_icms = 0;
        $primeira_emissao = $ultima_emissao = $primeiro_numero = $ultimo_numero = "";
 
        
        $classe_consumo = $this->config->item("classe_consumo"); //Classe de consumo ou tipo de assinante
        $fase = $this->config->item("fase"); //Fase ou tipo de utilização
        $grupo_tensao = $this->config->item("grupo_tensao");
        unset($coluna);
        foreach ($faturas as $linha_mestre => $coluna) {
 
            $isentos = "000000000000";
 
            //define situacao
            if ($coluna['flg_estorno'] == "1" || $coluna['flg_origem_pag'] == '2') {
                $situacao = 'S';
                $conta_canceladas++;
            } else if ($coluna['flg_tipo_movimentacao'] == "R"){
                $situacao = "R";}
            else{
                $situacao = "N";}
 
 
            //tira o . do código fisco
            $cod_fisco = $coluna['cod_fisco'];
            
            $linha_mestre_fiscal .= $coulna['num_cpf_cnpj']; //CNPJ ou CPF
            $linha_mestre_fiscal .= $coulna['num_insc_est']; //IE
            $linha_mestre_fiscal .= $coluna['nom_cliente']; //Razão social
            $linha_mestre_fiscal .= "MG"; //UF
            $linha_mestre_fiscal .= $classe_consumo; //Classe de consumo ou tipo de assinante
            $linha_mestre_fiscal .= $fase; //Fase ou tipo de utilização
            $linha_mestre_fiscal .= $grupo_tensao; //grupo de tensao 00
            
            $linha_mestre_fiscal .= $coluna['han_cliente']; //código de dentificação do consumidor ou assinante
            $linha_mestre_fiscal .= $coluna['dat_emissao']; //data de emissão
            $linha_mestre_fiscal .= $dadosfiscais['modelo_nota']; //modelo da nota 21
            $linha_mestre_fiscal .= $dadosfiscais['serie']; //serie
            $linha_mestre_fiscal .= $coluna['num_fatura']; //Número da nota;
            $linha_mestre_fiscal .= $cod_fisco; //Código fisco
            $linha_mestre_fiscal .= $coluna['val_fatura'];//$valor_nota; //Valor total
            $linha_mestre_fiscal .= $coluna['val_fatura'];//$valor_nota; //BC ICMS
            $linha_mestre_fiscal .= $isentos; //ICMS
            $linha_mestre_fiscal .= $isentos; //Operações isentas ou não tributadas
            $linha_mestre_fiscal .= $isentos; //Outros valores
            $linha_mestre_fiscal .= $situacao; //situação do documento S, R ou N
            $linha_mestre_fiscal .= $coluna['ano_mes_ref']; //Ano e mês de referência de apuração
            $linha_mestre_fiscal .= $item_fatura[$coluna['num_fatura']]; //aguardando para saber o que colocar
            $linha_mestre_fiscal .= $coluna['num_telefone'];
            $linha_mestre_fiscal .= "   ";
 
            /*$md5_total = md5($linha_mestre_fiscal);
            $linha_mestre_fiscal .= $md5_total;*/
 
            $linha_mestre_fiscal .="\r\n";
            
        }

 

depois desse foreach eu crio o arquivo com as informacoes da variavel linha_mestre_fiscal, porem nao está sendo executado pq o processo esta morrendo dentro do for.

 

alguem saberia me dizer porque esse processo está tao demorado?

Compartilhar este post


Link para o post
Compartilhar em outros sites

As vezes a demora não é nem relacionada com a execução do script PHP em si, mas pode ser, também, referente a consulta no SGBD.

Tabelas com muito registros, sem os devidos cuidados, tendem a consumir mais recursos conforme a complexidade da consulta.

A casos que, consultas que retornam apenas dez registros em uma tabela de um milhão de registros são mais custos que mil registros em uma tabela de dez mil. Mas há casos contrários, aonde todos os cuidados necessários foram tomados, em que o resultado encontrado é o contrário do acima demonstrado.

Teste diretamente no SGBD, verifique a quantidade registros. Além disso, registre, no PHP, a cada passo, o tempo levado pela execução do script.

 

Por exemplo:

list($usec , $sec) = explode(' ' , microtime());
$inicio = (float)$sec + (float)$usec;

echo 'Script iniciado';

chamadaDeFuncao();

list($usec , $sec) = explode(' ' , microtime());
$tempoParcial = round(((float)$sec + (float)$usec) - $inicio, 5);

echo 'Tempo após chamadaDeFuncao() decorrido: '.$tempoParcial.' segundos';

foreach...

list($usec , $sec) = explode(' ' , microtime());
$tempoParcial = round(((float)$sec + (float)$usec) - $inicio, 5);

echo 'Tempo após foreach decorrido: '.$tempoParcial.' segundos';

Esse é um script antigo, mas com ele, poderá ter uma ideia.

 


Também procure sobre otimização de tabelas e criação de indexes. O MySQL cria automaticamente os indexes mais "importantes" por assim dizer, mas existem casos especiais. No próprio phpmyadmin existe a possibilidade de otimizar uma tabela.



MySQL Table Optimization

How MySQL Uses Indexes

 

Apesar das indexes no MySQL são serem tão poderosas quanto em outros SGBDs (PostgreSQL, por exemplo), lhe dará um ganho expressivo em velocidade.



Outro porém, pode ser o consumo de memória. Verifique nas diretivas a quantidade disponível de memória e o quanto seu script pode estar ocupando.

 

memory_get_peak_usage

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então Gabriel, eu ate fiz os testes na base.. a string nao é das mais complexas, pelo contrario, mais simples do que a primeira que é executada em segundos...

 

na base de dados minha string retorna os resultados em 0.062 segundos.. foi a maior marca que eu encontrei em 10 execuções.

 

nao cheguei a fazer um teste como vc mostrou ai por ser um foreach com muitas linhas.

 

mas acredito que o problema nao seja na string.

 

contudo, obrigado pela dica, e vou ver se melhoro a string sql. talvez haja ainda uma forma de otimizá-la.. mas se alguem tiver outras dicas por favor nao deixem de postar....

Compartilhar este post


Link para o post
Compartilhar em outros sites

É complicado apontarmos onde está o seu problema porque você postou só o fragmento do foreach. Você está assumindo um cenário em que o foreach é o vilão, mas dada a simplicidade do mesmo - e desnecessária, diga-se de passagem, ao que pude perceber - ele não é o maior culpado.

 

Tem de analisar o antes e o depois desse loop. O antes é a consulta do banco de dados não apenas como o Gabriel disse, mas também a forma como seu programa PHP se conecta ao SGBD ou ainda o quão complexa é sua estrutura para que a requisição feita pelo browser atinja esse programa (caso de alguns frameworks fominhas).

 

E tem de ver também o depois do loop. Gravação de arquivos mexe, obviamente, com o sistema de arquivos do sistema operacional. Por si só isso já é uma operação lenta e parte disso é culpa do Apache (em sendo esse o seu servidor).

 

Sem contar questões de hardware. Se você rodar um script num Pentium III com 128 MB de RAM e num IvyBridge com 8GB a diferença vai ser brutal.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Acho que, primeiro de tudo, deve saber exatamente aonde está ocorrendo o sistema. Sair atirando as "cegas", pode até funcionar, mas para alguém que já passou por algo parecido (eu no caso).

 

Entretanto, na época, eu sabia qual era o problema (uma view complexa, e como de praxe, fora do escopo inicial do projeto).

 

Sugiro ser paciente, nesse momento, e buscar primeiramente o problema, depois a solução.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E tem de ver também o depois do loop. Gravação de arquivos mexe, obviamente, com o sistema de arquivos do sistema operacional. Por si só isso já é uma operação lenta e parte disso é culpa do Apache (em sendo esse o seu servidor).

Bruno, realizei testes como o gabriel me sugeriu.

Marquei cada tempo antes e depois de determinados pontos para conseguir determinar a demora.. o retorno da pesquisa esta demorando menos de um segundo tambem no meu sistema.

 

por essa razao nao acredito ser a query o problema.

realmente a demora está a partir do momento que o processamento entra no foreach, antes de gravar o arquivo ele morre sozinho.

fui imprimindo uma contagem de linha em linha, e ele chegou apenas ate a linha 648 de 2600 que a pesquisa me retorna.

 

bom eu tambem acho que o problema nao seja simplesmente o foreach, dada a simplicidade do mesmo. porem nao sei mais o que pode ser, porque esta demorando mais de 5 minutos para criar a string texto de 450 linhas, e a anterior tem mais de 7000 linhas tem mais funcoes como por exemplo str_replace ou str_pad e demora manos de 30 segundos para ser executada e o arquivo gravado.

 

preciso muito melhorar essa execução mas nao sei o que pode ser.

 

acredito que encontrei o problema.

na linha

 

 

$linha_mestre_fiscal .= $item_fatura[$coluna['num_fatura']];

essa linha na verdade é referente ao foreach anterior.

primeiro eu crio um vetor com N posicoes. cada posicao tem como nome o numero da fatura, e guarda a posicao do item no arquivo anterior.

 

ai nesse foreach demorado, eu busco cada posicao de acordo com o numero da fatura que estou colocando na linha..

vou analisar com cuidado e volto para postar o resultado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Como você está gravando? Poste o código depois do foreach.

 

Um pequeno exemplo, ao usar file_put_contents() você pode ter uma ligeira queda de performance se usar fopen()/fwrite()/fclose()

Compartilhar este post


Link para o post
Compartilhar em outros sites

bom. por alguma coincidencia triste. ao remover a linha que citei a execução foi rapida. mas tres tentativas depois voltou a dar erro.

 

estou sim usando as tres funcoes seguidas fopen()/fwrite()/fclose() contudo, somente depois dos dois foreach, pra previnir que nao atrapalhe o loop. mesmo assim demora... mesmo que eu retire essas funcoes continua dando erro. ate porque a execução morre antes de terminar o foreach

Compartilhar este post


Link para o post
Compartilhar em outros sites

Experimente simplificar esse loop. Faça a consulta retornar os dados já na ordem que você está definindo, mesmo que você tenha de inverter alguns campos no teu SQL.

 

Assim você evita todas essas concatenações pois poderia simplesmente implodir o array obrtido.

 

Não é garantido, mas como não temos nada mais palpável...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Existe nos comentários do manual do PHP um teste onde a concatenação se torna mais lenta que o processamento das aspas duplas. Pois o interpretador do PHP acaba repetindo instruções em longas concatenações. Poderá ver alguns exemplos aqui:

http://forum.imasters.com.br/topic/469839-porque-usar-sprintf/#entry1863901

 

Como o Bruno já sugeriu, você pode utilizar meios "alterantivos", digamos assim. Como o implode, processamento de variáveis em aspas duplas ou até a concatenação na própria query, através do pipe duplo "||".

 

Realize alguns testes. Como já foi comentado anteriormente, não sabemos a causa da lentidão, são apenas palpites.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você pode utilizar do EXPLAIN EXTENDED para verificar o plano de execução da consulta.

 

:seta: http://dev.mysql.com/doc/refman/5.0/en/explain-extended.html

 

 

Ao invés de utilizar aspas duplas para tratar strings, utilize o apóstrofos, deste modo o PHP terá certeza de que o caso passado é uma string, e não precisará de executar rotina de buscar por variáveis, etc, dentro das aspas, é quase insignificante o ganho de tempo, mais que faz muita diferença em conjunto.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Algo simples que pode estar estorvando a execução é a resolução de nomes DNS do MySQL.

 

Experimente desativá-la ou defina o host diretamente pelo IP

 

exemplo:

host: localhost

troque para

host: 127.0.0.1

 

 

Mas independente disso, desenvolva técnicas de cache.

Se as consultas provém de dados estáticos, por exemplo, gere cache desses dados em FS ou mesmo em tabelas auxiliares.

Isso custa muito tempo para desenvolver.. é trabalhoso, mas vale a pena.

 

 

FS -> File System

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não cheguei a modificar o nome do servidor no arquivo.

testei direto no online e lah é executado em poucos segundos. apesar de nao ser recomendado estou realizando os testes online.

 

mas muitooo obrigado pela dica.. tentarei mudar par o ip do servidor pra saber se melhora.

obrigado a todos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

bom coloquei o ip e tbm nao resolveu...

eu acredito ser a minha maquina mesmo.

deve ter alguma coisa sacaneando esse processo.

 

por essa razao estou tratando as informacoes no online.. enquanto for soh a montagem dos arquivos di boa....

Compartilhar este post


Link para o post
Compartilhar em outros sites

na sua consulta tem distinct?

Compartilhar este post


Link para o post
Compartilhar em outros sites

talvez seja meio noob meu comentário mas eu estava com um problema em um while, demorava muito e às vezes até travava... coloquei um usleep de meio segundo dentro do laço e agora não trava mais e ficou mais rápido (parece, pelo menos)...

no meu caso específico era porque demorava muito para gerar a saída pois era do boletophp... mas quem sabe né :)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom guilherme, esse fim de semana, fiz esse negocio do usleep, e nao funcionou, como eu imaginei que houvesse alguma coisa na minha maquina sacaneando a execução da rotina, mandei formatar a maquina, e hoje com a maquina "limpinha" continua nao executando na minha maquina. no online funciona.

 

o que muito me desespera, é que cheguei numa etapa onde nao posso simplesmente realizar os testes na producao. pq terei que alterar algumas coisas que só poderei fazer se tudo estiver corretinho...

 

alguem mais tem alguma ideia?

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.