ExecutorService rendimiento lento de subprocesos múltiples

Estoy intentando ejecutar un cálculo simple (llama aMath.random() 10000000 veces). Sorprendentemente, ejecutarlo en un método simple funciona mucho más rápido que usar ExecutorService.

He leído otro hilo enl sorprendente punto de equilibrio del rendimiento de ExecutorService --- ¿reglas generales? e intenté seguir la respuesta ejecutandoCallable usando lotes, pero el rendimiento sigue siendo malo

¿Cómo mejoro el rendimiento según mi código actual?

import java.util.*;
import java.util.concurrent.*;

public class MainTest {
    public static void main(String[]args) throws Exception {
        new MainTest().start();;
    }

    final List<Worker> workermulti = new ArrayList<Worker>();
    final List<Worker> workersingle = new ArrayList<Worker>();
    final int count=10000000;

    public void start() throws Exception {
        int n=2;

        workersingle.add(new Worker(1));
        for (int i=0;i<n;i++) {
            // worker will only do count/n job
            workermulti.add(new Worker(n));
        }

        ExecutorService serviceSingle = Executors.newSingleThreadExecutor();
        ExecutorService serviceMulti = Executors.newFixedThreadPool(n);
        long s,e;
        int tests=10;
        List<Long> simple = new ArrayList<Long>();
        List<Long> single = new ArrayList<Long>();
        List<Long> multi = new ArrayList<Long>();

        for (int i=0;i<tests;i++) {
            // simple
            s = System.currentTimeMillis();
            simple();
            e = System.currentTimeMillis();
            simple.add(e-s);

            // single thread
            s = System.currentTimeMillis();
               serviceSingle.invokeAll(workersingle); // single thread
            e = System.currentTimeMillis();
            single.add(e-s);

            // multi thread
            s = System.currentTimeMillis();
               serviceMulti.invokeAll(workermulti);
            e = System.currentTimeMillis();
            multi.add(e-s);
        }
        long avgSimple=sum(simple)/tests;
        long avgSingle=sum(single)/tests;
        long avgMulti=sum(multi)/tests;
        System.out.println("Average simple: "+avgSimple+" ms");
        System.out.println("Average single thread: "+avgSingle+" ms");
        System.out.println("Average multi thread: "+avgMulti+" ms");

        serviceSingle.shutdown();
        serviceMulti.shutdown();
    }

    long sum(List<Long> list) {
        long sum=0;
        for (long l : list) {
            sum+=l;
        }
        return sum;
    }

    private void simple() {
        for (int i=0;i<count;i++){
            Math.random();
        }
    }

    class Worker implements Callable<Void> {
        int n;

        public Worker(int n) {
            this.n=n;
        }

        @Override
        public Void call() throws Exception {
            // divide count with n to perform batch execution
            for (int i=0;i<(count/n);i++) {
                Math.random();
            }
            return null;
        }
    }
}

La salida de este código

Average simple: 920 ms
Average single thread: 1034 ms
Average multi thread: 1393 ms

EDIT: el rendimiento se ve afectado debido a que Math.random () es un método sincronizado ... después de cambiar Math.random () con un nuevo objeto Random para cada subproceso, el rendimiento mejoró

La salida para el nuevo código (después de reemplazar Math.random () con Random para cada hilo)

Average simple: 928 ms
Average single thread: 1046 ms
Average multi thread: 642 ms

Respuestas a la pregunta(3)

Su respuesta a la pregunta