1

I am working on Multithreaded code from which I am trying to measure how much time a particular method is taking as I am trying to benchmark most of our teammate codes as I am doing Load and Performance testing of our Client code and then our Service code.

So for this performance measurement, I am using-

System.nanoTime();

And I am having Multithreaded code from which I am spawning multiple threads and trying to measure how much time that code is taking.

Below is the sample example by which I am trying to measure the performance of any code- In the below code I am trying to measure-

beClient.getAttributes method

Below is the code-

public class BenchMarkTest {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        try {

            for (int i = 0; i < 3 * 5; i++) {
                executor.submit(new ThreadTask(i));
            }

            executor.shutdown();
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) {

        }
    }

}

Below is the class that implements Runnable interface

class ThreadTask implements Runnable {
    private int id;
    public static ConcurrentHashMap<Long, AtomicLong> selectHistogram = new ConcurrentHashMap<Long, AtomicLong>();


    public ThreadTask(int id) {
        this.id = id;
    }

    @Override
    public void run() {


        long start = System.nanoTime();

        attributes = beClient.getAttributes(columnsList);

        long end = System.nanoTime() - start;

        final AtomicLong before = selectHistogram.putIfAbsent(end / 1000000L, new AtomicLong(1L));
        if (before != null) {
            before.incrementAndGet();
        }
    }
}

Whatever code I want to measure, I usually put the below line just above that method

long start = System.nanoTime();

And these two lines after the same method but with different ConcurrentHashMap

long end = System.nanoTime() - start;

final AtomicLong before = selectHistogram.putIfAbsent(end / 1000000L, new AtomicLong(1L));
        if (before != null) {
            before.incrementAndGet();
        }

Today I had the meeting with one of my senior folks and he said incrementAndGet method of ConcurrentHashMap is a blocking call. So your thread will be waiting for some time there.

And he asked me to make that Asynchronous call.

Is there any possibility of making that Asynchronous call?

Because in all our client code and service code to measure the performance of each method, I am using the same above three lines that I usually put before and after each method to measure the performance of those method. And after the program finishes, I am printing it out the result from those maps.

So now I am thinking of making that Asynchronous call? Can anyone help me to do that?

Basically, I am trying to measure the performance of a particular method in an asynchronous manner so that each thread won't wait and gets blocked.

I think, I can do this using Futures. Can anyone provide an example related to that?

Thanks for the help.

arsenal
  • 23,366
  • 85
  • 225
  • 331
  • I got confused here, incrementAndGet is not a method of `ConcurrentHashMap`, instead of it's a method of `AtomicLong`. So is this your question? – Nándor Krácser Apr 13 '13 at 06:20
  • Yeah. I messed up somehow. Just update the question with changes. – arsenal Apr 13 '13 at 06:24
  • @TechGeeky I did an update to the answer, may improve your code's performance. Check it out and use it if you like. Cheers. – acdcjunior Apr 13 '13 at 18:40
  • Thanks. Can you update the answer accordingly? By that way, I won't be missing any key things. If I try to understand like this, it might be possible, I can miss some important things in my code. Thanks for the help. – arsenal Apr 13 '13 at 19:02
  • @TechGeeky there you go. Check it below "Full updated code". – acdcjunior Apr 13 '13 at 19:57
  • Thanks acdcjunior for the help. It perfectly makes sense now. Is there a way to wrap all these things in a single class file? Why I am asking is because, if I need to benchmark other classes file or any other methods, I don't want to keep on adding 10-14 lines before and after that method. So if we can wrap this thing out in a single class file and then use some start and stop method of that class file to benchmark it. Then that will be great. let me know your thoughts on this. – arsenal Apr 13 '13 at 20:21

2 Answers2

1

The line:

if (before != null) {
    before.incrementAndGet();
}

Will lock the current Thread until before.incrementAndGet() acquires the lock (if you must know, there is in fact no lock, there is a while(true) and a compare-and-swap method) and returns the long value (that you are not using).

You can make it asynchronous by calling that specific method in a Thread of its own, thus not blocking the current Thread.

To do this, I believe you already know how: use Thread.start(), an ExecutorService or a FutureTask (check "How to asynchronously call a method in Java" on how to do it in an elegant fashion).

In case I'm not clear, here's a solution using FutureTask:

public class BenchMarkTest {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        int threadNum = 2;
        ExecutorService taskExecutor = Executors.newFixedThreadPool(threadNum);
        List<FutureTask<Long>> taskList = new ArrayList<FutureTask<Long>>();

        try {

            for (int i = 0; i < 3 * 5; i++) {
                executor.submit(new ThreadTask(i, taskExecutor, taskList));
            }

            executor.shutdown();
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) {

        }

        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();
    }

}

ThreadTask class:

class ThreadTask implements Runnable {
    private int id;
    public static ConcurrentHashMap<Long, AtomicLong> selectHistogram = new ConcurrentHashMap<Long, AtomicLong>();

    private ExecutorService taskExecutor;
    private List<FutureTask<Long>> taskList;    

    public ThreadTask(int id, ExecutorService taskExecutor, List<FutureTask<Long>> taskList) {
        this.id = id;
        this.taskExecutor = taskExecutor;
        this.taskList = taskList;
    }

    @Override
    public void run() {


        long start = System.nanoTime();

        attributes = beClient.getAttributes(columnsList);

        long end = System.nanoTime() - start;

        final AtomicLong before = selectHistogram.putIfAbsent(end / 1000000L, new AtomicLong(1L));
        if (before != null) {
            FutureTask<Long> futureTask = new FutureTask<Long>(new Callable<Long>() {
                public Long call() {
                    return before.incrementAndGet();
                }
            });
            taskList.add(futureTask);
            taskExecutor.execute(futureTask);
        }
    }
}

Update:

I thought of a little possible improvement: Instead of telling the taskExecutor to execute the futureTask in the ThreadTask class, it may be better to postpone the tasks' executions to the end of the main method. I mean:

Remove the line below of ThreadTask.run():

            taskExecutor.execute(futureTask);

And, in the main() method, where you have:

        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();

Add the the execution of the tasks, thus having:

        taskExecutor.invokeAll(taskList);
        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();

(Also, you can remove ThreadTask's ExecutorService field, as it will no longer use it.)

This way, there is very little overhead while you are executing the benchmark (the overhead is adding an object to the taskList and nothing else).

Full updated code:

public class BenchMarkTest {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        List<FutureTask<Long>> taskList = new ArrayList<FutureTask<Long>>();

        try {

            for (int i = 0; i < 3 * 5; i++) {
                executor.submit(new ThreadTask(i, taskList));
            }

            executor.shutdown();
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) {

        }

        int threadNum = 2;
        ExecutorService taskExecutor = Executors.newFixedThreadPool(threadNum);
        taskExecutor.invokeAll(taskList);
        for (FutureTask<Long> futureTask : taskList) {
            futureTask.get(); // doing a job similar to joining threads
        }
        taskExecutor.shutdown();
    }

}

-

class ThreadTask implements Runnable {
    private int id;
    public static ConcurrentHashMap<Long, AtomicLong> selectHistogram = new ConcurrentHashMap<Long, AtomicLong>();

    private List<FutureTask<Long>> taskList;    

    public ThreadTask(int id, List<FutureTask<Long>> taskList) {
        this.id = id;
        this.taskList = taskList;
    }

    @Override
    public void run() {


        long start = System.nanoTime();

        attributes = beClient.getAttributes(columnsList);

        long end = System.nanoTime() - start;

        final AtomicLong before = selectHistogram.putIfAbsent(end / 1000000L, new AtomicLong(1L));
        if (before != null) {
            FutureTask<Long> futureTask = new FutureTask<Long>(new Callable<Long>() {
                public Long call() {
                    return before.incrementAndGet();
                }
            });
            taskList.add(futureTask);
        }
    }
}
Community
  • 1
  • 1
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • 1
    That makes sense acdcjunior. Thanks for the help. I was also in the same impression that I shoould be using Futures as well. Any possibility that you can provide an example for future as well? – arsenal Apr 13 '13 at 06:28
  • There you go. I will edit a little bit more, showing your final main(). – acdcjunior Apr 13 '13 at 06:44
  • Yeah that will be great acdcjunior. I got slightly confused with the way current things is setup now. Also I am slightly new to Futures as well. Thanks for the help btw. – arsenal Apr 13 '13 at 06:46
  • That's it. Let me know if it works (there may be some typos, as I'm not using a compiler here). Also, if you don't wish to change `ThreadTask` constructor, you may make `taskExecutor`, and `taskList` `static` and use it that way. – acdcjunior Apr 13 '13 at 06:52
  • Just to make clear `AtomicInteger.incrementAndGet()` never acquires a lock, since it's a CAS (compare-and-switch) based method. – Nándor Krácser Apr 13 '13 at 07:03
  • @NandorKracser you are correct. I thought it would be simpler to explain/easier to understand using "lock". I have updated the answer for future reference. – acdcjunior Apr 13 '13 at 07:09
0

I'm sure that incrementing an AtomicLong takes less time than creating a Runnable/Callable object and pass it to a ExecutorService. Is this causing you really the bottleneck? If you really want to have fast concurrent increments see LongAdder which will come in JDK8.

LongAdders can be used with a ConcurrentHashMap to maintain a scalable frequency map (a form of histogram or multiset). For example, to add a count to a ConcurrentHashMap freqs, initializing if not already present, you can use freqs.computeIfAbsent(k -> new LongAdder()).increment();

Nándor Krácser
  • 1,128
  • 8
  • 13
  • Probably so. But that's a constant time. Also, he is asking help on how to make that increment with an asynchronous call. Creating a new runnable/callable/etc is the only way. – acdcjunior Apr 13 '13 at 06:31
  • I am not sure what is causing the bottleneck. But I am trying to avoid all the possibilities like thread waiting for it or any other issues which can increase the time measurement from my side whenever I am trying to measure the performance of each methods for the client code and service code. – arsenal Apr 13 '13 at 06:31
  • How can you be sure that all the increments have been made in time with this async approach? – Nándor Krácser Apr 13 '13 at 06:35
  • That was also my other confusion. Can I get the accurate numbers from the async approach or not? – arsenal Apr 13 '13 at 06:37
  • You can't be sure, because the `ExecutorService` may throw Exceptions and you don't know when all the updates are ready, except you are maintaining a list of all Future objects and check if they finished. Anyhow the async thing just increase the overall CPU time, because at the end you are still incrementing an AtomicInteger. – Nándor Krácser Apr 13 '13 at 06:43