Przeglądałem istniejące opcje dla regex w Haskell, i chciałem zrozumieć, skąd wzięła się luka w wydajności, porównując różne opcje ze sobą, a zwłaszcza z prostym wywołaniem grep .. .Haskell Regex performance
mam stosunkowo małe (~ 110M, w porównaniu do zwykłych kilku 10s G w większości moich przypadków użycia) pliku śledzenia:
$ du radixtracefile
113120 radixtracefile
$ wc -l radixtracefile
1051565 radixtracefile
- raz pierwszy starał się znaleźć jak wiele dopasowań (arbitralnych) wzór
.*504.*ll
się tam przez grep:
$ time grep -nE ".*504.*ll" radixtracefile | wc -l
309
real 0m0.211s
user 0m0.202s
sys 0m0.010s
- I spojrzał Text.Regex.TDFA (wersja 1.2.1) z Data.ByteString:
import Control.Monad.Loops
import Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Text.Regex.TDFA
import qualified Data.ByteString as B
main = do
f <- B.readFile "radixtracefile"
matches :: [[B.ByteString]] <- f =~~ ".*504.*ll"
mapM_ (putStrLn . show . head) matches
Budowa i działanie:
$ ghc -O2 test-TDFA.hs -XScopedTypeVariables
[1 of 1] Compiling Main (test-TDFA.hs, test-TDFA.o)
Linking test-TDFA ...
$ time ./test-TDFA | wc -l
309
real 0m4.463s
user 0m4.431s
sys 0m0.036s
- Potem spojrzał na Data.Text.ICU.Regex (wersja 0.7.0.1) z obsługą Unicode:
import Control.Monad.Loops
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Text.ICU.Regex
main = do
re <- regex [] $ T.pack ".*504.*ll"
f <- TIO.readFile "radixtracefile"
setText re f
whileM_ (findNext re) $ do
a <- start re 0
putStrLn $ "last match at :"++(show a)
budowania i uruchamiania:
$ ghc -O2 test-ICU.hs
[1 of 1] Compiling Main (test-ICU.hs, test-ICU.o)
Linking test-ICU ...
$ time ./test-ICU | wc -l
309
real 1m36.407s
user 1m36.090s
sys 0m0.169s
Używam wersji ghc 7.6.3. Nie miałem okazji testowania innych opcji wyrażeń regularnych Haskell. Wiedziałem, że nie dostanę tego, co miałem z grepem i byłem z tego zadowolony, ale mniej więcej 20 razy wolniej niż TDFA i ByteString ... To bardzo przerażające. I naprawdę nie mogę zrozumieć, dlaczego tak właśnie jest, a naiwnie, że to było opakowanie w natywnym backend ... Czy w jakiś sposób nie używam tego modułu poprawnie?
(I niech nie wspominając OIOM + Tekst combo który przechodzi dachu)
Czy istnieje możliwość, że nie zostały jeszcze przetestowane, które uczyniłoby mnie szczęśliwszym?
EDIT:
- Text.Regex.PCRE (wersja 0.94.4) z Data.ByteString:
import Control.Monad.Loops
import Data.Maybe
import Text.Regex.PCRE
import qualified Data.ByteString as B
main = do
f <- B.readFile "radixtracefile"
matches :: [[B.ByteString]] <- f =~~ ".*504.*ll"
mapM_ (putStrLn . show . head) matches
budowania i uruchamiania:
$ ghc -O2 test-PCRE.hs -XScopedTypeVariables
[1 of 1] Compiling Main (test-PCRE.hs, test-PCRE.o)
Linking test-PCRE ...
$ time ./test-PCRE | wc -l
309
real 0m1.442s
user 0m1.412s
sys 0m0.031s
Lepsze, ale nadal z czynnikiem ~ 7-ish ...
O czym dokładnie mówisz o wdrożeniu Haskella? To może mieć znaczną różnicę. – vonbrand
@vonbrand: Fragmenty wymieniają 'ghc -O2'. Czy wersja (kompilator/biblioteka) ma znaczenie, lub co chcesz wiedzieć? – Bergi
@vonbrand: '$ ghc --version' podaje' The Glorious Glasgow Haskell Compilation System, wersja 7.6.3'. 'lista cabal regex-tdfa' daje (między innymi)' regex-tdfa [...] Wersje zainstalowane: 1.2.1' i 'cabal list text-icu',' text-icu [...] Zainstalowane wersje : 0.7.0.1'. Czy to była informacja, której szukałeś? Zmienię moje pytanie, aby dodać te informacje. – gameboo