Java Thread Affinity support for ExecutorService

Overview

The Java Thread Affinity version 1.4.1 library has support for the concurrency library through an AffinityThreadFactory.

This follows my previous article on HyperThreading support in the library which give mroe detail on what it does and how it works.

Example

In the AffinityThreadFactoryMain example,
private static final ExecutorService ES = Executors.newFixedThreadPool(4,
        new AffinityThreadFactory("bg", SAME_CORE, DIFFERENT_SOCKET, ANY));

public static void main(String... args) throws InterruptedException {
    for (int i = 0; i < 12; i++)
        ES.submit(new Callable() {
            @Override
            public Void call() throws InterruptedException {
                Thread.sleep(100);
                return null;
            }
        });
    Thread.sleep(200);
    System.out.println("\nThe assignment of CPUs is\n" + AffinityLock.dumpLocks());
    ES.shutdown();
    ES.awaitTermination(1, TimeUnit.SECONDS);
}

by changing the ThreadFactory, you can decide how threads in the factory should be laid out. The default behaviour is to allocate from highest CPU id to lowest, however by changing the AffinityStrategy you can make it try to minimise the number of cores it uses.
Assigning cpu 7 to Thread[bg,5,main]
Assigning cpu 6 to Thread[bg-2,5,main]
Assigning cpu 3 to Thread[bg-3,5,main]
Assigning cpu 2 to Thread[bg-4,5,main]

The assignment of CPUs is
0: General use CPU
1: General use CPU
2: Thread[bg-4,5,main] alive=true
3: Thread[bg-3,5,main] alive=true
4: General use CPU
5: General use CPU
6: Thread[bg-2,5,main] alive=true
7: Thread[bg,5,main] alive=true

Releasing cpu 7 from Thread[bg,5,main]
Releasing cpu 6 from Thread[bg-2,5,main]
Releasing cpu 3 from Thread[bg-3,5,main]
Releasing cpu 2 from Thread[bg-4,5,main]
In this case, the second thread is allocated to the SAME_CORE as the first, the third cannot get one on the same core so it tries to get on on a DIFFERENT_SOCKET, but there is only one so it takes ANY available core which can be reserved.

Related topics

Java Thread Affinity support for hyper threading.
Java Thread Affinity supports groups of threads.
Thread Affinity library for Java supports JNA
Thread Affinity library for Java.

Comments

  1. I wonder if it makes sense to use this library for testing the thread-safety of mutable shared objects. I use ExecutorService for this purpose, but I suppose by default the JVM will just evenly distribute new Threads across cores and so I only get deterministic results. Now to improve the testing, I'ld like to distribute the threads in a truly random way. Does it make sense to use this library for such a purpose?

    ReplyDelete
  2. You are right that more determinism, while helping performance, doesn't help detecting race conditions. I have found recently that a test which consistently breaks without this library shows no error when it is used.

    You could use this library with some variation to allow this, to force all the threads onto one core or thread to test under the worst possible configuration.

    I would start with what you believe might be the API for pseudo random (perhaps with a seed), or sub-optimal assignment and add an Issue to the project.

    ReplyDelete
  3. Thanks for your reply. Rethinking this, I don't think it makes sense to use this library for testing racing conditions. The default behavior of the JRE is to evenly distribute threads which is what I want to test if my classes work in the worst possible environment.

    ReplyDelete
  4. I would have though the worst possible situation is where all the thread are competing for the small number of resources e.g. one or two core.

    If you let the OS use all possible CPUs it doesn't a reasonably good job, but it may not indicate how your application behaves on a machine with less resources.

    ReplyDelete
  5. Well, I am not a hardware expert, but I think this would only be bad for performance. In terms of robustness however, I think the worst situation is an even distribution of threads across cores because then a lot of cache memory synchronization needs to be done when sharing mutable objects on the heap.

    Now my only concern is that while the standard algorithm in the JRE should do an even distribution, it might do so totally deterministic. That means although my test suite runs with flying colors on my system, it may fail on any other system.

    I think I'll give it a try and see what happens when I completely randomize the distribution in a non-deterministic fashion with a SecureRandom. This might require me some time however.

    ReplyDelete
  6. You will get far less performance running multi-threaded code on a few CPU then running it on all cpus.

    The JRE doesn't decide what runs when, its the OS which does this. You are right that code which is not thread safe can run fine on one platform (version of Java, OS, hardware) but fail on another.

    I imagine that using SecureRandom could slow down your application significantly. Even using Random could impact your results.

    ReplyDelete

Post a Comment

Popular posts from this blog

Low Latency Microservices, A Retrospective

Unusual Java: StackTrace Extends Throwable

System wide unique nanosecond timestamps