178 lines
4.9 KiB
Java
178 lines
4.9 KiB
Java
/*
|
|
* Copyright (C) 2012 The Guava Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.common.base;
|
|
|
|
import com.google.caliper.BeforeExperiment;
|
|
import com.google.caliper.Benchmark;
|
|
import com.google.caliper.Param;
|
|
import java.util.Arrays;
|
|
import java.util.Iterator;
|
|
|
|
/**
|
|
* Benchmarks {@link Joiner} against some common implementations of delimiter-based string joining.
|
|
*
|
|
* @author Adomas Paltanavicius
|
|
*/
|
|
public class JoinerBenchmark {
|
|
|
|
private static final String DELIMITER_STRING = ",";
|
|
private static final char DELIMITER_CHARACTER = ',';
|
|
|
|
private static final Joiner JOINER_ON_STRING = Joiner.on(DELIMITER_STRING);
|
|
private static final Joiner JOINER_ON_CHARACTER = Joiner.on(DELIMITER_CHARACTER);
|
|
|
|
@Param({"3", "30", "300"})
|
|
int count;
|
|
|
|
@Param({"0", "1", "16", "32", "100"})
|
|
int componentLength;
|
|
|
|
private Iterable<String> components;
|
|
|
|
@BeforeExperiment
|
|
void setUp() {
|
|
String component = Strings.repeat("a", componentLength);
|
|
String[] raw = new String[count];
|
|
Arrays.fill(raw, component);
|
|
components = Arrays.asList(raw);
|
|
}
|
|
|
|
/** {@link Joiner} with a string delimiter. */
|
|
@Benchmark
|
|
int joinerWithStringDelimiter(int reps) {
|
|
int dummy = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
dummy ^= JOINER_ON_STRING.join(components).length();
|
|
}
|
|
return dummy;
|
|
}
|
|
|
|
/** {@link Joiner} with a character delimiter. */
|
|
@Benchmark
|
|
int joinerWithCharacterDelimiter(int reps) {
|
|
int dummy = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
dummy ^= JOINER_ON_CHARACTER.join(components).length();
|
|
}
|
|
return dummy;
|
|
}
|
|
|
|
/**
|
|
* Mimics what the {@link Joiner} class does internally when no extra options like ignoring {@code
|
|
* null} values are used.
|
|
*/
|
|
@Benchmark
|
|
int joinerInlined(int reps) {
|
|
int dummy = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
StringBuilder sb = new StringBuilder();
|
|
Iterator<String> iterator = components.iterator();
|
|
if (iterator.hasNext()) {
|
|
sb.append(iterator.next().toString());
|
|
while (iterator.hasNext()) {
|
|
sb.append(DELIMITER_STRING);
|
|
sb.append(iterator.next());
|
|
}
|
|
}
|
|
dummy ^= sb.toString().length();
|
|
}
|
|
return dummy;
|
|
}
|
|
|
|
/**
|
|
* Only appends delimiter if the accumulated string is non-empty. Note: this isn't a candidate
|
|
* implementation for Joiner since it fails on leading empty components.
|
|
*/
|
|
@Benchmark
|
|
int stringBuilderIsEmpty(int reps) {
|
|
int dummy = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (String comp : components) {
|
|
if (sb.length() > 0) {
|
|
sb.append(DELIMITER_STRING);
|
|
}
|
|
sb.append(comp);
|
|
}
|
|
dummy ^= sb.toString().length();
|
|
}
|
|
return dummy;
|
|
}
|
|
|
|
/**
|
|
* Similar to the above, but keeps a boolean flag rather than checking for the string accumulated
|
|
* so far being empty. As a result, it does not have the above-mentioned bug.
|
|
*/
|
|
@Benchmark
|
|
int booleanIfFirst(int reps) {
|
|
int dummy = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
StringBuilder sb = new StringBuilder();
|
|
boolean append = false;
|
|
for (String comp : components) {
|
|
if (append) {
|
|
sb.append(DELIMITER_STRING);
|
|
}
|
|
sb.append(comp);
|
|
append = true;
|
|
}
|
|
dummy ^= sb.toString().length();
|
|
}
|
|
return dummy;
|
|
}
|
|
|
|
/**
|
|
* Starts with an empty delimiter and changes to the desired value at the end of the iteration.
|
|
*/
|
|
@Benchmark
|
|
int assignDelimiter(int reps) {
|
|
int dummy = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
StringBuilder sb = new StringBuilder();
|
|
String delim = "";
|
|
for (String comp : components) {
|
|
sb.append(delim);
|
|
sb.append(comp);
|
|
delim = DELIMITER_STRING;
|
|
}
|
|
dummy ^= sb.toString().length();
|
|
}
|
|
return dummy;
|
|
}
|
|
|
|
/**
|
|
* Always append the delimiter after the component, and in the very end shortens the buffer to get
|
|
* rid of the extra trailing delimiter.
|
|
*/
|
|
@Benchmark
|
|
int alwaysAppendThenBackUp(int reps) {
|
|
int dummy = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (String comp : components) {
|
|
sb.append(comp);
|
|
sb.append(DELIMITER_STRING);
|
|
}
|
|
if (sb.length() > 0) {
|
|
sb.setLength(sb.length() - DELIMITER_STRING.length());
|
|
}
|
|
dummy ^= sb.toString().length();
|
|
}
|
|
return dummy;
|
|
}
|
|
}
|