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

zipWith' to packZipWith as per #208 #295

Merged
merged 13 commits into from
Oct 11, 2020

Conversation

elikoga
Copy link
Contributor

@elikoga elikoga commented Oct 2, 2020

Renames zipWith' to packZipWith and exports it
Recovers test cases for packZipWith from comments

A PR renaming zipWith' to packZipWith and exporting it would be welcome.
Originally posted by @Bodigrim in #208 (comment)

All test cases pass

Copy link
Contributor

@Bodigrim Bodigrim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Please do the same for Data.ByteString.Char8, Data.ByteString.Lazy and Data.ByteString.Lazy.Char8.

tests/Properties.hs Outdated Show resolved Hide resolved
Data/ByteString.hs Outdated Show resolved Hide resolved
@elikoga
Copy link
Contributor Author

elikoga commented Oct 3, 2020

Cool! Please do the same for Data.ByteString.Char8, Data.ByteString.Lazy and Data.ByteString.Lazy.Char8.

I'm not sure how comfortable I am replicating packZipWith given that there is no original zipWith' in those modules to rename and the version in Data.ByteString uses unsafeDupablePerformIO. I might play around with it a bit later though unless you've got a different idea.

@elikoga
Copy link
Contributor Author

elikoga commented Oct 3, 2020

I added packZipWith to Data.ByteString.Char8 since the function from Data.ByteString only requires Storable a.

@Bodigrim
Copy link
Contributor

Bodigrim commented Oct 3, 2020

You can impement packZipWith for lazy bytestrings using packZipWith for strict bytestrings. Zip data Chunk by Chunk. Here is an example of this technique:

cmp :: ByteString -> ByteString -> Ordering
cmp Empty Empty = EQ
cmp Empty _ = LT
cmp _ Empty = GT
cmp (Chunk a@(S.BS ap al) as) (Chunk b@(S.BS bp bl) bs) =
case compare al bl of
LT -> case compare a (S.BS bp al) of
EQ -> cmp as (Chunk (S.BS (S.plusForeignPtr bp al) (bl - al)) bs)
result -> result
EQ -> case compare a b of
EQ -> cmp as bs
result -> result
GT -> case compare (S.BS ap bl) b of
EQ -> cmp (Chunk (S.BS (S.plusForeignPtr ap bl) (al - bl)) as) bs
result -> result

@elikoga
Copy link
Contributor Author

elikoga commented Oct 3, 2020

On it

@Bodigrim
Copy link
Contributor

Bodigrim commented Oct 3, 2020

Great! Please add tests for new functions.

@elikoga
Copy link
Contributor Author

elikoga commented Oct 7, 2020

Great! Please add tests for new functions.

I've only added test cases where there were ones for the packZipWith previously. If there are any more needed just ask

Data/ByteString/Char8.hs Outdated Show resolved Hide resolved
tests/Properties.hs Outdated Show resolved Hide resolved
Copy link
Member

@sjakobi sjakobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

EQ -> packZipWith f as bs
GT -> packZipWith f (Chunk (S.drop bl a) as) bs
{-# INLINE packZipWith #-}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding a RULE similar to the one for the strict version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that in future we intend to decomission the rewrite rule, we can probably remove the sentence advertising it already.

  • Bodigrim

Don't know how I feel about adding a rule only for it to be decommisioned later, still added the rule to Data.ByteString,Lazy though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjakobi As discussed in #208, such rule is harmful when the output of zipWith is consumed lazily. We cannot drop the existing rule for strict zipWith lightly, without further discussion, but from my perspective we should not introduce it for lazy bytestrings.

Copy link
Contributor

@Bodigrim Bodigrim Oct 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of copying a rule from the strict version, I would rather add

{-# RULES "Specialise zipWith" pack (zipWith f p q) = packZipWith f p q #-} 

(both for strict and lazy bytestrings)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading #208 again I agree. I changed the rewrite rule

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data/ByteString.hs:1649:1: warning: [-Winline-rule-shadowing]
    Rule "ByteString specialise zipWith" may never fire
      because ‘pack’ might inline first
    Probable fix: add an INLINE[n] or NOINLINE[n] pragma for ‘pack’
     |
1649 | "ByteString specialise zipWith" forall (f :: Word8 -> Word8 -> Word8) p q .
     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

GHC is complaining. I'm not that read up on the inline/noinline pragmas. What needs to be changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added {-# INLINE [1] pack #-} to Data.ByteString and Data.ByteString.Lazy since they only resolve to packBytes and I'm sure it would get inlined anyways.

I added {-# INLINE [1] zipWith #-} to Data.ByteString.Lazy but I'm not so sure about the performance characteristics of that. Shouldn't be too big of an issue since it just resolves to go a as b bs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for experimenting. I don't mind {-# INLINE [1] zipWith #-}, but not quite sure about {-# INLINE [1] pack #-}. It seems innocent, but deserves a thorough investigation of the impact, because pack is used pretty much everywhere.

To stop going down the rabbit hole, let's reset back to d93cdd4 and be with it for now. Rules issue can be resolved in a separate PR later. Sorry that it became so laborious.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the confusion – I had missed the discussion in #208.

@Bodigrim Bodigrim merged commit f6e4476 into haskell:master Oct 11, 2020
@Bodigrim
Copy link
Contributor

@elikoga Thanks, well done!

@Bodigrim Bodigrim added this to the 0.11.1.0 milestone Oct 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants