Skip to content

factor: Add 32b variant for modular arithmetic#1554

Merged
rivy merged 29 commits into
uutils:masterfrom
nicoonoclaste:factor/faster/montgomery32
Jul 24, 2020
Merged

factor: Add 32b variant for modular arithmetic#1554
rivy merged 29 commits into
uutils:masterfrom
nicoonoclaste:factor/faster/montgomery32

Conversation

@nicoonoclaste
Copy link
Copy Markdown
Contributor

@nicoonoclaste nicoonoclaste commented Jun 22, 2020

Twice as fast as master

  • Implement and test 32b variant.
  • Refactor to avoid code duplication between the 32 and 64b versions.
  • Use specialised basis for the Miller-Rabin primality test.

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

I'm not sure what's the way forward there: ideally, I can make Montgomery generic over a numeric type, which means needing something like num-traits which IIRC isn't the most pleasant thing to work with.

@Arcterus
Copy link
Copy Markdown
Collaborator

@nbraud I consolidated Montgomery and Montgomery32 into one. The code needs to be cleaned up a bit, but the commit is ef50150. If you’d prefer, I can just push it to your fork.

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

nicoonoclaste commented Jun 24, 2020

I successfully generalised the function for computing modular inverses (which might also be useful if we want to make the trial division table smaller, etc.), but I'm running into a couple of issues trying to refactor Montgomery to make it similarly generic:

  • I don't only need an integer type, but also a type of intermediate values with double the width (i.e. u32 -> u64, u64 -> u128, etc.) ; that can be solved with an associated type, but the conversions get pretty messy.
  • Not being able to use integer literals (i.e. write 2 * x + 1 in source or somesuch) really hurts readability.
  • It would make sense to replace Arithmetic::{from,to}_u64 with a {from,to}_int that takes the datatype's native integer type (i.e. u32 for Montgomery32, u64 for Montgomery64, etc.), so we would need to make the Arithmetic trait itself generic.
  • There are some optimisations which only apply to certain sizes; for instance, in Montgomery64 we can safely assume that 2⁶⁴ > n > 2³² (i.e. n is a u64, but couldn't be a u32 otherwise we would use Montgomery32) and that we only want to reduce 64b integers x mod n, so n² > x and we can use Barrett reduction ( I expect that to be a pretty large win, as the 128b reduction mod n in Montgomery::from_u64 consumes ~20% of the runtime in master )

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

I thought I had a trait-based solution for this, but it doesn't work out: where clauses on associated types are unstable and do not support “constraints cycles” (like type DoubleWidth: From<Self> where Self: From<Self::DoubleWidth>)

I guess the way forward is to accept a little bit of code duplication, as it seems like a macro-based solution would not be too good here (even worse maintainability)

@nicoonoclaste nicoonoclaste marked this pull request as ready for review June 24, 2020 17:35
@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

@nbraud I consolidated Montgomery and Montgomery32 into one. The code needs to be cleaned up a bit, but the commit is ef50150. If you’d prefer, I can just push it to your fork.

Oops, sorry @Arcterus, the Github UI was hiding this message until I refreshed. I will do have a look, thanks :)

@Arcterus
Copy link
Copy Markdown
Collaborator

Oh? cp -r failed, but only on one build environment? I guess this is something to investigate later.

@Arcterus
Copy link
Copy Markdown
Collaborator

I think we need to add RUST_BACKTRACE=1 to the CI and dump stderr on failure, as we have no idea why things fail with sporadic failures like this.

I think we can apply size-specific optimizations by playing around with the traits a bit more.

Copy link
Copy Markdown
Collaborator

@Arcterus Arcterus left a comment

Choose a reason for hiding this comment

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

I don’t really have any other changes. I guess just double-check that my casts/conversions are correct if you haven’t already.

Comment thread src/uu/factor/src/numeric.rs Outdated
Comment thread src/uu/factor/src/numeric.rs Outdated
@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

@Arcterus Thanks for the feedback, I'll look later this week; maybe tomorrow if I feel up for it

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

I think we can apply size-specific optimizations by playing around with the traits a bit more.

Yes, I think I can do the optimization I had in mind in a pretty straightforward way

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

nicoonoclaste commented Jul 1, 2020

I can't seem to load the log for the failing CI instance :(
Nevermind, looks like it's green now; I guess someone re-ran it?

Copy link
Copy Markdown
Collaborator

@Arcterus Arcterus left a comment

Choose a reason for hiding this comment

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

These comments are rather nit-picky. After you address these, I think the code is good to go.

Comment thread src/uu/factor/src/numeric.rs Outdated
Comment thread src/uu/factor/src/numeric.rs Outdated
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like as_double() will confuse people coming from more C-like languages. It sounds like you are converting the value into a f64.

The name could be changed to as_dint() or something like that (although I don’t really like that name either). Another alternative is to find a different name for DoubleInt, although I can’t think of a good name at the moment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Eh; I had the same thought, but I really couldn't find a better name (except maybe spelling out “double-width” in full, but that doesn't seem more helpful).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@Arcterus Were you able to think of a more-helpful name? I wasn't :(

Copy link
Copy Markdown
Contributor

@rivy rivy Jul 20, 2020

Choose a reason for hiding this comment

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

From my review of the code, it looks like DoubleInt is being used as a dual-precision integer, generally to avoid overflow issues. Correct?

If so, how about DualPrecisionInt (or maybe VariablePrecisionInt)?
That might lead to as_high_precision() and from_high_precision() trait functions and annotations like T::HighPrecision::one().

More verbose, but possibly more informative to readers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@Arcterus @rivy I just pushed 96224f6, which switches to hopefully-clearer names :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

PS: Also documented the type in 33a1c44

Comment thread src/uu/factor/Cargo.toml Outdated
@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

The build failure is due to one task, Style (macos-latest, macos), failing to install the Rust toolchain.
Why are we even running what's presumably cargo fmt and clippy on 3 different OSes?

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

@Arcterus Sorry, this got a bit out of hand, but I ended up doing some serious improvements to the testsuite to be confident that there were no bugs left, ended up finding a couple more issues in miller_rabin (which are now fixed)

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

Also, this is currently twice as fast as master :)

@sylvestre
Copy link
Copy Markdown
Contributor

Bravo for the coverage
I am afraid I don't know enough about this kind of math to do a proper review

Comment thread src/uu/factor/Cargo.toml Outdated
@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

Bravo for the coverage
I am afraid I don't know enough about this kind of math to do a proper review

Thanks regardless 💜

@sylvestre
Copy link
Copy Markdown
Contributor

@nbraud could you please fix the conflicts? sorry

thanks

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

@nbraud could you please fix the conflicts? sorry

No need to apologise, I already had a local commit to resolve the merge conflict, I just needed to know which PR it would go to :)

@rivy rivy self-requested a review July 20, 2020 12:16
@rivy
Copy link
Copy Markdown
Contributor

rivy commented Jul 20, 2020

Tests are passing.
@nbraud , if you're happy with the PR status, I'll take a quick overview look later today and merge.

Copy link
Copy Markdown
Contributor

@rivy rivy left a comment

Choose a reason for hiding this comment

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

I think that a bit of name refactoring for DoubleInt, ..., etc. would help improve the code clarity.

Comment thread src/uu/factor/src/numeric.rs Outdated
Copy link
Copy Markdown
Contributor

@rivy rivy Jul 20, 2020

Choose a reason for hiding this comment

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

From my review of the code, it looks like DoubleInt is being used as a dual-precision integer, generally to avoid overflow issues. Correct?

If so, how about DualPrecisionInt (or maybe VariablePrecisionInt)?
That might lead to as_high_precision() and from_high_precision() trait functions and annotations like T::HighPrecision::one().

More verbose, but possibly more informative to readers.

Comment thread src/uu/factor/src/numeric.rs
- `DoubleInt::Double` renamed to `DoubleWidth`
- `{as,from}_double()` renamed to `{as,from}_double_width()`.

This should hopefully clarify that this is not a “double precision”
floating-point type, but an integer type with a larger range (used
for storing intermediate results, typ. from a multiplication)
It was unused, the debug assertions only need `to_u128`.
@nicoonoclaste nicoonoclaste requested review from Arcterus and rivy July 21, 2020 17:51
@rivy
Copy link
Copy Markdown
Contributor

rivy commented Jul 22, 2020

@nbraud , I'm happy with this ...
@Arcterus , any remaining concerns on your end?

If no, I'll merge on Friday and then start getting the the next couple of @nbraud's PRs.

@nicoonoclaste
Copy link
Copy Markdown
Contributor Author

@rivy Thanks!

@rivy rivy merged this pull request into uutils:master Jul 24, 2020
@rivy
Copy link
Copy Markdown
Contributor

rivy commented Jul 24, 2020

Merged.
If you're happy with the other two PRs and will rebase them, I'll merge them later today.

rivy added a commit that referenced this pull request Jul 24, 2020
factor: Refactor and improve performance (plus a few bug fixes)
@nicoonoclaste nicoonoclaste deleted the factor/faster/montgomery32 branch July 24, 2020 21:02
@nicoonoclaste nicoonoclaste mentioned this pull request Jul 24, 2020
3 tasks
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.

4 participants