Skip to content

Commit f47b31e

Browse files
committed
Make a faster create method in NumberedDirProvider
1 parent e72f4b7 commit f47b31e

File tree

3 files changed

+263
-4
lines changed

3 files changed

+263
-4
lines changed

stroom-proxy/stroom-proxy-app/src/main/java/stroom/proxy/app/handler/NumberedDirProvider.java

+22-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import stroom.util.logging.LogUtil;
66
import stroom.util.shared.NullSafe;
77

8-
import com.google.common.base.Strings;
9-
108
import java.io.IOException;
119
import java.io.UncheckedIOException;
1210
import java.nio.file.Files;
@@ -56,8 +54,28 @@ public Path get() throws IOException {
5654
* @param num The number to create the name from.
5755
* @return A `0` padded string representing the supplied number.
5856
*/
59-
private String create(final long num) {
60-
return Strings.padStart(Long.toString(num), 10, '0');
57+
static String create(final long num) {
58+
// This method is ~2x as quick as
59+
// return Strings.padStart(Long.toString(num), 10, '0');
60+
if (num == 0) {
61+
return "0000000000";
62+
} else {
63+
final String str = String.valueOf(num);
64+
int len = str.length();
65+
return switch (len) {
66+
case 0 -> "0000000000";
67+
case 1 -> "000000000" + str;
68+
case 2 -> "00000000" + str;
69+
case 3 -> "0000000" + str;
70+
case 4 -> "000000" + str;
71+
case 5 -> "00000" + str;
72+
case 6 -> "0000" + str;
73+
case 7 -> "000" + str;
74+
case 8 -> "00" + str;
75+
case 9 -> "0" + str;
76+
default -> str;
77+
};
78+
}
6179
}
6280

6381
/**

stroom-proxy/stroom-proxy-app/src/test/java/stroom/proxy/app/handler/TestNumberedDirProvider.java

+138
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package stroom.proxy.app.handler;
22

33
import stroom.proxy.app.handler.NumberedDirProvider.DirId;
4+
import stroom.test.common.TestUtil;
5+
import stroom.test.common.TestUtil.TimedCase;
46
import stroom.test.common.util.test.StroomUnitTest;
57
import stroom.util.io.FileUtil;
8+
import stroom.util.logging.LambdaLogger;
9+
import stroom.util.logging.LambdaLoggerFactory;
610

11+
import com.google.common.base.Strings;
712
import org.assertj.core.api.Assertions;
13+
import org.junit.jupiter.api.Disabled;
814
import org.junit.jupiter.api.Test;
915
import org.junit.jupiter.api.io.TempDir;
1016

@@ -17,6 +23,8 @@
1723

1824
public class TestNumberedDirProvider extends StroomUnitTest {
1925

26+
private static final LambdaLogger LOGGER = LambdaLoggerFactory.getLogger(TestNumberedDirProvider.class);
27+
2028
@Test
2129
void test() throws Exception {
2230
final Path dir = Files.createTempDirectory("test");
@@ -99,4 +107,134 @@ void testDirIdComparator() {
99107
.toList())
100108
.containsExactly(2L, 5L, 7L, 10L);
101109
}
110+
111+
@Test
112+
void testCreate() {
113+
for (int i = 0; i < 10; i++) {
114+
final long num = (long) Math.pow(10, i);
115+
final String str = Long.toString(num);
116+
LOGGER.info("str: {}, len: {}", str, str.length());
117+
final String expected = Strings.padStart(str, 10, '0');
118+
final String actual = NumberedDirProvider.create(num);
119+
Assertions.assertThat(actual)
120+
.isEqualTo(expected);
121+
}
122+
}
123+
124+
@Disabled // Manual perf test only
125+
@Test
126+
void testCreatePerf() {
127+
128+
final TimedCase timedCase1 = TimedCase.of("create1", (round, iterations) -> {
129+
for (long i = 0; i < iterations; i++) {
130+
final String str = create1(i);
131+
//noinspection ConstantValue
132+
if (str == null) {
133+
throw new RuntimeException("null");
134+
}
135+
}
136+
});
137+
138+
final TimedCase timedCase2 = TimedCase.of("create2", (round, iterations) -> {
139+
for (long i = 0; i < iterations; i++) {
140+
final String str = create2(i);
141+
if (str == null) {
142+
throw new RuntimeException("null");
143+
}
144+
}
145+
});
146+
147+
final TimedCase timedCase3 = TimedCase.of("create3", (round, iterations) -> {
148+
for (long i = 0; i < iterations; i++) {
149+
final String str = create3(i);
150+
if (str == null) {
151+
throw new RuntimeException("null");
152+
}
153+
}
154+
});
155+
156+
final TimedCase timedCase4 = TimedCase.of("create4", (round, iterations) -> {
157+
for (long i = 0; i < iterations; i++) {
158+
final String str = create4(i);
159+
if (str == null) {
160+
throw new RuntimeException("null");
161+
}
162+
}
163+
});
164+
165+
TestUtil.comparePerformance(
166+
5,
167+
10_000_000L,
168+
LOGGER::info,
169+
timedCase1,
170+
timedCase2,
171+
timedCase3,
172+
timedCase4);
173+
}
174+
175+
private static String create1(final long num) {
176+
return Strings.padStart(Long.toString(num), 10, '0');
177+
}
178+
179+
private static String create2(final long num) {
180+
if (num == 0) {
181+
return "0000000000";
182+
} else {
183+
int length = (int) (Math.log10(num) + 1);
184+
return switch (length) {
185+
case 0 -> "0000000000";
186+
case 1 -> "000000000" + num;
187+
case 2 -> "00000000" + num;
188+
case 3 -> "0000000" + num;
189+
case 4 -> "000000" + num;
190+
case 5 -> "00000" + num;
191+
case 6 -> "0000" + num;
192+
case 7 -> "000" + num;
193+
case 8 -> "00" + num;
194+
case 9 -> "0" + num;
195+
case 10 -> "" + num;
196+
default -> throw new IllegalArgumentException("num is too big");
197+
};
198+
}
199+
}
200+
201+
private static String create3(final long num) {
202+
if (num == 0) {
203+
return "0000000000";
204+
} else {
205+
final String str = String.valueOf(num);
206+
int len = str.length();
207+
return switch (len) {
208+
case 0 -> "0000000000";
209+
case 1 -> "000000000" + str;
210+
case 2 -> "00000000" + str;
211+
case 3 -> "0000000" + str;
212+
case 4 -> "000000" + str;
213+
case 5 -> "00000" + str;
214+
case 6 -> "0000" + str;
215+
case 7 -> "000" + str;
216+
case 8 -> "00" + str;
217+
case 9 -> "0" + str;
218+
default -> str;
219+
};
220+
}
221+
}
222+
223+
private static String create4(final long num) {
224+
final String str = String.valueOf(num);
225+
int len = str.length();
226+
return switch (len) {
227+
case 0 -> "0000000000";
228+
case 1 -> "000000000" + str;
229+
case 2 -> "00000000" + str;
230+
case 3 -> "0000000" + str;
231+
case 4 -> "000000" + str;
232+
case 5 -> "00000" + str;
233+
case 6 -> "0000" + str;
234+
case 7 -> "000" + str;
235+
case 8 -> "00" + str;
236+
case 9 -> "0" + str;
237+
default -> str;
238+
};
239+
}
102240
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package stroom.proxy.app.handler;
2+
3+
import stroom.util.logging.LogUtil;
4+
5+
import com.google.common.base.Strings;
6+
import org.openjdk.jmh.annotations.Benchmark;
7+
import org.openjdk.jmh.annotations.BenchmarkMode;
8+
import org.openjdk.jmh.annotations.Fork;
9+
import org.openjdk.jmh.annotations.Level;
10+
import org.openjdk.jmh.annotations.Measurement;
11+
import org.openjdk.jmh.annotations.Mode;
12+
import org.openjdk.jmh.annotations.Param;
13+
import org.openjdk.jmh.annotations.Scope;
14+
import org.openjdk.jmh.annotations.Setup;
15+
import org.openjdk.jmh.annotations.State;
16+
import org.openjdk.jmh.infra.Blackhole;
17+
18+
import java.util.function.LongFunction;
19+
20+
public class TestNumberedDirProviderBenchmark {
21+
22+
@Fork(value = 1, warmups = 1)
23+
@Benchmark
24+
@BenchmarkMode(Mode.Throughput)
25+
@Measurement(iterations = 1)
26+
public void benchProviderMethod1(ExecutionPlan plan, Blackhole blackhole) {
27+
final LongFunction<String> function = plan.func;
28+
blackhole.consume(function.apply(plan.input));
29+
}
30+
31+
@State(Scope.Benchmark)
32+
public static class ExecutionPlan {
33+
34+
@Param({"5", "500000000"})
35+
public long input;
36+
37+
@Param({"1", "3"})
38+
public int methodNo;
39+
40+
public LongFunction<String> func;
41+
42+
@Setup(Level.Invocation)
43+
public void setUp() {
44+
if (methodNo == 1) {
45+
func = ExecutionPlan::create1;
46+
} else if (methodNo == 2) {
47+
func = ExecutionPlan::create2;
48+
} else if (methodNo == 3) {
49+
func = ExecutionPlan::create3;
50+
} else {
51+
throw new RuntimeException(LogUtil.message("Bad methodNo {}", methodNo));
52+
}
53+
}
54+
55+
private static String create1(final long num) {
56+
return Strings.padStart(Long.toString(num), 10, '0');
57+
}
58+
59+
private static String create2(final long num) {
60+
if (num == 0) {
61+
return "0000000000";
62+
} else {
63+
int length = (int) (Math.log10(num) + 1);
64+
return switch (length) {
65+
case 0 -> "0000000000";
66+
case 1 -> "000000000" + num;
67+
case 2 -> "00000000" + num;
68+
case 3 -> "0000000" + num;
69+
case 4 -> "000000" + num;
70+
case 5 -> "00000" + num;
71+
case 6 -> "0000" + num;
72+
case 7 -> "000" + num;
73+
case 8 -> "00" + num;
74+
case 9 -> "0" + num;
75+
case 10 -> "" + num;
76+
default -> throw new IllegalArgumentException("num is too big");
77+
};
78+
}
79+
}
80+
81+
private static String create3(final long num) {
82+
if (num == 0) {
83+
return "0000000000";
84+
} else {
85+
final String str = String.valueOf(num);
86+
int len = str.length();
87+
return switch (len) {
88+
case 0 -> "0000000000";
89+
case 1 -> "000000000" + str;
90+
case 2 -> "00000000" + str;
91+
case 3 -> "0000000" + str;
92+
case 4 -> "000000" + str;
93+
case 5 -> "00000" + str;
94+
case 6 -> "0000" + str;
95+
case 7 -> "000" + str;
96+
case 8 -> "00" + str;
97+
case 9 -> "0" + str;
98+
default -> str;
99+
};
100+
}
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)