1 /*
2  * Copyright Amazon.com Inc. 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.zip;
25 
26 import org.openjdk.jmh.annotations.Benchmark;
27 import org.openjdk.jmh.annotations.BenchmarkMode;
28 import org.openjdk.jmh.annotations.Mode;
29 import org.openjdk.jmh.annotations.OutputTimeUnit;
30 import org.openjdk.jmh.annotations.Level;
31 import org.openjdk.jmh.annotations.Param;
32 import org.openjdk.jmh.annotations.Scope;
33 import org.openjdk.jmh.annotations.Setup;
34 import org.openjdk.jmh.annotations.State;
35 
36 import java.io.ByteArrayInputStream;
37 import java.io.File;
38 import java.io.FileInputStream;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.util.Random;
42 import java.util.concurrent.TimeUnit;
43 import java.util.zip.Inflater;
44 import java.util.zip.InflaterOutputStream;
45 import java.util.zip.DeflaterOutputStream;
46 
47 /**
48  * Test the average execution time of "InflaterOutputStream.write()" depending
49  * on the size of the internal "InflaterOutputStream" byte buffer and the size
50  * of the compressed data input buffer passed to "write()".
51  *
52  * The size of the compressed data input buffer is controlled by the "size"
53  * parameter which runs from "512" to "65536".
54  *
55  * The size of the internal byte buffer is a multiple of "size" controlled by
56  * the "scale" paramter which runs from "1" to "8".
57  *
58  * For peak perfomance the internal buffer should be big enough to hold all
59  * the data decompressed from the input buffer. This of course depends on
60  * the compression rate of the input data. E.g. if the compression rate of
61  * the compressed input data is 4 (i.e. the original input data was compressed
62  * to 1/4 of its original size) the internal buffer should be four times bigger
63  * than the size of the compressed data buffer passed to "write()" because in
64  * that case one single call to the native zlib "inflate()" method is sufficent
65  * to decompress all data and store it in the output buffer from where it can
66  * be written to the output stream with one single call to the output streams
67  * "write()" method.
68  */
69 @BenchmarkMode(Mode.AverageTime)
70 @OutputTimeUnit(TimeUnit.MILLISECONDS)
71 @State(Scope.Thread)
72 public class Streams {
73 
74     private FileInputStream in;
75     private FileOutputStream out;
76     @Param({"512", "1024", "2048", "4096", "8192", "16384", "32768", "65536"})
77     private int size;
78     @Param({"1", "2", "4", "8"})
79     private int scale;
80     private byte[] buf;
81 
82     private static byte[] data = new byte[1024 * 1024];
83 
84     @Setup(Level.Trial)
beforeRun()85     public void beforeRun() throws IOException {
86         // The reason for this whole dance is to programmatically create a one
87         // megabyte file which can be compressed by factor ~6. This will give
88         // us good results for the various scale factors (i.e. the relation
89         // between the deflated input buffer and the inflated output buffer).
90         // We achieve the desired compression factor by creating a 64 byte
91         // array of random data and than fill the final 1mb file with random
92         // 8-byte substrings of these 64 random bytes. This vaguely mimics
93         // a language with 8 character words over a set of 64 different characters.
94         final int characters = 64;
95         final int wordLength = 8;
96         buf = new byte[characters];
97         Random r = new Random(123456789);
98         r.nextBytes(buf);
99         for (int i = 0; i < data.length / wordLength; i++) {
100             System.arraycopy(buf, r.nextInt(characters - wordLength), data, i * wordLength, wordLength);
101         }
102         ByteArrayInputStream bais = new ByteArrayInputStream(data);
103 
104         File deflated = File.createTempFile("inflaterOutputStreamWrite", ".deflated");
105         deflated.deleteOnExit();
106         FileOutputStream fout = new FileOutputStream(deflated);
107         DeflaterOutputStream defout = new DeflaterOutputStream(fout);
108         bais.transferTo(defout);
109         // We need to close the DeflaterOutputStream in order to flush all the
110         // compressed data in the Deflater and the underlying FileOutputStream.
111         defout.close();
112         in = new FileInputStream(deflated);
113         File inflated = File.createTempFile("inflaterOutputStreamWrite", ".inflated");
114         inflated.deleteOnExit();
115         out = new FileOutputStream(inflated);
116     }
117 
118     @Setup(Level.Iteration)
beforeIteration()119     public void beforeIteration() throws IOException {
120         in.getChannel().position(0);
121         out.getChannel().position(0);
122         buf = new byte[size];
123     }
124 
125     @Benchmark
inflaterOutputStreamWrite()126     public void inflaterOutputStreamWrite() throws IOException {
127         in.getChannel().position(0);
128         out.getChannel().position(0);
129         InflaterOutputStream inflate = new InflaterOutputStream(out, new Inflater(), scale * size);
130         int len;
131         // buf.length == size
132         while ((len = in.read(buf)) != -1) {
133             inflate.write(buf, 0, len);
134         }
135         inflate.finish();
136     }
137 }
138