les: use fixed64

This commit is contained in:
rjl493456442 2020-03-02 17:35:21 +08:00
parent 6714ac0933
commit 3d99858372
7 changed files with 100 additions and 62 deletions

View file

@ -35,8 +35,8 @@ const (
// expirationController controls the exponential expiration of positive and negative // expirationController controls the exponential expiration of positive and negative
// balances // balances
type expirationController interface { type expirationController interface {
posExpiration(mclock.AbsTime) float64 posExpiration(mclock.AbsTime) fixed64
negExpiration(mclock.AbsTime) float64 negExpiration(mclock.AbsTime) fixed64
} }
// priceFactors determine the pricing policy (may apply either to positive or // priceFactors determine the pricing policy (may apply either to positive or

View file

@ -25,11 +25,11 @@ import (
type zeroExpCtrl struct{} type zeroExpCtrl struct{}
func (z zeroExpCtrl) posExpiration(mclock.AbsTime) float64 { func (z zeroExpCtrl) posExpiration(mclock.AbsTime) fixed64 {
return 0 return 0
} }
func (z zeroExpCtrl) negExpiration(mclock.AbsTime) float64 { func (z zeroExpCtrl) negExpiration(mclock.AbsTime) fixed64 {
return 0 return 0
} }

View file

@ -20,7 +20,6 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"io" "io"
"math"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -138,18 +137,18 @@ func (db *nodeDB) key(id []byte, neg bool) []byte {
return append(prefix, id...) return append(prefix, id...)
} }
func (db *nodeDB) getExpiration() (float64, float64) { func (db *nodeDB) getExpiration() (fixed64, fixed64) {
blob, err := db.db.Get(expirationKey) blob, err := db.db.Get(expirationKey)
if err != nil || len(blob) != 16 { if err != nil || len(blob) != 16 {
return 0, 0 return 0, 0
} }
return math.Float64frombits(binary.BigEndian.Uint64(blob[:8])), math.Float64frombits(binary.BigEndian.Uint64(blob[8:16])) return fixed64(binary.BigEndian.Uint64(blob[:8])), fixed64(binary.BigEndian.Uint64(blob[8:16]))
} }
func (db *nodeDB) setExpiration(pos, neg float64) { func (db *nodeDB) setExpiration(pos, neg fixed64) {
var buff [16]byte var buff [16]byte
binary.BigEndian.PutUint64(buff[:8], math.Float64bits(pos)) binary.BigEndian.PutUint64(buff[:8], uint64(pos))
binary.BigEndian.PutUint64(buff[8:16], math.Float64bits(neg)) binary.BigEndian.PutUint64(buff[8:16], uint64(neg))
db.db.Put(expirationKey, buff[:16]) db.db.Put(expirationKey, buff[:16])
} }

View file

@ -67,7 +67,7 @@ func TestNodeDB(t *testing.T) {
} }
} }
} }
posExp, negExp := 100.1234, 200.5678 posExp, negExp := fixed64(1000), fixed64(2000)
ndb.setExpiration(posExp, negExp) ndb.setExpiration(posExp, negExp)
if pos, neg := ndb.getExpiration(); pos != posExp || neg != negExp { if pos, neg := ndb.getExpiration(); pos != posExp || neg != negExp {
t.Fatalf("Expiration mismatch, want %v / %v, got %v / %v", posExp, negExp, pos, neg) t.Fatalf("Expiration mismatch, want %v / %v, got %v / %v", posExp, negExp, pos, neg)

View file

@ -95,10 +95,11 @@ type clientPool struct {
disableBias bool // Disable connection bias(used in testing) disableBias bool // Disable connection bias(used in testing)
// fields in this group are protected by expLock // fields in this group are protected by expLock
expLock sync.RWMutex expLock sync.RWMutex
posExpTC, negExpTC uint64 posExpTC, negExpTC uint64
posExp, negExp float64 posExp, negExp fixed64
freeRatioLastUpdate mclock.AbsTime posExpTCi, negExpTCi float64 // already inverted (logMultiplier/time)
freeRatioLastUpdate mclock.AbsTime
} }
// clientPoolPeer represents a client peer in the pool. // clientPoolPeer represents a client peer in the pool.
@ -201,7 +202,7 @@ func newClientPool(db ethdb.Database, minCap, freeClientCap uint64, clock mclock
// and both of these decay exponentially over time. Delete them if the // and both of these decay exponentially over time. Delete them if the
// value is small enough. // value is small enough.
ndb.evictCallBack = func(now mclock.AbsTime, neg bool, b tokenBalance) bool { ndb.evictCallBack = func(now mclock.AbsTime, neg bool, b tokenBalance) bool {
var expiration float64 var expiration fixed64
if neg { if neg {
expiration = pool.negExpiration(now) expiration = pool.negExpiration(now)
} else { } else {
@ -292,13 +293,8 @@ func (f *clientPool) updateFreeRatio() {
f.averageFreeRatio -= (f.freeRatio - f.averageFreeRatio) * math.Expm1(-float64(dt)/float64(freeRatioTC)) f.averageFreeRatio -= (f.freeRatio - f.averageFreeRatio) * math.Expm1(-float64(dt)/float64(freeRatioTC))
f.freeRatioLastUpdate = now f.freeRatioLastUpdate = now
dt /= mclock.AbsTime(time.Second) f.posExp += fixed64(float64(dt) * f.posExpTCi * f.freeRatio)
if f.posExpTC != 0 { f.negExp += fixed64(float64(dt) * f.negExpTCi * f.freeRatio)
f.posExp += float64(dt) / float64(f.posExpTC) * f.freeRatio
}
if f.negExpTC != 0 {
f.negExp += float64(dt) / float64(f.negExpTC) * f.freeRatio
}
f.expLock.Unlock() f.expLock.Unlock()
} }
@ -311,6 +307,16 @@ func (f *clientPool) setExpirationTCs(pos, neg uint64) {
f.expLock.Lock() f.expLock.Lock()
f.posExpTC, f.negExpTC = pos, neg f.posExpTC, f.negExpTC = pos, neg
if pos > 0 {
f.posExpTCi = fixedFactor / float64(pos*uint64(time.Second))
} else {
f.posExpTCi = 0
}
if neg > 0 {
f.negExpTCi = fixedFactor / float64(neg*uint64(time.Second))
} else {
f.negExpTCi = 0
}
f.expLock.Unlock() f.expLock.Unlock()
} }
@ -325,7 +331,7 @@ func (f *clientPool) getExpirationTCs() (pos, neg uint64) {
// posExpiration implements expirationController. Expiration happens only when // posExpiration implements expirationController. Expiration happens only when
// free service is available. // free service is available.
func (f *clientPool) posExpiration(now mclock.AbsTime) float64 { func (f *clientPool) posExpiration(now mclock.AbsTime) fixed64 {
f.expLock.RLock() f.expLock.RLock()
defer f.expLock.RUnlock() defer f.expLock.RUnlock()
@ -337,12 +343,12 @@ func (f *clientPool) posExpiration(now mclock.AbsTime) float64 {
dt = 0 dt = 0
} }
dt /= mclock.AbsTime(time.Second) dt /= mclock.AbsTime(time.Second)
return f.posExp + float64(dt)/float64(f.posExpTC)*f.freeRatio return f.posExp + fixed64(float64(dt)/float64(f.posExpTC)*f.freeRatio)
} }
// negExpiration implements expirationController. Expiration happens only when // negExpiration implements expirationController. Expiration happens only when
// free service is available. // free service is available.
func (f *clientPool) negExpiration(now mclock.AbsTime) float64 { func (f *clientPool) negExpiration(now mclock.AbsTime) fixed64 {
f.expLock.RLock() f.expLock.RLock()
defer f.expLock.RUnlock() defer f.expLock.RUnlock()
@ -354,7 +360,7 @@ func (f *clientPool) negExpiration(now mclock.AbsTime) float64 {
dt = 0 dt = 0
} }
dt /= mclock.AbsTime(time.Second) dt /= mclock.AbsTime(time.Second)
return f.negExp + float64(dt)/float64(f.negExpTC)*f.freeRatio return f.negExp + fixed64(float64(dt)/float64(f.negExpTC)*f.freeRatio)
} }
// totalTokenLimit returns the current token supply limit. Token prices are based // totalTokenLimit returns the current token supply limit. Token prices are based
@ -686,7 +692,7 @@ func (f *clientPool) finalizeBalance(c *clientInfo, now mclock.AbsTime) {
for index, value := range []expiredValue{pos, neg} { for index, value := range []expiredValue{pos, neg} {
var ( var (
id []byte id []byte
expiration float64 expiration fixed64
) )
neg := index == 1 neg := index == 1
if !neg { if !neg {

View file

@ -41,14 +41,15 @@ type expiredValue struct {
} }
// value calculates the value at the given moment. // value calculates the value at the given moment.
func (e expiredValue) value(logOffset float64) uint64 { func (e expiredValue) value(logOffset fixed64) uint64 {
return uint64(float64(e.base) * math.Pow(2, float64(e.exp)-logOffset)) offset := uint64ToFixed64(e.exp) - logOffset
return uint64(float64(e.base) * offset.pow2Fixed())
} }
// add adds a signed value at the given moment // add adds a signed value at the given moment
func (e *expiredValue) add(amount int64, logOffset float64) int64 { func (e *expiredValue) add(amount int64, logOffset fixed64) int64 {
integer, frac := uint64(logOffset), logOffset-float64(uint64(logOffset)) integer, frac := logOffset.toUint64(), logOffset.fraction()
factor := math.Pow(2, frac) factor := frac.pow2Fixed()
base := factor * float64(amount) base := factor * float64(amount)
if integer < e.exp { if integer < e.exp {
base /= math.Pow(2, float64(e.exp-integer)) base /= math.Pow(2, float64(e.exp-integer))
@ -93,3 +94,35 @@ func (e *expiredValue) subExp(a expiredValue) {
e.base = 0 e.base = 0
} }
} }
// fixedFactor is the factor used by fixed64
const fixedFactor = 0x1000000
// fixed64 is a float64 wrapper that uses integer arithmetic
// to avoid precision loss in floating-point arithmetic.
type fixed64 int64
// uint64ToFixed64 converts uint64 integer to fixed64 format.
func uint64ToFixed64(f uint64) fixed64 {
return fixed64(f * fixedFactor)
}
// float64ToFixed64 converts float64 to fixed64 format.
func float64ToFixed64(f float64) fixed64 {
return fixed64(f * fixedFactor)
}
// toUint64 converts fixed64 format to uint64.
func (f64 fixed64) toUint64() uint64 {
return uint64(f64) / fixedFactor
}
// fraction returns the fraction of the fixed64.
func (f64 fixed64) fraction() fixed64 {
return f64 % fixedFactor
}
// pow2Fixed returns the 2 based pow of the fixed value.
func (f64 fixed64) pow2Fixed() float64 {
return math.Pow(2, float64(f64)/fixedFactor)
}

View file

@ -21,14 +21,14 @@ import "testing"
func TestValueExpiration(t *testing.T) { func TestValueExpiration(t *testing.T) {
var cases = []struct { var cases = []struct {
input expiredValue input expiredValue
timeOffset float64 timeOffset fixed64
expect uint64 expect uint64
}{ }{
{expiredValue{base: 128, exp: 0}, 0, 128}, {expiredValue{base: 128, exp: 0}, uint64ToFixed64(0), 128},
{expiredValue{base: 128, exp: 0}, 1, 64}, {expiredValue{base: 128, exp: 0}, uint64ToFixed64(1), 64},
{expiredValue{base: 128, exp: 0}, 2, 32}, {expiredValue{base: 128, exp: 0}, uint64ToFixed64(2), 32},
{expiredValue{base: 128, exp: 2}, 2, 128}, {expiredValue{base: 128, exp: 2}, uint64ToFixed64(2), 128},
{expiredValue{base: 128, exp: 2}, 3, 64}, {expiredValue{base: 128, exp: 2}, uint64ToFixed64(3), 64},
} }
for _, c := range cases { for _, c := range cases {
if got := c.input.value(c.timeOffset); got != c.expect { if got := c.input.value(c.timeOffset); got != c.expect {
@ -41,29 +41,29 @@ func TestValueAddition(t *testing.T) {
var cases = []struct { var cases = []struct {
input expiredValue input expiredValue
addend int64 addend int64
timeOffset float64 timeOffset fixed64
expect uint64 expect uint64
expectNet int64 expectNet int64
}{ }{
// Addition // Addition
{expiredValue{base: 128, exp: 0}, 128, 0, 256, 128}, {expiredValue{base: 128, exp: 0}, 128, uint64ToFixed64(0), 256, 128},
{expiredValue{base: 128, exp: 2}, 128, 0, 640, 128}, {expiredValue{base: 128, exp: 2}, 128, uint64ToFixed64(0), 640, 128},
// Addition with offset // Addition with offset
{expiredValue{base: 128, exp: 0}, 128, 1, 192, 128}, {expiredValue{base: 128, exp: 0}, 128, uint64ToFixed64(1), 192, 128},
{expiredValue{base: 128, exp: 2}, 128, 1, 384, 128}, {expiredValue{base: 128, exp: 2}, 128, uint64ToFixed64(1), 384, 128},
{expiredValue{base: 128, exp: 2}, 128, 3, 192, 128}, {expiredValue{base: 128, exp: 2}, 128, uint64ToFixed64(3), 192, 128},
// Subtraction // Subtraction
{expiredValue{base: 128, exp: 0}, -64, 0, 64, -64}, {expiredValue{base: 128, exp: 0}, -64, uint64ToFixed64(0), 64, -64},
{expiredValue{base: 128, exp: 0}, -128, 0, 0, -128}, {expiredValue{base: 128, exp: 0}, -128, uint64ToFixed64(0), 0, -128},
{expiredValue{base: 128, exp: 0}, -192, 0, 0, -128}, {expiredValue{base: 128, exp: 0}, -192, uint64ToFixed64(0), 0, -128},
// Subtraction with offset // Subtraction with offset
{expiredValue{base: 128, exp: 0}, -64, 1, 0, -64}, {expiredValue{base: 128, exp: 0}, -64, uint64ToFixed64(1), 0, -64},
{expiredValue{base: 128, exp: 0}, -128, 1, 0, -64}, {expiredValue{base: 128, exp: 0}, -128, uint64ToFixed64(1), 0, -64},
{expiredValue{base: 128, exp: 2}, -128, 1, 128, -128}, {expiredValue{base: 128, exp: 2}, -128, uint64ToFixed64(1), 128, -128},
{expiredValue{base: 128, exp: 2}, -128, 2, 0, -128}, {expiredValue{base: 128, exp: 2}, -128, uint64ToFixed64(2), 0, -128},
} }
for _, c := range cases { for _, c := range cases {
if net := c.input.add(c.addend, c.timeOffset); net != c.expectNet { if net := c.input.add(c.addend, c.timeOffset); net != c.expectNet {
@ -79,13 +79,13 @@ func TestExpiredValueAddition(t *testing.T) {
var cases = []struct { var cases = []struct {
input expiredValue input expiredValue
another expiredValue another expiredValue
timeOffset float64 timeOffset fixed64
expect uint64 expect uint64
}{ }{
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 0, 256}, {expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(0), 256},
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 0, 384}, {expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(0), 384},
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, 0, 384}, {expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, uint64ToFixed64(0), 384},
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 1, 128}, {expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(1), 128},
} }
for _, c := range cases { for _, c := range cases {
c.input.addExp(c.another) c.input.addExp(c.another)
@ -99,13 +99,13 @@ func TestExpiredValueSubtraction(t *testing.T) {
var cases = []struct { var cases = []struct {
input expiredValue input expiredValue
another expiredValue another expiredValue
timeOffset float64 timeOffset fixed64
expect uint64 expect uint64
}{ }{
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 0, 0}, {expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(0), 0},
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, 0, 0}, {expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, uint64ToFixed64(0), 0},
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 0, 128}, {expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(0), 128},
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 1, 64}, {expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(1), 64},
} }
for _, c := range cases { for _, c := range cases {
c.input.subExp(c.another) c.input.subExp(c.another)