diff --git a/src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java b/src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java
new file mode 100644
index 0000000..7f17d60
--- /dev/null
+++ b/src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java
@@ -0,0 +1,152 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.visitor.paint;
+
+import java.io.PrintStream;
+import java.util.List;
+import java.util.function.Supplier;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer.StyleRecord;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * This class is notified of the various stages of a render pass.
+ *
+ * @author Michael Zangl
+ * @since xxx
+ */
+public class RenderBenchmarkCollector {
+    /**
+     * Notified when the renderer method starts preparing the data
+     * @param circum The current circum of the view.
+     */
+    public void renderStart(double circum) {
+        // nop
+    }
+    /**
+     * Notified when the renderer method starts sorting the styles
+     * @return <code>true</code> if the renderer should continue to render
+     */
+    public boolean renderSort() {
+        // nop
+        return true;
+    }
+    /**
+     * Notified when the renderer method starts drawing
+     * @param allStyleElems All the elements that are painted.
+     * @return <code>true</code> if the renderer should continue to render
+     */
+    public boolean renderDraw(List<StyleRecord> allStyleElems) {
+        // nop
+        return true;
+    }
+
+    /**
+     * Notified when the render method is done.
+     */
+    public void renderDone() {
+     // nop
+    }
+
+    /**
+     * A benchmark implementation that captures the times
+     * @author Michael Zangl
+     * @since xxx
+     */
+    public static class CapturingBenchmark extends RenderBenchmarkCollector {
+        protected long timeStart;
+        protected long timeGenerateDone;
+        protected long timeSortingDone;
+        protected long timeFinished;
+
+        @Override
+        public void renderStart(double circum) {
+            timeStart = System.currentTimeMillis();
+            super.renderStart(circum);
+        }
+
+        @Override
+        public boolean renderSort() {
+            timeGenerateDone = System.currentTimeMillis();
+            return super.renderSort();
+        }
+
+        @Override
+        public boolean renderDraw(List<StyleRecord> allStyleElems) {
+            timeSortingDone = System.currentTimeMillis();
+            return super.renderDraw(allStyleElems);
+        }
+
+        /**
+         * Get the time needed for generating the styles
+         * @return The time in ms
+         */
+        public long getGenerateTime() {
+            return timeGenerateDone - timeStart;
+        }
+
+        /**
+         * Get the time needed for computing the draw order
+         * @return The time in ms
+         */
+        public long getSortTime() {
+            return timeSortingDone - timeGenerateDone;
+        }
+
+        @Override
+        public void renderDone() {
+            timeFinished = System.currentTimeMillis();
+            super.renderDone();
+        }
+
+        /**
+         * Get the draw time
+         * @return The time in ms
+         */
+        public long getDrawTime() {
+            return timeFinished - timeGenerateDone;
+        }
+    }
+
+    /**
+     * A special version of the benchmark class that logs the output to stderr.
+     * @author Michael Zangl
+     * @since xxx
+     */
+    public static class LoggingBenchmark extends RenderBenchmarkCollector.CapturingBenchmark {
+        private final PrintStream outStream = System.err;
+        private double circum;
+
+        @Override
+        public void renderStart(double circum) {
+            this.circum = circum;
+            super.renderStart(circum);
+            outStream.print("BENCHMARK: rendering ");
+        }
+
+        @Override
+        public boolean renderDraw(List<StyleRecord> allStyleElems) {
+            boolean res = super.renderDraw(allStyleElems);
+            outStream.print("phase 1 (calculate styles): " + Utils.getDurationString(timeSortingDone - timeStart));
+            return res;
+        }
+
+        @Override
+        public void renderDone() {
+            super.renderDone();
+            outStream.println("; phase 2 (draw): " + Utils.getDurationString(timeFinished - timeGenerateDone) +
+                    "; total: " + Utils.getDurationString(timeFinished - timeStart) +
+                    " (scale: " + circum + " zoom level: " + Selector.GeneralSelector.scale2level(circum) + ')');
+        }
+    }
+
+    /**
+     * A supplier that gets the default benchmark class.
+     * @return A supplier that returns a nop or a logging benchmark.
+     */
+    public static Supplier<RenderBenchmarkCollector> defaultBenchmarkSupplier() {
+        return () -> Main.isTraceEnabled() || Main.pref.getBoolean("mappaint.render.benchmark", false)
+                ? new LoggingBenchmark() : new RenderBenchmarkCollector();
+    }
+}
diff --git a/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java b/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
index 3bef3af..ec4f480 100644
--- a/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
+++ b/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
@@ -36,6 +36,7 @@ import java.util.NoSuchElementException;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.ForkJoinTask;
 import java.util.concurrent.RecursiveTask;
+import java.util.function.Supplier;
 
 import javax.swing.AbstractButton;
 import javax.swing.FocusManager;
@@ -62,7 +63,6 @@ import org.openstreetmap.josm.gui.mappaint.ElemStyles;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
 import org.openstreetmap.josm.gui.mappaint.StyleElementList;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
-import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
 import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement;
 import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement;
 import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.HorizontalTextAlignment;
@@ -191,7 +191,7 @@ public class StyledMapRenderer extends AbstractMapRenderer {
         }
     }
 
-    private static class StyleRecord implements Comparable<StyleRecord> {
+    public static class StyleRecord implements Comparable<StyleRecord> {
         private final StyleElement style;
         private final OsmPrimitive osm;
         private final int flags;
@@ -239,35 +239,16 @@ public class StyledMapRenderer extends AbstractMapRenderer {
 
             return Float.compare(this.style.objectZIndex, other.style.objectZIndex);
         }
-    }
-
-    /**
-     * Saves benchmark data for tests.
-     */
-    public static class BenchmarkData {
-        public long generateTime;
-        public long sortTime;
-        public long drawTime;
-        public Map<Class<? extends StyleElement>, Integer> styleElementCount;
-        public boolean skipDraw;
-
-        private void recordElementStats(List<StyleRecord> srs) {
-            styleElementCount = new HashMap<>();
-            for (StyleRecord r : srs) {
-                Class<? extends StyleElement> klass = r.style.getClass();
-                Integer count = styleElementCount.get(klass);
-                if (count == null) {
-                    count = 0;
-                }
-                styleElementCount.put(klass, count + 1);
-            }
 
+        /**
+         * Get the style for this style element.
+         * @return The style
+         */
+        public StyleElement getStyle() {
+            return style;
         }
     }
 
-    /* can be set by tests, if detailed benchmark data is requested */
-    public BenchmarkData benchmarkData;
-
     private static Map<Font, Boolean> IS_GLYPH_VECTOR_DOUBLE_TRANSLATION_BUG = new HashMap<>();
 
     /**
@@ -373,6 +354,8 @@ public class StyledMapRenderer extends AbstractMapRenderer {
     private boolean leftHandTraffic;
     private Object antialiasing;
 
+    private Supplier<RenderBenchmarkCollector> benchmarkFactory = RenderBenchmarkCollector.defaultBenchmarkSupplier();
+
     /**
      * Constructs a new {@code StyledMapRenderer}.
      *
@@ -1901,24 +1884,25 @@ public class StyledMapRenderer extends AbstractMapRenderer {
         }
     }
 
+    /**
+     * Sets the factory that creates the benchmark data receivers.
+     * @param benchmarkFactory The factory.
+     */
+    public void setBenchmarkFactory(Supplier<RenderBenchmarkCollector> benchmarkFactory) {
+        this.benchmarkFactory = benchmarkFactory;
+    }
+
     @Override
     public void render(final DataSet data, boolean renderVirtualNodes, Bounds bounds) {
+        RenderBenchmarkCollector benchmark = benchmarkFactory.get();
         BBox bbox = bounds.toBBox();
         getSettings(renderVirtualNodes);
-        boolean benchmarkOutput = Main.isTraceEnabled() || Main.pref.getBoolean("mappaint.render.benchmark", false);
-        boolean benchmark = benchmarkOutput || benchmarkData != null;
 
         data.getReadLock().lock();
         try {
             highlightWaySegments = data.getHighlightedWaySegments();
 
-            long timeStart = 0, timeGenerateDone = 0, timeSortingDone = 0, timeFinished;
-            if (benchmark) {
-                timeStart = System.currentTimeMillis();
-                if (benchmarkOutput) {
-                    System.err.print("BENCHMARK: rendering ");
-                }
-            }
+            benchmark.renderStart(circum);
 
             List<Node> nodes = data.searchNodes(bbox);
             List<Way> ways = data.searchWays(bbox);
@@ -1936,25 +1920,14 @@ public class StyledMapRenderer extends AbstractMapRenderer {
             THREAD_POOL.invoke(new ComputeStyleListWorker(new CompositeList<>(nodes, ways), allStyleElems,
                     Math.max(100, (nodes.size() + ways.size()) / THREAD_POOL.getParallelism() / 3)));
 
-            if (benchmark) {
-                timeGenerateDone = System.currentTimeMillis();
-                if (benchmarkOutput) {
-                    System.err.print("phase 1 (calculate styles): " + Utils.getDurationString(timeGenerateDone - timeStart));
-                }
-                if (benchmarkData != null) {
-                    benchmarkData.generateTime = timeGenerateDone - timeStart;
-                }
+            if (!benchmark.renderSort()) {
+                return;
             }
 
             Collections.sort(allStyleElems); // TODO: try parallel sort when switching to Java 8
 
-            if (benchmarkData != null) {
-                timeSortingDone = System.currentTimeMillis();
-                benchmarkData.sortTime = timeSortingDone - timeGenerateDone;
-                if (benchmarkData.skipDraw) {
-                    benchmarkData.recordElementStats(allStyleElems);
-                    return;
-                }
+            if (!benchmark.renderDraw(allStyleElems)) {
+                return;
             }
 
             for (StyleRecord r : allStyleElems) {
@@ -1968,20 +1941,9 @@ public class StyledMapRenderer extends AbstractMapRenderer {
                 );
             }
 
-            if (benchmark) {
-                timeFinished = System.currentTimeMillis();
-                if (benchmarkData != null) {
-                    benchmarkData.drawTime = timeFinished - timeGenerateDone;
-                    benchmarkData.recordElementStats(allStyleElems);
-                }
-                if (benchmarkOutput) {
-                    System.err.println("; phase 2 (draw): " + Utils.getDurationString(timeFinished - timeGenerateDone) +
-                        "; total: " + Utils.getDurationString(timeFinished - timeStart) +
-                        " (scale: " + circum + " zoom level: " + Selector.GeneralSelector.scale2level(circum) + ')');
-                }
-            }
-
             drawVirtualNodes(data, bbox);
+
+            benchmark.renderDone();
         } finally {
             data.getReadLock().unlock();
         }
diff --git a/test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java b/test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java
index 8357e65..8e5d8d5 100644
--- a/test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java
+++ b/test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java
@@ -10,9 +10,11 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumMap;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import javax.imageio.ImageIO;
 
@@ -29,7 +31,9 @@ import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.visitor.paint.RenderBenchmarkCollector.CapturingBenchmark;
 import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
+import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer.StyleRecord;
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.gui.mappaint.StyleSetting.BooleanStyleSetting;
@@ -207,16 +211,15 @@ public class MapRendererPerformanceTest {
                 } catch (InterruptedException ex) {
                     Main.warn(ex);
                 }
-                StyledMapRenderer.BenchmarkData data = new StyledMapRenderer.BenchmarkData();
-                data.skipDraw = skipDraw;
-                renderer.benchmarkData = data;
+                BenchmarkData data = new BenchmarkData();
+                renderer.setBenchmarkFactory(() -> data);
                 renderer.render(dsCity, false, bounds);
 
                 if (i > noWarmup) {
-                    generateTimes.add(data.generateTime);
-                    sortTimes.add(data.sortTime);
-                    drawTimes.add(data.drawTime);
-                    totalTimes.add(data.generateTime + data.sortTime + data.drawTime);
+                    generateTimes.add(data.getGenerateTime());
+                    sortTimes.add(data.getSortTime());
+                    drawTimes.add(data.getDrawTime());
+                    totalTimes.add(data.getGenerateTime() + data.getSortTime() + data.getDrawTime());
                 }
                 if (i == 1) {
                     dumpElementCount(data);
@@ -317,20 +320,36 @@ public class MapRendererPerformanceTest {
         ImageIO.write(img, "png", outputfile);
     }
 
-    public static void dumpTimes(StyledMapRenderer.BenchmarkData bd) {
-        System.out.print(String.format("gen. %3d, sort %3d, draw %3d%n", bd.generateTime, bd.sortTime, bd.drawTime));
+    public static void dumpTimes(BenchmarkData bd) {
+        System.out.print(String.format("gen. %3d, sort %3d, draw %3d%n", bd.getGenerateTime(), bd.getSortTime(), bd.getDrawTime()));
     }
 
-    public static void dumpElementCount(StyledMapRenderer.BenchmarkData bd) {
-        String sep = null;
-        for (Map.Entry<Class<? extends StyleElement>, Integer> e : bd.styleElementCount.entrySet()) {
-            if (sep == null) {
-                sep = " ";
-            } else {
-                System.out.print(sep);
+    public static void dumpElementCount(BenchmarkData bd) {
+        System.out.println(bd.recordElementStats().entrySet().stream()
+                .map(e -> e.getKey().getSimpleName().replace("Element", "") + ":" + e.getValue()).collect(Collectors.joining(" ")));
+    }
+
+    public static class BenchmarkData extends CapturingBenchmark {
+
+        private List<StyleRecord> allStyleElems;
+
+        @Override
+        public boolean renderDraw(List<StyleRecord> allStyleElems) {
+            this.allStyleElems = allStyleElems;
+            return super.renderDraw(allStyleElems);
+        }
+
+        private Map<Class<? extends StyleElement>, Integer> recordElementStats() {
+            Map<Class<? extends StyleElement>, Integer> styleElementCount = new HashMap<>();
+            for (StyleRecord r : allStyleElems) {
+                Class<? extends StyleElement> klass = r.getStyle().getClass();
+                Integer count = styleElementCount.get(klass);
+                if (count == null) {
+                    count = 0;
+                }
+                styleElementCount.put(klass, count + 1);
             }
-            System.out.print(e.getKey().getSimpleName().replace("Element", "") + ":" + e.getValue());
+            return styleElementCount;
         }
-        System.out.println();
     }
 }
