Browse Source

Performance optimisations

Dmitry Panov 5 years ago
parent
commit
8acb9eda7d
4 changed files with 99 additions and 25 deletions
  1. 2 2
      ftoa/common.go
  2. 2 1
      ftoa/ftobasestr.go
  3. 61 22
      ftoa/ftostr.go
  4. 34 0
      ftoa/ftostr_test.go

+ 2 - 2
ftoa/common.go

@@ -97,7 +97,7 @@ func stuffBits(bits []byte, offset int, val uint32) {
 	bits[offset+3] = byte(val)
 	bits[offset+3] = byte(val)
 }
 }
 
 
-func d2b(d float64) (b *big.Int, e, bits int) {
+func d2b(d float64, bi *big.Int) (e, bits int) {
 	dBits := math.Float64bits(d)
 	dBits := math.Float64bits(d)
 	d0 := uint32(dBits >> 32)
 	d0 := uint32(dBits >> 32)
 	d1 := uint32(dBits)
 	d1 := uint32(dBits)
@@ -144,6 +144,6 @@ func d2b(d float64) (b *big.Int, e, bits int) {
 		e = de - bias - (p - 1) + 1 + k
 		e = de - bias - (p - 1) + 1 + k
 		bits = 32*i - hi0bits(z)
 		bits = 32*i - hi0bits(z)
 	}
 	}
-	b = (&big.Int{}).SetBytes(dbl_bits)
+	bi.SetBytes(dbl_bits)
 	return
 	return
 }
 }

+ 2 - 1
ftoa/ftobasestr.go

@@ -64,7 +64,8 @@ func FToBaseStr(num float64, radix int) string {
 		word0 := uint32(dBits >> 32)
 		word0 := uint32(dBits >> 32)
 		word1 := uint32(dBits)
 		word1 := uint32(dBits)
 
 
-		b, e, _ := d2b(df)
+		b := new(big.Int)
+		e, _ := d2b(df, b)
 		//            JS_ASSERT(e < 0);
 		//            JS_ASSERT(e < 0);
 		/* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */
 		/* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */
 
 

+ 61 - 22
ftoa/ftostr.go

@@ -36,6 +36,9 @@ var (
 	big5  = big.NewInt(5)
 	big5  = big.NewInt(5)
 	big10 = big.NewInt(10)
 	big10 = big.NewInt(10)
 
 
+	p05       = []*big.Int{big5, big.NewInt(25), big.NewInt(125)}
+	pow5Cache [7]*big.Int
+
 	dtoaModes = []int{
 	dtoaModes = []int{
 		ModeStandard:            0,
 		ModeStandard:            0,
 		ModeStandardExponential: 0,
 		ModeStandardExponential: 0,
@@ -100,7 +103,8 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
 	if sign {
 	if sign {
 		buf = append(buf, '-')
 		buf = append(buf, '-')
 	}
 	}
-	b, be, bbits := d2b(d)
+	b := new(big.Int)
+	be, bbits := d2b(d, b)
 	i := int((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
 	i := int((word0 >> exp_shift1) & (exp_mask >> exp_shift1))
 	var d2 float64
 	var d2 float64
 	var denorm bool
 	var denorm bool
@@ -534,9 +538,8 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
 		}
 		}
 		/* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */
 		/* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */
 		/* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */
 		/* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */
-
+		var z, delta big.Int
 		for i = 1; ; i++ {
 		for i = 1; ; i++ {
-			z := new(big.Int)
 			z.DivMod(b, S, b)
 			z.DivMod(b, S, b)
 			dig = byte(z.Int64() + '0')
 			dig = byte(z.Int64() + '0')
 			/* Do we yet have the shortest decimal string
 			/* Do we yet have the shortest decimal string
@@ -544,12 +547,12 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
 			 */
 			 */
 			j = b.Cmp(mlo)
 			j = b.Cmp(mlo)
 			/* j is b/S compared with mlo/S. */
 			/* j is b/S compared with mlo/S. */
-			delta := new(big.Int).Sub(S, mhi)
+			delta.Sub(S, mhi)
 			var j1 int
 			var j1 int
 			if delta.Sign() <= 0 {
 			if delta.Sign() <= 0 {
 				j1 = 1
 				j1 = 1
 			} else {
 			} else {
-				j1 = b.Cmp(delta)
+				j1 = b.Cmp(&delta)
 			}
 			}
 			/* j1 is b/S compared with 1 - mhi/S. */
 			/* j1 is b/S compared with 1 - mhi/S. */
 			if (j1 == 0) && (mode == 0) && ((_word1(d) & 1) == 0) {
 			if (j1 == 0) && (mode == 0) && ((_word1(d) & 1) == 0) {
@@ -560,13 +563,13 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
 						k++
 						k++
 						buf = append(buf, '1')
 						buf = append(buf, '1')
 					}
 					}
-					return buf, int(k + 1)
+					return buf, k + 1
 				}
 				}
 				if j > 0 {
 				if j > 0 {
 					dig++
 					dig++
 				}
 				}
 				buf = append(buf, dig)
 				buf = append(buf, dig)
-				return buf, int(k + 1)
+				return buf, k + 1
 			}
 			}
 			if (j < 0) || ((j == 0) && (mode == 0) && ((_word1(d) & 1) == 0)) {
 			if (j < 0) || ((j == 0) && (mode == 0) && ((_word1(d) & 1) == 0)) {
 				if j1 > 0 {
 				if j1 > 0 {
@@ -583,28 +586,25 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
 								k++
 								k++
 								buf = append(buf, '1')
 								buf = append(buf, '1')
 							}
 							}
-							return buf, int(k + 1)
+							return buf, k + 1
 						}
 						}
 					}
 					}
 				}
 				}
 				buf = append(buf, dig)
 				buf = append(buf, dig)
-				return buf, int(k + 1)
+				return buf, k + 1
 			}
 			}
 			if j1 > 0 {
 			if j1 > 0 {
 				if dig == '9' { /* possible if i == 1 */
 				if dig == '9' { /* possible if i == 1 */
-					//                    round_9_up:
-					//                        *s++ = '9';
-					//                        goto roundoff;
 					buf = append(buf, '9')
 					buf = append(buf, '9')
 					buf, flag := roundOff(buf)
 					buf, flag := roundOff(buf)
 					if flag {
 					if flag {
 						k++
 						k++
 						buf = append(buf, '1')
 						buf = append(buf, '1')
 					}
 					}
-					return buf, int(k + 1)
+					return buf, k + 1
 				}
 				}
 				buf = append(buf, dig+1)
 				buf = append(buf, dig+1)
-				return buf, int(k + 1)
+				return buf, k + 1
 			}
 			}
 			buf = append(buf, dig)
 			buf = append(buf, dig)
 			if i == ilim {
 			if i == ilim {
@@ -619,9 +619,8 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
 			}
 			}
 		}
 		}
 	} else {
 	} else {
+		var z big.Int
 		for i = 1; ; i++ {
 		for i = 1; ; i++ {
-			//                (char)(dig = quorem(b,S) + '0');
-			z := new(big.Int)
 			z.DivMod(b, S, b)
 			z.DivMod(b, S, b)
 			dig = byte(z.Int64() + '0')
 			dig = byte(z.Int64() + '0')
 			buf = append(buf, dig)
 			buf = append(buf, dig)
@@ -642,13 +641,13 @@ func ftoa(d float64, mode int, biasUp bool, ndigits int, buf []byte) ([]byte, in
 		if flag {
 		if flag {
 			k++
 			k++
 			buf = append(buf, '1')
 			buf = append(buf, '1')
-			return buf, int(k + 1)
+			return buf, k + 1
 		}
 		}
 	} else {
 	} else {
 		buf = stripTrailingZeroes(buf)
 		buf = stripTrailingZeroes(buf)
 	}
 	}
 
 
-	return buf, int(k + 1)
+	return buf, k + 1
 }
 }
 
 
 func insert(b []byte, p int, c byte) []byte {
 func insert(b []byte, p int, c byte) []byte {
@@ -658,6 +657,16 @@ func insert(b []byte, p int, c byte) []byte {
 	return b
 	return b
 }
 }
 
 
+func expand(b []byte, delta int) []byte {
+	newLen := len(b) + delta
+	if newLen <= cap(b) {
+		return b[:newLen]
+	}
+	b1 := make([]byte, newLen)
+	copy(b1, b)
+	return b1
+}
+
 func FToStr(d float64, mode FToStrMode, precision int, buffer []byte) []byte {
 func FToStr(d float64, mode FToStrMode, precision int, buffer []byte) []byte {
 	if mode == ModeFixed && (d >= 1e21 || d <= -1e21) {
 	if mode == ModeFixed && (d >= 1e21 || d <= -1e21) {
 		mode = ModeStandard
 		mode = ModeStandard
@@ -736,10 +745,13 @@ func FToStr(d float64, mode FToStrMode, precision int, buffer []byte) []byte {
 				if sign {
 				if sign {
 					o = 1
 					o = 1
 				}
 				}
-				for i := 0; i < 1-decPt; i++ {
-					buffer = insert(buffer, o, '0')
+				buffer = expand(buffer, 2-decPt)
+				copy(buffer[o+2-decPt:], buffer[o:])
+				buffer[o] = '0'
+				buffer[o+1] = '.'
+				for i := o + 2; i < o+2-decPt; i++ {
+					buffer[i] = '0'
 				}
 				}
-				buffer = insert(buffer, o+1, '.')
 			}
 			}
 		}
 		}
 	}
 	}
@@ -792,8 +804,26 @@ func stripTrailingZeroes(buf []byte) []byte {
 }
 }
 
 
 /* Set b = b * 5^k.  k must be nonnegative. */
 /* Set b = b * 5^k.  k must be nonnegative. */
-// XXXX the C version built a cache of these
 func pow5mult(b *big.Int, k int) *big.Int {
 func pow5mult(b *big.Int, k int) *big.Int {
+	if k < (1 << (len(pow5Cache) + 2)) {
+		i := k & 3
+		if i != 0 {
+			b.Mul(b, p05[i-1])
+		}
+		k >>= 2
+		i = 0
+		for {
+			if k&1 != 0 {
+				b.Mul(b, pow5Cache[i])
+			}
+			k >>= 1
+			if k == 0 {
+				break
+			}
+			i++
+		}
+		return b
+	}
 	return b.Mul(b, new(big.Int).Exp(big5, big.NewInt(int64(k)), nil))
 	return b.Mul(b, new(big.Int).Exp(big5, big.NewInt(int64(k)), nil))
 }
 }
 
 
@@ -812,3 +842,12 @@ func roundOff(buf []byte) ([]byte, bool) {
 	}
 	}
 	return buf[:stop], true
 	return buf[:stop], true
 }
 }
+
+func init() {
+	p := big.NewInt(625)
+	pow5Cache[0] = p
+	for i := 1; i < len(pow5Cache); i++ {
+		p = new(big.Int).Mul(p, p)
+		pow5Cache[i] = p
+	}
+}

+ 34 - 0
ftoa/ftostr_test.go

@@ -2,6 +2,7 @@ package ftoa
 
 
 import (
 import (
 	"math"
 	"math"
+	"strconv"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -31,7 +32,40 @@ func TestDtostr(t *testing.T) {
 	testFToStr(8.85, ModeExponential, 2, "8.8e+0", t)
 	testFToStr(8.85, ModeExponential, 2, "8.8e+0", t)
 	testFToStr(885, ModeExponential, 2, "8.9e+2", t)
 	testFToStr(885, ModeExponential, 2, "8.9e+2", t)
 	testFToStr(25, ModeExponential, 1, "3e+1", t)
 	testFToStr(25, ModeExponential, 1, "3e+1", t)
+	testFToStr(1e-6, ModeFixed, 7, "0.0000010", t)
 	testFToStr(math.Inf(1), ModeStandard, 0, "Infinity", t)
 	testFToStr(math.Inf(1), ModeStandard, 0, "Infinity", t)
 	testFToStr(math.NaN(), ModeStandard, 0, "NaN", t)
 	testFToStr(math.NaN(), ModeStandard, 0, "NaN", t)
 	testFToStr(math.SmallestNonzeroFloat64, ModeExponential, 40, "4.940656458412465441765687928682213723651e-324", t)
 	testFToStr(math.SmallestNonzeroFloat64, ModeExponential, 40, "4.940656458412465441765687928682213723651e-324", t)
 }
 }
+
+func BenchmarkDtostrSmall(b *testing.B) {
+	var buf [128]byte
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		FToStr(math.Pi, ModeExponential, 0, buf[:0])
+	}
+}
+
+func BenchmarkDtostrBig(b *testing.B) {
+	var buf [128]byte
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		FToStr(math.SmallestNonzeroFloat64, ModeExponential, 40, buf[:0])
+	}
+}
+
+func BenchmarkAppendFloatBig(b *testing.B) {
+	var buf [128]byte
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		strconv.AppendFloat(buf[:0], math.SmallestNonzeroFloat64, 'e', 40, 64)
+	}
+}
+
+func BenchmarkAppendFloatSmall(b *testing.B) {
+	var buf [128]byte
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		strconv.AppendFloat(buf[:0], math.Pi, 'e', -1, 64)
+	}
+}