"Но я не умею в вольтметр, смотри, у меня есть лампочка"
Вот код. Я специально закомментировал всё лишнее, чтобы можно было тестировать отдельно.
И добавил цикл. Мы же программу не ради однократного запуска ускоряем?
Вот результаты у меня:Код:public class MyTest {
public static void main(String[] args) {
new MyTest();
}
public MyTest() {
for(int i=0; i<100; i++) {
long time = System.currentTimeMillis();
// try {
// strTestD(new StringBuilder());
// } catch (Exception e) {}
try {
strTestU(new StringBuffer());
} catch (Exception e) {}
// strTestQ(new StringBuilder());
// strTestT(new StringBuffer());
// strTestS();
// strTestV();
time = System.currentTimeMillis() - time;
System.out.println(i + ", StringBuffer = " + time);
}
}
private void strTestU(Appendable obj) throws IOException {
for (int i = 0; i < 1E7; i++) {
obj.append("Irbis");
}
System.out.println("StringBuffer "+obj.toString().length());
}
private void strTestD(Appendable obj) throws IOException{
for (int i = 0; i < 1E7; i++) {
obj.append("Irbis");
}
System.out.println("StringBuilder "+obj.toString().length());
}
private void strTestT(StringBuffer obj) {
for (int i = 0; i < 1E7; i++) {
obj.append("Irbis");
}
System.out.println("StringBuffer "+obj.toString().length());
}
private void strTestQ(StringBuilder obj) {
for (int i = 0; i < 1E7; i++) {
obj.append("Irbis");
}
System.out.println("StringBuilder "+obj.toString().length());
}
private void strTestV() {
StringBuffer obj = new StringBuffer();
for (int i = 0; i < 1E7; i++) {
obj.append("Irbis");
}
System.out.println("StringBuffer "+obj.toString().length());
}
private void strTestS() {
StringBuilder obj = new StringBuilder();
for (int i = 0; i < 1E7; i++) {
obj.append("Irbis");
}
System.out.println("StringBuilder "+obj.toString().length());
}
}
Вот результаты для builder'аКод:StringBuffer 50000000
0, time = 680
StringBuffer 50000000
1, time = 1064
StringBuffer 50000000
2, time = 774
StringBuffer 50000000
3, time = 622
StringBuffer 50000000
4, time = 529
StringBuffer 50000000
5, time = 391
StringBuffer 50000000
6, time = 241
StringBuffer 50000000
7, time = 491
StringBuffer 50000000
8, time = 228
StringBuffer 50000000
9, time = 183
StringBuffer 50000000
10, time = 238
StringBuffer 50000000
11, time = 135
StringBuffer 50000000
12, time = 215
StringBuffer 50000000
13, time = 217
StringBuffer 50000000
14, time = 190
StringBuffer 50000000
15, time = 232
StringBuffer 50000000
16, time = 241
StringBuffer 50000000
17, time = 142
StringBuffer 50000000
18, time = 228
StringBuffer 50000000
Во первых, первые выполнения работают медленнее, чем последующие.Код:StringBuilder 50000000
0, StringBuilder = 413
StringBuilder 50000000
1, StringBuilder = 429
StringBuilder 50000000
2, StringBuilder = 437
StringBuilder 50000000
3, StringBuilder = 361
StringBuilder 50000000
4, StringBuilder = 277
StringBuilder 50000000
5, StringBuilder = 243
StringBuilder 50000000
6, StringBuilder = 224
StringBuilder 50000000
7, StringBuilder = 472
StringBuilder 50000000
8, StringBuilder = 238
StringBuilder 50000000
9, StringBuilder = 187
StringBuilder 50000000
10, StringBuilder = 338
StringBuilder 50000000
11, StringBuilder = 131
StringBuilder 50000000
12, StringBuilder = 133
StringBuilder 50000000
13, StringBuilder = 143
StringBuilder 50000000
14, StringBuilder = 131
StringBuilder 50000000
15, StringBuilder = 186
StringBuilder 50000000
16, StringBuilder = 210
StringBuilder 50000000
17, StringBuilder = 155
StringBuilder 50000000
18, StringBuilder = 154
StringBuilder 50000000
19, StringBuilder = 263
StringBuilder 50000000
20, StringBuilder = 175
StringBuilder 50000000
21, StringBuilder = 160
StringBuilder 50000000
22, StringBuilder = 151
StringBuilder 50000000
23, StringBuilder = 167
StringBuilder 50000000
24, StringBuilder = 166
StringBuilder 50000000
25, StringBuilder = 159
Во-вторых, результаты сильно завязаны на работу garbage collector'а. Ещё бы. Тут создаётся строка на 100 мегабайт и тут же выбрасывается.
Если добавить -verbose:gc, то сразу видно, что в начале работы JVM наращивает память (число в скобках растёт):
-verbose:gc
А если сразу выделить нужное количество памяти, то тест "разгоняется" гораздо бодрее:Код:StringBuffer 50000000
0, StringBuilder = 511
[GC (Allocation Failure) 416330K->365519K(497152K), 0.0094296 secs]
StringBuffer 50000000
1, StringBuilder = 601
[GC (Allocation Failure) 749030K->679696K(882688K), 0.0055799 secs]
StringBuffer 50000000
2, StringBuilder = 729
[GC (Allocation Failure) 1101541K->1012296K(1202688K), 0.0132236 secs]
StringBuffer 50000000
3, StringBuilder = 459
[GC (Allocation Failure) 1386750K->1266657K(1579520K), 0.0077790 secs]
StringBuffer 50000000
4, StringBuilder = 475
[GC (Allocation Failure) 1659860K->1414081K(1727488K), 0.0096428 secs]
StringBuffer 50000000
5, StringBuilder = 423
[GC (Allocation Failure) 1807284K->1561569K(2141184K), 0.0084224 secs]
-verbose:gc -Xms2g -Xmx2g
О чём это я?Код:StringBuffer 50000000
0, StringBuilder = 459
[GC (Allocation Failure) 501368K->37511K(2010112K), 0.0235897 secs]
StringBuffer 50000000
1, StringBuilder = 484
[GC (Allocation Failure) 504648K->74336K(2010112K), 0.0412089 secs]
StringBuffer 50000000
2, StringBuilder = 344
[GC (Allocation Failure) 476581K->74288K(2010112K), 0.0269566 secs]
StringBuffer 50000000
3, StringBuilder = 348
[GC (Allocation Failure) 467187K->74368K(2010112K), 0.0090283 secs]
StringBuffer 50000000
4, StringBuilder = 310
[GC (Allocation Failure) 467553K->74336K(1997824K), 0.0092012 secs]
StringBuffer 50000000
5, StringBuilder = 340
[GC (Allocation Failure) 467747K->74344K(2001920K), 0.0123743 secs]
StringBuffer 50000000
6, StringBuilder = 330
[GC (Allocation Failure) 467572K->74254K(2001408K), 0.0167865 secs]
О том, что конкретным замером вы измеряете не скорость StringBuilder, а:
1) "Время однократного запуска". Тут вообще просто свет тушить можно. Даже от простого перезапуска (или от перестановки порядка вызовов) диспозиция может измениться.
2) Работу garbage collector'а (и эргономик по автоматической настройке памяти, ведь, вы не указывали -Xms/-Xmx?)
3) Работу on-stack-replacement. Известно (из многочисленных экспериментов, и из того, что пишут сами авторы), что OSR генерирует менее эффективный машинный код.
4) Работу непонятного кода. Необходимость построить 10 мегабайтную строку и тут же её выбросить у вас часто возникала? Я не говорю, что именно такой тест замерять нельзя. Я просто говорю, что изначально тема развилась из "buffer капец как тормозной".
1, 2 и 3 устраняются, если использовать jmh. Вы специально упираетесь, и не используете jmh?