Java, Scala a regulární výrazy #2 - rychlost
Stará poučka říká, že v Javě bychom měli regulární výrazy vždy dopředu zkompilovat a pak je opakovaně používat, protože kompilování je časově náročné.
V Javě je na to volání Patten.compile("^===+$")
. Ve Scale je možné
použít kompaktnější zápis za pomocí kouzel implicitních metod
"^===+$".r
.
Ale jak pomalé to vlastně je? To se dá jednoduše zjistit.
Vytáhl jsem JMH, napsal benchmark, který hledá jednoduchý vzor. Jednou vzor nekompiluje, podruhé používá předkompilovaný vzor a potřetí hledá regex jen když je to nezbytně nutné.
@State(Scope.Thread) class regex { var lines: Array[String] = _ val regex = """^===+$""".r @Setup def prepare() = { lines = io.Source.fromFile("export.txt").getLines.toArray } @Benchmark def notCompiled(): Int = lines.count(l => l.matches("^===+$")) @Benchmark def compiled(): Int = lines.count { case regex() => true case _ => false } @Benchmark def compiledWithCheck(): Int = lines.count { l => (l.length > 0 && l.charAt(0) == '=') && regex.findFirstMatchIn(l).nonEmpty } }
Jako testovací data jsem použil kompletní export zdrojových textů blogu k47.cz, které mají dohromady něco kolem 8 MB a 143000 řádek textu.
Výsledky jsou následující:
Benchmark Mode Cnt Score Error Units notCompiled thrpt 6 28,936 ± 2,298 ops/s compiled thrpt 6 94,120 ± 1,195 ops/s compiledWithCheck thrpt 6 526,849 ± 96,101 ops/s
Kompilovaný regex je tři-a-něco-krát rychlejší než nekompilovaný. Ale ten zaostává za případem, kdy se vyhodnocování regexu úplně vyhnu. Přitom regex by měl selhat hned potom, co přečte první znak a tedy by neměl dělat víc práce, než explicitní kontrola. Ale očividně má nezanedbatelnou režii.
Pokud regex nebude často prováděn, je rychlejší dělat něco jako:
// zkompilovaný regex val linkRegex = """(?x) " ([^"]+?) " : \[ ([^\]\n]+?) \]""".r val linkCheck = "\":[" // používat ho následovně if (txt.contains(linkCheck)) { linkRegex.replaceAllIn(txt, m => makeLink(m)) }
Narychlo zkontrolovat, jestli zdrojový řetězec obsahuje nějakou fixní část regexu a teprve potom nechat regex dělat těžkou práci. Ale jako v případě každé optimalizace je třeba profilovat a benchmarkovat.
Tento přístup zrychlil renderování textu asciiblogu o 75%.
Relevantní čtení: