package org.cache2k.benchmark.util; /* * #%L * Benchmarks: utilities * %% * Copyright (C) 2013 - 2019 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 it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; import org.tukaani.xz.XZInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.Iterator; import java.util.zip.GZIPInputStream; /** * A finite list of requests to a cache with some calculated properties. * * @author Jens Wilke; created: 2013-11-14 */ public class AccessTrace implements Iterable<Integer> { private AccessPattern pattern; private int[] trace = null; private Integer[] objectTrace = null; private int valueCount = -1; private int lowValue = -Integer.MAX_VALUE; private int highValue = Integer.MIN_VALUE; private String name; /** * Read in a compressed trace */ public AccessTrace(String _fileName) { InputStream _resourceInput = AccessTrace.class.getResourceAsStream( "/org/cache2k/benchmark/traces/" + _fileName); try { InputStream _inputForTrace; if (_fileName.endsWith(".bz2")) { _inputForTrace = new BZip2CompressorInputStream(_resourceInput); } else if (_fileName.endsWith(".xz")) { _inputForTrace = new XZInputStream(_resourceInput); } else { _inputForTrace = new GZIPInputStream(_resourceInput); } readFromStream(_inputForTrace); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * Read in access trace from file. The file format is binary integer * values (4 bytes) in sequence, the order is big endian. */ public AccessTrace(File f) throws IOException { FileChannel in = new FileInputStream(f).getChannel(); ByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size()); trace = new int[(int) (in.size() / 4)]; buf.order(ByteOrder.BIG_ENDIAN); buf.asIntBuffer().get(trace); in.close(); } /** * Read in access trace. The format is binary integer * values (4 bytes) in sequence, the order is big endian. */ public AccessTrace(InputStream in) throws IOException { readFromStream(in); } public AccessTrace(int[] trace) { this.trace = trace; } private void readFromStream(final InputStream in) throws IOException { byte[] ba = readToByteArray(in); ByteBuffer buf = ByteBuffer.wrap(ba); trace = new int[ba.length / 4]; buf.order(ByteOrder.BIG_ENDIAN); buf.asIntBuffer().get(trace); in.close(); } private byte[] readToByteArray(InputStream in) throws IOException { byte[] ba = new byte[2048]; int pos = 0; do { int _possibleLength = ba.length - pos; int l = in.read(ba, pos, _possibleLength); if (l > 0) { pos += l; if (pos >= ba.length) { byte[] ba2 = new byte[ba.length * 2]; System.arraycopy(ba, 0, ba2, 0, ba.length); ba = ba2; } } else { break; } } while (true); byte[] ba2 = new byte[pos]; System.arraycopy(ba, 0, ba2, 0, pos); return ba2; } /** * New trace of complete pattern. */ public AccessTrace(AccessPattern... ps) { AccessPattern p = Patterns.concat(ps); if (p.isEternal()) { throw new IllegalArgumentException("Pattern is expected not to be eternal"); } pattern = p; } public AccessTrace name(String s) { if (name != null) { throw new IllegalStateException(); } name = s; return this; } public Integer[] getObjectArray() { if (objectTrace != null) { return objectTrace; } int[] _trace = getArray(); Integer[] ia = new Integer[_trace.length]; for (int i = 0; i < _trace.length; i++) { ia[i] = _trace[i]; } return objectTrace = ia; } /** * Return the array of the trace. It is not allowed to modify the array, since this * is the trace data itself. This is a poor API, however, when used in benchmarking we * don't want to have to array copy in the timing. */ public int[] getArray() { if (trace != null) { return trace; } try { trace = prepareTrace(pattern); } catch (Exception e) { throw new IllegalArgumentException("Error creating trace", e); } return trace; } public void write(File f) throws IOException { FileChannel out = new RandomAccessFile(f, "rw").getChannel(); ByteBuffer buf = out.map(FileChannel.MapMode.READ_WRITE, 0, getArray().length * 4); buf.order(ByteOrder.BIG_ENDIAN); buf.asIntBuffer().put(getArray()); out.close(); } /** * Return an access pattern which starts at the beginning of the trace. */ public AccessPattern newPattern() { final int[] ia = getArray(); return new AccessPattern() { int idx = 0; @Override public boolean isEternal() { return false; } @Override public boolean hasNext() { return idx < ia.length; } @Override public int next() { return ia[idx++]; } }; } /** * Return the distinct values in this trace. */ public int getValueCount() { if (valueCount < 0) { initStatistics(); } return valueCount; } public int getHighValue() { if (valueCount < 0) { initStatistics(); } return highValue; } public int getLowValue() { if (valueCount < 0) { initStatistics(); } return lowValue; } public int getLength() { return getArray().length; } private void initStatistics() { IntSet _values = new IntOpenHashSet(); for (int v : getArray()) { _values.add(v); if (v < lowValue) { lowValue = v; } if (v > highValue) { highValue = v; } } valueCount = _values.size(); } public String getName() { return name; } public String toString() { return String.format("AccessTrace(name=%s, length=%d, values=%d)", getName(), getLength(), getValueCount()); } private static int[] prepareTrace(AccessPattern p, int _maxSize) throws Exception { int[] ia = new int[1024]; int i = 0; while (p.hasNext() && i < _maxSize) { if (i >= ia.length) { int[] ia2 = new int[ia.length * 2]; System.arraycopy(ia, 0, ia2, 0, i); ia = ia2; } ia[i] = p.next(); i++; } int[] ia2 = new int[i]; System.arraycopy(ia, 0, ia2, 0, i); p.close(); return ia2; } /** Read until pattern ends */ private static int[] prepareTrace(AccessPattern p) throws Exception { return prepareTrace(p, Integer.MAX_VALUE); } @Override public Iterator<Integer> iterator() { final int[] array = trace; return new Iterator<Integer>() { int idx = 0; @Override public boolean hasNext() { return idx < array.length; } @Override public Integer next() { return array[idx++]; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }