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

Use pass-by-copy for Hrp when possible #148

Merged
merged 1 commit into from
Oct 21, 2023

Conversation

tcharding
Copy link
Member

@tcharding tcharding commented Oct 18, 2023

We currently have some code that uses pass-by-reference and some that uses pass-by-copy.

Further investigation shows that when messing with iterators we have to have a reference to the Hrp so that the iterator has somewhere in memory to point to while iterating.

The iterator API is intentionally low level, we can make the higher level APIs more ergonomic by using pass-by-copy.

@tcharding tcharding force-pushed the 10-19-pass-hrp-by-ref branch 2 times, most recently from 7ba372f to c65666e Compare October 19, 2023 00:19
@apoelstra
Copy link
Member

I feel like if it implements Copy then we ought to copy it. And if it's too big to pass by reference then we ought to remove Copy from it.

What threshold do you have in mind for copying by reference vs copy?

@tcharding
Copy link
Member Author

I feel like if it implements Copy then we ought to copy it. And if it's too big to pass by reference then we ought to remove Copy from it.

I thought a struct was Copy if all its fields were Copy irrespective of how big it is. Isn't Copy more about how cheap/expensive it is to do than about how big it is? Hrp is just an array so its cheap to copy but we still don't want to do so in every function call because its big, right? (Definition of "big" below.)

What threshold do you have in mind for copying by reference vs copy?

I had 32 bytes in mind, based on discussion a few years ago about passing hashes by copy.

@apoelstra
Copy link
Member

I thought a struct was Copy if all its fields were Copy irrespective of how big it is.

You need to explicitly derive Copy, and if you don't want people to copy your structure, you shouldn't implement it. If some fields are non-Copy then you can't derive it, but you're never obilgated to derive it.

32 bytes seems a little small, though I could believe that that's roughly where the performance starts to equal out.

In this case though I don't think the performance benefit (if there is one) outweighs the loss of ergonomics. It's not like anybody would ever be manipulating Hrps in a tight loop.

@tcharding
Copy link
Member Author

I'm convinced. I'll change this to "remove pass-by-reference"

@tcharding tcharding changed the title Pass Hrp by reference Use pass-by-copy for Hrp when possible Oct 20, 2023
We currently have some code that uses pass-by-reference and some that
uses pass-by-copy.

Further investigation shows that when messing with iterators we have to
have a reference to the `Hrp` so that the iterator has somewhere in
memory to point to while iterating.

The iterator API is intentionally low level, we can make the higher
level APIs more ergonomic by using pass-by-copy.
Copy link
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK faf8651

@apoelstra apoelstra merged commit 7207926 into rust-bitcoin:master Oct 21, 2023
12 checks passed
@tcharding tcharding deleted the 10-19-pass-hrp-by-ref branch October 27, 2023 01:39
@Kixunil
Copy link

Kixunil commented Oct 31, 2023

Regarding Copy, just forget that terrible name. What it should've been called is WillNotImplDrop. Then the decision is clear: if a type will never implement Drop it should be Copy.

@apoelstra
Copy link
Member

@Kixunil it also shouldn't be Copy if it has state that we don't want the user to accidentally duplicate (e.g. iterators) or if it's massive (e.g. contains 20 fields which are all 100-byte arrays). In these cases the name Copy seems to give the right intuition.

@Kixunil
Copy link

Kixunil commented Oct 31, 2023

state that we don't want the user to accidentally duplicate

That to me feels like kind of an abuse of the type system but since there's Clone it's not a big deal. And yeah, we should follow the convention.

or if it's massive

What is the justification for this? The compiler will produce exactly the same amount of memcpy calls with Copy or without it. I don't know of any std type or official guidance saying one should not impl Copy for big types.

@apoelstra
Copy link
Member

The compiler will produce exactly the same amount of memcpy calls with Copy or without it. I

If Copy is missing then users will be forced to pass-by-reference in most cases.

@Kixunil
Copy link

Kixunil commented Oct 31, 2023

Which will also produce memcpy calls whenever they need owned value inside the function.

I think this is not something for libraries to decide, it's better to have lints and such if any actual performance benefit can be identified.

@apoelstra
Copy link
Member

You can't get an owned value from a borrow of a non-Copy type.

I think this is not something for libraries to decide,

I believe we're only making this decision for our own internal code. And for our public APIs we have to decide whether to take a value by reference or by value.

@Kixunil
Copy link

Kixunil commented Nov 2, 2023

You can if it's Clone, which does memcpy underneath for types with derived Clone that could've been Copy.

I believe we're only making this decision for our own internal code.

I'd be fine with this.

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.

None yet

3 participants