scylladb / alternator-load-balancing

Various tricks, scripts, and libraries, for load balancing multiple Alternator nodes
Apache License 2.0
18 stars 11 forks source link

Java: remove synchronized parts #42

Closed dkropachev closed 1 week ago

dkropachev commented 1 week ago

Using CopyOnWriteArrayList and AtomicInteger will provide more performance.

Bouncheck commented 1 week ago

What is the issue with synchronized? Old methods looks fine to me.

dkropachev commented 1 week ago

@nyh, @Bouncheck , please check one more time.

dkropachev commented 1 week ago

@Bouncheck , I somehow accidentially merged it, please let me know if you want to roll it back

mykaul commented 1 week ago

Do we have numbers to show it improves performance?

dkropachev commented 1 week ago

Do we have numbers to show it improves performance?

Unfortunately java does not have built-in microbenchmark framework, and this repo does not have one as well. So, having these test will require some effort. I have created a naive benchmark that runs 100 threads, each thread runs 100000 nextAsURI, old version completes test in 2752 ms, while new one in 802, so it is 3+ times faster in given circumstances. To be fair this test does not represent real scenario, rather extreme case, but it showcase congestion that can be created by exclusive locks on class instance, which sinchronize is.

Test:

package com.scylladb.alternator;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class AlternatorLiveNodesBenchmark {
    static public class MockAlternatorLiveNodes extends AlternatorLiveNodes {
        private MockAlternatorLiveNodes() {
            super("http", Arrays.asList("1.1.1.1"), 8080);
        }

        @Override
        List<String> getNodesListFromURL(URI uri) throws IOException {
            String[] tmp = {"1.1.1.2", "1.1.1.3", "1.1.1.4"};
            return Arrays.asList(tmp);
        }
    }

    public synchronized static void main(String[] args) {
        Integer threads = 100;
        Integer iterations = 100000;

        AlternatorLiveNodes ln = new MockAlternatorLiveNodes();
        ln.setDaemon(true);
        ln.start();
        List<Thread> th = new ArrayList<>();
        for (Integer n = 0; n < threads; n++) {
            th.add(new Thread(() -> {
                for (int i = 0; i < iterations; i++) {
                    URI tmp = ln.nextAsURI();
                }
            }));
        }
        long startTime = System.currentTimeMillis();
        th.forEach(Thread::start);
        th.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        long lasted = System.currentTimeMillis() - startTime;
        System.out.println(String.format("done waiting - lasted %s", lasted));
        System.exit(0);
    }
}