Dorival, There is a great deal to learn from this issue. Some interesting issues:
Exponentiation by arbitrary real or complex powers is a little complicated to evaluate quickly with accuracy. Other than special cases, you're forced toward x**y ==> Exp(y*Log(x)), where each of Exp() and Log() are related to Taylor series expansions. Obviously if you know that your x is in (-1,0,1} then magic is possible. Same if you have complex values and both R(x) and I(x) are in {-1,0,1}. Likewise if y is integral. In that case, Exp/Log is not forced and multiplications are possible. You never need more multiplies than the number of bits in the binary expression of y. peterGo shared code with you above. Mine looks similar: // Integer power: compute a**b using binary powering algorithm // See Donald Knuth, The Art of Computer Programming, Volume 2, Section 4.6.3 func Pow(a, b int) int { p := 1 for b > 0 { if b&1 != 0 { p *= a } b >>= 1 a *= a } return p } This binary powering method is not necessarily optimal. There can be shorter sequences than that chosen by the binary method. Searching for "addition chains" will give background on this. I have a little Go program for generating such things, and here is some simple output: celeste:power mtj$ power -n 200 1: cost= 0 parent= 0 needed=1 2: cost= 1 parent= 1 needed=1 2 3: cost= 2 parent= 2 needed=1 2 3 4: cost= 2 parent= 2 needed=1 2 4 5: cost= 3 parent= 3 needed=1 2 3 5 6: cost= 3 parent= 3 needed=1 2 3 6 7: cost= 4 parent= 4 needed=1 2 3 4 7 8: cost= 3 parent= 4 needed=1 2 4 8 9: cost= 4 parent= 6 needed=1 2 3 6 9 10: cost= 4 parent= 5 needed=1 2 3 5 10 11: cost= 5 parent= 6 needed=1 2 3 5 6 11 12: cost= 4 parent= 6 needed=1 2 3 6 12 13: cost= 5 parent= 10 needed=1 2 3 5 10 13 14: cost= 5 parent= 7 needed=1 2 3 4 7 14 15: cost= 5 parent= 9 needed=1 2 3 6 9 15 16: cost= 4 parent= 8 needed=1 2 4 8 16 17: cost= 5 parent= 16 needed=1 2 4 8 16 17 18: cost= 5 parent= 9 needed=1 2 3 6 9 18 19: cost= 6 parent= 16 needed=1 2 3 4 8 16 19 20: cost= 5 parent= 10 needed=1 2 3 5 10 20 : 4088: cost= 15 parent= 2044 needed=1 2 3 6 9 15 30 60 120 240 241 271 511 1022 2044 4088 4089: cost= 15 parent= 2726 needed=1 2 3 5 10 20 40 80 160 320 640 643 1283 1363 2726 4089 4090: cost= 15 parent= 2045 needed=1 2 4 8 16 17 32 49 98 196 392 409 818 1227 2045 4090 4091: cost= 16 parent= 3994 needed=1 2 4 8 16 32 64 65 97 161 322 644 709 1288 1997 3994 4091 4092: cost= 15 parent= 2046 needed=1 2 4 8 16 17 32 49 81 162 324 341 682 1023 2046 4092 4093: cost= 16 parent= 3352 needed=1 2 4 8 16 32 64 65 97 161 322 419 741 838 1676 3352 4093 4094: cost= 16 parent= 2047 needed=1 2 4 8 16 17 32 64 128 145 290 307 580 887 1160 2047 4094 4095: cost= 15 parent= 2340 needed=1 2 4 8 16 32 64 65 130 195 390 585 1170 1755 2340 4095 4096: cost= 12 parent= 2048 needed=1 2 4 8 16 32 64 128 256 512 1024 2048 4096 We can understand this as follows, x**18 has an inherent cost of 5 multiplies: x2 := x*x x3 := x2*x x6 := x3*x3 x9 := x6*x3 x18 := x9*x9 The "parent" is the larger factor, x18 needs x9, x9 needs x6, and so on. However, since you mentioned special cases for powers under 20, you should realize that there is an issue with expressing this in Go. If you use a switch statement, and since switch statements don't "pack" dense alternatives using a jump table, the code for x18 will likely be at the end of 18 if tests (if power==1, if power ==2, ..), which will cost more than the multiplies do. When I need this kind of speed up, i use such programs to generate assembler and also generate a jump table as well, which ends up looking like (in a different application than powering) and in a Go setting: var dispatchSLBS = []func(int, [][]int, int) [][]int{ nil, solutionListBySum1, solutionListBySum2, solutionListBySum3, solutionListBySum4, solutionListBySum5, solutionListBySum6, solutionListBySum7, solutionListBySum8, solutionListBySum9, solutionListBySum10, solutionListBySum11, solutionListBySum12, solutionListBySum13, solutionListBySum14, solutionListBySum15, I have never tried this for something like exponentiation, but an all-Go solution with a jump table and autogenerated functions might work for you. Michael P.S. Early on, I urged the Go designers to provide an exponentiation operator just for this situation...where the compiler could translate a static x**18 into optimal code. IBM did it in 1959 so it seemed that maybe we could learn from that. The vote against was logical---people don't write code like that. But some do, and i guess that you and I are two of them. :-) On Fri, Aug 4, 2017 at 5:36 AM, peterGo <go.peter...@gmail.com> wrote: > Dorival Pedroso, > > PowP has a lot of code. PowG is simpler and it is modestly slower for > small values of n and much faster for larger values of n. > > func PowG(x float64, n uint32) float64 { > y := 1.0 > for i := n; i > 0; i >>= 1 { > if i&1 == 1 { > y *= x > } > x *= x > } > return y > } > > > BenchmarkPowP1 1000000000 2.89 ns/op > BenchmarkPowP5 100000000 13.6 ns/op > BenchmarkPowP10 50000000 29.2 ns/op > BenchmarkPowP20 20000000 89.6 ns/op > BenchmarkPowP200 200000 11666 ns/op > BenchmarkPowG1 1000000000 2.66 ns/op > BenchmarkPowG5 100000000 17.9 ns/op > BenchmarkPowG10 30000000 45.1 ns/op > BenchmarkPowG20 20000000 97.7 ns/op > BenchmarkPowG200 300000 3972 ns/op > > Peter > > > On Friday, August 4, 2017 at 10:08:36 AM UTC, Dorival Pedroso wrote: >> >> Since my ASM skills are limited, for positive integers, I'm planning on >> using: >> >> // PowP computes real raised to positive integer xⁿ >> func PowP(x float64, n uint32) (r float64) { >> if n == 0 { >> return 1.0 >> } >> if n == 1 { >> return x >> } >> if n == 2 { >> return x * x >> } >> if n == 3 { >> return x * x * x >> } >> if n == 4 { >> r = x * x >> return r * r >> } >> if n == 5 { >> r = x * x >> return r * r * x >> } >> if n == 6 { >> r = x * x * x >> return r * r >> } >> if n == 7 { >> r = x * x * x >> return r * r * x >> } >> if n == 8 { >> r = x * x * x * x >> return r * r >> } >> if n == 9 { >> r = x * x * x >> return r * r * r >> } >> if n == 10 { >> r = x * x * x >> return r * r * r * x >> } >> r = x * x * x >> r = r * r * r * x >> var i uint32 >> for i = 11; i <= 20; i++ { >> r *= x >> if n == i { >> return >> } >> } >> return math.Pow(x, float64(n)) >> } >> >> which gives a speedup of 5-8 times for small positive integers (<20). the >> case of negative integers could be handled too. >> >> output: >> BenchmarkPowP10-32 50000000 30.4 ns/op >> BenchmarkPowP10std-32 5000000 247 ns/op >> BenchmarkPowP20-32 20000000 98.2 ns/op >> BenchmarkPowP20std-32 3000000 561 ns/op >> BenchmarkPowP200-32 200000 10145 ns/op >> BenchmarkPowP200std-32 200000 9231 ns/op >> >> all files are here: https://gist.github.com/ >> cpmech/9f871df5b59fa8407221b0d3fb361a3d >> >> I'm also looking at the Cephes library: http://www.netlib.org/cephes/ >> for ideas >> >> maybe we could have a similar function in Go (and many more optimised >> ones, especially for complex numbers...) >> >> cheers >> d >> >> >> >> On Friday, August 4, 2017 at 4:20:41 PM UTC+10, Dorival Pedroso wrote: >>> >>> I've noticed that this C code: >>> >>> #include "math.h" >>> int main() { >>> double x = 2.5; >>> int Nmax = 10000000; >>> for (int N=0; N<Nmax; N++) { >>> for (int i=0; i<20; i++) { >>> pow(x, i); >>> } >>> } >>> } >>> >>> can run up to 50x faster than this Go code: >>> >>> package main >>> >>> import "math" >>> >>> func main() { >>> x := 2.5 >>> Nmax := 10000000 >>> for N := 0; N < Nmax; N++ { >>> for i := 0; i < 20; i++ { >>> math.Pow(x, float64(i)) >>> } >>> } >>> } >>> >>> The C code was compiled with: gcc -O2 ccode.c -o ccode -lm >>> then run with time ./ccode >>> >>> The Go code was compiled with: go build gcode.go >>> then run with time ./gcode >>> >>> I've used the time command on Linux (Ubuntu) to get some estimate. >>> >>> So the question is: how can we make the Go code faster? >>> >> -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- Michael T. Jones michael.jo...@gmail.com -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.