-
Notifications
You must be signed in to change notification settings - Fork 17.5k
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
math: Pow(x,y) optimization for cases: constant integer y
.
#29456
Comments
y
. y
.
Change https://golang.org/cl/155921 mentions this issue: |
Note that for:
The newer Go Gc compilers are smart enough to infer that the loop body computation is never used and optimize the loop body away. Therefore this is likely benchmarking an empty loop. To avoid some issues with dead code elimination and constant folding code like below can be used:
Some question for the optimization:
I do not think rewriting to |
Dear @martisch ,
I agree with your point, if a := math.Max(b,12.7) no need all checks of y in function math.Max |
Thanks for checking and clarification.
Yes and the compiler with better inlining support could determine that generally (also outside of the math package) and only use and inline the checks that are relevant. There are also examples where this would help in the strings package e.g. Count with string to count of length 1. Its something that can be handled better generally without the need for hardcoded AST optimizations. |
I'm watching the changes here. If we're going to do this may I recommend using a simple better method? For background, the choices are: celeste:power7 mtj$ go test -v -bench=. The math lib approach, which benchmarks for me at an average of 52.9 ns/op across a range of exponents. The iterative method under review, which is ~37s for the specific case of x**124 and 24-40ns generally. The well-known binary powering method (below) which is 9.09 ns/op in general and 7.08 ns/op for the x**124 case. The tricky set of auto-generated magic optimal powering trees as generated by my program (and Knuth's, and lots of other people's). With this you get 5.33 ns/op overall and 4.95 ns/op for the specific case of x**124. (The direct case does not apply here; it is for the situation where you know what the exponent is at compile time and call the specific routine directly. This would only happen if exponentiation were an operator in Go, which it is not.) The universal solution for powering using the binary powering algorithm is as follows. I suggest that you incorporate this into your inner loop. // Compute a**b using binary powering algorithm The tricky version, which was decided here long ago as "too much code for the problem's importance and saving 2-3 ns/call over the binary method" looks like this... // PowerM124 computes x**124 with 9 multiplications (version 311 of 315) ...and has a companion dispatcher... // PowerM computes x**y using minimal multiplications var callPowerM = []func(float64) float64{ If this decision is ever changed, I have the optimal code for all the powers in the range 2..256 and the program to generate them arbitrarily. My suggestion here is simply to use the binary powering algorithm precisely as shown above. It's rounding error, and other aspects are fine. 9 ns is quite a bit less than 30 ns which in turn is less than 52 ns. |
Dear @martisch , Dear @MichaelTJones , One more think about present design of function ...
case y == 0.5:
return Sqrt(x)
case y == -0.5:
return 1 / Sqrt(x)
... I think we can optimize that part too, for example after // optimization for cases:
// y = 1/(2^r) , where integer value r = 1 ... 127 ...
if n := int8(1.0/y); n%2 == 0 && float64(n) == 1.0/y{
// we have
// y = 0.5 n = 2
// y = 0.25 n = 4
// ...
// convert `n` to `r` if possible. if not possible then default alorithm(example n = 6, y=0.16666... it is not 1/(2^r))
// calculation
result := x
for i:= 0;i<r;i++{
result = math.Sqrt(result)
}
return result
} But I haven't idea for convert |
/cc @rsc (as an owner for math) |
Note that there is already an open issue #25270 for the current |
Hello everyone, What is the current status of this optimisation? I replicated the benchmark and it seems that in 1.21.4 there is still a huge gap between package main
import (
"fmt"
"math"
"time"
)
func compute_square_with_pow(a float64) float64 {
return math.Pow(a, 2)
}
func compute_square(a float64) float64 {
return a * a
}
func main() {
length := 1000000000 // Adjust the length as needed for your benchmark
// Benchmark compute_square_with_pow
start := time.Now()
for i := 0; i < length; i++ {
compute_square_with_pow(2.5)
}
durationWithPow := time.Since(start)
fmt.Printf("compute_square_with_pow took %v\n", durationWithPow)
// Benchmark compute_square
start = time.Now()
for i := 0; i < length; i++ {
compute_square(2.5)
}
duration := time.Since(start)
fmt.Printf("compute_square took %v\n", duration)
// Comparing the results and calculating relative difference
var relativeDifference float64
if duration < durationWithPow {
absoluteDifference := durationWithPow - duration
relativeDifference = (absoluteDifference.Seconds() / durationWithPow.Seconds()) * 100
fmt.Printf("Direct multiplication is faster by %v (relative difference: %.2f%%)\n", absoluteDifference, relativeDifference)
} else if duration > durationWithPow {
absoluteDifference := duration - durationWithPow
relativeDifference = (absoluteDifference.Seconds() / duration.Seconds()) * 100
fmt.Printf("math.Pow is faster by %v (relative difference: %.2f%%)\n", absoluteDifference, relativeDifference)
} else {
fmt.Println("Both methods took the same amount of time")
}
}
|
math.Pow(x,y) optimization for cases: constant integer
y
. For example:... math.Pow(length, 2)...
... math.Pow(length, 3.0)...
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
yes for
go 1.6
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
What did you expect to see?
same or preliminary same performance between that 2 cases.
What did you see instead?
run benchmark
Different more 70 times.
Note
Changing function
pow
in math/pow.go is not enough for solving performance problem.result is better, but not
the best
:Have we any optimization place for change AST from function expressions
math.Pow
with parametery
= 2 to expressionx*x
? (Like I understoodssa
is not a right place).The text was updated successfully, but these errors were encountered: