Skip to content

gh-102221: speed up math.lcm by swapping numbers #111450

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

kevin1kevin1k
Copy link

@kevin1kevin1k kevin1kevin1k commented Oct 29, 2023

Try to speed up lcm by ensuring a <= b before each (a // g) * b operation.
Some discussions:

  • Should long_lcm(res, x) be changed to long_lcm(x, res)? This could save some swapping operations
  • Are some more tests or benchmarks needed?
  • Currently I re-use g for the swapping; could have introduced another one (maybe r just like in _PyLong_GCD)

@ghost
Copy link

ghost commented Oct 29, 2023

All commit authors signed the Contributor License Agreement.
CLA signed

@kevin1kevin1k
Copy link
Author

kevin1kevin1k commented Oct 29, 2023

Hi @corona10 I am contributing the PR during the PyCon APAC 2023 sprint; please review it if you have time (seems I am not able to assign reviewers). Thanks!

@kevin1kevin1k kevin1kevin1k force-pushed the gh-102221-speed-up-lcm branch 2 times, most recently from 350b32f to 2674494 Compare October 29, 2023 07:12
g = _PyLong_GCD(a, b);

/* Make sure a <= b to speed up (a // g) * b. */
if (PyObject_RichCompareBool(b, a, Py_LT)) {
Copy link
Member

Choose a reason for hiding this comment

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

PyObject_RichCompareBool() can fail and set error.

What if arguments are negative?

Is there a practical difference between comparing values of integers and only their sizes?

Copy link
Author

Choose a reason for hiding this comment

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

Oh thanks for the reminder, I would add some checks on the compared result.
On the other hand the comment #102221 (comment) in the issue seems to agree with your point: it suffices to compare the sizes (bits) of them. Let me modify so.

Copy link
Member

@mdickinson mdickinson left a comment

Choose a reason for hiding this comment

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

The idea seems fine in principle, but I agree with others that we want to compare sizes rather than actual values - and as @serhiy-storchaka notes, the current code doesn't seem like the right solution if arguments could be negative.

So there are really two separate issues here:

  • make sure that whatever swapping logic is implemented doesn't depend on signs of the inputs
  • decide how fine-grained a comparison we want, and benchmark - do we want to compare absolute values? compare bit counts? compare limb counts? I'd expect that comparing limb counts would be enough.

@serhiy-storchaka
Copy link
Member

What is a "limb count"?

I would consider two options:

  • compare only sizes (the numbers of digits).
  • compare sizes, and if they are equal, compare the values of the highest digit.

And then benchmark.

@mdickinson
Copy link
Member

What is a "limb count"?

Sorry, "digit count" would be better terminology for this context. ("limb" is GMP terminology, though I think it's more widespread than that; "digit" has the issue that it's somewhat ambiguous, since some might interpret it to mean with "decimal digit".)

@skirpichev skirpichev self-assigned this Apr 19, 2025
@skirpichev
Copy link
Contributor

@kevin1kevin1k are you interested in addressing reviews?

BTW, to compare sizes you could use private _PyLong_DigitCount() helper. I suggest you to start with this part of Serhiy's comment.

@kevin1kevin1k
Copy link
Author

@kevin1kevin1k are you interested in addressing reviews?

BTW, to compare sizes you could use private _PyLong_DigitCount() helper. I suggest you to start with this part of Serhiy's comment.

Sorry to all for the late response! I am willing to continue working on this, and thanks for the pointer to the helper function.

@kevin1kevin1k kevin1kevin1k force-pushed the gh-102221-speed-up-lcm branch from 7bdc2c5 to 2e0bcbb Compare June 15, 2025 02:32
@skirpichev
Copy link
Contributor

@kevin1kevin1k, please avoid force-pushing PR's.

@kevin1kevin1k kevin1kevin1k force-pushed the gh-102221-speed-up-lcm branch from 2e0bcbb to e1af338 Compare June 15, 2025 05:56
@kevin1kevin1k kevin1kevin1k force-pushed the gh-102221-speed-up-lcm branch from e1af338 to e35ba2a Compare June 15, 2025 06:17
@kevin1kevin1k
Copy link
Author

@skirpichev @serhiy-storchaka I've rebased with new changes:

  • optimize the order in math_lcm_impl: long_lcm(res, x) -> long_lcm(x, res)
  • compare the numbers a and b with _PyLong_DigitCount instead of directly comparing their values

Using the similar profiling code that @pochmann provided in the issue:

  1. before (main)
    test_lcm (test.test_math.MathTests.test_lcm) ... 1.67 ms ± 0.03 ms lcm

  2. after (swapping long_lcm order + gcd optimization)
    test_lcm (test.test_math.MathTests.test_lcm) ... 1.08 ms ± 0.05 ms lcm

Copy link
Contributor

@skirpichev skirpichev left a comment

Choose a reason for hiding this comment

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

I've rebased with new changes

You have asked to not do this. @kevin1kevin1k, please avoid altering commit history next time in any way.

kevin1kevin1k and others added 2 commits June 15, 2025 16:13
Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
@kevin1kevin1k
Copy link
Author

Sorry I did not notice the previous comment on not force pushing, and thanks for the reminders. I will not do it in the future.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

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

Well, it works, and brings speed up. But I tried other algorithm, and it brings larger speed up. Details are in the issue.

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
@python-cla-bot
Copy link

python-cla-bot bot commented Jun 18, 2025

All commit authors signed the Contributor License Agreement.

CLA signed

@kevin1kevin1k
Copy link
Author

Well, it works, and brings speed up. But I tried other algorithm, and it brings larger speed up. Details are in the issue.

Yes I saw the discussion and find it interesting (and educative!). Will defer proceeding here until a conclusion is reached there.

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.

4 participants