Už je to nějaký pátek, co jsem objevil knihovnu JavaSlang, která do Javy přináší trochu funkcionálního paradigmatu a dále taky pohodlnější způsob, jak pracovat se streamy. Říkal jsem si, jestli by byl dobrý nápad tuto knihovnu použít v některých našich projektech a tak jsem se rozhodl udělat benchmark pro verzi 2.0.0-RC4, jak je na tom s rychlostí oproti standardnímu Stream API v Javě 8.
Příprava
Pro každý běh jsem si připravil List se 100 000 čísly a každou metodu nechám proběhnout 1000x, aby měl JIT dostatek času (a motivace) na optimalizaci – snad to bude stačit :-).
public class JavaSlangBenchmarkTest {
private int runCycles = 1000;
private List<Integer> numbers;
@Before
public void setUp() throws Exception {
numbers = IntStream.range(0, 100000)
.boxed()
.collect(Collectors.toList());
}
Test JavaSlangu
@Test
public void testToListSlang() {
for ( int i = 0; i < runCycles; i++) {
Stream.ofAll(numbers)
.filter( n -> n % 2 == 0)
.map( n -> n*2)
.map(String::valueOf)
.toList();
}
}
Výsledek: 14,9s. Zajímavé však je, že pokud použiju .toJavaList(), je to o něco rychlejší: 11,7s.
Dále zkusme spojit List v řetězec:
@Test
public void testToStringSlang() {
for ( int i = 0; i < runCycles; i++) {
Stream.ofAll(numbers)
.filter( n -> n % 2 == 0)
.map( n -> n*2)
.map(String::valueOf)
.mkString(",");
}
}
Výsledek: 13,9s
A na závěr třeba grupování. Musel jsem to omezit pouze na 1000 prvků, ať to netrvá do nekonečna :-).
for ( int i = 0; i < runCycles; i++) {
Stream.ofAll(numbers)
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.map(String::valueOf)
.take(1000)
.toList()
.groupBy( s -> s.length());
}
Výsledek: 15,4s.
Test java.util.stream
A nyní to samé s použitím klasického Stream API:
@Test
public void testToListStandard() {
for ( int i = 0; i < runCycles; i++) {
numbers.stream()
.filter( n -> n % 2 == 0)
.map( n -> n*2)
.map(String::valueOf)
.collect(Collectors.toList());
}
}
Výsledek: 2,9s.
@Test
public void testToStringStandard() {
for ( int i = 0; i < runCycles; i++) {
numbers.stream()
.filter( n -> n % 2 == 0)
.map( n -> n*2)
.map(String::valueOf)
.collect(Collectors.joining(","));
}
}
Výsledek: 4s.
@Test
public void testGroupByStandard() {
for ( int i = 0; i < runCycles; i++) {
numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.map(String::valueOf)
.limit(1000)
.collect(Collectors.groupingBy(s -> s.length()));
}
}
Výsledek: 0,225s.
Závěr
JavaSlang nám sice přináší spoustu zajímavých metod (podobně jako kolekce v jazyce Scala), ale je zde třeba počítat s podstatným výkonnostním úbytkem.
| Test | JavaSlang | java.util.stream |
|---|---|---|
| toList | 14,9s / 11,7s | 2,9s |
| toString | 13,9s | 4s |
| groupBy | 15,4s | 0,225s |