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 |