Opened 9 years ago

Last modified 3 years ago

#2625 new bug

Unexpected -ddump-simpl output for derived Ord instance and UNPACKed fields

Reported by: aslatter Owned by:
Priority: low Milestone:
Component: Compiler Version: 6.8.3
Keywords: Cc: aslatter@…
Operating System: Unknown/Multiple Architecture: x86_64 (amd64)
Type of failure: Runtime performance bug Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


In the following example, with either -O or -O2

In the derived Eq instance for A, in '==' nothing ever gets re-packed into a B constructor.

However in the derived Ord instance, in the 'compile' function the code from -ddump-simpl shows that the RHS of 'compare' is unpacked from the 'A' constructor only to be repacked in 'B' constructor and then passed on to a different function.

Is there any way we can do for 'compare' what was done for '==' ?


module Bug where

data A = A {-# UNPACK #-} !B
 deriving (Eq, Ord)

data B = B {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
           {-# UNPACK #-} !Int
 deriving (Eq, Ord)

Change History (4)

comment:1 Changed 9 years ago by igloo

difficulty: Unknown
Milestone: 6.10 branch

Thanks for the report; we'll take a look.

comment:2 Changed 9 years ago by simonmar

Operating System: UnknownUnknown/Multiple

comment:3 Changed 9 years ago by simonpj

Milestone: 6.10 branch_|_
Priority: normallow

Here's what is happening. The compare functions for A and B look like this (initially):

compareA :: A -> A -> Ordering
compareA (A x1 .. x8) (A y1 .. y8)
  = compare (B x1 .. x8) (B y1 .. y8)

compareB :: B -> B -> Ordering
compareB (B x1 .. x8) (B y1 .. y8) 
  = if x1>y1 then GT else ... blah blah blah...

Note that the signature of compareB must be B -> B -> Ordering, so compareA is obliged to re-box the B argument. That always potentially happens when you use {-# UNPACK #-}: if you match on the constructor you may need to construct a value of the unpacked type to pass on to some other function.

OK after strictness analysis you might hope that compareB gets 16 arguments. But GHC is wary about giving workers an arbitrarily large number of arguments, so it has an arbitrary threshold of 10. As a result, the worker for compareB looks like this:

$wcompareB :: Int# -> ... -> Int# -> B -> Ordering
$wcompareB x1 .. x8 (B y1 .. y8) = <blah>

The first argument is unpacked but not the second. And that is what is happening to you.

I don't know a good solution in general. {-# UNPACK #-} can (and must) lead to the possibility of reboxing. (In this case compareB is strict, so its wrapper may do unboxing ,up to the threshold, but in general the called function may be lazy.)

There is a workaround here: the flag -fmax-worker-args=N will change the threshold

to N, and that fixes the problem. But it's not a satisfying fix.

I'll leave this open at low priority. By all means contribute suggestions!


comment:4 Changed 3 years ago by thomie

Type of failure: Runtime performance bug

Since 7.8, setting -fmax-worker-args=20 (higher than 16), as suggested by Simon in comment:3, doesn't have any effect on the core of the program from the description. It did in 7.6 and below.

I can't tell whether the bug is fixed or not.

Note: See TracTickets for help on using tickets.