1 /* 2 * Copyright (c) , 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 package org.openjdk.bench.java.util.stream.tasks.PhoneCode; 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.Scope; 31 import org.openjdk.jmh.annotations.State; 32 33 import java.util.Collection; 34 import java.util.Collections; 35 import java.util.HashSet; 36 import java.util.concurrent.TimeUnit; 37 import java.util.function.Function; 38 import java.util.stream.Collectors; 39 import java.util.stream.IntStream; 40 import java.util.stream.Stream; 41 42 import static org.openjdk.bench.java.util.stream.tasks.PhoneCode.PhoneCodeProblem.wordsForNumber; 43 44 /** 45 * This benchmark compare various strategies solving the phone code problem. 46 * The result should offer some insights on strength/drawbacks of underlying 47 * implementation. 48 */ 49 @BenchmarkMode(Mode.Throughput) 50 @OutputTimeUnit(TimeUnit.MINUTES) 51 @State(Scope.Benchmark) 52 public class Bulk { 53 // several method choke up with 6-digit problem 54 private final static int SIZE = 5; 55 private Stream<String> join(String head, 56 String tail, 57 Function<String, Stream<String>> encoder) 58 { 59 Stream<String> s = wordsForNumber(head).stream(); 60 61 if (! tail.isEmpty()) { 62 s = s.flatMap(h -> encoder.apply(tail).map(t -> h + " " + t)); 63 } 64 65 return s; 66 } 67 68 private Stream<String> encode_par1(String number) { 69 return IntStream.range(1, number.length() + 1) 70 .parallel() 71 .boxed() 72 .flatMap(i -> join(number.substring(0, i), 73 number.substring(i), 74 this::encode_par1)); 75 } 76 77 private Stream<String> encode_par2(String number) { 78 return IntStream.range(1, number.length() + 1) 79 .boxed() 80 .parallel() 81 .flatMap(i -> join(number.substring(0, i), 82 number.substring(i), 83 this::encode_par2)); 84 } 85 86 private Stream<String> encode_ser(String number) { 87 return IntStream.range(1, number.length() + 1) 88 .boxed() 89 .flatMap(i -> join(number.substring(0, i), 90 number.substring(i), 91 this::encode_ser)); 92 } 93 94 private Stream<String> encode_loop_concat(String number) { 95 if (number.isEmpty()) { 96 return Stream.empty(); 97 } 98 // full number 99 Stream<String> s = wordsForNumber(number).stream(); 100 // split 101 for (int i = 1; i < number.length(); i++) { 102 s = Stream.concat(s, join(number.substring(0, i), 103 number.substring(i), 104 this::encode_loop_concat)); 105 } 106 107 return s; 108 } 109 110 private Collection<String> encode_loop_collect(String number) { 111 if (number.isEmpty()) { 112 return Collections.emptySet(); 113 } 114 115 Collection<String> rv = new HashSet<>(); 116 117 for (int i = 1; i <= number.length(); i++) { 118 join(number.substring(0, i), 119 number.substring(i), 120 s -> encode_loop_collect(s).stream()).forEach(rv::add); 121 } 122 123 return rv; 124 } 125 126 private Collection<String> encode_inline(String number) { 127 if (number.isEmpty()) { 128 return Collections.emptySet(); 129 } 130 131 Collection<String> rv = new HashSet<>(); 132 133 for (int i = 1; i < number.length(); i++) { 134 String front = number.substring(0, i); 135 String rest = number.substring(i); 136 wordsForNumber(front).stream() 137 .flatMap(h -> encode_inline(rest).stream().map(t -> h + " " + t)) 138 .forEach(rv::add); 139 } 140 141 rv.addAll(wordsForNumber(number)); 142 143 return rv; 144 } 145 146 @Benchmark 147 public int bulk_par_range_concurrent() { 148 // force collect 149 return PhoneCodeProblem.get(SIZE) 150 .flatMap(this::encode_par1) 151 .collect(Collectors.toConcurrentMap( 152 Function.identity(), 153 Function.identity(), 154 (l, r) -> l)) 155 .keySet() 156 .size(); 157 } 158 159 @Benchmark 160 public int bulk_par_boxed_range_concurrent() { 161 // force collect 162 return PhoneCodeProblem.get(SIZE) 163 .flatMap(this::encode_par2) 164 .collect(Collectors.toConcurrentMap( 165 Function.identity(), 166 Function.identity(), 167 (l, r) -> l)) 168 .keySet() 169 .size(); 170 } 171 172 @Benchmark 173 public int bulk_par_range() { 174 // force collect 175 return PhoneCodeProblem.get(SIZE) 176 .flatMap(this::encode_par1) 177 .collect(Collectors.toSet()) 178 .size(); 179 } 180 181 @Benchmark 182 public int bulk_par_boxed_range() { 183 // force collect 184 return PhoneCodeProblem.get(SIZE) 185 .flatMap(this::encode_par2) 186 .collect(Collectors.toSet()) 187 .size(); 188 } 189 190 @Benchmark 191 public int bulk_ser_range() { 192 // force collect 193 return PhoneCodeProblem.get(SIZE) 194 .flatMap(this::encode_ser) 195 .collect(Collectors.toSet()) 196 .size(); 197 } 198 199 @Benchmark 200 public int bulk_ser_loop_concat() { 201 // force collect 202 return PhoneCodeProblem.get(SIZE) 203 .flatMap(this::encode_loop_concat) 204 .collect(Collectors.toSet()) 205 .size(); 206 } 207 208 @Benchmark 209 public int bulk_ser_loop_collect() { 210 return PhoneCodeProblem.get(SIZE) 211 .map(this::encode_loop_collect) 212 .map(Collection::size) 213 .reduce(0, Integer::sum); 214 } 215 216 @Benchmark 217 public int bulk_ser_inline() { 218 return PhoneCodeProblem.get(SIZE) 219 .map(this::encode_inline) 220 .map(Collection::size) 221 .reduce(0, Integer::sum); 222 } 223 }