rrnewton / haskell-lockfree

A collection of different packages for CAS based data structures.
106 stars 25 forks source link

For at least GHC 7.6, replace CMM code with straight C code #27

Open rrnewton opened 10 years ago

rrnewton commented 10 years ago

Johan (@tibbe) pointed out something I didn't know, "MutableByteArray#" can be sent through the FFI. Thus it should be possible to move the logic we have in Cmm code into C code, and avoid a DOUBLE function call overhead.

Because the GHC 7.8 primops have this double funcall overhead, they may end up as useless from the perspective of this package. The hope was to at last avoid the fragility of foreign primops by including them in GHC. Still, that problem is eliminated just as well by eliminating the Cmm itself.

tibbe commented 10 years ago

I think this

stg_fetchAddByteArrayIntzh
/* MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #) */
{
    W_ arr, p, ind, incr, h, len;
    arr  = R1; 
    ind  = R2;
    incr = R3;

    p = arr + SIZEOF_StgArrWords + WDS(ind);
    (h) = foreign "C" atomic_inc_with(incr, p) [];

    RET_N(h);
}

could be replaced by

foreign import ccall unsafe "atomic_inc_with" fetchAddByteArrayInt
    :: MutableByteArray# s -> Int -> Int -> IO Int

except I'm not quite sure about the s parameter to MBA#.

Relevant docs: https://www.haskell.org/ghc/docs/7.6.3/html/users_guide/ffi.html#idp56267600

tibbe commented 10 years ago

Figured it out: you also need to give the UnliftedFFITypes language pragma to be allowed to mention MBA# in the FFI import.

rrnewton commented 10 years ago

How do you picture this working for CAS? It's a funky case because it currently returns a pair from the CMM primop:

stg_casMutVarzh ( gcptr mv, gcptr old, gcptr new )
{
    gcptr h;
    (h) = ccall cas(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var,
                          old, new);
    if (h != old) {
        return (1,h);
    } else {
        if (GET_INFO(mv) == stg_MUT_VAR_CLEAN_info) {
           ccall dirty_MUT_VAR(BaseReg "ptr", mv "ptr");
        }
        return (0,new);
    }

Assuming it is possible to do the GET_INFO/dirty bits in C, then there's just the issue of returning the two values efficiently. Could pass an output param for one and poke the pointer...

tibbe commented 10 years ago

Poking a pointer sounds like the way to go.

On Sun, Apr 13, 2014 at 3:57 AM, Ryan Newton notifications@github.comwrote:

How do you picture this working for CAS? It's a funky case because it currently returns a pair from the CMM primop:

stg_casMutVarzh ( gcptr mv, gcptr old, gcptr new ){ gcptr h; (h) = ccall cas(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var, old, new); if (h != old) { return (1,h); } else { if (GET_INFO(mv) == stg_MUT_VAR_CLEAN_info) { ccall dirty_MUT_VAR(BaseReg "ptr", mv "ptr"); } return (0,new); }

Assuming it is possible to do the GET_INFO/dirty bits in C, then there's just the issue of returning the two values efficiently. Could pass an output param for one and poke the pointer...

Reply to this email directly or view it on GitHubhttps://github.com/rrnewton/haskell-lockfree/issues/27#issuecomment-40298247 .

rrnewton commented 10 years ago

Yeah, it's a write to memory rather than returning in registers but worth it to turn the double function call into a single one.