Ticket #2420 (closed bug: fixed)

Opened 4 years ago

Last modified 3 years ago

Multi-method classes are inlined/specialized better than single-method classes for strict types

Reported by: sedillard Owned by:
Priority: normal Milestone: 6.10 branch
Component: Compiler Version: 6.8.3
Keywords: Cc:
Operating System: Linux Architecture: Unknown/Multiple
Type of failure: Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

Instances of single-method class are optimized better than instances of multi-method classes. Attached is a small example,

class Square a where
  square  :: a -> a

and a simple data type

data Pair a = Pair !a !a

instance Num a => Square (Pair a) where
  square  (Pair a b) = Pair (a*a) (b*b)

and a specialized function

squarepd :: Pair Double -> Pair Double
squarepd = pair

Compiled as is, squarepd will turn into this

Test.squarepd :: Test.Pair GHC.Float.Double -> Test.Pair GHC.Float.Double
[GlobalId]
[Arity 1
 NoCafRefs
 Str: DmdType U(U(L)U(L))m]
Test.squarepd =
  \ (eta_sgh :: Test.Pair GHC.Float.Double) ->
    case eta_sgh of wild_B1 { Test.Pair a_a60 b_a61 ->
    case a_a60 of wild1_agE { GHC.Float.D# x_agG ->
    case b_a61 of wild2_Xh2 { GHC.Float.D# x1_Xh7 ->
    Test.Pair
      @ GHC.Float.Double
      (GHC.Float.D# (GHC.Prim.*## x_agG x_agG))
      (GHC.Float.D# (GHC.Prim.*## x1_Xh7 x1_Xh7))    

}}}

Now if you add another method to class Square (anything at all), this happens

Test.squarepd :: Test.Pair GHC.Float.Double -> Test.Pair GHC.Float.Double
[GlobalId]
[Arity 1
 NoCafRefs
 Str: DmdType U(U(L)U(L))m]
Test.squarepd =
  __inline_me (\ (ds_dgv :: Test.Pair GHC.Float.Double) ->
                 case ds_dgv of wild_Xi { Test.Pair a_a5Y b_a5Z ->
                 Test.$WPair
                   @ GHC.Float.Double
                   (GHC.Float.timesDouble a_a5Y a_a5Y)
                   (GHC.Float.timesDouble b_a5Z b_a5Z)
                 })

Which also what you get when you remove the strictness annotations from 'Pair'.

Attachments

Square.hs Download (345 bytes) - added by sedillard 4 years ago.

Change History

Changed 4 years ago by sedillard

Changed 4 years ago by igloo

  • difficulty set to Unknown
  • milestone set to 6.10 branch

Thanks for the report and a good testcase. We'll have a look.

Changed 4 years ago by simonpj

I know something about what is going on here.

First, there's nothing very bad happening. The code looks worse, but at any call to squarepd, the whole definition will be inlined, and then $WPair will be inlined, and it'll all turn out like the previous case. So efficiency should be similar either way.

Why the difference between the two cases? Because the details of inlining are regrettably delicate. The __inline_me__ note makes the enclosed expression look small, so that it'll be keen to inline. When it is inlined at an application, the __inline_me__ wrapper is removed. But in your example you have

squarepd = square

where square in this case is the Pair instance function, which is marked INLINE. This isn't an application of square; rather it's more like a renaming, so we end up with an __inline_me__ around the squarepd RHS.

Inside __inline_me__ GHC is very reluctant to do any inlining at all; it just blows up the thing to be inlined. Leave it until it is inlined.

If you instead wrote

squarepd x = square x

now it looks like a call of square, so the thing it inlined and the __inline_me__ is stripped off, and then $WPair can get inlined.

That's the general idea. For 6.10 I have two changes in the works that will affect this: (a) a new way of treating INLINE pragmas, and (b) a new way of treating instance declarations. So I'm not going to pursue this further until I've done that. I'll leave it open to remind me to take another look then.

Meanwhile: do you think the difference has a performance impact (contrary to my claim)?

Simon

Changed 4 years ago by simonmar

  • architecture changed from Unknown to Unknown/Multiple

Changed 3 years ago by simonpj

  • status changed from new to closed
  • resolution set to fixed

I think this is fixed in GHC 6.10.

Simon

Note: See TracTickets for help on using tickets.