-
Couldn't load subscription status.
- Fork 186
Closed
Description
I noticed that foldMapWithIndex on sequences is significantly faster than foldMap in certain cases:
benchmarking 50000/getSum . foldMap Sum
time 1.051 ms (1.003 ms .. 1.093 ms)
0.988 R² (0.983 R² .. 0.994 R²)
mean 1.123 ms (1.094 ms .. 1.154 ms)
std dev 110.7 μs (93.16 μs .. 131.6 μs)
variance introduced by outliers: 72% (severely inflated)
benchmarking 50000/getSum . foldMapWithIndex (const Sum)
time 435.5 μs (433.0 μs .. 438.2 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 433.1 μs (431.5 μs .. 435.4 μs)
std dev 6.490 μs (4.910 μs .. 9.372 μs)
benchmarking 50000/foldl' (+) 0
time 420.6 μs (419.2 μs .. 422.8 μs)
1.000 R² (0.999 R² .. 1.000 R²)
mean 429.2 μs (425.4 μs .. 433.8 μs)
std dev 15.71 μs (12.04 μs .. 19.77 μs)
variance introduced by outliers: 30% (moderately inflated)
Unfortunately, the core output is too large for me to really understand, but I think there's some specialisation not happening which does happen in the manually-specialised foldMapWithIndex.
When the definition for foldMap mimics foldMapWithIndex:
instance Foldable Seq where
foldMap f' (Seq xs') = foldMapTreeE (lift_elem f') xs'
where
lift_elem :: (a -> m) -> (Elem a -> m)
#if __GLASGOW_HASKELL__ >= 708
lift_elem g = coerce g
#else
lift_elem g = \(Elem a) -> g a
#endif
foldMapTreeE :: Monoid m => (Elem a -> m) -> FingerTree (Elem a) -> m
foldMapTreeE _ EmptyT = mempty
foldMapTreeE f (Single xs) = f xs
foldMapTreeE f (Deep _ pr m sf) =
foldMapDigitE f pr <>
foldMapTreeN (foldMapNodeE f) m <>
foldMapDigitE f sf
foldMapTreeN :: Monoid m => (Node a -> m) -> FingerTree (Node a) -> m
foldMapTreeN _ EmptyT = mempty
foldMapTreeN f (Single xs) = f xs
foldMapTreeN f (Deep _ pr m sf) =
foldMapDigitN f pr <>
foldMapTreeN (foldMapNodeN f) m <>
foldMapDigitN f sf
foldMapDigitE :: Monoid m => (Elem a -> m) -> Digit (Elem a) -> m
foldMapDigitE f t = foldDigit (<>) f t
foldMapDigitN :: Monoid m => (Node a -> m) -> Digit (Node a) -> m
foldMapDigitN f t = foldDigit (<>) f t
foldMapNodeE :: Monoid m => (Elem a -> m) -> Node (Elem a) -> m
foldMapNodeE f t = foldNode (<>) f t
foldMapNodeN :: Monoid m => (Node a -> m) -> Node (Node a) -> m
foldMapNodeN f t = foldNode (<>) f t
#if __GLASGOW_HASKELL__
{-# INLINABLE foldMap #-}
#endifThe speeds are more what you'd expect:
benchmarking 50000/getSum . foldMap Sum
time 317.9 μs (316.3 μs .. 319.2 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 318.2 μs (316.8 μs .. 320.2 μs)
std dev 5.982 μs (4.938 μs .. 7.338 μs)
variance introduced by outliers: 11% (moderately inflated)
benchmarking 50000/getSum . foldMapWithIndex (const Sum)
time 431.7 μs (428.6 μs .. 434.0 μs)
0.999 R² (0.998 R² .. 1.000 R²)
mean 443.1 μs (436.4 μs .. 451.8 μs)
std dev 23.79 μs (18.88 μs .. 33.82 μs)
variance introduced by outliers: 48% (moderately inflated)
benchmarking 50000/foldl' (+) 0
time 409.0 μs (405.5 μs .. 414.5 μs)
0.998 R² (0.996 R² .. 1.000 R²)
mean 418.6 μs (415.3 μs .. 426.1 μs)
std dev 19.48 μs (15.71 μs .. 27.21 μs)
variance introduced by outliers: 42% (moderately inflated)
Although I'm not entirely sure why.
Metadata
Metadata
Assignees
Labels
No labels