Skip to content

Commit

Permalink
Auto merge of #122884 - mzabaluev:pow-remove-exit-branch, r=Amanieu
Browse files Browse the repository at this point in the history
Optimize integer `pow` by removing the exit branch

The branch at the end of the `pow` implementations is redundant with multiplication code already present in the loop. By rotating the exit check, this branch can be largely removed, improving code size and reducing instruction cache misses.

Testing on my machine (`x86_64`, 11th Gen Intel Core i5-1135G7 @ 2.40GHz), the `num::int_pow` benchmarks improve by some 40% for the unchecked operations and show some slight improvement for the checked operations as well.
  • Loading branch information
bors committed Apr 12, 2024
2 parents 6475796 + 76d2530 commit 87b8256
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 73 deletions.
61 changes: 26 additions & 35 deletions library/core/src/num/int_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1418,18 +1418,17 @@ macro_rules! int_impl {
let mut base = self;
let mut acc: Self = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = try_opt!(acc.checked_mul(base));
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return Some(acc);
}
}
exp /= 2;
base = try_opt!(base.checked_mul(base));
}
// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc.checked_mul(base)
}

/// Strict exponentiation. Computes `self.pow(exp)`, panicking if
Expand Down Expand Up @@ -1469,18 +1468,17 @@ macro_rules! int_impl {
let mut base = self;
let mut acc: Self = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = acc.strict_mul(base);
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return acc;
}
}
exp /= 2;
base = base.strict_mul(base);
}
// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc.strict_mul(base)
}

/// Returns the square root of the number, rounded down.
Expand Down Expand Up @@ -2104,19 +2102,17 @@ macro_rules! int_impl {
let mut base = self;
let mut acc: Self = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = acc.wrapping_mul(base);
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return acc;
}
}
exp /= 2;
base = base.wrapping_mul(base);
}

// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc.wrapping_mul(base)
}

/// Calculates `self` + `rhs`
Expand Down Expand Up @@ -2610,9 +2606,14 @@ macro_rules! int_impl {
// Scratch space for storing results of overflowing_mul.
let mut r;

while exp > 1 {
loop {
if (exp & 1) == 1 {
r = acc.overflowing_mul(base);
// since exp!=0, finally the exp must be 1.
if exp == 1 {
r.1 |= overflown;
return r;
}
acc = r.0;
overflown |= r.1;
}
Expand All @@ -2621,14 +2622,6 @@ macro_rules! int_impl {
base = r.0;
overflown |= r.1;
}

// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
r = acc.overflowing_mul(base);
r.1 |= overflown;
r
}

/// Raises self to the power of `exp`, using exponentiation by squaring.
Expand All @@ -2655,19 +2648,17 @@ macro_rules! int_impl {
let mut base = self;
let mut acc = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = acc * base;
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return acc;
}
}
exp /= 2;
base = base * base;
}

// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc * base
}

/// Returns the square root of the number, rounded down.
Expand Down
64 changes: 26 additions & 38 deletions library/core/src/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,20 +1414,17 @@ macro_rules! uint_impl {
let mut base = self;
let mut acc: Self = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = try_opt!(acc.checked_mul(base));
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return Some(acc);
}
}
exp /= 2;
base = try_opt!(base.checked_mul(base));
}

// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.

acc.checked_mul(base)
}

/// Strict exponentiation. Computes `self.pow(exp)`, panicking if
Expand Down Expand Up @@ -1467,18 +1464,17 @@ macro_rules! uint_impl {
let mut base = self;
let mut acc: Self = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = acc.strict_mul(base);
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return acc;
}
}
exp /= 2;
base = base.strict_mul(base);
}
// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc.strict_mul(base)
}

/// Saturating integer addition. Computes `self + rhs`, saturating at
Expand Down Expand Up @@ -1939,19 +1935,17 @@ macro_rules! uint_impl {
let mut base = self;
let mut acc: Self = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = acc.wrapping_mul(base);
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return acc;
}
}
exp /= 2;
base = base.wrapping_mul(base);
}

// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc.wrapping_mul(base)
}

/// Calculates `self` + `rhs`
Expand Down Expand Up @@ -2396,9 +2390,14 @@ macro_rules! uint_impl {
// Scratch space for storing results of overflowing_mul.
let mut r;

while exp > 1 {
loop {
if (exp & 1) == 1 {
r = acc.overflowing_mul(base);
// since exp!=0, finally the exp must be 1.
if exp == 1 {
r.1 |= overflown;
return r;
}
acc = r.0;
overflown |= r.1;
}
Expand All @@ -2407,15 +2406,6 @@ macro_rules! uint_impl {
base = r.0;
overflown |= r.1;
}

// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
r = acc.overflowing_mul(base);
r.1 |= overflown;

r
}

/// Raises self to the power of `exp`, using exponentiation by squaring.
Expand All @@ -2440,19 +2430,17 @@ macro_rules! uint_impl {
let mut base = self;
let mut acc = 1;

while exp > 1 {
loop {
if (exp & 1) == 1 {
acc = acc * base;
// since exp!=0, finally the exp must be 1.
if exp == 1 {
return acc;
}
}
exp /= 2;
base = base * base;
}

// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc * base
}

/// Returns the square root of the number, rounded down.
Expand Down

0 comments on commit 87b8256

Please sign in to comment.