- 你的測試基於I/O操作(最昂貴的操作)
- 如果你想使用並行流,你必須採取線程創建的時間開銷考慮到了。所以,只有當你的操作從中受益,然後使用它(這是重型操作的情況)。如果不是,那麼只需使用普通流或常規的for-loop。
測量的基本規則:
- 不要使用I/O操作。
- 重複相同的測試更多然後只是一次。
因此,如果我們不得不再次重新制定測試方案,那麼我們可能有一個測試輔助類定義如下:
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Benchmark {
public static <T> T performTest(Callable<T> callable, int iteration, String name) throws Exception {
Map<String, Iteraion> map = new HashMap<>();
T last = null;
for (int i = 0; i < iteration; i++) {
long s = System.nanoTime();
T temp = callable.call();
long f = System.nanoTime();
map.put(UUID.randomUUID().toString(), new Iteraion(s, f));
if (i == iteration - 1) {
last = temp;
}
}
System.out.print("TEST :\t" + name + "\t\t\t");
System.out.print("ITERATION: " + map.size());
long sum = 0l;
for (String i : map.keySet()) {
sum += (map.get(i).finish - map.get(i).start);
}
long avg = (sum/map.size())/1000000;
System.out.println("\t\t\tAVERAGE: " + avg + " ms");
return last;
}
public interface Callable<T> {
T call() throws Exception;
}
static class Iteraion {
Long start;
Long finish;
public Iteraion(Long s, Long f) {
start = s;
finish = f;
}
}
}
現在我們可以使用不同的執行相同的測試,曾多次操作。以下代碼顯示使用兩種不同方案執行的測試。
import java.util.ArrayList;
import java.util.List;
import static java.lang.Math.*;
@SuppressWarnings("unused")
public class Test {
public static void main(String[] args) {
try {
final int iteration = 100;
final List<String> data = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
data.add("data" + i);
}
/**
* Scenario 1
*/
Benchmark.performTest(new Callable<Void>() {
@Override
public Void call() throws Exception {
data.parallelStream().forEach(x -> {
x.trim();
});
return (Void) null;
}
}, iteration, "PARALEL_STREAM_ASSIGN_VAL");
Benchmark.performTest(new Callable<Void>() {
@Override
public Void call() throws Exception {
data.stream().forEach(x -> {
x.trim();
});
return (Void) null;
}
}, iteration, "NORMAL_STREAM_ASSIGN_VAL");
Benchmark.performTest(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < data.size(); i++) {
data.get(i).trim();
}
return (Void) null;
}
}, iteration, "NORMAL_FOREACH_ASSIGN_VAL");
/**
* Scenario 2
*/
Benchmark.performTest(new Callable<Void>() {
@Override
public Void call() throws Exception {
data.parallelStream().forEach(x -> {
Integer i = Integer.parseInt(x.substring(4, x.length()));
double d = tan(atan(tan(atan(i))));
});
return (Void) null;
}
}, iteration, "PARALEL_STREAM_COMPUTATION");
Benchmark.performTest(new Callable<Void>() {
@Override
public Void call() throws Exception {
data.stream().forEach(x -> {
Integer i = Integer.parseInt(x.substring(4, x.length()));
double d = tan(atan(tan(atan(i))));
});
return (Void) null;
}
}, iteration, "NORMAL_STREAM_COMPUTATION");
Benchmark.performTest(new Callable<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < data.size(); i++) {
Integer x = Integer.parseInt(data.get(i).substring(4, data.get(i).length()));
double d = tan(atan(tan(atan(x))));
}
return (Void) null;
}
}, iteration, "NORMAL_FOREACH_COMPUTATION");
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 第一種方案使用所
trim()
方法相同的測試100次爲包含10_000_000元件,因此它使用並行流,則普通流和最後的一個列表老學校for循環。
- 第二種方案使用與第一種情況相同的技術對相同列表執行一些相對較重的操作,如
tan(atan(tan(atan(i))))
。
的結果是:
// First scenario, average times
Parallel stream: 78 ms
Regular stream: 113 ms
For-loop: 110 ms
// Second scenario, average times
Parallel stream: 1397 ms
Regular stream: 3866 ms
For-loop: 3826 ms
請注意,您可以調試上面的代碼,那麼您注意到並行數據流的程序下名字[ForkJoinPool-1]
創建三個額外的線程 ,[ForkJoinPool-2]
和[ForkJoinPool-3]
。
編輯: 順序流和for循環使用調用者線程。
打印已同步。 –
同意管理線程和從線程收集數據也在「映射」操作的情況下..如果並行流處理10000000,然後應該花費較少的時間理想......所以我們應該避免使用並行流。 –
雖然代碼的基礎很快,但在每次迭代中都包含一個打印。與其他計算相比,打印是一個速度緩慢的命令。它與常規的I/O命令甚至互聯網連接一樣。所以基本上這個印刷報表非常主宰你的時間測量的其他部分。再加上你總是有管理多線程的開銷。但是在大多數情況下,這種開銷比多線程的好處要小。但正如所說的,您的打印主宰了一切,嘗試去除它以接收更準確的時間測量。 – Zabuza