package org.cache2k.benchmark.util; /* * #%L * Benchmarks: utilities * %% * Copyright (C) 2013 - 2017 headissue GmbH, Munich * %% * 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. * #L% */ import java.util.Random; /** * Operations on patterns. This is a toolbox to manipulate access patterns * and create artificial access traces. There is no heavy usage of this und not * much tests, so beware of bugs. * * @author Jens Wilke; created: 2013-11-24 */ public class Patterns { /** * Sequence from 0 to size (exclusive) */ public static AccessPattern sequence(int _size) { return new Sequence(0, _size); } public static AccessPattern sequence(int _start, int _end) { return new Sequence(_start, _end); } public static AccessPattern revert(AccessPattern p) { return new Revert(p); } public static AccessPattern loop(AccessPattern p, int n) { return new Loop(p, n); } public static AccessPattern concat(AccessPattern... p) { return new Concat(p); } public static AccessPattern strip(AccessPattern p, int _stripCount) { return new Strip(p, _stripCount); } public static class Sequence extends AccessPattern { int pos = 0; int end; /** * Counting up sequence from start to end (exclusive). */ public Sequence(int _start, int _end) { pos = _start; end = _end; } @Override public boolean isEternal() { return false; } public boolean hasNext() throws Exception { return pos < end; } public int next() throws Exception { return pos++; } } /** * Sequence: start=1, increment=1, step=4, end=3, count=3, gives: 1 2 3 5 6 7 9 10 11 */ public static class InterleavedSequence extends AccessPattern { int pos; int start; int increment; int end; int step; int sequenceCount; public InterleavedSequence(int _start, int _end, int _increment, int _step, int _sequenceCount) { pos = _start; this.start = _start; this.increment = _increment; this.end = _end; this.step = _step; this.sequenceCount = _sequenceCount; } @Override public boolean isEternal() { return false; } public boolean hasNext() throws Exception { if (pos >= end) { start += step; end += step; pos = start; sequenceCount--; } return sequenceCount > 0; } public int next() throws Exception { return pos += increment; } } static class Loop extends AccessPattern { AccessPattern pattern; int size; int pos; boolean once = true; int[] buffer; int count = -1; Loop(AccessPattern _pattern) { if (_pattern.isEternal()) { throw new IllegalArgumentException("pattern is eternal"); } this.pattern = _pattern; } Loop(AccessPattern pattern, int _count) { if (pattern.isEternal()) { throw new IllegalArgumentException("pattern is eternal"); } this.pattern = pattern; count = _count; } @Override public boolean isEternal() { return count < 0; } public boolean hasNext() throws Exception { if (once) { int i = 0; int ia[] = new int[10000]; while (pattern.hasNext()) { ia[i++] = pattern.next(); if (i >= ia.length) { int ia2[] = new int[ia.length * 2]; System.arraycopy(ia, 0, ia2, 0, ia.length); ia = ia2; } } buffer = ia; size = i; pos = 0; once = false; } return count>0; } public int next() { int v = buffer[pos++]; if (pos >= size) { pos = 0; count--; } return v; } } public static class RevertLoop extends Loop { public RevertLoop(AccessPattern _pattern) { super(_pattern); } public RevertLoop(AccessPattern _pattern, int _count) { super(_pattern, _count); } public int next() { pos--; if (pos < 0) { pos = size - 1; count--; } return buffer[pos]; } } public static class Revert extends RevertLoop { Revert(AccessPattern pattern) { super(pattern); } public boolean isEternal() { return false; } public boolean hasNext() throws Exception { if (once) { super.hasNext(); return true; } return pos != 0; } } static class MyPattern extends PatternProxy { MyPattern() { AccessPattern _iseqs = new Strip(new ScatterMix( 80, new Patterns.InterleavedSequence(10, 500, 3, 1,3), 20, new RandomAccessPattern(1000) ), 50000 ); AccessPattern _hotterStuff = new Hotter(13, 4, new DistAccessPattern(999999)); pattern = new ScatterMix( 80, new Loop(_iseqs), 20, _hotterStuff); } } /** * Buffer that writes to an array. When the buffer is full the least element * inserted gets overwritten. * We can retrieve the latest element with get(0), the second lates with get(1), etc. * */ static class CircularIntBuffer { int[] buffer; int pos = 0; boolean full = false; CircularIntBuffer(int _size) { buffer = new int[_size]; } void add(int v) { buffer[pos++] = v; if (pos >= buffer.length) { pos = 0; full = true; } } /** 0 is the last inserted element */ int get(int idx) { idx = pos - 1 - idx; if (idx < 0) { idx += buffer.length; } return buffer[idx]; } int size() { return full ? buffer.length : pos; } } /** * Increase hotness/frequency of an input pattern by the given factor. */ static class Hotter extends AccessPattern { AccessPattern pattern; Random random = new Random(4711); CircularIntBuffer buffer; int factor; int count = 0; /** * @param _pattern input pattern * @param _size buffer size, which we use to look back * @param _factor 2 means per input element two will be selected randomly from the look back buffer */ Hotter(int _size, int _factor, AccessPattern _pattern) { pattern = _pattern; factor = _factor; buffer = new CircularIntBuffer(_size); } @Override public boolean isEternal() { return pattern.isEternal(); } public boolean hasNext() throws Exception { if (count > 0) { count--; return true; } if (!pattern.hasNext()) { return false; } count = factor; buffer.add(pattern.next()); return true; } public int next() throws Exception { return buffer.get(random.nextInt(buffer.size())); } } /** * Mix sequenced of two patterns together */ static class SeqMix extends AccessPattern { Random random = new Random(9876); int len1; int len2; int countDown = -1; AccessPattern pattern1; AccessPattern pattern2; SeqMix(AccessPattern pattern1, int len1, AccessPattern pattern2, int len2) { this.len1 = len1; this.len2 = len2; this.pattern1 = pattern1; this.pattern2 = pattern2; countDown = random.nextInt(len1); } public boolean isEternal() { return pattern1.isEternal() || pattern2.isEternal(); } public boolean hasNext() throws Exception { if (pattern1 == pattern2) { return pattern1.hasNext(); } if (countDown < 0) { AccessPattern p = pattern1; pattern1 = pattern2; pattern2 = p; int l = len1; len1 = len2; len2 = l; countDown = random.nextInt(len1); } countDown--; if (pattern1.hasNext()) { return true; } pattern2 = pattern1; return pattern2.hasNext(); } public int next() throws Exception { return pattern1.next(); } } static class ScatterMix extends AccessPattern { Random random = new Random(5432); int probability1; int probability2; int countDown = -1; AccessPattern pattern; AccessPattern pattern1; AccessPattern pattern2; boolean endReached = false; ScatterMix(int _probability1, AccessPattern pattern1, int _probability2, AccessPattern _pattern2) { this.probability1 = _probability1; this.probability2 = _probability2; this.pattern1 = pattern1; this.pattern2 = _pattern2; countDown = random.nextInt(_probability1); } public boolean isEternal() { return pattern1.isEternal() || pattern2.isEternal(); } public boolean hasNext() throws Exception { if (pattern1 == pattern2) { return pattern.hasNext(); } int v = random.nextInt(probability1 + probability2); if (v < probability1) { pattern = pattern1; } else { pattern = pattern2; } if (pattern.hasNext()) { return true; } if (pattern1 == pattern) { pattern1 = pattern = pattern2; } else { pattern2 = pattern = pattern1; } return pattern.hasNext(); } public int next() throws Exception { return pattern.next(); } } static class Concat extends AccessPattern { int pos = 0; AccessPattern pattern[]; protected Concat(AccessPattern... _pattern) { pattern = _pattern; } public boolean isEternal() { for (AccessPattern p : pattern) { if (!p.isEternal()) { return false; } } return true; } public boolean hasNext() throws Exception { while (pos < pattern.length) { if (pattern[pos].hasNext()) { return true; } pos++; } return false; } public int next() throws Exception { return pattern[pos].next(); } public void close() throws Exception { if (pattern != null) { for (AccessPattern p : pattern) { if (p != null) { p.close(); } } pattern = null; } } } static class Strip extends AccessPattern { AccessPattern pattern; int count; protected Strip(AccessPattern pattern, int count) { this.pattern = pattern; this.count = count; } public boolean isEternal() { return false; } public boolean hasNext() throws Exception { if (count > 0) { count--; return pattern.hasNext(); } if (pattern != null) { pattern.close(); pattern = null; } return false; } public int next() throws Exception { return pattern.next(); } public void close() throws Exception { if (pattern != null) { pattern.close(); pattern = null; } } } static class PatternProxy extends AccessPattern { AccessPattern pattern; protected PatternProxy() { } PatternProxy(AccessPattern pattern) { this.pattern = pattern; } public boolean isEternal() { return pattern.isEternal(); } public boolean hasNext() throws Exception { return pattern.hasNext(); } public int next() throws Exception { return pattern.next(); } } }