我将使用以下导入。
import Data.Char
import Data.List
import qualified Data.Text.Lazy as T
import Criterion.Main
import Test.QuickCheck
与博客文章中的此参考实现相比,我设法获得了惊人的速度:
capitalize :: T.Text -> T.Text
capitalize = T.tail . T.scanl (\a b -> if isSpace a then toUpper b else b) ' '
使用mapAccumL 要快得多。这是String 和Text 版本。
{-# INLINE f #-}
f a b = (b, if isSpace a then toUpper b else b)
string :: String -> String
string = snd . mapAccumL f ' '
text :: T.Text -> T.Text
text = snd . T.mapAccumL f ' '
首先,让我们确保优化是有效的
λ. quickCheck $ \xs ->
capitalize (T.pack xs) == text (T.pack xs)
+++ OK, passed 100 tests.
现在获取来自criterion 的一些基准测试结果,在 3.2 M 的 Lorem Ipsum 文件上运行每个函数。这是我们的参考速度。
benchmarking reference
collecting 100 samples, 1 iterations each, in estimated 56.19690 s
mean: 126.4616 ms, lb 126.0039 ms, ub 128.6617 ms, ci 0.950
std dev: 4.432843 ms, lb 224.7290 us, ub 10.55986 ms, ci 0.950
String 仅比优化参考 Text 版本慢 30% 左右,而使用 Text 的 mapAccumL 版本几乎快两倍!
benchmarking string
collecting 100 samples, 1 iterations each, in estimated 16.45751 s
mean: 165.1451 ms, lb 165.0927 ms, ub 165.2112 ms, ci 0.950
std dev: 301.0338 us, lb 250.2601 us, ub 370.2991 us, ci 0.950
benchmarking text
collecting 100 samples, 1 iterations each, in estimated 16.88929 s
mean: 67.67978 ms, lb 67.65432 ms, ub 67.72081 ms, ci 0.950
std dev: 162.8791 us, lb 114.9346 us, ub 246.0348 us, ci 0.950
但还有更容易获得的收益。 Data.Char.isSpace 以其性能问题而闻名,所以让我们试试快速的Data.Attoparsec.Char8.isSpace。我们的quickcheck 测试不会通过,但性能很棒。
benchmarking string/atto
collecting 100 samples, 1 iterations each, in estimated 12.91881 s
mean: 129.2176 ms, lb 129.1328 ms, ub 129.4941 ms, ci 0.950
std dev: 705.3433 us, lb 238.2757 us, ub 1.568524 ms, ci 0.950
benchmarking text/atto
collecting 100 samples, 1 iterations each, in estimated 15.76300 s
mean: 38.63183 ms, lb 38.62850 ms, ub 38.63730 ms, ci 0.950
std dev: 21.41514 us, lb 15.27777 us, ub 33.98801 us, ci 0.950
我们现在比原始参考快了大约3x。为了比较,非常快的python代码(只是调用C),
print open('lorem.txt').read().title()
翻阅30ms中的文本文件。