/* * Copyright 2015 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.simulator; import java.util.stream.LongStream; import com.github.benmanes.caffeine.cache.simulator.BasicSettings.SyntheticSettings.HotspotSettings; import com.github.benmanes.caffeine.cache.simulator.BasicSettings.SyntheticSettings.UniformSettings; import com.yahoo.ycsb.generator.CounterGenerator; import com.yahoo.ycsb.generator.ExponentialGenerator; import com.yahoo.ycsb.generator.HotspotIntegerGenerator; import com.yahoo.ycsb.generator.NumberGenerator; import com.yahoo.ycsb.generator.ScrambledZipfianGenerator; import com.yahoo.ycsb.generator.SkewedLatestGenerator; import com.yahoo.ycsb.generator.UniformIntegerGenerator; import com.yahoo.ycsb.generator.ZipfianGenerator; /** * A generator of synthetic cache events to simulate different caching patterns. * * @author ben.manes@gmail.com (Ben Manes) */ public final class Synthetic { private Synthetic() {} /** Returns a sequence of events based on the setting's distribution. */ public static LongStream generate(BasicSettings settings) { int events = settings.synthetic().events(); switch (settings.synthetic().distribution().toLowerCase()) { case "counter": return counter(settings.synthetic().counter().start(), events); case "exponential": return exponential(settings.synthetic().exponential().mean(), events); case "hotspot": HotspotSettings hotspot = settings.synthetic().hotspot(); return Synthetic.hotspot(hotspot.lowerBound(), hotspot.upperBound(), hotspot.hotOpnFraction(), hotspot.hotsetFraction(), events); case "zipfian": return zipfian(settings.synthetic().zipfian().items(), events); case "scrambled-zipfian": return scrambledZipfian(settings.synthetic().zipfian().items(), events); case "skewed-zipfian-latest": return skewedZipfianLatest(settings.synthetic().zipfian().items(), events); case "uniform": UniformSettings uniform = settings.synthetic().uniform(); return uniform(uniform.lowerBound(), uniform.upperBound(), events); default: throw new IllegalStateException("Unknown distribution: " + settings.synthetic().distribution()); } } /** * Returns a sequence of unique integers. * * @param start the number that the counter starts from * @param events the number of events in the distribution */ public static LongStream counter(int start, int events) { return generate(new CounterGenerator(start), events); } /** * Returns a sequence of events based on an exponential distribution. Smaller intervals are more * frequent than larger ones, and there is no bound on the length of an interval. * * @param mean mean arrival rate of gamma (a half life of 1/gamma) * @param events the number of events in the distribution */ public static LongStream exponential(double mean, int events) { return generate(new ExponentialGenerator(mean), events); } /** * Returns a sequence of events resembling a hotspot distribution where x% of operations access y% * of data items. The parameters specify the bounds for the numbers, the percentage of the of the * interval which comprises the hot set and the percentage of operations that access the hot set. * Numbers of the hot set are always smaller than any number in the cold set. Elements from the * hot set and the cold set are chose using a uniform distribution. * * @param lowerBound lower bound of the distribution * @param upperBound upper bound of the distribution * @param hotsetFraction percentage of data item * @param hotOpnFraction percentage of operations accessing the hot set * @param events the number of events in the distribution */ public static LongStream hotspot(int lowerBound, int upperBound, double hotsetFraction, double hotOpnFraction, int events) { return generate(new HotspotIntegerGenerator(lowerBound, upperBound, hotsetFraction, hotOpnFraction), events); } /** * Returns a sequence of events where some items are more popular than others, according to a * zipfian distribution. Unlike {@link #zipfian}, the generated sequence scatters the "popular" * items across the item space. Use if you don't want the head of the distribution (the popular * items) clustered together. * * @param items the number of items in the distribution * @param events the number of events in the distribution */ public static LongStream scrambledZipfian(int items, int events) { return generate(new ScrambledZipfianGenerator(items), events); } /** * Returns a zipfian sequence with a popularity distribution of items, skewed to favor recent * items significantly more than older items * * @param items the number of items in the distribution * @param events the number of events in the distribution */ public static LongStream skewedZipfianLatest(int items, int events) { return generate(new SkewedLatestGenerator(new CounterGenerator(items)), events); } /** * Returns a sequence of events where some items are more popular than others, according to a * zipfian distribution. * * @param items the number of items in the distribution * @param events the number of events in the distribution */ public static LongStream zipfian(int items, int events) { return generate(new ZipfianGenerator(items), events); } /** * Returns a sequence of events where items are selected uniformly randomly from the interval * inclusively. * * @param lowerBound lower bound of the distribution * @param upperBound upper bound of the distribution * @param events the number of events in the distribution * @return a stream of cache events */ public static LongStream uniform(int lowerBound, int upperBound, int events) { return generate(new UniformIntegerGenerator(lowerBound, upperBound), events); } /** Returns a sequence of items constructed by the generator. */ private static LongStream generate(NumberGenerator generator, long count) { return LongStream.range(0, count).map(ignored -> generator.nextValue().intValue()); } }