aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java
blob: 478f15f721dd14f800614a00a91761a164ef3289 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.benchmark;

import com.yahoo.jdisc.application.BindingRepository;
import com.yahoo.jdisc.application.BindingSet;
import com.yahoo.jdisc.application.UriPattern;
import org.junit.jupiter.api.Test;

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author Simon Thoresen Hult
 */
public class BindingMatchingTestCase {

    private static final int NUM_CANDIDATES = 1024;
    private static final int NUM_MATCHES = 100;
    private static final int MIN_THREADS = 1;
    private static final int MAX_THREADS = 64;
    private static final Random random = new Random();
    private static final ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS);

    @Test
    void runThroughtputMeasurements() throws Exception {
        System.err.format("%15s%15s%15s%15s%15s%15s%15s%15s\n",
                "No. of Bindings", "1 thread", "2 thread", "4 thread", "8 thread", "16 thread", "32 thread", "64 thread");
        for (int numBindings : Arrays.asList(1, 10, 25, 50, 100, 250)) {
            BindingRepository<Object> repo = new BindingRepository<>();
            for (int binding = 0; binding < numBindings; ++binding) {
                repo.bind("http://*/v" + binding + "/*/data/", new Object());
            }
            System.err.format("%15s", numBindings + " binding(s)");

            List<URI> candidates = newCandidates(repo);
            measureThroughput(repo.activate(), candidates, MAX_THREADS); // warmup

            BindingSet<Object> bindings = repo.activate();
            for (int numThreads = MIN_THREADS;
                 numThreads <= MAX_THREADS;
                 numThreads *= 2)
            {
                System.err.format("%15s", measureThroughput(bindings, candidates, numThreads));
            }
            System.err.format("\n");
        }
    }

    private static long measureThroughput(BindingSet<Object> bindings, List<URI> candidates, int numThreads) throws Exception {
        List<MatchTask> tasks = new LinkedList<>();
        for (int i = 0; i < numThreads; ++i) {
            MatchTask task = new MatchTask(bindings, candidates);
            tasks.add(task);
        }
        List<Future<Long>> results = executor.invokeAll(tasks);
        long nanos = 0;
        for (Future<Long> res : results) {
            nanos = Math.max(nanos, res.get());
        }
        return (numThreads * NUM_MATCHES * TimeUnit.SECONDS.toNanos(1)) / nanos;
    }

    private List<URI> newCandidates(BindingRepository<Object> bindings) {
        List<URI> lst = new ArrayList<>(NUM_CANDIDATES);
        Iterator<Map.Entry<UriPattern, Object>> it = bindings.iterator();
        for (int i = 0; i < NUM_CANDIDATES; ++i) {
            if (!it.hasNext()) {
                it = bindings.iterator();
            }
            lst.add(newCandidate(it.next().getKey()));
        }
        return lst;
    }

    private URI newCandidate(UriPattern key) {
        String pattern = key.toString();
        StringBuilder uri = new StringBuilder();
        for (int i = 0, len = pattern.length(); i < len; ++i) {
            char c = pattern.charAt(i);
            if (c == '*') {
                uri.append(random.nextInt(Integer.MAX_VALUE));
            } else {
                uri.append(c);
            }
        }
        return URI.create(uri.toString());
    }

    private static class MatchTask implements Callable<Long> {

        final BindingSet<Object> bindings;
        final List<URI> candidates;

        MatchTask(BindingSet<Object> bindings, List<URI> candidates) {
            this.bindings = bindings;
            this.candidates = candidates;
        }

        @Override
        public Long call() throws Exception {
            Iterator<URI> it = candidates.iterator();
            for (int i = 0, len = random.nextInt(candidates.size()); i < len; ++i) {
                it.next();
            }
            long time = System.nanoTime();
            for (int i = 0; i < NUM_MATCHES; ++i) {
                if (!it.hasNext()) {
                    it = candidates.iterator();
                }
                bindings.match(it.next());
            }
            return System.nanoTime() - time;
        }
    }
}