1 /*
   2  * Copyright (c) 2009, 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 import java.util.*;
  25 import java.nio.*;
  26 import java.nio.charset.*;
  27 import java.util.concurrent.*;
  28 import java.util.regex.Pattern;
  29 
  30 /**
  31  * Usage: java StringCodingBenchmark
  32  * [-Diterations=N] [-Dsize=N] [-Dsubsize=N] [-Dmaxchar=N]
  33  * [-Dfilter=REGEXP] [-DSecurityManager=true]
  34  */
  35 public class StrCodingBenchmark {
  36     abstract static class Job {
  37         private final String name;
  38         public Job(String name) { this.name = name; }
  39         public String name() { return name; }
  40         public abstract void work() throws Throwable;
  41     }
  42 
  43     private static void collectAllGarbage() {
  44         final java.util.concurrent.CountDownLatch drained
  45             = new java.util.concurrent.CountDownLatch(1);
  46         try {
  47             System.gc();        // enqueue finalizable objects
  48             new Object() { protected void finalize() {
  49                 drained.countDown(); }};
  50             System.gc();        // enqueue detector
  51             drained.await();    // wait for finalizer queue to drain
  52             System.gc();        // cleanup finalized objects
  53         } catch (InterruptedException e) { throw new Error(e); }
  54     }
  55 
  56     /**
  57      * Runs each job for long enough that all the runtime compilers
  58      * have had plenty of time to warm up, i.e. get around to
  59      * compiling everything worth compiling.
  60      * Returns array of average times per job per run.
  61      */
  62     public static long[] time0(Job ... jobs) throws Throwable {
  63         //final long warmupNanos = 10L * 1000L * 1000L * 1000L;
  64         final long warmupNanos = 100L * 100L;
  65         long[] nanoss = new long[jobs.length];
  66         for (int i = 0; i < jobs.length; i++) {
  67             collectAllGarbage();
  68             long t0 = System.nanoTime();
  69             long t;
  70             int j = 0;
  71             do { jobs[i].work(); j++; }
  72             while ((t = System.nanoTime() - t0) < warmupNanos);
  73             nanoss[i] = t/j;
  74         }
  75         return nanoss;
  76     }
  77 
  78     public static void time(Job ... jobs) throws Throwable {
  79 
  80         long[] warmup = time0(jobs); // Warm up run
  81         long[] nanoss = time0(jobs); // Real timing run
  82         long[] milliss = new long[jobs.length];
  83         double[] ratios = new double[jobs.length];
  84 
  85         final String nameHeader   = "Method";
  86         final String millisHeader = "Millis";
  87         final String ratioHeader  = "Ratio";
  88 
  89         int nameWidth   = nameHeader.length();
  90         int millisWidth = millisHeader.length();
  91         int ratioWidth  = ratioHeader.length();
  92 
  93         for (int i = 0; i < jobs.length; i++) {
  94             nameWidth = Math.max(nameWidth, jobs[i].name().length());
  95 
  96             milliss[i] = nanoss[i]/(1000L * 1000L);
  97             millisWidth = Math.max(millisWidth,
  98                                    String.format("%d", milliss[i]).length());
  99 
 100             ratios[i] = (double) nanoss[i] / (double) nanoss[0];
 101             ratioWidth = Math.max(ratioWidth,
 102                                   String.format("%.3f", ratios[i]).length());
 103         }
 104         String format = String.format("%%-%ds %%%dd %n",
 105                                       nameWidth, millisWidth);
 106         String headerFormat = String.format("%%-%ds %%%ds%n",
 107                                             nameWidth, millisWidth);
 108         System.out.printf(headerFormat, "Method", "Millis");
 109 
 110         // Print out absolute and relative times, calibrated against first job
 111         for (int i = 0; i < jobs.length; i++)
 112             System.out.printf(format, jobs[i].name(), milliss[i], ratios[i]);
 113     }
 114 
 115     public static Job[] filter(Pattern filter, Job[] jobs) {
 116         if (filter == null) return jobs;
 117         Job[] newJobs = new Job[jobs.length];
 118         int n = 0;
 119         for (Job job : jobs)
 120             if (filter.matcher(job.name()).find())
 121                 newJobs[n++] = job;
 122         // Arrays.copyOf not available in JDK 5
 123         Job[] ret = new Job[n];
 124         System.arraycopy(newJobs, 0, ret, 0, n);
 125         return ret;
 126     }
 127 
 128     static class PermissiveSecurityManger extends SecurityManager {
 129         @Override public void checkPermission(java.security.Permission p) {
 130         }
 131     }
 132 
 133     public static void main(String[] args) throws Throwable {
 134         final int itrs = Integer.getInteger("iterations", 100000);
 135         final int size       = Integer.getInteger("size", 2048);
 136         final int subsize    = Integer.getInteger("subsize", 128);
 137         final int maxchar    = Integer.getInteger("maxchar", 128);
 138         final String regex = System.getProperty("filter");
 139         final Pattern filter = (regex == null) ? null : Pattern.compile(regex);
 140         final boolean useSecurityManager = Boolean.getBoolean("SecurityManager");
 141         if (useSecurityManager)
 142             System.setSecurityManager(new PermissiveSecurityManger());
 143         final Random rnd = new Random();
 144 
 145         for (Charset charset:  Charset.availableCharsets().values()) {
 146             if (!("ISO-8859-1".equals(charset.name()) ||
 147                   "US-ASCII".equals(charset.name()) ||
 148                   charset.newDecoder() instanceof sun.nio.cs.SingleByte.Decoder))
 149                 continue;
 150             final String csn = charset.name();
 151             final Charset cs = charset;
 152             final StringBuilder sb = new StringBuilder();
 153             {
 154                 final CharsetEncoder enc = cs.newEncoder();
 155                 for (int i = 0; i < size; ) {
 156                     char c = (char) rnd.nextInt(maxchar);
 157                     if (enc.canEncode(c)) {
 158                         sb.append(c);
 159                         i++;
 160                     }
 161                 }
 162             }
 163             final String string = sb.toString();
 164             final byte[] bytes  = string.getBytes(cs);
 165 
 166             System.out.printf("%n--------%s---------%n", csn);
 167             for (int sz = 4; sz <= 2048; sz *= 2) {
 168                 System.out.printf("   [len=%d]%n", sz);
 169                 final byte[] bs  = Arrays.copyOf(bytes, sz);
 170                 final String str = new String(bs, csn);
 171                 Job[] jobs = {
 172                     new Job("String decode: csn") {
 173                     public void work() throws Throwable {
 174                         for (int i = 0; i < itrs; i++)
 175                             new String(bs, csn);
 176                     }},
 177 
 178                     new Job("String decode: cs") {
 179                     public void work() throws Throwable {
 180                         for (int i = 0; i < itrs; i++)
 181                             new String(bs, cs);
 182                     }},
 183 
 184                     new Job("String encode: csn") {
 185                     public void work() throws Throwable {
 186                         for (int i = 0; i < itrs; i++)
 187                                 str.getBytes(csn);
 188                     }},
 189 
 190                     new Job("String encode: cs") {
 191                     public void work() throws Throwable {
 192                          for (int i = 0; i < itrs; i++)
 193                           str.getBytes(cs);
 194                     }},
 195                 };
 196                 time(filter(filter, jobs));
 197             }
 198         }
 199     }
 200 }