Duvida sobre Performance, existe outra forma de implementar?

3 respostas
K

Pessoal, estou fazendo um cógido que lê um arquivo onde cada linha é um registro, a classe principal quebra estes registros em listas de 2000 registros, onde para cada lista cria uma thread para a classe que vai processar registro a registro da lista.

A minha dúvida é se tem algo que eu possa estar fazendo para melhorar a performance?
E o consumo de memória, estou fazendo algo que o prejudique ?
A forma que estou implementando está legal ?

segue o código:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Processo {
	public static BufferedWriter writer;
	public static int qtTotalItens = 0;
	public static int qtTotalGravada = 0;

	public static void main(String[] args) throws IOException {
		
		//Recupera a variavel com o caminho do arquivo de leitura e instancia o leitor
		BufferedReader reader = new BufferedReader(new FileReader("c:/entrada.txt"));
       
		//Recupera a variavel com o caminho do arquivo de escrita e instancia o escritor
		writer = new BufferedWriter(new FileWriter("c:/saida.txt"));

		while(reader.ready()){
		//limita a quantidade de Threads
			if(Thread.activeCount()<=15){
				String[] listaRegistros = new String[2000];
				for (int i = 0; i < 2000; i++) {
					listaRegistros[i] = reader.readLine();
					if(!reader.ready()){
						break;
					}
				}
				//Instancia um thread para avaliação
				Thread thread = new Thread(new Efetua(listaRegistros));
	 			thread.start();
				//tive que forçar o gc, porque no unix estava estourando memory exception, acredito que tenha faltado memória para startar a thread do gc
				if((Runtime.getRuntime().totalMemory()/5)>Runtime.getRuntime().freeMemory()){
					System.out.println("garbage");
					System.gc(); 
				}
			}
		}
	}
	public static synchronized void writeOutputFile(String[] listaRetornos) throws IOException{

		//Faz a gravação dos registros encontrados
		for (int i = 0; i < listaRetornos.length; i++) {
			if(listaRetornos[i] != null){
				writer.write(listaRetornos[i]);
				writer.newLine();
				qtTotalGravada++;
			}
		}
		writer.flush();
        
		//Se ja gravou todos os itens fecha o arquivo
		if(qtTotalGravada == qtTotalItens){
			writer.close();
			return;
		}
	}
}

Segue a classe que é chamada pela thread, onde leio os registros da lista e realizo um processamento registro a registro. No final eu mando gravar um arquivo de saída pelo método synchronized da classe Processo

import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
import java.util.LinkedHashMap;
import java.util.Map;
import Util;

public class Efetua implements Runnable {

	private String[] listaRegistros;
	private String[] listaRetorno;

	public Efetua(String[] listaRegistros) {
		this.listaRegistros = listaRegistros;
		this.listaRetorno = new String[listaRegistros.length];
	}

	public void run() {
		for (int i = 0; i < listaRegistros.length; i++) {
			String bufferEntrada = listaRegistros[i];
			//Caso chegue no fim do array
			if (bufferEntrada == null) {
				break;
			}
			//Caso seja o registro de header
			if (bufferEntrada.charAt(0) == 'A') {
				continue;
			}
			//Caso seja o registro de trailer
			if (bufferEntrada.charAt(0) == 'Z') {
				Processo.qtTotalItens =
					Integer.parseInt(bufferEntrada.substring(1, 16));
				break;
			}
			//despreza o primeiro caracter que é um indicativo de registro.
			bufferEntrada = bufferEntrada.substring(1, bufferEntrada.length());

			//Executa o processamento das informações através de uma classe utilitária 
			//Este processo demora um pouco, por isto que a implementação foi feita utilizando threads.
			listaRetorno[i] = Util.processa(bufferEntrada);
			bufferEntrada = null;
		}
		//Grava resultado no aquivo passando a listagem de registros das avaliações
		try {
			Processo.writeOutputFile(listaRetorno);
			listaRegistros = null;
			listaRetorno = null;
		} catch (IOException e) { //Caso a escrita nao esteja OK lança uma mensagem
			System.out.println(
			Thread.currentThread().getName() + ":" + e.getMessage());
			System.exit(07);
		}
	}
}

[ ]'s

3 Respostas

K

Do jeito que está, consigo rodar com 1GB de memória para a VM, mas ao rodar com 512 tenho java.lang.OutOfMemoryError.

JVMST109: Insufficient space in Javaheap to satisfy allocation request java.lang.OutOfMemoryError
Estou usando um arquivo com 60.000 registros.
Aguém pode me ajudar?

fantomas

Oi kozak,

Vamos lá…

//Executa o processamento das informações através de uma classe utilitária //Este processo demora um pouco, por isto que a implementação foi feita utilizando threads. listaRetorno[i] = Util.processa(bufferEntrada);

  1. Eu entendi que vc quiz dizer que esse ponto é demorado (Util.processa(bomba), logo tem como posta-lo também?

  2. Vc está utilizando threads mas acho que isso não está lhe ajudando, porque vc tem pontos que estão sincronizados, 2 pelo menos; o ponto onde grava e o outro Util.processa(), além do que se vc está utilizando um máquina com um único processador acho que vc não está tendo processamento paralelo, logo vc está enchendo a memória de dados mas não está ganhando nada em processamento.

for (int i = 0; i < listaRegistros.length; i++) { String bufferEntrada = listaRegistros[i];
Tente transformar isso em:

for(String bufferEntrada : listaRegistros)
talvez a JVM possa te ajudar em algum momento.

4) Se o problema maior for memória talvez devesse eliminar a String[] listaRetorno e utilizar a própria String[] listaRegistros, sei que isto tem um custo...mas a questão é: economizar a memória, aumentar a velocitadade ou os 2?

5) Veja esse ponto:
if (bufferEntrada == null) {  
                 break;  
             }  
             //Caso seja o registro de header  
             if (bufferEntrada.charAt(0) == 'A') {  
                 continue;  
             }  
             //Caso seja o registro de trailer  
             if (bufferEntrada.charAt(0) == 'Z') {  
                 Processo.qtTotalItens =  
                     Integer.parseInt(bufferEntrada.substring(1, 16));  
                 break;  
             }
vc concorda em passar essa parte para simplesmente:
if (bufferEntrada.charAt(0) == 'Z') {  
                 Processo.qtTotalItens =  
                     Integer.parseInt(bufferEntrada.substring(1, 16));  

                  //despreza o primeiro caracter que é um indicativo de registro.  
                  bufferEntrada = bufferEntrada.substring(1, bufferEntrada.length());  
   
                   //Executa o processamento das informações através de uma classe utilitária   
                   //Este processo demora um pouco, por isto que a implementação foi feita utilizando threads.  
                      listaRetorno[i] = Util.processa(bufferEntrada);  
                     bufferEntrada = null; 
             }
Eu quiz eliminar os outros 2 ifs (tempo gasto na avaliação), o ganho não é muito mas no mínimo o código ficará mais legível.

Pelo que entendi acho que o grande vilão é o Util.processa(), esse cara tem que ser muuuuuuuuuito rápido fora isso não vejo mudanças de grande impacto.

Espero ter ajudado...

[]'s

K

Opa valeu fantomas, era este tipo de ajuda que estou precisando mesmo.
tenho como prioridad economizar a memória mas a otimização de tempo também é importante.

Então vamos lá:

1º) o método Util.processa(bomba), não é meu, e somente tenho acesso ao jar, infelizmente não posso postá-lo por motivos de força maior.

2º) não sei porque, mas fiz um teste sem as threads, e deu diferença no tempo de processamento, fiquei em dúvida quando isto.

3º)tentei utilizar o: for(String bufferEntrada : listaRegistros), mas não passou pelo compilador, minha JRE é a 1.4.02 creio que deve ser por isto.

4º)Eliminei a String[]listaRetorno e passei a utilizar a listaRegistros[i] conforme você falou, e te agradeço…rs… fez uma diferença n tempo de processamento.5º) Não entendi por que remover o if abaixo: pois se o primeiro caracter for A, eu tenho que desprezar o registro com continue.if (bufferEntrada.charAt(0) == 'A') { continue; }

5º)Alterei o substring abaixo e vi que ajudou um pouco também. bufferEntrada = bufferEntrada.substring(1, bufferEntrada.length()); //para bufferEntrada = bufferEntrada.substring(1);

Creio que é isto, fico feliz das suas dicas, pois só o ajuste da String[] já me rendeu 10 segundos no processamento em teste, imagino que com uma carga de stress possa me economizar alguns minutos.

[ ]'s

Criado 2 de outubro de 2008
Ultima resposta 3 de out. de 2008
Respostas 3
Participantes 2