Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove custom unsafeShift{L,R} definitions #281

Merged
merged 1 commit into from
Jul 12, 2020
Merged

Conversation

sjakobi
Copy link
Member

@sjakobi sjakobi commented Jul 2, 2020

These have been available from base since v4.5.

These have been available from base since v4.5.
@treeowl
Copy link
Collaborator

treeowl commented Jul 2, 2020 via email

@sjakobi
Copy link
Member Author

sjakobi commented Jul 2, 2020

IIRC the question in containers was whether these had the same inlining behavior.

Can you give more detail, or just a link to that discussion?

I just had a look at the changes in the generated Core for Data.HashMap.Base with GHC 8.10.1 and the only differences I could find were in some source locations due to the removal of the import statement.

Is there a better way to inspect the inlining behaviour here?

@treeowl
Copy link
Collaborator

treeowl commented Jul 2, 2020 via email

@sjakobi
Copy link
Member Author

sjakobi commented Jul 2, 2020

I just compared the Core generated with 7.8.4 (the oldest GHC version we support) and there are quite a lot of differences.

I'll have to take a closer look. Maybe we can figure out the earliest GHC version that does the desired inlining and move the custom unsafeShift* definitions behind some CPP…


EDIT: Relevant links: haskell/containers#569, haskell/containers#216, https://gitlab.haskell.org/ghc/ghc/-/issues/12022

@sjakobi sjakobi marked this pull request as draft July 2, 2020 16:31
@treeowl
Copy link
Collaborator

treeowl commented Jul 2, 2020

No. CPP is for compatibility, not extreme performance. I don't care if there are perf regressions on 7.8.

@sjakobi
Copy link
Member Author

sjakobi commented Jul 3, 2020

In order to get more insight into the inlining behaviour, I've extracted this bit of code:

-- shift.hs
module M (mask) where

import Data.Bits
import Data.Word

bitsPerSubkey :: Int
bitsPerSubkey = 4

subkeyMask :: Word
subkeyMask = 1 `unsafeShiftL` bitsPerSubkey - 1

mask :: Word -> Int -> Int
mask w s = 1 `unsafeShiftL` index w s
{-# INLINE mask #-}

index :: Word -> Int -> Int
index w s = fromIntegral $ unsafeShiftR w s .&. subkeyMask
{-# INLINE index #-}

I've compared the Core produced for this module with ghc -O with GHC 7.8.4 and up. The only significant change seems to happen from 8.0.2 to 8.2.2 in the unfolding Tmpl:

 mask [InlPrag=INLINE (sat-args=2)] :: Word -> Int -> Int
 [GblId,
  Arity=2,
  Caf=NoCafRefs,
- Str=DmdType <S(S),1*U(U)><S(S),1*U(U)>m,
+ Str=<S(S),1*U(U)><S(S),1*U(U)>m,
  Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
          WorkFree=True, Expandable=True,
          Guidance=ALWAYS_IF(arity=2,unsat_ok=False,boring_ok=False)
-         Tmpl= \ (w [Occ=Once] :: Word) (s [Occ=Once] :: Int) ->
-                 Data.Bits.$fBitsInt_$cunsafeShiftL
-                   (GHC.Types.I# 1#)
-                   ($ @ 'GHC.Types.PtrRepLifted
-                      @ Word
-                      @ Int
-                      (\ (ds [Occ=Once!] :: Word) ->
-                         case ds of _ [Occ=Dead] { GHC.Types.W# x# [Occ=Once] ->
-                         GHC.Types.I# (GHC.Prim.word2Int# x#)
-                         })
-                      (Data.Bits.$fBitsWord_$c.&.
-                         (Data.Bits.$fBitsWord_$cunsafeShiftR w s) subkeyMask))}]
-mask =
-  \ (w :: Word) (s :: Int) ->
-    case w of _ [Occ=Dead] { GHC.Types.W# x# ->
-    case s of _ [Occ=Dead] { GHC.Types.I# i# ->
-    GHC.Types.I#
-      (GHC.Prim.uncheckedIShiftL#
-         1#
-         (GHC.Prim.word2Int#
-            (GHC.Prim.and# (GHC.Prim.uncheckedShiftRL# x# i#) 15##)))
-    }
-    }
+         Tmpl= \ (w [Occ=Once!] :: Word) (s [Occ=Once!] :: Int) ->
+                 case w of { GHC.Types.W# x# [Occ=Once] ->
+                 case s of { GHC.Types.I# i# [Occ=Once] ->
+                 GHC.Types.I#
+                   (GHC.Prim.uncheckedIShiftL#
+                      1#
+                      (GHC.Prim.word2Int#
+                         (GHC.Prim.and# (GHC.Prim.uncheckedShiftRL# x# i#) 15##)))
+                 }
+                 }}]
+mask
+  = \ (w :: Word) (s :: Int) ->
+      case w of { GHC.Types.W# x# ->
+      case s of { GHC.Types.I# i# ->
+      GHC.Types.I#
+        (GHC.Prim.uncheckedIShiftL#
+           1#
+           (GHC.Prim.word2Int#
+              (GHC.Prim.and# (GHC.Prim.uncheckedShiftRL# x# i#) 15##)))
+      }
+      }

With 8.0.2 the unfolding still references the unsafeShift[L,R] methods – with 8.2.2, the unfolding uses the primops directly.

Based on this insight, I'd suspect that this PR would cause a performance deterioration with GHC < 8.2.

@sjakobi
Copy link
Member Author

sjakobi commented Jul 4, 2020

Based on this insight, I'd suspect that this PR would cause a performance deterioration with GHC < 8.2.

Indeed benchmarks with GHC 8.0.2 seem to indicate a small hit, mostly on the order of 1–6%.

I think the simplification might still be worth it.

@sjakobi sjakobi marked this pull request as ready for review July 4, 2020 01:12
@sjakobi
Copy link
Member Author

sjakobi commented Jul 12, 2020

I was somewhat on the fence about this patch, since it kind of prioritizes maintainability over performance, but I guess I don't care enough about users of GHC 7.8, 7.10 and 8.0.

@sjakobi sjakobi merged commit 7485f5c into master Jul 12, 2020
@sjakobi sjakobi deleted the sjakobi/unsafeShift branch July 12, 2020 23:19
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.

2 participants