ExecutorService's surprising performance break-even point — rules of thumb?

后端 未结 9 1992
臣服心动
臣服心动 2020-12-08 03:10

I\'m trying to figure out how to correctly use Java\'s Executors. I realize submitting tasks to an ExecutorService has its own overhead. However, I\'m surpris

9条回答
  •  Happy的楠姐
    2020-12-08 03:56

    1. Using executors is about utilizing CPUs and / or CPU cores, so if you create a thread pool that utilizes the amount of CPUs at best, you have to have as many threads as CPUs / cores.
    2. You are right, creating new objects costs too much. So one way to reduce the expenses is to use batches. If you know the kind and amount of computations to do, you create batches. So think about thousand(s) computations done in one executed task. You create batches for each thread. As soon as the computation is done (java.util.concurrent.Future), you create the next batch. Even the creation of new batches can be done in parralel (4 CPUs -> 3 threads for computation, 1 thread for batch provisioning). In the end, you may end up with more throughput, but with higher memory demands (batches, provisioning).

    Edit: I changed your example and I let it run on my little dual-core x200 laptop.

    provisioned 2 batches to be executed
    simpleCompuation:14
    computationWithObjCreation:17
    computationWithObjCreationAndExecutors:9
    

    As you see in the source code, I took the batch provisioning and executor lifecycle out of the measurement, too. That's more fair compared to the other two methods.

    See the results by yourself...

    import java.util.List;
    import java.util.Vector;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    
    public class ExecServicePerformance {
    
        private static int count = 100000;
    
        public static void main( String[] args ) throws InterruptedException {
    
            final int cpus = Runtime.getRuntime().availableProcessors();
    
            final ExecutorService es = Executors.newFixedThreadPool( cpus );
    
            final Vector< Batch > batches = new Vector< Batch >( cpus );
    
            final int batchComputations = count / cpus;
    
            for ( int i = 0; i < cpus; i++ ) {
                batches.add( new Batch( batchComputations ) );
            }
    
            System.out.println( "provisioned " + cpus + " batches to be executed" );
    
            // warmup
            simpleCompuation();
            computationWithObjCreation();
            computationWithObjCreationAndExecutors( es, batches );
    
            long start = System.currentTimeMillis();
            simpleCompuation();
            long stop = System.currentTimeMillis();
            System.out.println( "simpleCompuation:" + ( stop - start ) );
    
            start = System.currentTimeMillis();
            computationWithObjCreation();
            stop = System.currentTimeMillis();
            System.out.println( "computationWithObjCreation:" + ( stop - start ) );
    
            // Executor
    
            start = System.currentTimeMillis();
            computationWithObjCreationAndExecutors( es, batches );    
            es.shutdown();
            es.awaitTermination( 10, TimeUnit.SECONDS );
            // Note: Executor#shutdown() and Executor#awaitTermination() requires
            // some extra time. But the result should still be clear.
            stop = System.currentTimeMillis();
            System.out.println( "computationWithObjCreationAndExecutors:"
                    + ( stop - start ) );
        }
    
        private static void computationWithObjCreation() {
    
            for ( int i = 0; i < count; i++ ) {
                new Runnable() {
    
                    @Override
                    public void run() {
    
                        double x = Math.random() * Math.random();
                    }
    
                }.run();
            }
    
        }
    
        private static void simpleCompuation() {
    
            for ( int i = 0; i < count; i++ ) {
                double x = Math.random() * Math.random();
            }
    
        }
    
        private static void computationWithObjCreationAndExecutors(
                ExecutorService es, List< Batch > batches )
                throws InterruptedException {
    
            for ( Batch batch : batches ) {
                es.submit( batch );
            }
    
        }
    
        private static class Batch implements Runnable {
    
            private final int computations;
    
            public Batch( final int computations ) {
    
                this.computations = computations;
            }
    
            @Override
            public void run() {
    
                int countdown = computations;
                while ( countdown-- > -1 ) {
                    double x = Math.random() * Math.random();
                }
            }
        }
    }
    

提交回复
热议问题