简体   繁体   中英

why is this Haskell function slow?

I have a one-line function which is taking up 25% of my execution time, in a way which is really counter-intuitive to me.

The context is an AI for a board game (Blokus), and specifically what we're doing here is trying to determine if it's legal to put a piece in a particular orientation in the neighborhood of a particular board cell. We have already pre-computed a Word64 (placementBitmap) which shows which cells are taken up by the piece in a particular orientation, and more recently we have computed a Word64 (cornerBitmap) which shows which cells are available surrounding this square. So now we compare them:

legalAt (TerritoryCorner _ _ cornerBitmap) (Placement _ _ _ placementBitmap) = (placementBitmap .&. cornerBitmap) == placementBitmap

I don't understand how a couple of bitwise operations here could take up more time than the process to calculate the cornerBitmap in the first place. Boxing issues? I'm pretty new to Haskell.

The data constructors for both TerritoryCorner and Placement are defined so that the last argument is strict, for whatever that's worth.

The context in which this is used is a list comprehension:

[getMyChild corner placement | corner <- myCorners, placement <- myPlacements, legalAt corner placement]

I managed to produce the following Core, but I have no idea how to interpret it:

GameState.legalAt [InlPrag=INLINE[0]]
  :: Types.TerritoryCorner -> Types.Placement -> GHC.Bool.Bool
[GblId,
 Arity=2,
 Caf=NoCafRefs,
 Str=DmdType U(AAAL)U(UUUL),
 Unf=Unf{Src=InlineStable, TopLvl=True, Arity=2, Value=True,
         ConLike=True, Cheap=True, Expandable=True,
         Guidance=ALWAYS_IF(unsat_ok=True,boring_ok=False)
         Tmpl= \ (w_soat [Occ=Once!] :: Types.TerritoryCorner)
                 (w1_soaA [Occ=Once!] :: Types.Placement) ->
                 case w_soat of _ { Types.TerritoryCorner _ _ _ ww3_soay ->
                 case w1_soaA of _ { Types.Placement _ _ _ ww7_soaF ->
                 __scc {legalAt main:GameState}
                 case {__pkg_ccall ghc-prim hs_and64 GHC.Prim.Word64#
                               -> GHC.Prim.Word64#
                               -> GHC.Prim.State# GHC.Prim.RealWorld
                               -> (# GHC.Prim.State# GHC.Prim.RealWorld, GHC.Prim.Word64# #)}_aHK
                        ww7_soaF ww3_soay GHC.Prim.realWorld#
                 of _ { (# _, ds3_aHQ [Occ=Once] #) ->
                 GHC.IntWord64.eqWord64# ds3_aHQ ww7_soaF
                 }
                 }
                 }}]
GameState.legalAt =
  \ (w_soat :: Types.TerritoryCorner) (w1_soaA :: Types.Placement) ->
    case w_soat
    of _ { Types.TerritoryCorner ww_soav ww1_soaw ww2_soax ww3_soay ->
    case w1_soaA
    of _ { Types.Placement ww4_soaC ww5_soaD ww6_soaE ww7_soaF ->
    __scc {legalAt main:GameState}
    case {__pkg_ccall ghc-prim hs_and64 GHC.Prim.Word64#
                               -> GHC.Prim.Word64#
                               -> GHC.Prim.State# GHC.Prim.RealWorld
                               -> (# GHC.Prim.State# GHC.Prim.RealWorld, GHC.Prim.Word64# #)}_aHK
           ww7_soaF ww3_soay GHC.Prim.realWorld#
    of _ { (# _, ds3_aHQ #) ->
    GHC.IntWord64.eqWord64# ds3_aHQ ww7_soaF
    }
    }
    }

You're on a 32-bit platform, so the 64-bit operations are C-calls, that makes them somewhat slower than on 64-bit platforms. However, that shouldn't cost so much, and the core for legalAt is as good as one can hope. I think contrary to your belief, the computations of cornerBitmap and placementBitmap have not been done before and are forced by legalAt and by the profiler the cost is attributed to that.

We'd need to see more code and context to find out more.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM