/* * Copyright (C) 2014-2021 Daniel Dietsch (dietsch@informatik.uni-freiburg.de) * Copyright (C) 2015-2021 University of Freiburg * * This file is part of the ULTIMATE Util Library. * * The ULTIMATE Util Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ULTIMATE Util Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ULTIMATE Util Library. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify the ULTIMATE Util Library, or any covered work, by linking * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), * containing parts covered by the terms of the Eclipse Public License, the * licensors of the ULTIMATE Util Library grant you additional permission * to convey the resulting work. */ package de.uni_freiburg.informatik.ultimate.util.statistics; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger; import de.uni_freiburg.informatik.ultimate.util.CoreUtil; import de.uni_freiburg.informatik.ultimate.util.csv.ICsvProvider; import de.uni_freiburg.informatik.ultimate.util.csv.ICsvProviderProvider; import de.uni_freiburg.informatik.ultimate.util.csv.SimpleCsvProvider; /** * This class provides functions to measure runtime and memory consumption * * @author dietsch@informatik.uni-freiburg.de * */ public class Benchmark implements ICsvProviderProvider { // Get maximum size of heap in bytes. The heap cannot grow beyond this // size. Any attempt will result in an OutOfMemoryException. private long mMaxMemorySizeBytes; private Map mWatches; private TimeMemoryTracker mGlobalWatch; public Benchmark() { reset(); } /** * Register a new watch, but do not start it. Useful for starting many watches at the same time with * {@link #startAll()}, and then stopping them separately. * * @param title * The title of the watch to register. Titles have to be unique and non-null. */ public void register(final String title) { if (!mWatches.containsKey(title)) { mWatches.put(title, new TimeMemoryTracker(title, mMaxMemorySizeBytes)); } } /** * Unregisters a specific watch. * * @param title * The title of the watch to unregister. If the watch does not exist, this method will do nothing. */ public void unregister(final String title) { mWatches.remove(title); } /** * Starts a specific watch. Starting means taking the starting time and the various heap sizes. If the watch is not * already registered, it will be afterwards. * * @param title * The title of the watch to register. Titles have to be unique and non-null. If the watch did not exists * previously, it will be registered automatically. */ public void start(final String title) { final TimeMemoryTracker watch = mWatches.computeIfAbsent(title, a -> new TimeMemoryTracker(a, mMaxMemorySizeBytes)); watch.reset(); watch.start(); } public void startAll() { mGlobalWatch.reset(); mGlobalWatch.start(); } public void stop(final String title) { stopInternal(title, System.nanoTime()); } public void stopAll() { final long stopTime = System.nanoTime(); for (final String key : mWatches.keySet()) { stopInternal(key, stopTime); } } private void stopInternal(final String title, final long stopTime) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return; } if (watch.mStartTime == -1 && mGlobalWatch.mStartTime == -1) { return; } if (watch.mStartTime == -1) { // this watch was started via startAll watch.mStartTime = mGlobalWatch.mStartTime; watch.mStartMemorySizeBytes = mGlobalWatch.mStartMemorySizeBytes; watch.mStartMemoryFreeSizeBytes = mGlobalWatch.mStartMemoryFreeSizeBytes; watch.mStartPeakMemorySizeBytes = mGlobalWatch.mStartPeakMemorySizeBytes; } watch.stop(stopTime); } public void pause(final String title) { stop(title); } public void unpause(final String title) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return; } watch.start(); } /** * Resets the benchmark object and clears all watches. */ public void reset() { mMaxMemorySizeBytes = Runtime.getRuntime().maxMemory(); mGlobalWatch = new TimeMemoryTracker("Global", mMaxMemorySizeBytes); mWatches = new LinkedHashMap<>(); } public void printResult(final ILogger logger) { for (final TimeMemoryTracker s : mWatches.values()) { logger.info(s); } } @Override public String toString() { final StringBuilder sb = new StringBuilder(); final String lineSeparator = System.getProperty("line.separator"); sb.append("Benchmark results are:").append(lineSeparator); for (final TimeMemoryTracker s : mWatches.values()) { sb.append(" * ").append(s).append(lineSeparator); } sb.delete(sb.length() - lineSeparator.length(), sb.length()); return sb.toString(); } public String getReportString(final String title) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return ""; } return watch.toString(); } public double getElapsedTime(final String title, final TimeUnit unit) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return -1; } return CoreUtil.convertTimeUnit(watch.mElapsedTimeNs, TimeUnit.NANOSECONDS, unit); } public long getStartHeapSize(final String title) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return -1; } return watch.mStartMemorySizeBytes; } public long getStopHeapSize(final String title) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return -1; } return watch.mStopMemorySizeBytes; } public long getStartMemoryFreeSize(final String title) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return -1; } return watch.mStartMemoryFreeSizeBytes; } public long getStopMemoryFreeSize(final String title) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return -1; } return watch.mStopMemoryFreeSizeBytes; } public long getPeakMemoryConsumed(final String title) { final TimeMemoryTracker watch = mWatches.get(title); if (watch == null) { return -1; } return watch.mPeakMemorySizeBytes - watch.mStartPeakMemorySizeBytes; } public long getMaxHeapSize(final String title) { return mMaxMemorySizeBytes; } public List getTitles() { final ArrayList rtr = new ArrayList<>(); for (final TimeMemoryTracker w : mWatches.values()) { rtr.add(w.mTitle); } return rtr; } static boolean isHeap(final String memoryPoolName) { switch (memoryPoolName) { case "Code Cache": case "Perm Gen": case "PS Perm Gen": case "Perm Gen [shared-ro]": case "Perm Gen [shared-rw]": case "Metaspace": case "Compressed Class Space": case "CodeHeap 'non-nmethods'": case "CodeHeap 'profiled nmethods'": case "CodeHeap 'non-profiled nmethods'": return false; case "G1 Eden Space": case "G1 Old Gen": case "G1 Survivor Space": case "Eden Space": case "PS Eden Space": case "PS Survivor Space": case "Survivor Space": case "PS Old Gen": case "Tenured Gen": return true; default: throw new IllegalArgumentException("Unknown memory pool name \"" + memoryPoolName + "\""); } } @Override public ICsvProvider createCsvProvider() { final List columHeaders = new ArrayList<>(); columHeaders.add("Runtime (ns)"); columHeaders.add("Peak memory consumption (bytes)"); columHeaders.add("Allocated memory start (bytes)"); columHeaders.add("Allocated memory end (bytes)"); columHeaders.add("Free memory start (bytes)"); columHeaders.add("Free memory end (bytes)"); columHeaders.add("Max. memory available (bytes)"); final SimpleCsvProvider rtr = new SimpleCsvProvider<>(columHeaders); for (final TimeMemoryTracker w : mWatches.values()) { final List values = new ArrayList<>(); values.add(Double.valueOf(w.mElapsedTimeNs)); values.add(Double.valueOf(w.getPeakMemoryDelta())); values.add(Double.valueOf(w.mStartMemorySizeBytes)); values.add(Double.valueOf(w.mStopMemorySizeBytes)); values.add(Double.valueOf(w.mStartMemoryFreeSizeBytes)); values.add(Double.valueOf(w.mStopMemoryFreeSizeBytes)); values.add(Double.valueOf(mMaxMemorySizeBytes)); rtr.addRow(w.mTitle, values); } return rtr; } }