我一直在研究
Haskell中正则表达式的现有选项,我想了解在将各种选项相互比较时,特别是通过简单调用grep时,性能差距来自哪里…
我有一个相对较小的(约110M,与我通常的几十个G在我的大多数用例中)跟踪文件:
@H_403_13@$du radixtracefile 113120 radixtracefile $wc -l radixtracefile 1051565 radixtracefile>我首先尝试找到(任意)模式的多少匹配.* 504. * ll通过grep在那里:
@H_403_13@$time grep -nE ".*504.*ll" radixtracefile | wc -l 309 real 0m0.211s user 0m0.202s sys 0m0.010s>我使用Data.ByteString查看了Text.Regex.TDFA(版本1.2.1):
@H_403_13@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建立和运行:
@H_403_13@$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>然后,我查看了具有Unicode支持的Data.Text.ICU.Regex(版本0.7.0.1):
@H_403_13@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)建立和运行:
@H_403_13@$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我使用ghc版本7.6.3.我还没有测试其他Haskell正则表达式选项的机会.我知道我不会得到我用grep的表现,并且对此感到非常满意,但对于TDFA和ByteString来说,或多或少慢了20倍……这非常可怕.我无法理解为什么它是什么,因为我天真地虽然这是一个本机后端的包装…我不知道怎么没有正确使用该模块?
(我们暂不提及正在通过屋顶的ICU Text组合)
有没有我尚未测试的选项会让我更开心?
编辑:
>带有Data.ByteString的Text.Regex.PCRE(版本0.94.4):
@H_403_13@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建立和运行:
@H_403_13@$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更好,但仍然有~7-ish …
解决方法
所以,在查看了其他库之后,我最终尝试了PCRE.Ligth(版本0.4.0.4):
@H_403_13@import Control.Monad
import Text.Regex.PCRE.Light
import qualified Data.ByteString.Char8 as B
main = do
f <- B.readFile "radixtracefile"
let lines = B.split '\n' f
let re = compile (B.pack ".*504.*ll") []
forM_ lines $\l -> maybe (return ()) print $match re l []
这是我从中得到的:
@H_403_13@$ghc -O2 test-PCRELight.hs -XScopedTypeVariables [1 of 1] Compiling Main ( test-PCRELight.hs,test-PCRELight.o ) Linking test-PCRELight ... $time ./test-PCRELight | wc -l 309 real 0m0.832s user 0m0.803s sys 0m0.027s我认为这对我的目的而言足够好.我可能会试着看看当我手动进行线分割时其他库会发生什么,就像我在这里做的那样,尽管我怀疑它会产生很大的不同.