Skip to content

Commit be0e5fa

Browse files
ShimuuarRyanGlScott
andcommitted
Reduce arity of foldlOf' and related functions
This is done in order to make GHC inliner more eager to inline them and do that earlier. Standard heuristic is to inline function when all arguments are applied. For example this means foldlOf' will be inlined into body of sumOf and this gives GHC more opportunities for optimization Simple benchmark for summong list using sumOf see 8x performance improvement Fixed #1084 Co-authored-by: Ryan Scott <[email protected]>
1 parent 738418a commit be0e5fa

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

benchmarks/folds.hs

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ main = defaultMainWith config
3131
[ bench "native" $ nf (V.toList . V.indexed) v
3232
, bench "itraversed" $ nf (itoListOf itraversed) v
3333
]
34+
, bgroup "sum"
35+
[ bench "native" $ whnf V.sum v
36+
, bench "each" $ whnf (sumOf each) v
37+
]
3438
]
3539
, bgroup "unboxed-vector"
3640
[ bgroup "toList"
@@ -41,6 +45,10 @@ main = defaultMainWith config
4145
[ bench "native" $ nf (U.toList . U.indexed) u
4246
, bench "vTraverse" $ nf (itoListOf vectorTraverse) u
4347
]
48+
, bgroup "sum"
49+
[ bench "native" $ whnf U.sum u
50+
, bench "each" $ whnf (sumOf each) u
51+
]
4452
]
4553
, bgroup "sequence"
4654
[ bgroup "toList"
@@ -72,6 +80,10 @@ main = defaultMainWith config
7280
[ bench "native" $ nf (zip [(0::Int)..]) l
7381
, bench "itraversed" $ nf (itoListOf itraversed) l
7482
]
83+
, bgroup "sum"
84+
[ bench "native" $ whnf sum l
85+
, bench "each" $ whnf (sumOf each) l
86+
]
7587
]
7688
, bgroup "map"
7789
[ bgroup "toList"

src/Control/Lens/Fold.hs

+32-9
Original file line numberDiff line numberDiff line change
@@ -1754,8 +1754,9 @@ lookupOf l k = foldrOf l (\(k',v) next -> if k == k' then Just v else next) Noth
17541754
-- 'foldr1Of' :: 'Traversal'' s a -> (a -> a -> a) -> s -> a
17551755
-- @
17561756
foldr1Of :: HasCallStack => Getting (Endo (Maybe a)) s a -> (a -> a -> a) -> s -> a
1757-
foldr1Of l f xs = fromMaybe (error "foldr1Of: empty structure")
1758-
(foldrOf l mf Nothing xs) where
1757+
-- See: NOTE: [Inlining and arity]
1758+
foldr1Of l f = fromMaybe (error "foldr1Of: empty structure")
1759+
. foldrOf l mf Nothing where
17591760
mf x my = Just $ case my of
17601761
Nothing -> x
17611762
Just y -> f x y
@@ -1780,7 +1781,8 @@ foldr1Of l f xs = fromMaybe (error "foldr1Of: empty structure")
17801781
-- 'foldl1Of' :: 'Traversal'' s a -> (a -> a -> a) -> s -> a
17811782
-- @
17821783
foldl1Of :: HasCallStack => Getting (Dual (Endo (Maybe a))) s a -> (a -> a -> a) -> s -> a
1783-
foldl1Of l f xs = fromMaybe (error "foldl1Of: empty structure") (foldlOf l mf Nothing xs) where
1784+
-- See: NOTE: [Inlining and arity]
1785+
foldl1Of l f = fromMaybe (error "foldl1Of: empty structure") . foldlOf l mf Nothing where
17841786
mf mx y = Just $ case mx of
17851787
Nothing -> y
17861788
Just x -> f x y
@@ -1800,7 +1802,8 @@ foldl1Of l f xs = fromMaybe (error "foldl1Of: empty structure") (foldlOf l mf No
18001802
-- 'foldrOf'' :: 'Traversal'' s a -> (a -> r -> r) -> r -> s -> r
18011803
-- @
18021804
foldrOf' :: Getting (Dual (Endo (Endo r))) s a -> (a -> r -> r) -> r -> s -> r
1803-
foldrOf' l f z0 xs = foldlOf l f' (Endo id) xs `appEndo` z0
1805+
-- See: NOTE: [Inlining and arity]
1806+
foldrOf' l f z0 = \xs -> foldlOf l f' (Endo id) xs `appEndo` z0
18041807
where f' (Endo k) x = Endo $ \ z -> k $! f x z
18051808
{-# INLINE foldrOf' #-}
18061809

@@ -1818,7 +1821,8 @@ foldrOf' l f z0 xs = foldlOf l f' (Endo id) xs `appEndo` z0
18181821
-- 'foldlOf'' :: 'Traversal'' s a -> (r -> a -> r) -> r -> s -> r
18191822
-- @
18201823
foldlOf' :: Getting (Endo (Endo r)) s a -> (r -> a -> r) -> r -> s -> r
1821-
foldlOf' l f z0 xs = foldrOf l f' (Endo id) xs `appEndo` z0
1824+
-- See: NOTE: [Inlining and arity]
1825+
foldlOf' l f z0 = \xs -> foldrOf l f' (Endo id) xs `appEndo` z0
18221826
where f' x (Endo k) = Endo $ \z -> k $! f z x
18231827
{-# INLINE foldlOf' #-}
18241828

@@ -1838,7 +1842,8 @@ foldlOf' l f z0 xs = foldrOf l f' (Endo id) xs `appEndo` z0
18381842
-- 'foldr1Of'' :: 'Traversal'' s a -> (a -> a -> a) -> s -> a
18391843
-- @
18401844
foldr1Of' :: HasCallStack => Getting (Dual (Endo (Endo (Maybe a)))) s a -> (a -> a -> a) -> s -> a
1841-
foldr1Of' l f xs = fromMaybe (error "foldr1Of': empty structure") (foldrOf' l mf Nothing xs) where
1845+
-- See: NOTE: [Inlining and arity]
1846+
foldr1Of' l f = fromMaybe (error "foldr1Of': empty structure") . foldrOf' l mf Nothing where
18421847
mf x Nothing = Just $! x
18431848
mf x (Just y) = Just $! f x y
18441849
{-# INLINE foldr1Of' #-}
@@ -1859,7 +1864,8 @@ foldr1Of' l f xs = fromMaybe (error "foldr1Of': empty structure") (foldrOf' l mf
18591864
-- 'foldl1Of'' :: 'Traversal'' s a -> (a -> a -> a) -> s -> a
18601865
-- @
18611866
foldl1Of' :: HasCallStack => Getting (Endo (Endo (Maybe a))) s a -> (a -> a -> a) -> s -> a
1862-
foldl1Of' l f xs = fromMaybe (error "foldl1Of': empty structure") (foldlOf' l mf Nothing xs) where
1867+
-- See: NOTE: [Inlining and arity]
1868+
foldl1Of' l f = fromMaybe (error "foldl1Of': empty structure") . foldlOf' l mf Nothing where
18631869
mf Nothing y = Just $! y
18641870
mf (Just x) y = Just $! f x y
18651871
{-# INLINE foldl1Of' #-}
@@ -1881,7 +1887,8 @@ foldl1Of' l f xs = fromMaybe (error "foldl1Of': empty structure") (foldlOf' l mf
18811887
foldrMOf :: Monad m
18821888
=> Getting (Dual (Endo (r -> m r))) s a
18831889
-> (a -> r -> m r) -> r -> s -> m r
1884-
foldrMOf l f z0 xs = foldlOf l f' return xs z0
1890+
-- See: NOTE: [Inlining and arity]
1891+
foldrMOf l f z0 = \xs -> foldlOf l f' return xs z0
18851892
where f' k x z = f x z >>= k
18861893
{-# INLINE foldrMOf #-}
18871894

@@ -1902,10 +1909,26 @@ foldrMOf l f z0 xs = foldlOf l f' return xs z0
19021909
foldlMOf :: Monad m
19031910
=> Getting (Endo (r -> m r)) s a
19041911
-> (r -> a -> m r) -> r -> s -> m r
1905-
foldlMOf l f z0 xs = foldrOf l f' return xs z0
1912+
-- See: NOTE: [Inlining and arity]
1913+
foldlMOf l f z0 = \xs -> foldrOf l f' return xs z0
19061914
where f' x k z = f z x >>= k
19071915
{-# INLINE foldlMOf #-}
19081916

1917+
-- NOTE: [Inlining and arity]
1918+
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~
1919+
--
1920+
-- GHC uses the following inlining heuristic: a function body is inlined if
1921+
-- all its arguments on the LHS are applied. So the following two definitions
1922+
-- are not equivalent from the inliner's PoV:
1923+
--
1924+
-- > foldlOf' l f z0 xs = ...
1925+
-- > foldlOf' l f z0 = \xs -> ...
1926+
--
1927+
-- GHC will be less eager to inline the first one and this results in
1928+
-- worse code. For example, a simple list summation using `sumOf` will be 8x slower
1929+
-- with the first version.
1930+
1931+
19091932
-- | Check to see if this 'Fold' or 'Traversal' matches 1 or more entries.
19101933
--
19111934
-- >>> has (element 0) []

0 commit comments

Comments
 (0)