1 /*
   2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package org.openjdk.bench.java.util.jar;
  25 
  26 import org.openjdk.jmh.annotations.*;
  27 
  28 import java.io.File;
  29 import java.io.FileOutputStream;
  30 import java.io.IOException;
  31 import java.nio.file.Files;
  32 import java.util.Random;
  33 import java.util.concurrent.TimeUnit;
  34 import java.util.jar.Attributes;
  35 import java.util.jar.JarEntry;
  36 import java.util.jar.JarFile;
  37 import java.util.jar.JarOutputStream;
  38 import java.util.jar.Manifest;
  39 import java.util.zip.ZipEntry;
  40 import java.util.zip.ZipFile;
  41 
  42 /**
  43  * Simple benchmark measuring cost of looking up versioned entries in a zip
  44  * file.
  45  *
  46  * Before JDK-XXXXXXX
  47  * Benchmark                                    (size)  Mode  Cnt    Score     Error   Units
  48  * JarFileGetVersionedEntry.getEntryHit           1024  avgt    5  211.543    25.846   ns/op
  49  *   gc.alloc.rate.norm                           1024  avgt    5  232.020     0.011    B/op
  50  * JarFileGetVersionedEntry.getEntryHitUncached   1024  avgt    5  233.740    40.079   ns/op
  51  *   gc.alloc.rate.norm                           1024  avgt    5  288.025     0.004    B/op
  52  * JarFileGetVersionedEntry.getEntryMiss          1024  avgt    5  174.355    30.211   ns/op
  53  *   gc.alloc.rate.norm                           1024  avgt    5   72.007     0.010    B/op
  54  * JarFileGetVersionedEntry.getEntryMissUncached  1024  avgt    5  202.069    52.302   ns/op
  55  *   gc.alloc.rate.norm                           1024  avgt    5  128.199     0.007    B/op
  56  *
  57  * After JDK-XXXXXXX
  58  * Benchmark                                    (size)  Mode  Cnt    Score     Error   Units
  59  * JarFileGetVersionedEntry.getEntryHit           1024  avgt    5  153.930    24.785   ns/op
  60  *    gc.alloc.rate.norm                          1024  avgt    5  144.012     0.002    B/op
  61  * JarFileGetVersionedEntry.getEntryHitUncached   1024  avgt    5  205.168    26.654   ns/op
  62  *    gc.alloc.rate.norm                          1024  avgt    5  200.016     0.002    B/op
  63  * JarFileGetVersionedEntry.getEntryMiss          1024  avgt    5   57.551    23.338   ns/op
  64  *    gc.alloc.rate.norm                          1024  avgt    5   16.001     0.003    B/op
  65  * JarFileGetVersionedEntry.getEntryMissUncached  1024  avgt    5  116.451    28.830   ns/op
  66  *    gc.alloc.rate.norm                          1024  avgt    5   72.194     0.002    B/op
  67  */
  68 @BenchmarkMode(Mode.AverageTime)
  69 @OutputTimeUnit(TimeUnit.NANOSECONDS)
  70 @State(Scope.Thread)
  71 @Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
  72 @Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
  73 @Fork(3)
  74 public class JarFileGetVersionedEntry {
  75 
  76     @Param({"512", "1024"})
  77     private int size;
  78 
  79     public JarFile jarFile;
  80     public String[]         entryNames;
  81     public String[]         missingEntryNames;
  82     public StringBuilder[]  entryNameBuilders;
  83     public StringBuilder[]  missingEntryNameBuilders;
  84 
  85     public int index = 0;
  86 
  87     @Setup(Level.Trial)
  88     public void beforeRun() throws IOException {
  89         // Create a test Zip file with the number of entries.
  90         File tempFile = Files.createTempFile("jar-mr-micro", ".jar").toFile();
  91         tempFile.deleteOnExit();
  92 
  93         entryNameBuilders = new StringBuilder[size];
  94         missingEntryNameBuilders = new StringBuilder[size];
  95 
  96         entryNames = new String[size];
  97         missingEntryNames = new String[size];
  98 
  99         Attributes attributes = new Attributes();
 100         attributes.putValue("Manifest-Version", "1.0");
 101         attributes.putValue("Created-By", "1.9.0-internal (Oracle Corporation)");
 102         attributes.putValue("Multi-Release", "true");
 103         try (FileOutputStream fos = new FileOutputStream(tempFile);
 104              JarOutputStream jos = new JarOutputStream(fos)) {
 105 
 106             JarEntry me = new JarEntry("META-INF/MANIFEST.MF");
 107             jos.putNextEntry(me);
 108             Manifest manifest = new Manifest();
 109             manifest.getMainAttributes().putAll(attributes);
 110             manifest.write(jos);
 111             jos.closeEntry();
 112 
 113             Random random = new Random(4711);
 114             for (int i = 0; i < size; i++) {
 115                 String ename = "entry-" + (random.nextInt(90000) + 10000) + "-" + i;
 116                 jos.putNextEntry(new ZipEntry(ename));
 117 
 118                 entryNames[i] = ename;
 119                 entryNameBuilders[i] = new StringBuilder(ename);
 120 
 121                 missingEntryNames[i] = ename + "-";
 122                 missingEntryNameBuilders[i] = new StringBuilder(missingEntryNames[i]);
 123 
 124                 ename = "META-INF/versions/" + JarFile.runtimeVersion().feature() + "/" + ename;
 125                 jos.putNextEntry(new ZipEntry(ename));
 126             }
 127         }
 128 
 129         jarFile = new JarFile(tempFile, true, ZipFile.OPEN_READ,
 130                 JarFile.runtimeVersion());
 131     }
 132 
 133     @Benchmark
 134     public void getEntryHit() {
 135         if (index >= size) {
 136             index = 0;
 137         }
 138         jarFile.getEntry(entryNames[index++]);
 139     }
 140 
 141     @Benchmark
 142     public void getEntryMiss() {
 143         if (index >= size) {
 144             index = 0;
 145         }
 146         jarFile.getEntry(missingEntryNames[index++]);
 147     }
 148 
 149     @Benchmark
 150     public void getEntryHitUncached() {
 151         if (index >= size) {
 152             index = 0;
 153         }
 154         jarFile.getEntry(entryNameBuilders[index++].toString());
 155     }
 156 
 157     @Benchmark
 158     public void getEntryMissUncached() {
 159         if (index >= size) {
 160             index = 0;
 161         }
 162         jarFile.getEntry(missingEntryNameBuilders[index++].toString());
 163     }
 164 }