/* * Copyright 2017 Ben Manes. All Rights Reserved. * * 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.github.benmanes.caffeine.cache; import java.lang.ref.ReferenceQueue; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; /** * <pre>{@code * ./gradlew jmh -PincludePattern=TimerWheelBenchmark * }</pre> * * @author ben.manes@gmail.com (Ben Manes) */ @State(Scope.Benchmark) public class TimerWheelBenchmark { private static final int SIZE = (2 << 14); private static final int MASK = SIZE - 1; private static final long DELTA = TimeUnit.MINUTES.toNanos(5); private static final long UPPERBOUND = TimeUnit.DAYS.toNanos(5); TimerWheel<Integer, Integer> timerWheel; long[] times; Timer timer; @State(Scope.Thread) public static class ThreadState { int index; } @Setup public void setup() { timer = new Timer(0); times = new long[SIZE]; timerWheel = new TimerWheel<>(new MockCache()); for (int i = 0; i < SIZE; i++) { times[i] = ThreadLocalRandom.current().nextLong(UPPERBOUND); timerWheel.schedule(new Timer(times[i])); } timerWheel.schedule(timer); } @Benchmark public void findBucket(ThreadState threadState) { timerWheel.findBucket(times[threadState.index++ & MASK]); } @Benchmark public void reschedule(ThreadState threadState) { timer.setVariableTime(times[threadState.index++ & MASK]); timerWheel.reschedule(timer); } @Benchmark public void expire(ThreadState threadState) { long time = times[threadState.index++ & MASK]; timer.setVariableTime(time); timerWheel.nanos = (time - DELTA); timerWheel.advance(time); timerWheel.schedule(timer); } static final class Timer implements Node<Integer, Integer> { Node<Integer, Integer> prev; Node<Integer, Integer> next; long time; Timer(long time) { setVariableTime(time); } @Override public long getVariableTime() { return time; } @Override public void setVariableTime(long time) { this.time = time; } @Override public Node<Integer, Integer> getPreviousInVariableOrder() { return prev; } @Override public void setPreviousInVariableOrder(@Nullable Node<Integer, Integer> prev) { this.prev = prev; } @Override public Node<Integer, Integer> getNextInVariableOrder() { return next; } @Override public void setNextInVariableOrder(@Nullable Node<Integer, Integer> next) { this.next = next; } @Override public Integer getKey() { return null; } @Override public Object getKeyReference() { return null; } @Override public Integer getValue() { return null; } @Override public Object getValueReference() { return null; } @Override public void setValue(Integer value, ReferenceQueue<Integer> referenceQueue) {} @Override public boolean containsValue(Object value) { return false; } @Override public boolean isAlive() { return false; } @Override public boolean isRetired() { return false; } @Override public boolean isDead() { return false; } @Override public void retire() {} @Override public void die() {} } static final class MockCache extends BoundedLocalCache<Integer, Integer> { @SuppressWarnings({"unchecked", "rawtypes"}) protected MockCache() { super((Caffeine) Caffeine.newBuilder(), /* cacheLoader */ null, /* isAsync */ false); } @Override boolean evictEntry(Node<Integer, Integer> node, RemovalCause cause, long now) { return true; } } }