Com o Java 8 e lambdas, é fácil iterar sobre coleções como fluxos e tão fácil quanto usar um fluxo paralelo. Dois exemplos dos documentos , o segundo usando parallelStream:
myShapesCollection.stream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
myShapesCollection.parallelStream() // <-- This one uses parallel
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
Desde que eu não me importe com a ordem, sempre seria benéfico usar o paralelo? Alguém poderia pensar que é mais rápido dividir o trabalho em mais núcleos.
Existem outras considerações? Quando o fluxo paralelo deve ser usado e quando o não paralelo deve ser usado?
(Esta pergunta é solicitada para iniciar uma discussão sobre como e quando usar fluxos paralelos, não porque eu acho que sempre usá-los seja uma boa idéia.)
fonte
Runnable
que eu chamostart()
para usá-los comoThreads
, está tudo bem mudar isso usando java 8 streams em um.forEach()
paralelo? Então eu seria capaz de retirar o código do segmento da classe. Mas existem algumas desvantagens?A API Stream foi projetada para facilitar a gravação de cálculos de uma maneira que foi abstraída da maneira como eles seriam executados, facilitando a alternância entre seqüencial e paralelo.
No entanto, só porque é fácil, não significa que é sempre uma boa ideia e, de fato, é uma má idéia simplesmente cair
.parallel()
por todo o lugar simplesmente porque você pode.Primeiro, observe que o paralelismo não oferece outros benefícios além da possibilidade de execução mais rápida quando houver mais núcleos disponíveis. Uma execução paralela sempre envolverá mais trabalho do que seqüencial, porque além de resolver o problema, ela também deve executar o envio e a coordenação de subtarefas. A esperança é que você consiga chegar à resposta mais rapidamente, dividindo o trabalho em vários processadores; se isso realmente acontece depende de muitas coisas, incluindo o tamanho do seu conjunto de dados, quanta computação você está fazendo em cada elemento, a natureza da computação (especificamente, o processamento de um elemento interage com o processamento de outros?) , o número de processadores disponíveis e o número de outras tarefas que competem por esses processadores.
Além disso, observe que o paralelismo também frequentemente expõe o não determinismo na computação que geralmente é oculta por implementações seqüenciais; às vezes isso não importa ou pode ser mitigado restringindo as operações envolvidas (ou seja, os operadores de redução devem ser apátridas e associativos).
Na realidade, às vezes o paralelismo acelera a computação, às vezes não, e às vezes até diminui a velocidade. É melhor desenvolver primeiro usando a execução sequencial e depois aplicar o paralelismo onde
(A) você sabe que há realmente benefícios em aumentar o desempenho e
(B) que realmente proporcionará um desempenho aprimorado.
(A) é um problema comercial, não técnico. Se você é um especialista em desempenho, geralmente poderá analisar o código e determinar (B), mas o caminho inteligente é medir. (E nem se preocupe até que você esteja convencido de (A); se o código for rápido o suficiente, melhor aplicar seu cérebro a outros lugares.)
O modelo de desempenho mais simples para paralelismo é o modelo "NQ", em que N é o número de elementos e Q é o cálculo por elemento. Em geral, você precisa que o NQ do produto exceda algum limite antes de começar a obter um benefício de desempenho. Para um problema de baixa Q, como "some números de 1 a N", geralmente você verá um ponto de equilíbrio entre N = 1000 e N = 10000. Com problemas com Q mais alto, você verá interrupções em limites mais baixos.
Mas a realidade é bastante complicada. Portanto, até que você atinja a perícia, primeiro identifique quando o processamento seqüencial realmente está lhe custando algo e depois avalie se o paralelismo ajudará.
fonte
findAny
vez defindFirst
...myListOfURLs.stream().map((url) -> downloadPage(url))...
).ForkJoinPool.commonPool()
e você não deseja que tarefas de bloqueio sejam executadas lá.Eu assisti a uma das apresentações de Brian Goetz (Java Language Architect e líder de especificação para Lambda Expressions) . Ele explica detalhadamente os quatro pontos a serem considerados antes de ir para a paralelização:
Custos de divisão / decomposição
- Às vezes, a divisão é mais cara do que apenas fazer o trabalho!
Custos de despacho / gerenciamento de tarefas
- pode fazer muito trabalho no tempo necessário para entregar o trabalho a outro encadeamento.
Custos da combinação de resultados
- Às vezes, a combinação envolve copiar muitos dados. Por exemplo, adicionar números é barato, enquanto a fusão de conjuntos é cara.
Localidade
- O elefante na sala. Este é um ponto importante que todos podem sentir falta. Você deve considerar falhas de cache, se uma CPU aguardar dados devido a falhas de cache, você não obterá nada por paralelização. É por isso que as fontes baseadas em array são paralelas às melhores, pois os próximos índices (próximos ao índice atual) são armazenados em cache e há menos chances de a CPU sofrer uma falha de cache.
Ele também menciona uma fórmula relativamente simples para determinar uma chance de aceleração paralela.
Modelo NQ :
onde,
N = número de itens de dados
Q = quantidade de trabalho por item
fonte
JB bateu na unha na cabeça. A única coisa que posso acrescentar é que o Java 8 não faz processamento paralelo puro, mas paraquencial . Sim, eu escrevi o artigo e faço F / J há trinta anos, então entendo a questão.
fonte
ArrayList
/HashMap
.Outras respostas já abordaram a criação de perfil para evitar otimização prematura e custos indiretos no processamento paralelo. Esta resposta explica a escolha ideal de estruturas de dados para streaming paralelo.
Fonte: Item # 48 Tenha cuidado ao criar fluxos paralelos e eficazes em Java 3e por Joshua Bloch
fonte
Nunca paralelize um fluxo infinito com um limite. Aqui está o que acontece:
Resultado
Mesmo se você usar
.limit(...)
Explicação aqui: Java 8, usando .parallel em um fluxo causa erro de OOM
Da mesma forma, não use paralelo se o fluxo for ordenado e tiver muito mais elementos do que você deseja processar, por exemplo
Isso pode demorar muito mais, porque os encadeamentos paralelos podem funcionar em vários intervalos de números, em vez do crucial, de 0 a 100, fazendo com que isso demore muito tempo.
fonte