mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
lib/decimal: increase decimal->float speed conversion for integer numbers
This commit is contained in:
parent
f1f2eff08f
commit
3d1f4408cf
3 changed files with 73 additions and 71 deletions
|
@ -45,10 +45,6 @@ func CalibrateScale(a []int64, ae int16, b []int64, be int16) (e int16) {
|
||||||
}
|
}
|
||||||
if downExp > 0 {
|
if downExp > 0 {
|
||||||
for i, v := range b {
|
for i, v := range b {
|
||||||
if v == vInfPos || v == vInfNeg {
|
|
||||||
// Special case for these values - do not touch them.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
adjExp := downExp
|
adjExp := downExp
|
||||||
for adjExp > 0 {
|
for adjExp > 0 {
|
||||||
v /= 10
|
v /= 10
|
||||||
|
@ -83,36 +79,35 @@ func ExtendInt64sCapacity(dst []int64, additionalItems int) []int64 {
|
||||||
// AppendDecimalToFloat converts each item in va to f=v*10^e, appends it
|
// AppendDecimalToFloat converts each item in va to f=v*10^e, appends it
|
||||||
// to dst and returns the resulting dst.
|
// to dst and returns the resulting dst.
|
||||||
func AppendDecimalToFloat(dst []float64, va []int64, e int16) []float64 {
|
func AppendDecimalToFloat(dst []float64, va []int64, e int16) []float64 {
|
||||||
if fastnum.IsInt64Zeros(va) {
|
|
||||||
return fastnum.AppendFloat64Zeros(dst, len(va))
|
|
||||||
}
|
|
||||||
if e == 0 && fastnum.IsInt64Ones(va) {
|
|
||||||
return fastnum.AppendFloat64Ones(dst, len(va))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extend dst capacity in order to eliminate memory allocations below.
|
// Extend dst capacity in order to eliminate memory allocations below.
|
||||||
dst = ExtendFloat64sCapacity(dst, len(va))
|
dst = ExtendFloat64sCapacity(dst, len(va))
|
||||||
|
|
||||||
|
if fastnum.IsInt64Zeros(va) {
|
||||||
|
return fastnum.AppendFloat64Zeros(dst, len(va))
|
||||||
|
}
|
||||||
|
if e == 0 {
|
||||||
|
if fastnum.IsInt64Ones(va) {
|
||||||
|
return fastnum.AppendFloat64Ones(dst, len(va))
|
||||||
|
}
|
||||||
|
for _, v := range va {
|
||||||
|
f := float64(v)
|
||||||
|
dst = append(dst, f)
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
// increase conversion precision for negative exponents by dividing by e10
|
// increase conversion precision for negative exponents by dividing by e10
|
||||||
isMul := true
|
|
||||||
if e < 0 {
|
if e < 0 {
|
||||||
e = -e
|
e10 := math.Pow10(int(-e))
|
||||||
isMul = false
|
for _, v := range va {
|
||||||
|
f := float64(v) / e10
|
||||||
|
dst = append(dst, f)
|
||||||
|
}
|
||||||
|
return dst
|
||||||
}
|
}
|
||||||
e10 := math.Pow10(int(e))
|
e10 := math.Pow10(int(e))
|
||||||
for _, v := range va {
|
for _, v := range va {
|
||||||
// Manually inline ToFloat here for better performance
|
f := float64(v) * e10
|
||||||
f := float64(v)
|
|
||||||
if isMul {
|
|
||||||
f *= e10
|
|
||||||
} else {
|
|
||||||
f /= e10
|
|
||||||
}
|
|
||||||
if v == vInfPos {
|
|
||||||
f = infPos
|
|
||||||
} else if v == vInfNeg {
|
|
||||||
f = infNeg
|
|
||||||
}
|
|
||||||
dst = append(dst, f)
|
dst = append(dst, f)
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
|
@ -263,26 +258,12 @@ func maxUpExponent(v int64) int16 {
|
||||||
|
|
||||||
// ToFloat returns f=v*10^e.
|
// ToFloat returns f=v*10^e.
|
||||||
func ToFloat(v int64, e int16) float64 {
|
func ToFloat(v int64, e int16) float64 {
|
||||||
// increase conversion precision for negative exponents by dividing by e10
|
|
||||||
isMul := true
|
|
||||||
if e < 0 {
|
|
||||||
e = -e
|
|
||||||
isMul = false
|
|
||||||
}
|
|
||||||
e10 := math.Pow10(int(e))
|
|
||||||
f := float64(v)
|
f := float64(v)
|
||||||
if isMul {
|
// increase conversion precision for negative exponents by dividing by e10
|
||||||
f *= e10
|
if e < 0 {
|
||||||
} else {
|
return f / math.Pow10(int(-e))
|
||||||
f /= e10
|
|
||||||
}
|
}
|
||||||
if v == vInfPos {
|
return f * math.Pow10(int(e))
|
||||||
return infPos
|
|
||||||
}
|
|
||||||
if v == vInfNeg {
|
|
||||||
return infNeg
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -293,11 +274,6 @@ const (
|
||||||
vMin = -1<<63 + 1
|
vMin = -1<<63 + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
infPos = math.Inf(1)
|
|
||||||
infNeg = math.Inf(-1)
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromFloat converts f to v*10^e.
|
// FromFloat converts f to v*10^e.
|
||||||
//
|
//
|
||||||
// It tries minimizing v.
|
// It tries minimizing v.
|
||||||
|
@ -327,7 +303,7 @@ func FromFloat(f float64) (int64, int16) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromFloatInf(f float64) (int64, int16) {
|
func fromFloatInf(f float64) (int64, int16) {
|
||||||
// Special case for Inf
|
// Limit infs by max and min values for int64
|
||||||
if math.IsInf(f, 1) {
|
if math.IsInf(f, 1) {
|
||||||
return vInfPos, 0
|
return vInfPos, 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,16 @@ func TestAppendDecimalToFloat(t *testing.T) {
|
||||||
testAppendDecimalToFloat(t, []int64{0}, -10, []float64{0})
|
testAppendDecimalToFloat(t, []int64{0}, -10, []float64{0})
|
||||||
testAppendDecimalToFloat(t, []int64{-1, -10, 0, 100}, 2, []float64{-1e2, -1e3, 0, 1e4})
|
testAppendDecimalToFloat(t, []int64{-1, -10, 0, 100}, 2, []float64{-1e2, -1e3, 0, 1e4})
|
||||||
testAppendDecimalToFloat(t, []int64{-1, -10, 0, 100}, -2, []float64{-1e-2, -1e-1, 0, 1})
|
testAppendDecimalToFloat(t, []int64{-1, -10, 0, 100}, -2, []float64{-1e-2, -1e-1, 0, 1})
|
||||||
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -9, []float64{0.000874957, 0.001130435})
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -5, []float64{8.74957, 1.130435e1})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -6, []float64{8.74957e-1, 1.130435})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -7, []float64{8.74957e-2, 1.130435e-1})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -8, []float64{8.74957e-3, 1.130435e-2})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -9, []float64{8.74957e-4, 1.130435e-3})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -10, []float64{8.74957e-5, 1.130435e-4})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -11, []float64{8.74957e-6, 1.130435e-5})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -12, []float64{8.74957e-7, 1.130435e-6})
|
||||||
|
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -13, []float64{8.74957e-8, 1.130435e-7})
|
||||||
|
testAppendDecimalToFloat(t, []int64{vInfPos, vInfNeg, 1, 2}, 0, []float64{9.223372036854776e+18, -9.223372036854776e+18, 1, 2})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAppendDecimalToFloat(t *testing.T, va []int64, e int16, fExpected []float64) {
|
func testAppendDecimalToFloat(t *testing.T, va []int64, e int16, fExpected []float64) {
|
||||||
|
@ -97,7 +106,7 @@ func TestCalibrateScale(t *testing.T) {
|
||||||
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{500, 100}, 0, 2, []int64{vInfPos, 1200}, []int64{500e2, 100e2}, 0)
|
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{500, 100}, 0, 2, []int64{vInfPos, 1200}, []int64{500e2, 100e2}, 0)
|
||||||
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{500, 100}, 0, -2, []int64{vInfPos, 1200}, []int64{5, 1}, 0)
|
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{500, 100}, 0, -2, []int64{vInfPos, 1200}, []int64{5, 1}, 0)
|
||||||
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{3500, 100}, 0, -3, []int64{vInfPos, 1200}, []int64{3, 0}, 0)
|
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{3500, 100}, 0, -3, []int64{vInfPos, 1200}, []int64{3, 0}, 0)
|
||||||
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{35, 1}, 0, 40, []int64{vInfPos, 0}, []int64{35e17, 1e17}, 23)
|
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{35, 1}, 0, 40, []int64{0, 0}, []int64{35e17, 1e17}, 23)
|
||||||
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{35, 1}, 40, 0, []int64{vInfPos, 1200}, []int64{0, 0}, 40)
|
testCalibrateScale(t, []int64{vInfPos, 1200}, []int64{35, 1}, 40, 0, []int64{vInfPos, 1200}, []int64{0, 0}, 40)
|
||||||
testCalibrateScale(t, []int64{vInfNeg, 1200}, []int64{35, 1}, 35, -5, []int64{vInfNeg, 1200}, []int64{0, 0}, 35)
|
testCalibrateScale(t, []int64{vInfNeg, 1200}, []int64{35, 1}, 35, -5, []int64{vInfNeg, 1200}, []int64{0, 0}, 35)
|
||||||
testCalibrateScale(t, []int64{vMax, vMin, 123}, []int64{100}, 0, 3, []int64{vMax, vMin, 123}, []int64{100e3}, 0)
|
testCalibrateScale(t, []int64{vMax, vMin, 123}, []int64{100}, 0, 3, []int64{vMax, vMin, 123}, []int64{100e3}, 0)
|
||||||
|
@ -163,6 +172,8 @@ func TestMaxUpExponent(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f(vInfPos, 0)
|
||||||
|
f(vInfNeg, 0)
|
||||||
f(0, 1024)
|
f(0, 1024)
|
||||||
f(-1<<63, 0)
|
f(-1<<63, 0)
|
||||||
f((-1<<63)+1, 0)
|
f((-1<<63)+1, 0)
|
||||||
|
@ -210,6 +221,9 @@ func TestAppendFloatToDecimal(t *testing.T) {
|
||||||
// no-op
|
// no-op
|
||||||
testAppendFloatToDecimal(t, []float64{}, nil, 0)
|
testAppendFloatToDecimal(t, []float64{}, nil, 0)
|
||||||
testAppendFloatToDecimal(t, []float64{0}, []int64{0}, 0)
|
testAppendFloatToDecimal(t, []float64{0}, []int64{0}, 0)
|
||||||
|
testAppendFloatToDecimal(t, []float64{infPos, infNeg, 123}, []int64{vInfPos, vInfNeg, 123}, 0)
|
||||||
|
testAppendFloatToDecimal(t, []float64{infPos, infNeg, 123, 1e-4, 1e32}, []int64{92233, -92233, 0, 0, 1000000000000000000}, 14)
|
||||||
|
testAppendFloatToDecimal(t, []float64{float64(vInfPos), float64(vInfNeg), 123}, []int64{9223372036854775000, -9223372036854775000, 123}, 0)
|
||||||
testAppendFloatToDecimal(t, []float64{0, -0, 1, -1, 12345678, -123456789}, []int64{0, 0, 1, -1, 12345678, -123456789}, 0)
|
testAppendFloatToDecimal(t, []float64{0, -0, 1, -1, 12345678, -123456789}, []int64{0, 0, 1, -1, 12345678, -123456789}, 0)
|
||||||
|
|
||||||
// upExp
|
// upExp
|
||||||
|
@ -330,10 +344,12 @@ func TestFloatToDecimalRoundtrip(t *testing.T) {
|
||||||
f(0.000874957)
|
f(0.000874957)
|
||||||
f(0.001130435)
|
f(0.001130435)
|
||||||
|
|
||||||
f(math.Inf(1))
|
f(2933434554455e245)
|
||||||
f(math.Inf(-1))
|
f(3439234258934e-245)
|
||||||
f(1<<63 - 1)
|
f(float64(vInfPos))
|
||||||
f(-1 << 63)
|
f(float64(vInfNeg))
|
||||||
|
f(infPos)
|
||||||
|
f(infNeg)
|
||||||
|
|
||||||
for i := 0; i < 1e4; i++ {
|
for i := 0; i < 1e4; i++ {
|
||||||
v := rand.NormFloat64()
|
v := rand.NormFloat64()
|
||||||
|
@ -357,9 +373,26 @@ func roundFloat(f float64, exp int) float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func equalFloat(f1, f2 float64) bool {
|
func equalFloat(f1, f2 float64) bool {
|
||||||
|
f1 = adjustInf(f1)
|
||||||
|
f2 = adjustInf(f2)
|
||||||
if math.IsInf(f1, 0) {
|
if math.IsInf(f1, 0) {
|
||||||
return math.IsInf(f1, 1) == math.IsInf(f2, 1) || math.IsInf(f1, -1) == math.IsInf(f2, -1)
|
return math.IsInf(f1, 1) == math.IsInf(f2, 1) || math.IsInf(f1, -1) == math.IsInf(f2, -1)
|
||||||
}
|
}
|
||||||
eps := math.Abs(f1 - f2)
|
eps := math.Abs(f1 - f2)
|
||||||
return eps == 0 || eps*conversionPrecision < math.Abs(f1)+math.Abs(f2)
|
return eps == 0 || eps*conversionPrecision < math.Abs(f1)+math.Abs(f2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func adjustInf(f float64) float64 {
|
||||||
|
if f == float64(vInfPos) {
|
||||||
|
return infPos
|
||||||
|
}
|
||||||
|
if f == float64(vInfNeg) {
|
||||||
|
return infNeg
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
infPos = math.Inf(1)
|
||||||
|
infNeg = math.Inf(-1)
|
||||||
|
)
|
||||||
|
|
|
@ -9,26 +9,26 @@ import (
|
||||||
|
|
||||||
func BenchmarkAppendDecimalToFloat(b *testing.B) {
|
func BenchmarkAppendDecimalToFloat(b *testing.B) {
|
||||||
b.Run("RealFloat", func(b *testing.B) {
|
b.Run("RealFloat", func(b *testing.B) {
|
||||||
benchmarkAppendDecimalToFloat(b, testVA)
|
benchmarkAppendDecimalToFloat(b, testVA, vaScale)
|
||||||
})
|
})
|
||||||
b.Run("Integers", func(b *testing.B) {
|
b.Run("Integers", func(b *testing.B) {
|
||||||
benchmarkAppendDecimalToFloat(b, testIntegers)
|
benchmarkAppendDecimalToFloat(b, testIntegers, integersScale)
|
||||||
})
|
})
|
||||||
b.Run("Zeros", func(b *testing.B) {
|
b.Run("Zeros", func(b *testing.B) {
|
||||||
benchmarkAppendDecimalToFloat(b, testZeros)
|
benchmarkAppendDecimalToFloat(b, testZeros, 0)
|
||||||
})
|
})
|
||||||
b.Run("Ones", func(b *testing.B) {
|
b.Run("Ones", func(b *testing.B) {
|
||||||
benchmarkAppendDecimalToFloat(b, testOnes)
|
benchmarkAppendDecimalToFloat(b, testOnes, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchmarkAppendDecimalToFloat(b *testing.B, a []int64) {
|
func benchmarkAppendDecimalToFloat(b *testing.B, a []int64, scale int16) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.SetBytes(int64(len(a)))
|
b.SetBytes(int64(len(a)))
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
var fa []float64
|
var fa []float64
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
fa = AppendDecimalToFloat(fa[:0], a, 0)
|
fa = AppendDecimalToFloat(fa[:0], a, scale)
|
||||||
atomic.AddUint64(&Sink, uint64(len(fa)))
|
atomic.AddUint64(&Sink, uint64(len(fa)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -99,15 +99,8 @@ var testFAInteger = func() []float64 {
|
||||||
return fa
|
return fa
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var testVA = func() []int64 {
|
var testVA, vaScale = AppendFloatToDecimal(nil, testFAReal)
|
||||||
va, _ := AppendFloatToDecimal(nil, testFAReal)
|
var testIntegers, integersScale = AppendFloatToDecimal(nil, testFAInteger)
|
||||||
return va
|
|
||||||
}()
|
|
||||||
|
|
||||||
var testIntegers = func() []int64 {
|
|
||||||
va, _ := AppendFloatToDecimal(nil, testFAInteger)
|
|
||||||
return va
|
|
||||||
}()
|
|
||||||
|
|
||||||
func BenchmarkFromFloat(b *testing.B) {
|
func BenchmarkFromFloat(b *testing.B) {
|
||||||
for _, f := range []float64{0, 1234, 12334345, 12343.4344, 123.45678901e12, 12.3454435e30} {
|
for _, f := range []float64{0, 1234, 12334345, 12343.4344, 123.45678901e12, 12.3454435e30} {
|
||||||
|
|
Loading…
Reference in a new issue