Stack Overflow em Português Asked by nullptr on December 31, 2020
Estava lendo um artigo relacionado à performance das streams/loops, e me assustei com a diferença de performance entre a utilização de loops em relação à grandes quantidades de dados.
Resolvi realizar um teste rápido utilizando o seguinte código:
public static void main(String[] args) {
final int limite = 10_000;
long inicioFor = System.currentTimeMillis();
for(int i = 0; i < limite; i++) {
System.out.println(i);
}
long terminoFor = System.currentTimeMillis();
long inicioStream = System.currentTimeMillis();
IntStream.range(0, limite).forEach(System.out::println);
long terminoStream = System.currentTimeMillis();
System.out.println();
System.out.println("Usando for: " + (terminoFor - inicioFor) );
System.out.println("Usando stream: " + (terminoStream - inicioStream) );
}
É um código relativamente simples, porém notei uma coisa interessante:
limite
é definido com valor 10_000
, o tempo resultante é o seguinte:Usando for: 54
Usando stream: 72
limite
é definido com valor 1_000_000
, a stream é relativamente mais rápida:Usando for: 4314
Usando stream: 4202
Entendo que este é um teste simples, sem utilização de ferramentas de benchmark entre outras métricas.
Gostaria de saber o porquê da existência desta diferença de tempo no processamento utilizando streams e loops, posso não estar correto, mas parece que as streams performam melhor quanto maior a quantidade de dados (caminhando para a questão das streams infinitas).
Complementando com algumas informações colocadas pelo @Victor, tive resultados extremamente diferentes retirando as operações de IO:
limite
é definido com valor 10_000
:Usando for: 0
Usando stream: 36
limite
é definido com valor 1_000_000
:Usando for: 2
Usando stream: 43
Conforme apontado também por @Maniero, a razão se deve ser o alto custo para fazer a infraestrutura funcionar de acordo.
Acabei de responder sobre isto (O LINQ é o stream do C#). O stream é uma abstração, é uma camada a mais para executar, há um custo para fazer esta infraestrutura funcionar, há chamadas de função onde não deveria ocorrer no puro, há indireções, tudo isso tem custo, então ele precisa ser mais caro que a execução pura e simples de um laço. Está executando mais coisas, tem que ser mais lento.
O stream é um método que tem um laço dentro dele e que executa um método dentro dele de forma indireta, neste exemplo é o System.out::println
. Que por sinal não é uma boa forma porque ele é IO e o custo disso é tão alto que você está medindo algo como 98% do tempo o IO e não o mecanismo do laço ou do stream. Em algo que custa pouco a diferença é muito maior.
Até me impressiona a excelente otimização que o compilador Java faz, mas o teste onde dá o for
mais lento certamente pegou alguma interferência, não tem como ele ser mais lento. Certamente em grande volume o custo da infraestrutura pode não impactar tanto, mas não pode ser mais rápido. E por isso que dá uma discrepância entre o seu resultado e do Victor, o dele executa muitas vezes que diminui a influência da infra do stream.
Correct answer by Maniero on December 31, 2020
Bem, fiz esse teste:
import java.util.stream.LongStream;
class Teste {
public static void main(String[] args) {
var limite = 50_000_000_000L;
var x = new long[1];
var inicioStream1 = System.currentTimeMillis();
LongStream.range(0, limite).forEach(z -> x[0] = z);
var terminoStream1 = System.currentTimeMillis();
var inicioFor1 = System.currentTimeMillis();
for (var i = 0L; i < limite; i++) {
x[0] = i;
}
var terminoFor1 = System.currentTimeMillis();
var inicioStream2 = System.currentTimeMillis();
LongStream.range(0, limite).forEach(z -> x[0] = z);
var terminoStream2 = System.currentTimeMillis();
var inicioFor2 = System.currentTimeMillis();
for (var i = 0L; i < limite; i++) {
x[0] = i;
}
var terminoFor2 = System.currentTimeMillis();
System.out.println();
System.out.println("Usando for: " + (terminoFor1 - inicioFor1));
System.out.println("Usando stream: " + (terminoStream1 - inicioStream1));
System.out.println("Usando for outra vez: " + (terminoFor2 - inicioFor2));
System.out.println("Usando stream outra vez: " + (terminoStream2 - inicioStream2));
}
}
O resultado foi esse:
Usando for: 15169
Usando stream: 18422
Usando for outra vez: 15140
Usando stream outra vez: 18498
Quando você for fazer um bechmark de alguma coisa, lembre-se que o IO para o console é uma operação custosa. Isso significa que você nunca deve usar System.out.println
ou System.out::println
dentro do código que você está avaliando, porque ele vai interferir bastante com a performance daquilo que você está avaliando.
Acreditar que o System.out.println
tem um custo de desempenho zero ou desprezível é um erro comum.
Answered by Victor Stafusa on December 31, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP