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

Tidy up bigint multiplication methods #132195

Merged
merged 1 commit into from
Dec 31, 2024
Merged

Conversation

clarfonthey
Copy link
Contributor

@clarfonthey clarfonthey commented Oct 26, 2024

This tidies up the library version of the bigint multiplication methods after the addition of the intrinsics in #133663. It follows this summary of what's desired for these methods.

Note that, if 2H = N, then uH::MAX * uH::MAX + uH::MAX + uH::MAX is uN::MAX, and that we can effectively add two "carry" values without overflowing.

For ease of terminology, the "low-order" or "least significant" or "wrapping" half of multiplication will be called the low part, and the "high-order" or "most significant" or "overflowing" half of multiplication will be called the high part. In all cases, the return convention is (low, high) and left unchanged by this PR, to be litigated later.

API Changes

The original API:

impl uN {
    // computes self * rhs
    pub const fn widening_mul(self, rhs: uN) -> (uN, uN);

    // computes self * rhs + carry
    pub const fn carrying_mul(self, rhs: uN, carry: uN) -> (uN, uN);
}

The added API:

impl uN {
    // computes self * rhs + carry1 + carry2
    pub const fn carrying2_mul(self, rhs: uN, carry: uN, add: uN) -> (uN, uN);
}
impl iN {
    // note that the low part is unsigned
    pub const fn widening_mul(self, rhs: iN) -> (uN, iN);
    pub const fn carrying_mul(self, rhs: iN, carry: iN) -> (uN, iN);
    pub const fn carrying_mul_add(self, rhs: iN, carry: iN, add: iN) -> (uN, iN);
}

Additionally, a naive implementation has been added for u128 and i128 since there are no double-wide types for those. Eventually, an intrinsic will be added to make these more efficient, but rather than doing this all at once, the library changes are added first.

Justifications for API

The unsigned parts are done to ensure consistency with overflowing addition: for a two's complement integer, you want to have unsigned overflow semantics for all parts of the integer except the highest one. This is because overflow for unsigned integers happens on the highest bit (from MAX to zero), whereas overflow for signed integers happens on the second highest bit (from MAX to MIN). Since the sign information only matters in the highest part, we use unsigned overflow for everything but that part.

There is still discussion on the merits of signed bigint addition methods, since getting the behaviour right is very subtle, but at least for signed bigint multiplication, the sign of the operands does make a difference. So, it feels appropriate that at least until we've nailed down the final API, there should be an option to do signed versions of these methods.

Additionally, while it's unclear whether we need all three versions of bigint multiplication (widening, carrying-1, and carrying-2), since it's possible to have up to two carries without overflow, there should at least be a method to allow that. We could potentially only offer the carry-2 method and expect that adding zero carries afterword will optimise correctly, but again, this can be litigated before stabilisation.

Note on documentation

While a lot of care was put into the documentation for the widening_mul and carrying_mul methods on unsigned integers, I have not taken this same care for carrying_mul_add or the signed versions. While I have updated the doc tests to be more appropriate, there will likely be many documentation changes done before stabilisation.

Note on tests

Alongside this change, I've added several tests to ensure that these methods work as expected. These are alongside the codegen tests for the intrinsics.

@rustbot
Copy link
Collaborator

rustbot commented Oct 26, 2024

r? @jhpratt

rustbot has assigned @jhpratt.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Oct 26, 2024
@clarfonthey
Copy link
Contributor Author

@rustbot author

Currently drafted since tests are failing and I need to fix them, but I wanted to at least save the stuff I typed up for the description.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 26, 2024
@rust-log-analyzer

This comment has been minimized.

@jhpratt
Copy link
Member

jhpratt commented Oct 26, 2024

Let me know when it's ready and I'll take a look. Unless you'd like feedback beforehand, of course.

@bors
Copy link
Contributor

bors commented Oct 27, 2024

☔ The latest upstream changes (presumably #132191) made this pull request unmergeable. Please resolve the merge conflicts.

@clarfonthey
Copy link
Contributor Author

Let me know when it's ready and I'll take a look. Unless you'd like feedback beforehand, of course.

Feel free to review the parts of the code that do work, and the API specifically as mentioned in the description. I just know that my naïve implementation for i128 is wrong and haven't fixed it yet. I mostly just wanted to clarify that I do know that it doesn't work, and but wanted to get this PR out in the meantime.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@clarfonthey
Copy link
Contributor Author

Oh cool, finally got it to work. Glad I added some tests with MIN and MAX to check for overflow.

@rustbot review

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 28, 2024
@clarfonthey clarfonthey marked this pull request as ready for review October 28, 2024 22:21
($left:expr, $right:expr$(, $($arg:tt)+)?) => {
{
fn runtime() {
assert_eq!($left, $right, $($arg)*);
assert_eq!($left, $right, $($($arg)*),*);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was just wrong before but unused, so, it never triggered any errors.

Comment on lines +112 to +104
($left:expr, $right:expr) => {
assert_eq_const_safe!($left, $right, concat!(stringify!($left), " == ", stringify!($right)));
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This helped with debugging, so, I kept it.

@bors
Copy link
Contributor

bors commented Oct 30, 2024

☔ The latest upstream changes (presumably #132238) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Contributor

bors commented Nov 3, 2024

☔ The latest upstream changes (presumably #132458) made this pull request unmergeable. Please resolve the merge conflicts.

@rustbot rustbot added S-blocked Status: Blocked on something else such as an RFC or other implementation work. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 1, 2024
@scottmcm
Copy link
Member

scottmcm commented Dec 1, 2024

My PR added an intrinsic for a*b+c+d, but didn't expose it, so taking this for that part would be good, and I didn't do anything for signed numbers either. So using this PR for that stuff sounds at least plausible.

@clarfonthey
Copy link
Contributor Author

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-blocked Status: Blocked on something else such as an RFC or other implementation work. labels Dec 28, 2024
@clarfonthey clarfonthey changed the title Tidy up bigint multiplication implementations Tidy up bigint multiplication methods Dec 28, 2024
@clarfonthey clarfonthey force-pushed the bigint-mul branch 2 times, most recently from 7020977 to d85387f Compare December 28, 2024 02:46
@clarfonthey
Copy link
Contributor Author

@rustbot ready

I think I've updated everything, including the PR description, after the other PR which added an intrinsic for this. Hopefully everything looks good now.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Dec 28, 2024
@rust-log-analyzer

This comment has been minimized.

@scottmcm
Copy link
Member

You might want to fix up the OP a bit -- it still says "Eventually, an intrinsic will be added to make these more efficient" and "carrying2_mul", for example.

/// additional amount of overflow. This allows for chaining together multiple
/// multiplications to create "big integers" which represent larger values.
///
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: would be good to also mention carrying_mul_add here, probably.

@scottmcm
Copy link
Member

Actually, all the code looks good here, so

@bors r+

(I would slightly prefer the methods not to be moved around for a simpler diff in uint_macros, but meh.)

@bors
Copy link
Contributor

bors commented Dec 31, 2024

📌 Commit f228458 has been approved by scottmcm

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 31, 2024
@scottmcm scottmcm assigned scottmcm and unassigned jhpratt Dec 31, 2024
@bors
Copy link
Contributor

bors commented Dec 31, 2024

⌛ Testing commit f228458 with merge d117b7f...

@bors
Copy link
Contributor

bors commented Dec 31, 2024

☀️ Test successful - checks-actions
Approved by: scottmcm
Pushing d117b7f to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Dec 31, 2024
@bors bors merged commit d117b7f into rust-lang:master Dec 31, 2024
7 checks passed
@rustbot rustbot added this to the 1.85.0 milestone Dec 31, 2024
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (d117b7f): comparison URL.

Overall result: ❌✅ regressions and improvements - no action needed

@rustbot label: -perf-regression

Instruction count

This is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.1% [0.1%, 0.1%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.4% [-0.4%, -0.4%] 1
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results (primary -1.2%, secondary -1.0%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-1.2% [-1.5%, -1.0%] 2
Improvements ✅
(secondary)
-1.0% [-1.0%, -1.0%] 1
All ❌✅ (primary) -1.2% [-1.5%, -1.0%] 2

Cycles

Results (primary -3.6%, secondary -0.8%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.0% [2.0%, 2.0%] 1
Improvements ✅
(primary)
-3.6% [-3.6%, -3.6%] 1
Improvements ✅
(secondary)
-3.6% [-3.6%, -3.6%] 1
All ❌✅ (primary) -3.6% [-3.6%, -3.6%] 1

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 762.584s -> 762.425s (-0.02%)
Artifact size: 325.56 MiB -> 325.57 MiB (0.00%)

@clarfonthey clarfonthey deleted the bigint-mul branch December 31, 2024 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants