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.io.InputStream;
32 import java.nio.file.Files;
33 import java.util.Random;
34 import java.util.concurrent.TimeUnit;
35 import java.util.jar.JarFile;
36 import java.util.jar.JarOutputStream;
37 import java.util.jar.Manifest;
38 import java.util.zip.ZipEntry;
39 
40 /**
41  * Simple benchmark measuring cost of various operations relating to jar
42  * meta-inf and manifests, especially costs incurred during opening of the
43  * file, and when opening an input stream (which runs
44  * JarFile.maybeInstantiateVerifier)
45  *
46  * Before JDK-8244624:
47  * Benchmark                          (size)  Mode  Cnt       Score    Error   Units
48  * getManifestFromJarWithManifest       1024  avgt    5     232.437   31.535   us/op
49  *   gc.alloc.rate.norm                 1024  avgt    5  206410.627    2.833    B/op
50  * getStreamFromJarWithManifest         1024  avgt    5     277.696   32.078   us/op
51  *   gc.alloc.rate.norm                 1024  avgt    5  250454.252    2.452    B/op
52  * getStreamFromJarWithNoManifest       1024  avgt    5     312.432   58.663   us/op
53  *   gc.alloc.rate.norm                 1024  avgt    5  301802.644   13.276    B/op
54  * getStreamFromJarWithSignatureFiles   1024  avgt    5     315.752   55.048   us/op
55  *   gc.alloc.rate.norm                 1024  avgt    5  305354.934   14.093    B/op
56  *
57  * With JDK-8244624:
58  * Benchmark                          (size)  Mode  Cnt       Score    Error   Units
59  * getManifestFromJarWithManifest       1024  avgt    5     215.242   32.085   us/op
60  *   gc.alloc.rate.norm                 1024  avgt    5  196609.220   14.788    B/op
61  * getStreamFromJarWithManifest         1024  avgt    5     216.435   10.876   us/op
62  *   gc.alloc.rate.norm                 1024  avgt    5  187960.147    9.026    B/op
63  * getStreamFromJarWithNoManifest       1024  avgt    5     204.256   25.744   us/op
64  *   gc.alloc.rate.norm                 1024  avgt    5  186784.347    1.841    B/op
65  * getStreamFromJarWithSignatureFiles   1024  avgt    5     247.972   38.574   us/op
66  *   gc.alloc.rate.norm                 1024  avgt    5  211577.268   15.109    B/op
67  */
68 @BenchmarkMode(Mode.AverageTime)
69 @OutputTimeUnit(TimeUnit.MICROSECONDS)
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 JarFileMeta {
75 
76     @Param({"512", "1024"})
77     private int size;
78 
79     public File jarManifest;
80     public File jarNoManifest;
81     public File jarManifestSignature;
82 
83     public int index = 0;
84 
85     @Setup(Level.Trial)
beforeRun()86     public void beforeRun() throws IOException {
87         jarNoManifest = createJar(false, false);
88         jarManifest = createJar(true, false);
89         jarManifestSignature = createJar(true, true);
90     }
91 
createJar(boolean manifest, boolean signatureFiles)92     private File createJar(boolean manifest, boolean signatureFiles) throws IOException {
93         // Create a test Zip file with the number of entries.
94         File tempFile = Files.createTempFile("jar-micro", ".jar").toFile();
95         tempFile.deleteOnExit();
96 
97         try (FileOutputStream fos = new FileOutputStream(tempFile);
98              JarOutputStream zos = manifest
99                      ? new JarOutputStream(fos, new Manifest())
100                      : new JarOutputStream(fos)) {
101 
102             // Always add this
103             zos.putNextEntry(new ZipEntry("README"));
104 
105             Random random = new Random(4711);
106             for (int i = 0; i < size; i++) {
107                 String ename = "directory-" + (random.nextInt(90000) + 10000) + "-" + i + "/";
108                 if (random.nextInt(100) > 70) {
109                     ename = "META-INF/" + ename;
110 
111                 }
112                 zos.putNextEntry(new ZipEntry(ename));
113 
114                 ename += "entry-"  + (random.nextInt(90000) + 10000)  + "-" + i;
115                 if (signatureFiles && random.nextInt(100) > 95) {
116                     ename += ".DSA";
117                 }
118                 zos.putNextEntry(new ZipEntry(ename));
119             }
120         }
121         return tempFile;
122     }
123 
openGetStreamAndClose(File file)124     private InputStream openGetStreamAndClose(File file) throws IOException {
125         JarFile jf = new JarFile(file);
126         InputStream is = jf.getInputStream(jf.getEntry("README"));
127         jf.close();
128         // we'll never actually read from the closed stream, rather just
129         // return it to avoid DCE
130         return is;
131     }
132 
133     @Benchmark
getStreamFromJarWithManifest()134     public InputStream getStreamFromJarWithManifest() throws IOException {
135         return openGetStreamAndClose(jarManifest);
136     }
137 
138     @Benchmark
getStreamFromJarWithNoManifest()139     public InputStream getStreamFromJarWithNoManifest() throws IOException {
140         return openGetStreamAndClose(jarNoManifest);
141     }
142 
143     @Benchmark
getStreamFromJarWithSignatureFiles()144     public InputStream getStreamFromJarWithSignatureFiles() throws IOException {
145         return openGetStreamAndClose(jarManifestSignature);
146     }
147 
148     @Benchmark
getManifestFromJarWithManifest()149     public Manifest getManifestFromJarWithManifest() throws IOException {
150         JarFile jf = new JarFile(jarManifest);
151         Manifest mf = jf.getManifest();
152         jf.close();
153         return mf;
154     }
155 }
156