How to check performace of Multi Threading over using only a single thread?

437 views Asked by At

I'm studying Java multi threading and trying to check performance with multiple threads.I am trying to check whether multi threading is better than with single thread. So, I wrote a code which sums to limit. It is working as I expected(multiple threads are faster than single thread) when limit gets larger but it didn't when limit is small like 100000L. Is this due to context-switching ? and is the code below is appropriate to check performance of multi threading ?

public class MultiThreadingSum {
    long count = 0;
    static long limit = 1000000000L;

    static void compareMultipleThreadToSingleThread(int threadCnt) {
        Runnable r = () -> {
            MultiThreadingSum mts = new MultiThreadingSum();
            long startTime = System.nanoTime();
            while(++mts.count<=limit);
            long endTime = System.nanoTime();
            long estimatedTime = endTime - startTime;
            double seconds = estimatedTime / 1000000000.0;

            System.out.println(Thread.currentThread().getName()+", elapsed time : "+seconds);
        };

        for(int i=0; i<threadCnt; i++) {
            new Thread(r, "multiThread"+i).start();
        }

        Runnable r2 = () -> {
            MultiThreadingSum mts = new MultiThreadingSum();
            long startTime = System.nanoTime();
            while(++mts.count<=limit*threadCnt);
            long endTime = System.nanoTime();
            long estimatedTime = endTime - startTime;
            double seconds = estimatedTime / 1000000000.0;

            System.out.println(Thread.currentThread().getName()+", elapsed time : "+seconds);
        };

        new Thread(r2, "singleThread").start();
    }

    public static void main(String[] args) {
        compareMultipleThreadToSingleThread(3);
    }
}
2

There are 2 answers

7
Basil Bourque On BEST ANSWER

Your code does not wait for the 3-thread experiment to finish before running the single-thread experiment. So you may be contaminating your results.

Your code seems needlessly complicated. Can't we run two separate experiments, one with 3 threads and one with 1 thread, separately, to reuse code?

In modern Java, we rarely need to address the Thread class. Instead, use the executor service framework added to Java 5.

Putting this all together, perhaps your experiment should look more like the following.

Caveat: This is just a very rough cut, I've not thought it through, and my caffeination has been exhausted. So revise this code thoughtfully. Perhaps I can revise this code in a day or two.

package work.basil.threading;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class Loopy
{
    public static void main ( String[] args )
    {
        Loopy app = new Loopy();

        List < Integer > inputThreadsLimit = List.of( 1 , 3 , ( Runtime.getRuntime().availableProcessors() - 1 ) );
        for ( Integer numberOfThreads : inputThreadsLimit )
        {
            System.out.println("----------|  Experiment for thread count: " + numberOfThreads + "  |--------------------------");
            Duration duration = app.demo( numberOfThreads ); // Waits here for the experiment to run to completion.
            System.out.println( numberOfThreads + " = " + duration + " total, each: " + duration.dividedBy( numberOfThreads ) );
        }
    }

    // Member fields
    final private AtomicInteger count = new AtomicInteger( 0 );

    private Duration demo ( final int numberOfThreads )
    {
        ExecutorService executorService = Executors.newFixedThreadPool( numberOfThreads );
        long start = System.nanoTime();
        for ( int i = 0 ; i < numberOfThreads ; i++ )
        {
            executorService.submit( new Task() );
        }
        executorService.shutdown();  // Ask the executor service to shutdown its backing pool of threads after all submitted tasks are done/canceled/failed.
        try { executorService.awaitTermination( 1 , TimeUnit.HOURS ); } catch ( InterruptedException e ) { e.printStackTrace(); } // Tries to force the shutdown after timeout.

        Duration elapsed = Duration.ofNanos( System.nanoTime() - start );
        return elapsed;
    }

    class Task implements Runnable
    {
        @Override
        public void run ( )
        {
            int countSoFar = count.incrementAndGet();  // Thread-safe way to access, increment, and write a counter.
            // … add code here to do some kind of work …
            System.out.println( "Thread ID " + Thread.currentThread().getId() + " is finishing run after incrementing the countSoFar to: " + countSoFar + " at " + Instant.now() );
        }
    }
}
0
Sharon Ben Asher On

this is not a good example. the multi and single threaded solutions run simultaneously and on the same counter. so practically you run one multi threaded process with four threads. you need to run one solution until thread is complete and shutdown, then the other. the easiest solution would be to run the single threaded process as a simple loop in the main method and run the multi threaded solution after the loop completes. also, i would have two separate counters, or, you can assign zero to counter after single thread loop completes