FlaterCriticalArray.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 2011, 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/**
25 * ZIP inflater/deflater performance.
26 */
27
28/*
29 * Run this test on JDK 6 and on later
30 * JDKs to compare results:
31 *     java -server -Xms1024M -Xmx1024M -Ddebug=true FlaterCriticalArray
32 *
33 * The performance issues can be readily seen on JDK 6, and so this code is
34 * written to compile on that platform: it does *not* use any JDK 7 - specific
35 * features.
36 */
37
38import java.io.*;
39import java.nio.*;
40import java.util.*;
41import java.util.zip.*;
42
43public class FlaterCriticalArray {
44    // If true, print information about performance
45    private static final boolean debug = System.getProperty("debug") != null;
46
47    private static void debug(String s) {
48        if (debug) System.out.println(s);
49    }
50
51    private static void debug(String name, String inOut, long time, int length) {
52        debug(name + ": Duration of " + inOut + "(in ms): " + time);
53        debug(name + ": " + inOut + "d data length: " + length + " bytes");
54    }
55
56    private static byte[] grow(byte[] a, int capacity) {
57        while (a.length < capacity) {
58            byte[] a2 = new byte[a.length * 2];
59            System.arraycopy(a, 0, a2, 0, a.length);
60            a = a2;
61        }
62        return a;
63    }
64
65    private static byte[] trim(byte[] a, int length) {
66        byte[] res = new byte[length];
67        System.arraycopy(a, 0, res, 0, length);
68        return res;
69    }
70
71    /*
72     * Base class for individual test cases
73     */
74    private abstract static class TestCase {
75        protected String name;  // For information in debug messages
76        protected byte data[];  // Data to be deflated and subsequently inflated
77        protected int level;    // Compression level for deflater
78
79        protected TestCase(String name, byte data[]) {
80            this(name, data, -1);
81        }
82
83        protected TestCase(String name, byte data[], int level) {
84            this.name = name;
85            this.data = data;
86            this.level = level;
87        }
88
89        public void runTest() throws Throwable {
90            long time0, time1;
91            byte deflated[], inflated[];
92
93            debug("");
94
95            time0 = System.currentTimeMillis();
96            deflated = deflate(data, level);
97            time1 = System.currentTimeMillis();
98            inform("Deflate", time1 - time0, deflated.length);
99
100            time0 = System.currentTimeMillis();
101            inflated = inflate(deflated);
102            time1 = System.currentTimeMillis();
103            inform("Inflate", time1 - time0, inflated.length);
104
105            check(Arrays.equals(data, inflated),
106                  name + ": Inflated and deflated arrays do not match");
107        }
108
109        private void inform(String inOut, long duration, int length) {
110            debug(name, inOut, duration, length);
111        }
112
113        protected abstract byte[] deflate(byte data[], int level) throws Throwable;
114
115        protected abstract byte[] inflate(byte deflated[]) throws Throwable;
116    }
117
118    /*
119     * Following are  the individual test cases
120     */
121
122    private static class StrideTest extends TestCase {
123        static final int STRIDE = 1024;
124
125        public StrideTest(byte data[], int level) {
126            super("STRIDE", data, level);
127        }
128
129        protected byte[] deflate(byte in[], int level) throws Throwable {
130            final int len = in.length;
131            final Deflater deflater = new Deflater(level);
132            final byte[] smallBuffer = new byte[32];
133            byte[] flated = new byte[32];
134            int count = 0;
135            for (int i = 0; i<len; i+= STRIDE) {
136                deflater.setInput(in, i, Math.min(STRIDE, len-i));
137                while (!deflater.needsInput()) {
138                    int n = deflater.deflate(smallBuffer);
139                    flated = grow(flated, count + n);
140                    System.arraycopy(smallBuffer, 0, flated, count, n);
141                    count += n;
142                }
143            }
144            deflater.finish();
145            int n;
146            do {
147                n = deflater.deflate(smallBuffer);
148                flated = grow(flated, count + n);
149                System.arraycopy(smallBuffer, 0, flated, count, n);
150                count += n;
151            } while (n > 0);
152            return trim(flated, count);
153        }
154
155        protected byte[] inflate(byte in[]) throws Throwable {
156            final int len = in.length;
157            final Inflater inflater = new Inflater();
158            final byte[] smallBuffer = new byte[3200];
159
160            byte[] flated = new byte[32];
161            int count = 0;
162
163            for (int i = 0; i<len; i+= STRIDE) {
164                inflater.setInput(in, i, Math.min(STRIDE, len-i));
165                while (!inflater.needsInput()) {
166                    int n;
167                    while ((n = inflater.inflate(smallBuffer)) > 0) {
168                        flated = grow(flated, count + n);
169                        System.arraycopy(smallBuffer, 0, flated, count, n);
170                        count += n;
171                    }
172                }
173            }
174            return trim(flated, count);
175        }
176    }
177
178    private static class NoStrideTest extends TestCase {
179        public NoStrideTest(byte data[], int level) {
180            super("NO STRIDE", data, level);
181        }
182
183        public byte[] deflate(byte in[], int level) throws Throwable {
184            final Deflater flater = new Deflater(level);
185            flater.setInput(in);
186            flater.finish();
187            final byte[] smallBuffer = new byte[32];
188            byte[] flated = new byte[32];
189            int count = 0;
190            int n;
191            while ((n = flater.deflate(smallBuffer)) > 0) {
192                flated = grow(flated, count + n);
193                System.arraycopy(smallBuffer, 0, flated, count, n);
194                count += n;
195            }
196            return trim(flated, count);
197        }
198
199        public byte[] inflate(byte in[]) throws Throwable {
200            final Inflater flater = new Inflater();
201            flater.setInput(in);
202            final byte[] smallBuffer = new byte[32];
203            byte[] flated = new byte[32];
204            int count = 0;
205            int n;
206            while ((n = flater.inflate(smallBuffer)) > 0) {
207                flated = grow(flated, count + n);
208                System.arraycopy(smallBuffer, 0, flated, count, n);
209                count += n;
210            }
211            return trim(flated, count);
212        }
213    }
214
215    /**
216     * Check Deflater{In,Out}putStream by way of GZIP{In,Out}putStream
217     */
218    private static class GZIPTest extends TestCase {
219        public GZIPTest(byte data[]) {
220            super("GZIP", data);
221        }
222
223        public byte[] deflate(byte data[], int ignored) throws Throwable {
224            ByteArrayOutputStream baos = new ByteArrayOutputStream();
225            OutputStream gzos = new GZIPOutputStream(baos);
226            gzos.write(data, 0, data.length);
227            gzos.close();
228            return baos.toByteArray();
229        }
230
231        public byte[] inflate(byte deflated[]) throws Throwable {
232            InputStream bais = new ByteArrayInputStream(deflated);
233            GZIPInputStream gzis = new GZIPInputStream(bais);
234            byte[] inflated = new byte[data.length];
235            int numRead = 0;
236            int count = 0;
237            while ((numRead = gzis.read(inflated, count, data.length - count)) > 0) {
238                count += numRead;
239            }
240            check(count == data.length, name + ": Read " + count + "; expected " + data.length);
241            return inflated;
242        }
243    }
244
245    public static void realMain(String[] args) throws Throwable {
246        byte data[];
247        int level = -1;
248        if (args.length > 0) {
249          level = Integer.parseInt(args[0]);
250        }
251        debug("Using level " + level);
252
253        if (args.length > 1) {
254            FileInputStream fis = new FileInputStream(args[1]);
255            int len = fis.available();
256            data = new byte[len];
257            check(fis.read(data, 0, len) == len, "Did not read complete file");
258            debug("Original data from " + args[1]);
259            fis.close();
260        } else {
261            ByteBuffer bb = ByteBuffer.allocate(8);
262            ByteArrayOutputStream baos = new ByteArrayOutputStream();
263            for (int i = 0; i < 1024 * 64; i++) { // data length
264                bb.putDouble(0, Math.random());
265                baos.write(bb.array(), 0, 8);
266            }
267            data = baos.toByteArray();
268            debug("Original data from random byte array");
269        }
270        debug("Original data length: " + data.length + " bytes");
271
272        new StrideTest(data, level).runTest();
273        new NoStrideTest(data, level).runTest();
274        new GZIPTest(data).runTest();
275    }
276
277    //--------------------- Infrastructure ---------------------------
278    static volatile int passed = 0, failed = 0;
279    static void pass() {passed++;}
280    static void pass(String msg) {System.out.println(msg); passed++;}
281    static void fail() {failed++; Thread.dumpStack();}
282    static void fail(String msg) {System.out.println(msg); fail();}
283    static void unexpected(Throwable t) {failed++; t.printStackTrace();}
284    static void unexpected(Throwable t, String msg) {
285        System.out.println(msg); failed++; t.printStackTrace();}
286    static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
287    static boolean check(boolean cond, String msg) {if (cond) pass(); else fail(msg); return cond;}
288    static void equal(Object x, Object y) {
289        if (x == null ? y == null : x.equals(y)) pass();
290        else fail(x + " not equal to " + y);}
291    public static void main(String[] args) throws Throwable {
292        try {realMain(args);} catch (Throwable t) {unexpected(t);}
293        System.out.println("\nPassed = " + passed + " failed = " + failed);
294        if (failed > 0) throw new AssertionError("Some tests failed");}
295}
296