uso adequado dos fluxos paralelos em Java

Lavish Kothari:

Eu estou experimentando com fluxos paralelos em Java e por isso eu tenho o seguinte código para o cálculo de número de primos antes n.

Basicamente eu estou tendo 2 métodos

  • calNumberOfPrimes(long n) - 4 variantes diferentes
  • isPrime(long n) - 2 variantes diferentes

Na verdade, eu estou tendo 2 variantes diferentes de cada um o método acima, uma variante que usa fluxos paralelos e outras variantes que não usam fluxos paralelos.

    // itself uses parallel stream and calls parallel variant isPrime
    private static long calNumberOfPrimesPP(long n) {
        return LongStream
                .rangeClosed(2, n)
                .parallel()
                .filter(i -> isPrimeParallel(i))
                .count();
    }

    // itself uses parallel stream and calls non-parallel variant isPrime
    private static long calNumberOfPrimesPNP(long n) {
        return LongStream
                .rangeClosed(2, n)
                .parallel()
                .filter(i -> isPrimeNonParallel(i))
                .count();
    }

    // itself uses non-parallel stream and calls parallel variant isPrime
    private static long calNumberOfPrimesNPP(long n) {
        return LongStream
                .rangeClosed(2, n)
                .filter(i -> isPrimeParallel(i))
                .count();
    }

    // itself uses non-parallel stream and calls non-parallel variant isPrime
    private static long calNumberOfPrimesNPNP(long n) {
        return LongStream
                .rangeClosed(2, n)
                .filter(i -> isPrimeNonParallel(i))
                .count();
    }
    // uses parallel stream
    private static boolean isPrimeParallel(long n) {
        return LongStream
                .rangeClosed(2, (long) Math.sqrt(n))
                .parallel()
                .noneMatch(i -> n % i == 0);
    }

    // uses non-parallel stream
    private static boolean isPrimeNonParallel(long n) {
        return LongStream
                .rangeClosed(2, (long) Math.sqrt(n))
                .noneMatch(i -> n % i == 0);
    }

Estou tentando razão para fora que, entre calNumberOfPrimesPP, calNumberOfPrimesPNP, calNumberOfPrimesNPPe calNumberOfPrimesNPNPé o melhor em termos de uso adequado dos fluxos paralelos com eficiência e por que ele é o melhor.

Tentei tempo todos estes 4 métodos em 50 vezes e tomou a média usando o seguinte código:

    public static void main(String[] args) throws Exception {
        int iterations = 50;
        int n = 1000000;
        double pp, pnp, npp, npnp;
        pp = pnp = npp = npnp = 0;
        for (int i = 0; i < iterations; i++) {
            Callable<Long> runner1 = () -> calNumberOfPrimesPP(n);
            Callable<Long> runner2 = () -> calNumberOfPrimesPNP(n);
            Callable<Long> runner3 = () -> calNumberOfPrimesNPP(n);
            Callable<Long> runner4 = () -> calNumberOfPrimesNPNP(n);

            pp += TimeIt.timeIt(runner1);
            pnp += TimeIt.timeIt(runner2);
            npp += TimeIt.timeIt(runner3);
            npnp += TimeIt.timeIt(runner4);
        }
        System.out.println("___________final results___________");
        System.out.println("avg PP = " + pp / iterations);
        System.out.println("avg PNP = " + pnp / iterations);
        System.out.println("avg NPP = " + npp / iterations);
        System.out.println("avg NPNP = " + npnp / iterations);
    }

TimeIt.timeItsimplesmente retorna o tempo de execução em mili-segundos. Eu tenho o seguinte resultado:

___________final results___________
avg PP = 2364.51336366
avg PNP = 265.27284506
avg NPP = 11424.194316620002
avg NPNP = 1138.15516624

Agora eu estou tentando raciocinar sobre os tempos de execução acima:

  • A PPvariante não é tão rápido como PNPvariante, porque todos os fluxos paralelos usar fork-join comum pool de threads e se enviar uma tarefa de longa duração, estamos bloqueando efetivamente os tópicos na piscina.
  • Mas o argumento acima também deve trabalhar para NPPvariante e assim a NPPvariante também deve ser aproximadamente tão rápido quanto a PNPvariante. (Mas este não é o caso, NPPvariante é o pior em termos de tempo necessário). Alguém pode explicar a razão por trás disso?

Minhas perguntas:

  • É o meu raciocínio correto para o pequeno tempo de execução da PNPvariante?
  • Estou esquecendo de algo?
  • Por NPPvariante é a pior (em termos de tempo de execução)?

Como TimeIté medir o tempo:

class TimeIt {
    private TimeIt() {
    }

    /**
     * returns the time to execute the Callable in milliseconds
     */
    public static <T> double timeIt(Callable<T> callable) throws Exception {
        long start = System.nanoTime();
        System.out.println(callable.call());
        return (System.nanoTime() - start) / 1.0e6;
    }
}

PS: Eu entendo que este não é o melhor método para contar o número de primos. Crivo de Eratóstenes e outros métodos mais sofisticados existe para fazer isso. Mas por este exemplo, eu só quero entender o comportamento dos fluxos paralelos e quando usá-los.

dada:

Eu acho que, é claro, porque NPP é tão lento.

Organizar os números resultantes de uma tabela:

       |    _P    |   _NP
-------+----------+---------
  P_   |   2364   |   265
-------+----------+---------
  NP_  |  11424   |  1138
-------+----------+---------

Então você vê que é sempre mais rápido quando o fluxo externo é paralelo. Isso ocorre porque há muito trabalho a ser feito no córrego. Assim, a sobrecarga adicional para o tratamento do fluxo paralelo é baixo quando comparado com o trabalho a ser feito.

Você também ver que é sempre mais rápido quando o fluxo interior não é paralelo. isPrimeNonParallelé mais rápido do que isPrimeParallel. Isso ocorre porque não há muito trabalho a ser feito no córrego. Na maioria dos casos, é evidente depois de alguns passos que o número não é primo. Metade dos números são ainda (apenas uma etapa). A sobrecarga adicional para o tratamento do fluxo paralelo é elevado em comparação com o trabalho a ser feito.

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=171589&siteId=1
Recomendado
Clasificación