Estou tendo problemas de performance ao percorrer os arquivos da maneira que segue adiante. Quando a busca termina ou eu clico em parar no programa, a próxima busca é muito mais rápida. A primeira busca demora mais de 1 minuto e da segunda em diante demora apenas uns 4 segundos. Isso ocorre pois boa parte da busca está na memória, ok eu entendo isso.
A minha dúvida seria como obter uma melhor performance na primeira varredura? Existe alguma técnica para isso? Eu poderia separar em várias threads (umas 5 por exemplo) ou isso não faria diferença? Afinal na primeira vez o problema não é processamento da CPU, e sim a leitura do HD que está limitando.
Obrigado pela ajuda desde já!
public void percorrerArquivos(String path)
{
File root = new File(path);
File[] list = root.listFiles();
if(list==null)
return;
for (File f : list)
{
if(parar) // o botão PARAR seta true na variavel "parar"
return;
if (f.isFile())
{
if(f.getAbsolutePath().endsWith(".jar"))
{
lerJar(f.getAbsolutePath());
}
}
else
{
percorrerArquivos(f.getAbsolutePath());
}
}
}
[quote=jweibe]Tenta fazer um filtro antes de carregar a sua lista de arquivo, isso evita carregar arquivos que não seja necessário.
File[] list = new File(path).listFiles(new FileFilter() {
public boolean accept(File pathname) {
return (pathname.isDirectory() || pathname.getName().endsWith("jar"));
}
});
O que seu método lerJar() faz?[/quote]
jweibe já implementei com um singleton (por causa da recursividade) a sua sugestão do FileFilter.
O usuário digitou uma string que seria o nome de um arquivo, por exemplo “pom.xml”, o método lerJar() vai percorrer os arquivos de dentro do jar para tentar encontrar este arquivo que o usuário digitou. Realmente eu concordo que isso que deixa muito lento, porém é necessário.
[quote=aloha]Estou tendo problemas de performance ao percorrer os arquivos da maneira que segue adiante. Quando a busca termina ou eu clico em parar no programa, a próxima busca é muito mais rápida. A primeira busca demora mais de 1 minuto e da segunda em diante demora apenas uns 4 segundos. Isso ocorre pois boa parte da busca está na memória, ok eu entendo isso.
A minha dúvida seria como obter uma melhor performance na primeira varredura? Existe alguma técnica para isso? Eu poderia separar em várias threads (umas 5 por exemplo) ou isso não faria diferença? Afinal na primeira vez o problema não é processamento da CPU, e sim a leitura do HD que está limitando.
Obrigado pela ajuda desde já!
[code]
public void percorrerArquivos(String path)
{
File root = new File(path);
File[] list = root.listFiles();
if(list==null)
return;
for (File f : list)
{
if(parar) // o botão PARAR seta true na variavel "parar"
return;
if (f.isFile())
{
if(f.getAbsolutePath().endsWith(".jar"))
{
lerJar(f.getAbsolutePath());
}
}
else
{
percorrerArquivos(f.getAbsolutePath());
}
}
}
[/code][/quote]
Algoritmos recursivos, embora sejam muito falados não são bons. Tente usar uma pilha (Queue)
A ideia é que vc pega a primeira pasta e coloca na fila, depois chama o método. O método irá fazer um while enquanto a pilha não é vazia.
Ele pegao primeiro cara da pilha e remove esse item da pilha. Ai ele processa esse item, colocando mais itens na pilha. O processo se repete até que ninguém ponha nada na pilha e/ou todos os itens da pilha tenham sido processados.
Depois que tiver este algoritmo, vc pode usar uma pilha com threads. Usando BlockingQueue. A ideia é que a pilha é partilhada pelas threads e cada thread processa o primeiro item da pilha.
sergiotaborda obrigado pela dica, esta implementação daria uma grande mudança em tudo que eu já fiz, vou estudar sobre esse mecanismo que você falou. Dar uma lida, antes de começar a programar. Valeu!
Taborda-san, nesse caso algoritmos recursivos são tão bons quanto os que têm pilhas (que forçam a implementação da recursividade pelo próprio programador, na verdade).
O que ocorre é o seguinte: dependendo do sistema operacional, File.listFiles() retorna arquivos e diretórios misturados em uma lista só, e pode ser que se percorrermos isso, vamos acabar saltando de um ponto para outro no disco (e é por isso que a primeira busca levou um tempão daqueles).
Eu recomendaria fazer o seguinte: ordenar a saída de File.listFiles() de forma que os arquivos fiquem primeiro e os diretórios fiquem depois, e ainda por cima em ordem alfabética.
Dependendo do sistema operacional e do filesystem, isso pode melhorar um pouco a velocidade do percorrimento dos diretórios e subdiretórios.
[quote=entanglement]Taborda-san, nesse caso algoritmos recursivos são tão bons quanto os que têm pilhas (que forçam a implementação da recursividade pelo próprio programador, na verdade).
O que ocorre é o seguinte: dependendo do sistema operacional, File.listFiles() retorna arquivos e diretórios misturados em uma lista só, e pode ser que se percorrermos isso, vamos acabar saltando de um ponto para outro no disco (e é por isso que a primeira busca levou um tempão daqueles).
[/quote]
Sim, pode existir uma dependência do OS no listFiles, mas o algoritmo recursivo é pior que o algoritmo com stack porque existe mais do stack do processador e incorre em stack overflow. É por isso que existem tecnicas como tail recursion e outras que se utilizam de truques para converter a recursividade em algoritmo de pilha. O Stack de chamadas de um metodo para si mesmo pode explodir, enquanto o Queue não pode. Além disso vc deixa de usar um recurso da linguagem para usar uma estrutura OO e é isso que lhe permite depois mudar o algoritmo para paralelo.
Os algoritmos recursivos têm um papel em programação funcional , mas não em OO em si. Em OO, vale mais usar a estrutura certa.
[quote=aloha][quote=sergiotaborda]
Algoritmos recursivos, embora sejam muito falados não são bons. Tente usar uma pilha (Queue)
A ideia é que vc pega a primeira pasta e coloca na fila, depois chama o método. O método irá fazer um while enquanto a pilha não é vazia.
Ele pegao primeiro cara da pilha e remove esse item da pilha. Ai ele processa esse item, colocando mais itens na pilha. O processo se repete até que ninguém ponha nada na pilha e/ou todos os itens da pilha tenham sido processados.
Depois que tiver este algoritmo, vc pode usar uma pilha com threads. Usando BlockingQueue. A ideia é que a pilha é partilhada pelas threads e cada thread processa o primeiro item da pilha.
[/quote]
sergiotaborda obrigado pela dica, esta implementação daria uma grande mudança em tudo que eu já fiz, vou estudar sobre esse mecanismo que você falou. Dar uma lida, antes de começar a programar. Valeu![/quote]
Veja que não muda assim tanto porque o método é o mesmo, vc só tem que mudar o miolo dele.
[quote=sergiotaborda]Sim, pode existir uma dependência do OS no listFiles, mas o algoritmo recursivo é pior que o algoritmo com stack porque existe mais do stack do processador e incorre em stack overflow. É por isso que existem tecnicas como tail recursion e outras que se utilizam de truques para converter a recursividade em algoritmo de pilha. O Stack de chamadas de um metodo para si mesmo pode explodir, enquanto o Queue não pode. Além disso vc deixa de usar um recurso da linguagem para usar uma estrutura OO e é isso que lhe permite depois mudar o algoritmo para paralelo.
Os algoritmos recursivos têm um papel em programação funcional , mas não em OO em si. Em OO, vale mais usar a estrutura certa.
[/quote]
Eu fiz uns testes e vi que tail recursion é algo que não tem como implementar em Java, eu digo, o compilador não vai otimizar o método recursivo para um loop, portanto a pilha de chamadas ainda cresce normalmente.
Se alguém conseguiu fazer, eu gostaria de saber como
[quote=Rodrigo Sasaki][quote=sergiotaborda]Sim, pode existir uma dependência do OS no listFiles, mas o algoritmo recursivo é pior que o algoritmo com stack porque existe mais do stack do processador e incorre em stack overflow. É por isso que existem tecnicas como tail recursion e outras que se utilizam de truques para converter a recursividade em algoritmo de pilha. O Stack de chamadas de um metodo para si mesmo pode explodir, enquanto o Queue não pode. Além disso vc deixa de usar um recurso da linguagem para usar uma estrutura OO e é isso que lhe permite depois mudar o algoritmo para paralelo.
Os algoritmos recursivos têm um papel em programação funcional , mas não em OO em si. Em OO, vale mais usar a estrutura certa.
[/quote]
Eu fiz uns testes e vi que tail recursion é algo que não tem como implementar em Java, eu digo, o compilador não vai otimizar o método recursivo para um loop, portanto a pilha de chamadas ainda cresce normalmente.
Se alguém conseguiu fazer, eu gostaria de saber como :)[/quote]
Em Linguagem java não tem como. Scala tem. O problema não é no byte code, ou seja a JVM suporta o conceito (porque na realidade é uma feature do compilador) é a linguagem java que não suporta (ainda, e provavelmente nunca).
Scala é uma linguagem funcional e para ela é muito importante suportar isso porque as chamadas recursivas são necessárias em orientação da função, como disse antes. Java ainda não tem esse paradigma ( e pelos vistos nunca terá) e por isso não precisa.
Mas o byte-code e JVM suportam o conceito e isso pode ser explorado por outras linguagens ( como o scala faz).