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 internal uses of withForeignPtr #536

Merged
merged 14 commits into from
Sep 26, 2022

Conversation

clyring
Copy link
Member

@clyring clyring commented Jul 29, 2022

As discussed at #535, withForeignPtr will be much slower in at least GHC-9.2.4 and GHC-9.4.1. So I made a quick pass at removing all internal uses of this function. To do this, I added variants of the create family of functions whose "action" arguments take ForeignPtr Word8 instead of Ptr Word8, allowing the caller to decide whether to use withForeignPtr or unsafeWithForeignPtr. Then I used those ForeignPtr variants everywhere.

I haven't yet had time to investigate whether this actually helps performance with 9.2.4 and obviously for a release I should need to do this against the 0.11 branch. But I thought I should push this for review now anyway.

Fixes #461.

@clyring
Copy link
Member Author

clyring commented Jul 30, 2022

My machine is giving me a very hard time, so I might not be able to continue work on this for a while. The timing is unfortunate. Maintainers, please feel free to push review suggestions to my branch as time is scarce.

A few other thoughts:

  • I'm not attached to the names of the helper functions I introduced.
  • Do we want to export these helper functions from D.B.Internal? This is always awkward. Anything we don't want to export from there can be moved to a new module like D.B.Internal.Utils which {-# SOURCE #-} imports D.B.Internal.
  • There are a few bytestring-internal uses of create that can't be safely replaced by create_but_with_unsafeWithForeignPtr. One of these is scanl, already handled correctly in this patch. It looks like I missed filter in my first pass. Are there others?

@Bodigrim
Copy link
Contributor

Sorry, I’m AFK atm, will take a look tomorrow night.

@Bodigrim
Copy link
Contributor

  • I'm not attached to the names of the helper functions I introduced.

  • Do we want to export these helper functions from D.B.Internal? This is always awkward. Anything we don't want to export from there can be moved to a new module like D.B.Internal.Utils which {-# SOURCE #-} imports D.B.Internal.

Yeah, I'd prefer to hide these helpers from users, D.B.Internal.Utils is a good location. And hiding them from users would save us bikeshedding of names.

@clyring
Copy link
Member Author

clyring commented Aug 7, 2022

I was finally able to work on this again tonight. The diff is rather large, but the majority of it is a very mechanical translation from code that uses Ptr to code that uses ForeignPtr via the new helper functions. So I hope it is easy to review despite its size.

  • I have moved the new helper functions into Data.ByteString.Internal.Utils, so there is no change to the exposed API.
  • I audited every use of unsafeWithForeignPtr in the library, and adjusted every use site that didn't look "obviously always terminating."
  • I briefly looked into backporting this to the 0.11 branch; there is a conflict since Improve memory-safety of Semigroup instances #443 has not been backported. But it's probably easy to fix, and we could also just backport Improve memory-safety of Semigroup instances #443 first.

@bgamari
Copy link
Contributor

bgamari commented Aug 7, 2022

Thank you for doing this, @clyring. For what it's worth, I agree with @Bodigrim regarding naming. The lower-case fp and f suffixes make for names that are very hard to visually parse.

This reverts commit b6b4de6.

Revert "Move unsafeWithForeignPtr shim into Utils"

This reverts commit 6df394e.

Revert "Move new utilities into D.B.Internal.Utils"

This reverts commit 6462e4f.
to Data.ByteString.Internal.Type. The re-export-only compatibility
module to replace it at Data.ByteString.Internal is intentionally
deleted from this commit, to allow for a more reviewable diff.
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.

Thanks @clyring, insane amount of work!

@Bodigrim Bodigrim requested a review from sjakobi August 12, 2022 20:26
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.

Thanks a lot, @clyring!

One minor wibble.

Data/ByteString/Internal/Type.hs Outdated Show resolved Hide resolved
@clyring
Copy link
Member Author

clyring commented Aug 22, 2022

ping @sjakobi

@sjakobi
Copy link
Member

sjakobi commented Aug 22, 2022

Apologies for the delay, @clyring! I intend to get back to this at the end of this week.

BTW, has anyone investigated the perf impact already?

I haven't yet had time to investigate whether this actually helps performance with 9.2.4 and obviously for a release I should need to do this against the 0.11 branch.

Otherwise I'll do that when I'm back.

@clyring
Copy link
Member Author

clyring commented Aug 22, 2022

BTW, has anyone investigated the perf impact already?

I haven't yet had time to investigate whether this actually helps performance with 9.2.4 and obviously for a release I should need to do this against the 0.11 branch.

Otherwise I'll do that when I'm back.

I tried measuring the effect on runtime via our benchmarks but the noise in my attempts was greater than the effect size.

I also just tried running our entire testsuite with --quickcheck-replay=953718 +RTS -s and found this branch consistently gets 0.6% fewer "bytes allocated in the heap" than does master. That seems promising, but I'm sure it's not a complete picture.

It has the same performance issues as does withForeignPtr.
@clyring
Copy link
Member Author

clyring commented Aug 26, 2022

I've verified that the improvement in runtime allocations is about 10% for the relevant GHC perf test. That test also allocates a bunch of list objects, so the improvement when looking at "only" bytestring stuff is probably a fair bit larger. (But I don't plan to investigate this any further.)

PS: Thanks sjakobi for bringing the performance question back up.

@sjakobi
Copy link
Member

sjakobi commented Aug 30, 2022

I've started comparing the benchmark results with this patch and GHC 9.2.4 vs. master with GHC 9.2.3. On first sight, this patch did not seem to recover a 2x slowdown in some of the Builder benchmarks. I'll take a closer look tomorrow or on Thursday.

@sjakobi
Copy link
Member

sjakobi commented Aug 30, 2022

Here are some examples of persistent slowdowns (comparing master with 9.2.3 (OLD) vs. this patch with 9.2.4 (NEW)):

Name,Old,New,Ratio
All.Data.ByteString.Builder.Encoding wrappers.foldMap word8 (10000),61623738,113407945,1.84033
All.Data.ByteString.Builder.ByteString insertion.foldMap byteStringInsert byteStringChunks4Data (10000),266916778,323701687,1.21274
All.Data.ByteString.Builder.ByteString insertion.foldMap byteString byteStringChunks4Data (10000),50016262,99267230,1.9847
All.Data.ByteString.Builder.ByteString insertion.foldMap byteStringCopy byteStringChunks4Data (10000),50212852,97953266,1.95076

@Bodigrim
Copy link
Contributor

Bodigrim commented Sep 3, 2022

(Off topic: @clyring would you mind to drop me an email? I have a question to discuss, but cannot find your contacts)

@Bodigrim
Copy link
Contributor

Not sure what else we can do here. If this branch is better than status quo, let’s merge as is? I’d like to push out 0.11.4.0 soon.

@clyring
Copy link
Member Author

clyring commented Sep 18, 2022

I'd like to understand the Builder regressions a bit better. But they seem likely unrelated to the expected keepAlive# regressions this patch aims to fix, and shouldn't block it.

Later today I will try to reproduce the 9.2.4 Builder regressions and inquire about them at GHC's issue tracker.

@clyring
Copy link
Member Author

clyring commented Sep 18, 2022

I was able to reproduce; it's rather terrible. byteStringHex gets 10x slower for me. That particular regression seems to happen because GHC is no longer eta-expanding something it probably should. I created this GHC issue on the matter.

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.

Sorry for the late response, guys! Indeed the regressions seem orthogonal to this patch.

Also, thanks a lot for all your work on bytestring and taking on maintainer duties, @clyring! :)

@Bodigrim Bodigrim merged commit 3b0f5d3 into haskell:master Sep 26, 2022
@Bodigrim Bodigrim added this to the 0.11.4.0 milestone Sep 26, 2022
Bodigrim pushed a commit to Bodigrim/bytestring that referenced this pull request Nov 26, 2022
* Avoid 'withForeignPtr' via 'createf' and variants

* remove stupid/wrong temporary cpp

* Move new utilities into D.B.Internal.Utils

* Move unsafeWithForeignPtr shim into Utils

* Fix imports for unsafeWithForeignPtr shim

The error messages from ghc-8.0 weren't red, so I didn't notice them.
Silly stuff.

* Clean up potentially-sketchy unsafeWithForeignPtr uses

* Revert documentation change to createAndTrim

* Revert "Fix imports for unsafeWithForeignPtr shim"

This reverts commit b6b4de6.

Revert "Move unsafeWithForeignPtr shim into Utils"

This reverts commit 6df394e.

Revert "Move new utilities into D.B.Internal.Utils"

This reverts commit 6462e4f.

* Rename ForeignPtr helpers to use "Fp" suffix

* [NONBUILDABLE] rename Data.ByteString.Internal

to Data.ByteString.Internal.Type. The re-export-only compatibility
module to replace it at Data.ByteString.Internal is intentionally
deleted from this commit, to allow for a more reviewable diff.

* Add compatibility stub "Data.ByteString.Internal"

* Replace Data.ByteString.Internal.Type's description

* Remove the use of withRawBuffer, too.

It has the same performance issues as does withForeignPtr.

Co-authored-by: Matthew Craven <clyring@users.noreply.github.com>
# Conflicts:
#	Data/ByteString/Char8.hs
#	Data/ByteString/Internal.hs
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.

unsafeCreate isn't unsafe enough for append or times
4 participants