Skip to content

2.pow(n) does not optimize well #47234

Description

@hanna-kruppe

A mibbit user noted in #rust-beginners that the integer pow method with a base of two generates far worse code than a shift: https://godbolt.org/g/mE1wjH (the shift is not equivalent for n >= 64, but you can see that it would still be better code if you accounted for that)

Perhaps we can special case the base 2 and use a shift in that case? One complication is that pow is subject to overflow checks: Whether it panics or returns 0 for excessive exponents depends on whether overflow checks are enabled, and I don't know of a way to branch depending on whether they are enabled (which is required for this optimization to not change behavior).

Alternatively, better LLVM optimizations could help, but this might be a bit too much to ask.

Activity

  1. nagisa commented on Jan 6, 2018

    @nagisa
    Member

    LLVM doesn’t optimise even a

    pub fn dopow(mut exp: u32) -> u64 {
                let mut acc = 1;
                while exp > 0 {
                    acc = acc * 2;
                    exp -= 1;
                }
                acc
    }
    

    so I don’t see it being able to optimise the more complicated algorithm used currently.

  2. est31 commented on Jan 6, 2018

    @est31
    Member

    Sooo... that means that there should be a MIR optimisation that checks whether the function is libcore::num::<sth>::pow and then checks whether the base is a constant expression and a power of two and if is, it should replace the expression by a shift instead?

    Or is this rather solved on the llvm side by having a pow intrinsic that works on integers?

  3. hanna-kruppe commented on Jan 6, 2018

    @hanna-kruppe
    ContributorAuthor

    A MIR pass would be a pretty big hammer. We'd have to make this random library function specially recognized by the compiler. Also the implementation effort wouldn't be justified for a one-off optimization.

    I think the closest thing to a practical solution would be changing the library code that implements pow. The problem with that is that it can't match the current algorithm wrt overflow checks. But that doesn't seem like it's unique to this function, surely other code runs into the same problem? Might be worth adding a way to query the status of overflow checks. Historically cfg!(debug_assertion) was that way (if you ignored -Z flags), but now we also have -C overflow-checks.

  4. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    I-slowIssue: Problems and improvements with respect to performance of generated code.
    on Jan 10, 2018
  5. added
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    T-libs-api[DEPRECATED; DO NOT USE]
    on Apr 10, 2018
  6. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Feb 2, 2020
  7. e00E commented on Jul 27, 2021

    @e00E
    Contributor

    As of 1.53.0 and today's nightly the bug still occurs: https://godbolt.org/z/Kjn4odTdh

  8. added
    C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing such
    on Oct 8, 2023
  9. NCGThompson commented on Dec 15, 2023

    @NCGThompson
    Contributor

    As of 1.74.0, Rust still isn't able to optimize 2u32.wrapping_pow(u). However, since 1.60.0, it is able to optimize 1u32.wrapping_pow(u).

    If the leading zeroes of the base are removed before exponentiation with a right shift and then added back with a left shift after exponentiation, then the 2u32.wrapping_pow(u) becomes a 1u32.wrapping_pow(u) and therefor can be optimized. In fact, it gives equivalent assembly to 1u32.checked_shl(u).unwrap_or_(0) However, removing the zeroes only significantly helps in with special cases like a constant power of two, so it isn't worth implementing.

    Here is an example. This wasn't thoroughly checked for logic errors: Compiler Explorer link

  10. nikic commented on Dec 15, 2023

    @nikic
    Contributor

    #114390 is the fix for this issue, but it doesn't have recent activity.

  11. NCGThompson commented on Dec 16, 2023

    @NCGThompson
    Contributor

    #114390 is the fix for this issue, but it doesn't have recent activity.

    @nikic Should I take it over or should I give the pull requester more time?

  12. NCGThompson commented on Jan 18, 2024

    @NCGThompson
    Contributor

    One complication is that pow is subject to overflow checks: Whether it panics or returns 0 for excessive exponents depends on whether overflow checks are enabled,

    We can fix that now that we have #[track_caller]. We will be further able to improve this further once we have #[cfg(overflow_checks)].

  13. added 3 commits that reference this issue on Jan 20, 2024
    a2c895e
    b54f424
    039d887
  14. added a commit that references this issue on Jan 26, 2024
    50a067d
  15. added a commit that references this issue on Feb 21, 2024
    d645e16
  16. yugr commented on Mar 12, 2025

    @yugr

    PR has been reverted due to #120537 . Perhaps this issue could be reopened ?

  17. reopened this on Mar 12, 2025
  18. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-enhancementCategory: An issue proposing an enhancement or a PR with one.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-slowIssue: Problems and improvements with respect to performance of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions