Created
June 7, 2019 02:52
-
-
Save eirikbakke/6519b3e623c0f4703ee82388d1587f08 to your computer and use it in GitHub Desktop.
SVG vs. PNG loading benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.common.collect.ImmutableSet; | |
import com.google.common.math.StatsAccumulator; | |
import java.awt.Graphics2D; | |
import java.awt.Image; | |
import java.awt.image.BufferedImage; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.URL; | |
import java.util.Set; | |
import javax.imageio.ImageIO; | |
import javax.imageio.ImageReadParam; | |
import javax.imageio.ImageReader; | |
import javax.imageio.stream.ImageInputStream; | |
import javax.swing.Icon; | |
import javax.swing.ImageIcon; | |
// Note: Before running this benchmark, be sure to disable Intel SpeedStep in the BIOS. | |
public class SVGBenchmark { | |
private static final Set<String> ICON_NAMES = ImmutableSet.of( | |
"warning", "arrow_down", "arrow_up", "cursoredit_data", "cursoredit_formula", | |
"cursoredit_label", "cursoredit_none", "database", "field_collapsed_join", | |
"field_new_primitive_data", "field_new_primitive_formula", "field_new_relation", | |
"field_primitive_data", "field_primitive_formula", "field_relation_noinst", "field_relation", | |
"fields_relation_only", "filter_range", "folder_closed", "folder_open", "general_minus", | |
"general_plus", "project_perspective", "project_table", "project_logo", "toolbar_edit", | |
"toolbar_redo", "toolbar_refresh", "toolbar_stop", "toolbar_undo", "toolbar_zoom_in", | |
"toolbar_zoom_original", "toolbar_zoom_out"); | |
private static final ImageReader PNG_READER; | |
static { | |
ImageIO.setUseCache(false); | |
PNG_READER = ImageIO.getImageReadersByMIMEType("image/png").next(); | |
} | |
private static URL getResourceURL(String iconName, boolean svg) { | |
String path = "com/somepackage/project/icons/" + iconName + "." + (svg ? "svg" : "png"); | |
URL ret = SVGBenchmark.class.getClassLoader().getResource(path); | |
if (ret == null) | |
throw new RuntimeException("Resource not found"); | |
return ret; | |
} | |
private static void loadAndPaintOneIcon(String iconName, boolean svg) { | |
final URL url = getResourceURL(iconName, svg); | |
final Icon icon; | |
if (svg) { | |
try { | |
icon = SVGIcon.load(url); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
} else { | |
Image image; | |
// Load in the same way as ImageUtilities opens PNG files. | |
try (ImageInputStream stream = ImageIO.createImageInputStream(url.openStream())) { | |
ImageReadParam param = PNG_READER.getDefaultReadParam(); | |
PNG_READER.setInput(stream, true, true); | |
image = PNG_READER.read(0, param); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
icon = new ImageIcon(image); | |
} | |
BufferedImage img = new BufferedImage( | |
icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB_PRE); | |
Graphics2D g = img.createGraphics(); | |
// Assume the Component parameter isn't used by the underlying implementation. | |
icon.paintIcon(null, g, 0, 0); | |
g.dispose(); | |
} | |
private static void measureOneSample(StatsAccumulator svgStats, StatsAccumulator pngStats) { | |
boolean order[] = Math.random() < 0.5 | |
? new boolean[] {false, true} | |
: new boolean[] {true, false}; | |
//boolean order[] = new boolean[] {true}; | |
for (boolean svg : order) { | |
long bef = System.nanoTime(); | |
for (String iconName : ICON_NAMES) | |
loadAndPaintOneIcon(iconName, svg); | |
long aft = System.nanoTime(); | |
(svg ? svgStats : pngStats).add(aft - bef); | |
} | |
} | |
private static void printStats(StatsAccumulator stats, boolean svg) { | |
double mean = (stats.count() == 0) ? Double.NaN : stats.mean(); | |
double standardError = stats.count() < 2 ? Double.NaN : | |
(stats.sampleStandardDeviation() / Math.sqrt(stats.count())); | |
double standardErrorPercentage = 100 * (standardError / mean); | |
double meanPerIcon = mean / ICON_NAMES.size(); | |
System.out.printf("Loading and painting %s took %.2f microseconds per icon +/- %.02f%%\n", | |
svg ? "SVG" : "PNG", (meanPerIcon / 1000.0), standardErrorPercentage); | |
} | |
private static void measure() { | |
StatsAccumulator svgStats = new StatsAccumulator(); | |
StatsAccumulator pngStats = new StatsAccumulator(); | |
// Warmup. | |
for (int i = 0; i < 300; i++) | |
measureOneSample(svgStats, pngStats); | |
// Throw away the warmup stats and start fresh. | |
svgStats = new StatsAccumulator(); | |
pngStats = new StatsAccumulator(); | |
// Now do the actual measurements. | |
for (int i = 0; i < 500; i++) | |
measureOneSample(svgStats, pngStats); | |
printStats(svgStats, true); | |
printStats(pngStats, false); | |
} | |
private static final void printFileSizeStats(boolean svg) { | |
StatsAccumulator sizeStats = new StatsAccumulator(); | |
for (String iconName : ICON_NAMES) { | |
URL url = getResourceURL(iconName, svg); | |
try (InputStream is = url.openStream()) { | |
sizeStats.add(is.readAllBytes().length); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
System.out.println("Average size of " + (svg ? "SVG" : "PNG") + " icon file is " + | |
Math.round(sizeStats.mean()) + " bytes (max was " + (int) sizeStats.max() + " bytes)"); | |
} | |
public static final void main(String args[]) { | |
printFileSizeStats(true); | |
printFileSizeStats(false); | |
measure(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment