Home | History | Annotate | Download | only in base
      1 /*
      2  * Copyright (C) 2012 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.base;
     18 
     19 import com.google.caliper.BeforeExperiment;
     20 import com.google.caliper.Benchmark;
     21 import com.google.caliper.Param;
     22 
     23 import java.util.Arrays;
     24 import java.util.Iterator;
     25 
     26 /**
     27  * Benchmarks {@link Joiner} against some common implementations of delimiter-based
     28  * string joining.
     29  *
     30  * @author Adomas Paltanavicius
     31  */
     32 public class JoinerBenchmark {
     33 
     34   private static final String DELIMITER_STRING = ",";
     35   private static final char DELIMITER_CHARACTER = ',';
     36 
     37   private static final Joiner JOINER_ON_STRING = Joiner.on(DELIMITER_STRING);
     38   private static final Joiner JOINER_ON_CHARACTER = Joiner.on(DELIMITER_CHARACTER);
     39 
     40   @Param({"3", "30", "300"}) int count;
     41   @Param({"0", "1", "16", "32", "100"}) int componentLength;
     42 
     43   private Iterable<String> components;
     44 
     45   @BeforeExperiment
     46   void setUp() {
     47     String component = Strings.repeat("a", componentLength);
     48     String[] raw = new String[count];
     49     Arrays.fill(raw, component);
     50     components = Arrays.asList(raw);
     51   }
     52 
     53   /**
     54    * {@link Joiner} with a string delimiter.
     55    */
     56   @Benchmark int joinerWithStringDelimiter(int reps) {
     57     int dummy = 0;
     58     for (int i = 0; i < reps; i++) {
     59       dummy ^= JOINER_ON_STRING.join(components).length();
     60     }
     61     return dummy;
     62   }
     63 
     64   /**
     65    * {@link Joiner} with a character delimiter.
     66    */
     67   @Benchmark int joinerWithCharacterDelimiter(int reps) {
     68     int dummy = 0;
     69     for (int i = 0; i < reps; i++) {
     70       dummy ^= JOINER_ON_CHARACTER.join(components).length();
     71     }
     72     return dummy;
     73   }
     74 
     75   /**
     76    * Mimics what the {@link Joiner} class does internally when no extra options like
     77    * ignoring {@code null} values are used.
     78    */
     79   @Benchmark int joinerInlined(int reps) {
     80     int dummy = 0;
     81     for (int i = 0; i < reps; i++) {
     82       StringBuilder sb = new StringBuilder();
     83       Iterator<String> iterator = components.iterator();
     84       if (iterator.hasNext()) {
     85         sb.append(iterator.next().toString());
     86         while (iterator.hasNext()) {
     87           sb.append(DELIMITER_STRING);
     88           sb.append(iterator.next());
     89         }
     90       }
     91       dummy ^= sb.toString().length();
     92     }
     93     return dummy;
     94   }
     95 
     96   /**
     97    * Only appends delimiter if the accumulated string is non-empty.
     98    * Note: this isn't a candidate implementation for Joiner since it fails on leading
     99    * empty components.
    100    */
    101   @Benchmark int stringBuilderIsEmpty(int reps) {
    102     int dummy = 0;
    103     for (int i = 0; i < reps; i++) {
    104       StringBuilder sb = new StringBuilder();
    105       for (String comp : components) {
    106         if (sb.length() > 0) {
    107           sb.append(DELIMITER_STRING);
    108         }
    109         sb.append(comp);
    110       }
    111       dummy ^= sb.toString().length();
    112     }
    113     return dummy;
    114   }
    115 
    116   /**
    117    * Similar to the above, but keeps a boolean flag rather than checking for the string
    118    * accumulated so far being empty. As a result, it does not have the above-mentioned bug.
    119    */
    120   @Benchmark int booleanIfFirst(int reps) {
    121     int dummy = 0;
    122     for (int i = 0; i < reps; i++) {
    123       StringBuilder sb = new StringBuilder();
    124       boolean append = false;
    125       for (String comp : components) {
    126         if (append) {
    127           sb.append(DELIMITER_STRING);
    128         }
    129         sb.append(comp);
    130         append = true;
    131       }
    132       dummy ^= sb.toString().length();
    133     }
    134     return dummy;
    135   }
    136 
    137   /**
    138    * Starts with an empty delimiter and changes to the desired value at the end of the
    139    * iteration.
    140    */
    141   @Benchmark int assignDelimiter(int reps) {
    142     int dummy = 0;
    143     for (int i = 0; i < reps; i++) {
    144       StringBuilder sb = new StringBuilder();
    145       String delim = "";
    146       for (String comp : components) {
    147         sb.append(delim);
    148         sb.append(comp);
    149         delim = DELIMITER_STRING;
    150       }
    151       dummy ^= sb.toString().length();
    152     }
    153     return dummy;
    154   }
    155 
    156   /**
    157    * Always append the delimiter after the component, and in the very end shortens the buffer
    158    * to get rid of the extra trailing delimiter.
    159    */
    160   @Benchmark int alwaysAppendThenBackUp(int reps) {
    161     int dummy = 0;
    162     for (int i = 0; i < reps; i++) {
    163       StringBuilder sb = new StringBuilder();
    164       for (String comp : components) {
    165         sb.append(comp);
    166         sb.append(DELIMITER_STRING);
    167       }
    168       if (sb.length() > 0) {
    169         sb.setLength(sb.length() - DELIMITER_STRING.length());
    170       }
    171       dummy ^= sb.toString().length();
    172     }
    173     return dummy;
    174   }
    175 }
    176