Ну вы можете догадаться, или вы можете сделать программы и измерить ее - результаты могут вас удивить.
import Data.ByteString as B
import Data.IntMap.Strict as I
import qualified Data.Vector.Unboxed.Mutable as M
import Data.Vector.Unboxed as V
import Criterion
import Criterion.Main
import System.Entropy
import System.IO.Unsafe
import Data.Word
main = do
bs <- getEntropy 1024
defaultMain [ bench "map count" $ nf mapCount bs
, bench "map group sort" $ nf mapGroup bs
, bench "fold counters" $ nf mapFoldCtr bs
, bench "vCount" $ nf vectorCount bs
]
-- O(n*m) (length of bytestring, length of list of element being counted up)
-- My guess: bad
mapCount :: ByteString -> [Int]
mapCount bs = Prelude.map (`B.count` bs) [0x00..0xFF]
-- Notice that B.sort uses counting sort, so there's already lots of
-- duplicate work done here.
-- O() isn't such a big deal as the use of lists - likely allocation and
-- large constant factors.
mapGroup :: ByteString -> [Int]
mapGroup = Prelude.map Prelude.length . Prelude.map B.unpack . B.group . B.sort
mapFoldCtr :: ByteString -> [Int]
mapFoldCtr bs = I.elems $ B.foldl' cnt I.empty bs
where
cnt :: I.IntMap Int -> Word8 -> I.IntMap Int
cnt m k = I.insertWith (+) (fromIntegral k) 1 m
-- create (do { v <- new 2; write v 0 'a'; write v 1 'b'; return v })
vectorCount :: B.ByteString -> [Int]
vectorCount bs = V.toList $ V.create $ do
v <- M.new 256
Prelude.mapM_ (\i -> M.unsafeWrite v i 0) [0..255]
Prelude.mapM_ (\i -> M.unsafeRead v (fromIntegral i) >>= M.unsafeWrite v (fromIntegral i) . (+1)) (B.unpack bs)
return v
И результаты (укороченный) отражают удивительно хорошо на карту группу рода, но оставить распакованное изменяемое решение вектора/стиль массива Unsurprisingly в лидерстве:
benchmarking map count
mean: 308.7067 us, lb 307.3562 us, ub 310.5099 us, ci 0.950
std dev: 7.942305 us, lb 6.269114 us, ub 10.08334 us, ci 0.950
benchmarking map group sort
mean: 43.03601 us, lb 42.93492 us, ub 43.15815 us, ci 0.950
std dev: 567.5979 ns, lb 486.8838 ns, ub 666.0098 ns, ci 0.950
benchmarking fold counters
mean: 191.5338 us, lb 191.1102 us, ub 192.0366 us, ci 0.950
std dev: 2.370183 us, lb 1.995243 us, ub 2.907595 us, ci 0.950
benchmarking vCount
mean: 12.98727 us, lb 12.96037 us, ub 13.02261 us, ci 0.950
std dev: 156.6505 ns, lb 123.6789 ns, ub 198.4892 ns, ci 0.950
Как ни странно, когда я увеличить размер массива байт до 200К, используемый Daniel, то отображение/группы/сортировать часы в на ~ 250us в то время как вектор решения принимает 500us:
benchmarking map count
mean: 5.796340 ms, lb 5.788830 ms, ub 5.805126 ms, ci 0.950
std dev: 41.65349 us, lb 35.69293 us, ub 48.39205 us, ci 0.950
benchmarking map group sort
mean: 260.7405 us, lb 259.2525 us, ub 262.4742 us, ci 0.950
std dev: 8.247289 us, lb 7.127576 us, ub 9.371299 us, ci 0.950
benchmarking fold counters
mean: 3.915101 ms, lb 3.892415 ms, ub 4.006287 ms, ci 0.950
std dev: 201.7632 us, lb 43.13063 us, ub 469.8170 us, ci 0.950
benchmarking vCount
mean: 556.5588 us, lb 545.4895 us, ub 567.9318 us, ci 0.950
std dev: 57.44888 us, lb 51.22270 us, ub 65.91105 us, ci 0.950
found 1 outliers among 100 samples (1.0%)
variance introduced by outliers: 80.038%
variance is severely inflated by outliers
Но разница огромна - возможно, некоторые играть с размерами кучи, чтобы это ушло (по крайней мере, в программной программе), но не быстро или легко для меня.
'IntMap' является лучшим решением (особенно для больших строк). – josejuan
@ josejuan На основании каких доказательств? Из трех, упомянутых в вопросе, решение length/group/sort действительно намного лучше. –
Хорошо, хорошо, вы используете 'B.sort', которые используют ...' unsafeCreate'! (чтобы считать) это обман! : D: D: D – josejuan