Skip to content

Improve performance of nub{Ord,Int}On after fusion#1206

Open
meooow25 wants to merge 1 commit intohaskell:masterfrom
meooow25:improve-nub-ord
Open

Improve performance of nub{Ord,Int}On after fusion#1206
meooow25 wants to merge 1 commit intohaskell:masterfrom
meooow25:improve-nub-ord

Conversation

@meooow25
Copy link
Copy Markdown
Contributor

INLINE the FB functions and use oneShot.

Closes #1202.


Benchmarks on GHC 9.14.1:

Name                         Time - - - - - - - -    Allocated - - - - -
                                  A       B     %         A       B     %
nubInt.issue1202_distinct     22 μs   12 μs  -45%    311 KB  217 KB  -30%
nubInt.issue1202_repeat       13 μs  3.1 μs  -76%    110 KB   16 KB  -85%
nubInt.no_fusion_distinct     10 μs  9.9 μs   +0%    178 KB  178 KB   +0%
nubInt.no_fusion_repeat      1.7 μs  1.6 μs   -3%    120 B   120 B    +0%
nubOrd.issue1202_distinct     50 μs   46 μs   -8%    743 KB  672 KB   -9%
nubOrd.issue1202_repeat      8.2 μs  3.2 μs  -60%    110 KB   16 KB  -85%
nubOrd.no_fusion_distinct     45 μs   43 μs   -3%    633 KB  633 KB   +0%
nubOrd.no_fusion_repeat      1.4 μs  1.3 μs   +0%    152 B   152 B    +0%

INLINE the FB functions and use oneShot.

Improves benchmark times on GHC 9.14:

nubInt.issue1202_distinct  -45%
nubInt.issue1202_repeat    -76%
nubOrd.issue1202_distinct   -8%
nubOrd.issue1202_repeat    -60%
@meooow25
Copy link
Copy Markdown
Contributor Author

For the record,

Core before
-- RHS size: {terms: 2, types: 4, coercions: 0, joins: 0/0}
z_r6by :: IntSet -> [Int]
[GblId, Arity=1, Str=<A>, Unf=OtherCon []]
z_r6by = constNubOn @[Int] @IntSet ([] @Int)

-- RHS size: {terms: 11, types: 6, coercions: 0, joins: 0/0}
c_r6bz :: Int -> [Int] -> [Int]
[GblId, Arity=2, Str=<ML><L>, Unf=OtherCon []]
c_r6bz
  = \ (x2_a60t :: Int) (ys_a60u [OS=OneShot] :: [Int]) ->
      : @Int
        (case x2_a60t of { I# x3_a552 -> I# (*# x3_a552 2#) })
        ys_a60u

Rec {
-- RHS size: {terms: 33, types: 33, coercions: 0, joins: 0/2}
go4_r6bA :: [[Int]] -> IntSet -> [Int]
[GblId, Arity=1, Str=<1L>, Unf=OtherCon []]
go4_r6bA
  = \ (ds_a60B :: [[Int]]) ->
      case ds_a60B of {
        [] -> z_r6by;
        : y_a60E ys_a60F ->
          let {
            z2_X1K :: IntSet -> [Int]
            [LclId]
            z2_X1K = go4_r6bA ys_a60F } in
          letrec {
            go6_X1L [Occ=LoopBreaker, Dmd=SC(S,L)] :: [Int] -> IntSet -> [Int]
            [LclId, Arity=1, Str=<1L>, Unf=OtherCon []]
            go6_X1L
              = \ (ds1_X1M :: [Int]) ->
                  case ds1_X1M of {
                    [] -> z2_X1K;
                    : y1_X1O ys1_X1P ->
                      case y1_X1O of wild2_a5Ye { I# x2_a5Yf ->
                      case x2_a5Yf of {
                        __DEFAULT ->
                          nubIntOnFB
                            @Int @[Int] (breakpoint @Int) c_r6bz wild2_a5Ye (go6_X1L ys1_X1P);
                        0# -> go6_X1L ys1_X1P
                      }
                      }
                  }; } in
          go6_X1L y_a60E
      }
end Rec }

-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
collectFrameworksDirs_nubInt_r20 :: [[Int]] -> [Int]
[GblId, Arity=1, Str=<1L>, Unf=OtherCon []]
collectFrameworksDirs_nubInt_r20
  = \ (x2_X1J :: [[Int]]) -> go4_r6bA x2_X1J Nil

-- RHS size: {terms: 2, types: 5, coercions: 0, joins: 0/0}
z1_r6bB :: Set Int -> [Int]
[GblId, Arity=1, Str=<A>, Unf=OtherCon []]
z1_r6bB = constNubOn @[Int] @(Set Int) ([] @Int)

Rec {
-- RHS size: {terms: 82, types: 64, coercions: 0, joins: 3/8}
go5_r6bC :: [[Int]] -> Set Int -> [Int]
[GblId, Arity=1, Str=<1L>, Unf=OtherCon []]
go5_r6bC
  = \ (ds_a60B :: [[Int]]) ->
      case ds_a60B of {
        [] -> z1_r6bB;
        : y_a60E ys_a60F ->
          let {
            z2_X1T :: Set Int -> [Int]
            [LclId]
            z2_X1T = go5_r6bC ys_a60F } in
          letrec {
            go6_X1U [Occ=LoopBreaker, Dmd=SC(S,L)] :: [Int] -> Set Int -> [Int]
            [LclId, Arity=1, Str=<1L>, Unf=OtherCon []]
            go6_X1U
              = \ (ds1_X1V :: [Int]) ->
                  case ds1_X1V of {
                    [] -> z2_X1T;
                    : y1_X1X ys1_X1Y ->
                      case y1_X1X of wild2_a5Ye { I# x2_a5Yf ->
                      case x2_a5Yf of wild3_X1 {
                        __DEFAULT ->
                          let {
                            r1_a5Tw [Dmd=LC(S,L)] :: Set Int -> [Int]
                            [LclId]
                            r1_a5Tw = go6_X1U ys1_X1Y } in
                          let {
                            lvl1_s694 :: Int#
                            [LclId]
                            lvl1_s694 = *# wild3_X1 2# } in
                          let {
                            lvl2_s684 :: Int
                            [LclId, Unf=OtherCon []]
                            lvl2_s684 = I# lvl1_s694 } in
                          \ (s_a5Tx :: Set Int) ->
                            join {
                              $j_s5TV :: [Int]
                              [LclId[JoinId(0)(Nothing)]]
                              $j_s5TV
                                = : @Int
                                    lvl2_s684
                                    (r1_a5Tw ($w$sgo4 wild2_a5Ye wild3_X1 s_a5Tx)) } in
                            join {
                              $j1_s5TX :: [Int]
                              [LclId[JoinId(0)(Nothing)]]
                              $j1_s5TX = r1_a5Tw s_a5Tx } in
                            joinrec {
                              $wgo3_s64q [InlPrag=[2], Occ=LoopBreaker, Dmd=SC(S,C(1,L))]
                                :: Int# -> Set Int -> [Int]
                              [LclId[JoinId(2)(Just [~, !])],
                               Arity=2,
                               Str=<L><1L>,
                               Unf=OtherCon []]
                              $wgo3_s64q (ww_s64n :: Int#) (ds2_s64p :: Set Int)
                                = case ds2_s64p of {
                                    Bin ipv_a5TH ipv1_a5TI ipv2_a5TJ ipv3_a5TK ->
                                      case ipv1_a5TI of { I# y#_s689 ->
                                      case <# ww_s64n y#_s689 of {
                                        __DEFAULT ->
                                          case ==# ww_s64n y#_s689 of {
                                            __DEFAULT -> jump $wgo3_s64q ww_s64n ipv3_a5TK;
                                            1# -> jump $j1_s5TX
                                          };
                                        1# -> jump $wgo3_s64q ww_s64n ipv2_a5TJ
                                      }
                                      };
                                    Tip -> jump $j_s5TV
                                  }; } in
                            jump $wgo3_s64q wild3_X1 s_a5Tx;
                        0# -> go6_X1U ys1_X1Y
                      }
                      }
                  }; } in
          go6_X1U y_a60E
      }
end Rec }

-- RHS size: {terms: 4, types: 4, coercions: 0, joins: 0/0}
collectFrameworksDirs_r1Z :: [[Int]] -> [Int]
[GblId, Arity=1, Str=<1L>, Unf=OtherCon []]
collectFrameworksDirs_r1Z
  = \ (x2_X1S :: [[Int]]) -> go5_r6bC x2_X1S (Tip @Int)
Core after
Rec {
-- RHS size: {terms: 113, types: 53, coercions: 0, joins: 3/5}
go4_r6be :: [[Int]] -> IntSet -> [Int]
[GblId, Arity=2, Str=<1L><L>, Unf=OtherCon []]
go4_r6be
  = \ (ds_a608 :: [[Int]]) (eta_B0 [OS=OneShot] :: IntSet) ->
      case ds_a608 of {
        [] -> [] @Int;
        : y_a60b ys_a60c ->
          letrec {
            go6_X1L [Occ=LoopBreaker, Dmd=SC(S,C(1,L))]
              :: [Int] -> IntSet -> [Int]
            [LclId, Arity=2, Str=<1L><L>, Unf=OtherCon []]
            go6_X1L
              = \ (ds1_X1M :: [Int]) (eta1_X1N [OS=OneShot] :: IntSet) ->
                  case ds1_X1M of {
                    [] -> go4_r6be ys_a60c eta1_X1N;
                    : y1_X1P ys1_X1Q ->
                      case y1_X1P of { I# x2_i5XY ->
                      case x2_i5XY of wild3_X1 {
                        __DEFAULT ->
                          let {
                            lvl123_i60x :: Word#
                            [LclId]
                            lvl123_i60x = int2Word# wild3_X1 } in
                          join {
                            $j_i60A :: [Int]
                            [LclId[JoinId(0)(Nothing)]]
                            $j_i60A
                              = : @Int
                                  (I# (*# wild3_X1 2#))
                                  (go6_X1L
                                     ys1_X1Q
                                     ($winsertBM
                                        (andI# wild3_X1 -64#)
                                        (uncheckedShiftL# 1## (andI# wild3_X1 63#))
                                        eta1_X1N)) } in
                          join {
                            exit_i60C [Dmd=LC(S,C(1,L))] :: Int# -> Word# -> [Int]
                            [LclId[JoinId(2)(Nothing)], Arity=2, Str=<L><L>]
                            exit_i60C (bx_i60D [OS=OneShot] :: Int#)
                                      (bx1_i60E [OS=OneShot] :: Word#)
                              = case ==# (andI# wild3_X1 -64#) bx_i60D of {
                                  __DEFAULT -> jump $j_i60A;
                                  1# ->
                                    case and# (uncheckedShiftL# 1## (andI# wild3_X1 63#)) bx1_i60E
                                    of {
                                      __DEFAULT -> go6_X1L ys1_X1Q eta1_X1N;
                                      0## -> jump $j_i60A
                                    }
                                } } in
                          joinrec {
                            go7_i60H [Occ=LoopBreaker, Dmd=SC(S,L)] :: IntSet -> [Int]
                            [LclId[JoinId(1)(Just [!])], Arity=1, Str=<1L>, Unf=OtherCon []]
                            go7_i60H (ds2_i60I :: IntSet)
                              = case ds2_i60I of {
                                  Bin bx_i60K l_i60L r2_i60M ->
                                    case andI#
                                           (xorI# wild3_X1 bx_i60K)
                                           (xorI# bx_i60K (negateInt# bx_i60K))
                                    of {
                                      __DEFAULT -> jump $j_i60A;
                                      0# ->
                                        case ltWord# lvl123_i60x (int2Word# bx_i60K) of {
                                          __DEFAULT -> jump go7_i60H r2_i60M;
                                          1# -> jump go7_i60H l_i60L
                                        }
                                    };
                                  Tip bx_i60Q bx1_i60R -> jump exit_i60C bx_i60Q bx1_i60R;
                                  Nil -> jump $j_i60A
                                }; } in
                          jump go7_i60H eta1_X1N;
                        0# -> go6_X1L ys1_X1Q eta1_X1N
                      }
                      }
                  }; } in
          go6_X1L y_a60b eta_B0
      }
end Rec }

-- RHS size: {terms: 4, types: 3, coercions: 0, joins: 0/0}
collectFrameworksDirs_nubInt_r1k :: [[Int]] -> [Int]
[GblId, Arity=1, Str=<1L>, Unf=OtherCon []]
collectFrameworksDirs_nubInt_r1k
  = \ (x2_X1J :: [[Int]]) -> go4_r6be x2_X1J Nil

Rec {
-- RHS size: {terms: 79, types: 57, coercions: 0, joins: 3/4}
go5_r6bf :: [[Int]] -> Set Int -> [Int]
[GblId, Arity=2, Str=<1L><L>, Unf=OtherCon []]
go5_r6bf
  = \ (ds_a608 :: [[Int]]) (eta_B0 [OS=OneShot] :: Set Int) ->
      case ds_a608 of {
        [] -> [] @Int;
        : y_a60b ys_a60c ->
          letrec {
            go6_X1U [Occ=LoopBreaker, Dmd=SC(S,C(1,L))]
              :: [Int] -> Set Int -> [Int]
            [LclId, Arity=2, Str=<1L><L>, Unf=OtherCon []]
            go6_X1U
              = \ (ds1_X1V :: [Int]) (eta1_X1W [OS=OneShot] :: Set Int) ->
                  case ds1_X1V of {
                    [] -> go5_r6bf ys_a60c eta1_X1W;
                    : y1_X1Y ys1_X1Z ->
                      case y1_X1Y of wild2_i5XX { I# x2_i5XY ->
                      case x2_i5XY of wild3_X1 {
                        __DEFAULT ->
                          join {
                            $j_i5T2 :: [Int]
                            [LclId[JoinId(0)(Nothing)]]
                            $j_i5T2
                              = : @Int
                                  (I# (*# wild3_X1 2#))
                                  (go6_X1U ys1_X1Z ($w$sgo4 wild2_i5XX wild3_X1 eta1_X1W)) } in
                          join {
                            $j1_i5T4 :: [Int]
                            [LclId[JoinId(0)(Nothing)]]
                            $j1_i5T4 = go6_X1U ys1_X1Z eta1_X1W } in
                          joinrec {
                            $wgo3_s63T [InlPrag=[2], Occ=LoopBreaker, Dmd=SC(S,C(1,L))]
                              :: Int# -> Set Int -> [Int]
                            [LclId[JoinId(2)(Just [~, !])],
                             Arity=2,
                             Str=<L><1L>,
                             Unf=OtherCon []]
                            $wgo3_s63T (ww_s63Q :: Int#) (ds2_s63S :: Set Int)
                              = case ds2_s63S of {
                                  Bin ipv_i5Ta ipv1_i5Tb ipv2_i5Tc ipv3_i5Td ->
                                    case ipv1_i5Tb of { I# y#_s67x ->
                                    case <# ww_s63Q y#_s67x of {
                                      __DEFAULT ->
                                        case ==# ww_s63Q y#_s67x of {
                                          __DEFAULT -> jump $wgo3_s63T ww_s63Q ipv3_i5Td;
                                          1# -> jump $j1_i5T4
                                        };
                                      1# -> jump $wgo3_s63T ww_s63Q ipv2_i5Tc
                                    }
                                    };
                                  Tip -> jump $j_i5T2
                                }; } in
                          jump $wgo3_s63T wild3_X1 eta1_X1W;
                        0# -> go6_X1U ys1_X1Z eta1_X1W
                      }
                      }
                  }; } in
          go6_X1U y_a60b eta_B0
      }
end Rec }

-- RHS size: {terms: 4, types: 4, coercions: 0, joins: 0/0}
collectFrameworksDirs_r1j :: [[Int]] -> [Int]
[GblId, Arity=1, Str=<1L>, Unf=OtherCon []]
collectFrameworksDirs_r1j
  = \ (x2_X1S :: [[Int]]) -> go5_r6bf x2_X1S (Tip @Int)

@sjakobi
Copy link
Copy Markdown
Member

sjakobi commented Apr 28, 2026

Thanks for fixing this so quickly, @meooow25! :)

Name                         Time - - - - - - - -    Allocated - - - - -
                                  A       B     %         A       B     %
nubInt.issue1202_distinct     22 μs   12 μs  -45%    311 KB  217 KB  -30%
nubInt.issue1202_repeat       13 μs  3.1 μs  -76%    110 KB   16 KB  -85%
nubInt.no_fusion_distinct     10 μs  9.9 μs   +0%    178 KB  178 KB   +0%
nubInt.no_fusion_repeat      1.7 μs  1.6 μs   -3%    120 B   120 B    +0%
nubOrd.issue1202_distinct     50 μs   46 μs   -8%    743 KB  672 KB   -9%
nubOrd.issue1202_repeat      8.2 μs  3.2 μs  -60%    110 KB   16 KB  -85%
nubOrd.no_fusion_distinct     45 μs   43 μs   -3%    633 KB  633 KB   +0%
nubOrd.no_fusion_repeat      1.4 μs  1.3 μs   +0%    152 B   152 B    +0%

Nice results! How did you generate this table BTW? It's a very nice format!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

id function not eliminated when nubOrd is fused

2 participants