/*
* Copyright 2014-2017 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spectator.perf;
import com.netflix.spectator.api.Counter;
import com.netflix.spectator.api.DefaultRegistry;
import com.netflix.spectator.api.Registry;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* Summary of results on m4.16xlarge:
*
* <pre>
* ### T1 Composite(Empty)
*
* ```
* Benchmark Mode Cnt Score Error Units
* Counters.cached thrpt 10 222995027.222 ± 5812215.685 ops/s
* Counters.lookup thrpt 10 34596370.526 ± 7975715.214 ops/s
* Counters.random thrpt 10 5699426.669 ± 604639.108 ops/s
* ```
*
* ### T1 Composite(Noop)
*
* ```
* Benchmark Mode Cnt Score Error Units
* Counters.cached thrpt 10 221034201.857 ± 9204618.077 ops/s
* Counters.lookup thrpt 10 33410400.013 ± 7828970.416 ops/s
* Counters.random thrpt 10 5977032.777 ± 679753.009 ops/s
* ```
*
* ### T1 Composite(Default)
*
* ```
* Benchmark Mode Cnt Score Error Units
* Counters.cached thrpt 10 61043422.331 ± 3085269.565 ops/s
* Counters.lookup thrpt 10 25989379.563 ± 4981909.126 ops/s
* Counters.random thrpt 10 4299422.647 ± 394069.294 ops/s
* ```
*
* ### T1 Composite(Noop, Noop)
*
* ```
* Benchmark Mode Cnt Score Error Units
* Counters.cached thrpt 10 65781502.616 ± 3124211.952 ops/s
* Counters.lookup thrpt 10 23914193.535 ± 6256980.210 ops/s
* Counters.random thrpt 10 3907696.564 ± 383335.366 ops/s
* ```
*
* ### T1 Composite(Default, Default)
*
* ```
* Benchmark Mode Cnt Score Error Units
* Counters.cached thrpt 10 37594426.749 ± 1302829.135 ops/s
* Counters.lookup thrpt 10 17151030.656 ± 3776435.406 ops/s
* Counters.random thrpt 10 2228890.157 ± 186029.279 ops/s
* ```
* </pre>
*/
@State(Scope.Thread)
public class Counters {
@State(Scope.Benchmark)
public static class Data {
Registry registry = new DefaultRegistry();
Counter cached = registry.counter("cached");
String[] names;
String[] newNames;
public Data() {
names = new String[100000];
newNames = new String[100000];
for (int i = 0; i < 100000; ++i) {
names[i] = UUID.randomUUID().toString();
registry.counter(names[i]).increment();
newNames[i] = UUID.randomUUID().toString();
}
}
}
@State(Scope.Thread)
public static class Metrics {
private Random random = ThreadLocalRandom.current();
Counter get(Data data) {
// Assumes about 5% of lookups will be for a new or expired counter. This is
// mostly just to have some activity that will cause an addition to the map
// mixed in with the reads.
String name = (random.nextDouble() < 0.05)
? data.newNames[random.nextInt(data.newNames.length)]
: data.names[random.nextInt(data.names.length)];
return data.registry.counter(name);
}
}
private long incrementAndGet(Counter c) {
c.increment();
return c.count();
}
@Benchmark
public long cached(Data data) {
return incrementAndGet(data.cached);
}
@Benchmark
public long lookup(Data data) {
return incrementAndGet(data.registry.counter("lookup"));
}
@Benchmark
public long random(Data data, Metrics metrics) {
return incrementAndGet(metrics.get(data));
}
@TearDown
public void check(Data data) {
final long cv = data.cached.count();
final long lv = data.registry.counter("lookup").count();
assert cv > 0 || lv > 0 : "counters haven't been incremented";
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(".*")
.forks(1)
.build();
new Runner(opt).run();
}
}