mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
les: use fixed64
This commit is contained in:
parent
6714ac0933
commit
3d99858372
7 changed files with 100 additions and 62 deletions
|
|
@ -35,8 +35,8 @@ const (
|
|||
// expirationController controls the exponential expiration of positive and negative
|
||||
// balances
|
||||
type expirationController interface {
|
||||
posExpiration(mclock.AbsTime) float64
|
||||
negExpiration(mclock.AbsTime) float64
|
||||
posExpiration(mclock.AbsTime) fixed64
|
||||
negExpiration(mclock.AbsTime) fixed64
|
||||
}
|
||||
|
||||
// priceFactors determine the pricing policy (may apply either to positive or
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ import (
|
|||
|
||||
type zeroExpCtrl struct{}
|
||||
|
||||
func (z zeroExpCtrl) posExpiration(mclock.AbsTime) float64 {
|
||||
func (z zeroExpCtrl) posExpiration(mclock.AbsTime) fixed64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (z zeroExpCtrl) negExpiration(mclock.AbsTime) float64 {
|
||||
func (z zeroExpCtrl) negExpiration(mclock.AbsTime) fixed64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -138,18 +137,18 @@ func (db *nodeDB) key(id []byte, neg bool) []byte {
|
|||
return append(prefix, id...)
|
||||
}
|
||||
|
||||
func (db *nodeDB) getExpiration() (float64, float64) {
|
||||
func (db *nodeDB) getExpiration() (fixed64, fixed64) {
|
||||
blob, err := db.db.Get(expirationKey)
|
||||
if err != nil || len(blob) != 16 {
|
||||
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
|
||||
binary.BigEndian.PutUint64(buff[:8], math.Float64bits(pos))
|
||||
binary.BigEndian.PutUint64(buff[8:16], math.Float64bits(neg))
|
||||
binary.BigEndian.PutUint64(buff[:8], uint64(pos))
|
||||
binary.BigEndian.PutUint64(buff[8:16], uint64(neg))
|
||||
db.db.Put(expirationKey, buff[:16])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
if pos, neg := ndb.getExpiration(); pos != posExp || neg != negExp {
|
||||
t.Fatalf("Expiration mismatch, want %v / %v, got %v / %v", posExp, negExp, pos, neg)
|
||||
|
|
|
|||
|
|
@ -95,10 +95,11 @@ type clientPool struct {
|
|||
disableBias bool // Disable connection bias(used in testing)
|
||||
|
||||
// fields in this group are protected by expLock
|
||||
expLock sync.RWMutex
|
||||
posExpTC, negExpTC uint64
|
||||
posExp, negExp float64
|
||||
freeRatioLastUpdate mclock.AbsTime
|
||||
expLock sync.RWMutex
|
||||
posExpTC, negExpTC uint64
|
||||
posExp, negExp fixed64
|
||||
posExpTCi, negExpTCi float64 // already inverted (logMultiplier/time)
|
||||
freeRatioLastUpdate mclock.AbsTime
|
||||
}
|
||||
|
||||
// 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
|
||||
// value is small enough.
|
||||
ndb.evictCallBack = func(now mclock.AbsTime, neg bool, b tokenBalance) bool {
|
||||
var expiration float64
|
||||
var expiration fixed64
|
||||
if neg {
|
||||
expiration = pool.negExpiration(now)
|
||||
} else {
|
||||
|
|
@ -292,13 +293,8 @@ func (f *clientPool) updateFreeRatio() {
|
|||
f.averageFreeRatio -= (f.freeRatio - f.averageFreeRatio) * math.Expm1(-float64(dt)/float64(freeRatioTC))
|
||||
f.freeRatioLastUpdate = now
|
||||
|
||||
dt /= mclock.AbsTime(time.Second)
|
||||
if f.posExpTC != 0 {
|
||||
f.posExp += float64(dt) / float64(f.posExpTC) * f.freeRatio
|
||||
}
|
||||
if f.negExpTC != 0 {
|
||||
f.negExp += float64(dt) / float64(f.negExpTC) * f.freeRatio
|
||||
}
|
||||
f.posExp += fixed64(float64(dt) * f.posExpTCi * f.freeRatio)
|
||||
f.negExp += fixed64(float64(dt) * f.negExpTCi * f.freeRatio)
|
||||
f.expLock.Unlock()
|
||||
}
|
||||
|
||||
|
|
@ -311,6 +307,16 @@ func (f *clientPool) setExpirationTCs(pos, neg uint64) {
|
|||
|
||||
f.expLock.Lock()
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
@ -325,7 +331,7 @@ func (f *clientPool) getExpirationTCs() (pos, neg uint64) {
|
|||
|
||||
// posExpiration implements expirationController. Expiration happens only when
|
||||
// free service is available.
|
||||
func (f *clientPool) posExpiration(now mclock.AbsTime) float64 {
|
||||
func (f *clientPool) posExpiration(now mclock.AbsTime) fixed64 {
|
||||
f.expLock.RLock()
|
||||
defer f.expLock.RUnlock()
|
||||
|
||||
|
|
@ -337,12 +343,12 @@ func (f *clientPool) posExpiration(now mclock.AbsTime) float64 {
|
|||
dt = 0
|
||||
}
|
||||
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
|
||||
// free service is available.
|
||||
func (f *clientPool) negExpiration(now mclock.AbsTime) float64 {
|
||||
func (f *clientPool) negExpiration(now mclock.AbsTime) fixed64 {
|
||||
f.expLock.RLock()
|
||||
defer f.expLock.RUnlock()
|
||||
|
||||
|
|
@ -354,7 +360,7 @@ func (f *clientPool) negExpiration(now mclock.AbsTime) float64 {
|
|||
dt = 0
|
||||
}
|
||||
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
|
||||
|
|
@ -686,7 +692,7 @@ func (f *clientPool) finalizeBalance(c *clientInfo, now mclock.AbsTime) {
|
|||
for index, value := range []expiredValue{pos, neg} {
|
||||
var (
|
||||
id []byte
|
||||
expiration float64
|
||||
expiration fixed64
|
||||
)
|
||||
neg := index == 1
|
||||
if !neg {
|
||||
|
|
|
|||
|
|
@ -41,14 +41,15 @@ type expiredValue struct {
|
|||
}
|
||||
|
||||
// value calculates the value at the given moment.
|
||||
func (e expiredValue) value(logOffset float64) uint64 {
|
||||
return uint64(float64(e.base) * math.Pow(2, float64(e.exp)-logOffset))
|
||||
func (e expiredValue) value(logOffset fixed64) uint64 {
|
||||
offset := uint64ToFixed64(e.exp) - logOffset
|
||||
return uint64(float64(e.base) * offset.pow2Fixed())
|
||||
}
|
||||
|
||||
// add adds a signed value at the given moment
|
||||
func (e *expiredValue) add(amount int64, logOffset float64) int64 {
|
||||
integer, frac := uint64(logOffset), logOffset-float64(uint64(logOffset))
|
||||
factor := math.Pow(2, frac)
|
||||
func (e *expiredValue) add(amount int64, logOffset fixed64) int64 {
|
||||
integer, frac := logOffset.toUint64(), logOffset.fraction()
|
||||
factor := frac.pow2Fixed()
|
||||
base := factor * float64(amount)
|
||||
if integer < e.exp {
|
||||
base /= math.Pow(2, float64(e.exp-integer))
|
||||
|
|
@ -93,3 +94,35 @@ func (e *expiredValue) subExp(a expiredValue) {
|
|||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ import "testing"
|
|||
func TestValueExpiration(t *testing.T) {
|
||||
var cases = []struct {
|
||||
input expiredValue
|
||||
timeOffset float64
|
||||
timeOffset fixed64
|
||||
expect uint64
|
||||
}{
|
||||
{expiredValue{base: 128, exp: 0}, 0, 128},
|
||||
{expiredValue{base: 128, exp: 0}, 1, 64},
|
||||
{expiredValue{base: 128, exp: 0}, 2, 32},
|
||||
{expiredValue{base: 128, exp: 2}, 2, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 3, 64},
|
||||
{expiredValue{base: 128, exp: 0}, uint64ToFixed64(0), 128},
|
||||
{expiredValue{base: 128, exp: 0}, uint64ToFixed64(1), 64},
|
||||
{expiredValue{base: 128, exp: 0}, uint64ToFixed64(2), 32},
|
||||
{expiredValue{base: 128, exp: 2}, uint64ToFixed64(2), 128},
|
||||
{expiredValue{base: 128, exp: 2}, uint64ToFixed64(3), 64},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := c.input.value(c.timeOffset); got != c.expect {
|
||||
|
|
@ -41,29 +41,29 @@ func TestValueAddition(t *testing.T) {
|
|||
var cases = []struct {
|
||||
input expiredValue
|
||||
addend int64
|
||||
timeOffset float64
|
||||
timeOffset fixed64
|
||||
expect uint64
|
||||
expectNet int64
|
||||
}{
|
||||
// Addition
|
||||
{expiredValue{base: 128, exp: 0}, 128, 0, 256, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, 0, 640, 128},
|
||||
{expiredValue{base: 128, exp: 0}, 128, uint64ToFixed64(0), 256, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, uint64ToFixed64(0), 640, 128},
|
||||
|
||||
// Addition with offset
|
||||
{expiredValue{base: 128, exp: 0}, 128, 1, 192, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, 1, 384, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, 3, 192, 128},
|
||||
{expiredValue{base: 128, exp: 0}, 128, uint64ToFixed64(1), 192, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, uint64ToFixed64(1), 384, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, uint64ToFixed64(3), 192, 128},
|
||||
|
||||
// Subtraction
|
||||
{expiredValue{base: 128, exp: 0}, -64, 0, 64, -64},
|
||||
{expiredValue{base: 128, exp: 0}, -128, 0, 0, -128},
|
||||
{expiredValue{base: 128, exp: 0}, -192, 0, 0, -128},
|
||||
{expiredValue{base: 128, exp: 0}, -64, uint64ToFixed64(0), 64, -64},
|
||||
{expiredValue{base: 128, exp: 0}, -128, uint64ToFixed64(0), 0, -128},
|
||||
{expiredValue{base: 128, exp: 0}, -192, uint64ToFixed64(0), 0, -128},
|
||||
|
||||
// Subtraction with offset
|
||||
{expiredValue{base: 128, exp: 0}, -64, 1, 0, -64},
|
||||
{expiredValue{base: 128, exp: 0}, -128, 1, 0, -64},
|
||||
{expiredValue{base: 128, exp: 2}, -128, 1, 128, -128},
|
||||
{expiredValue{base: 128, exp: 2}, -128, 2, 0, -128},
|
||||
{expiredValue{base: 128, exp: 0}, -64, uint64ToFixed64(1), 0, -64},
|
||||
{expiredValue{base: 128, exp: 0}, -128, uint64ToFixed64(1), 0, -64},
|
||||
{expiredValue{base: 128, exp: 2}, -128, uint64ToFixed64(1), 128, -128},
|
||||
{expiredValue{base: 128, exp: 2}, -128, uint64ToFixed64(2), 0, -128},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if net := c.input.add(c.addend, c.timeOffset); net != c.expectNet {
|
||||
|
|
@ -79,13 +79,13 @@ func TestExpiredValueAddition(t *testing.T) {
|
|||
var cases = []struct {
|
||||
input expiredValue
|
||||
another expiredValue
|
||||
timeOffset float64
|
||||
timeOffset fixed64
|
||||
expect uint64
|
||||
}{
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 0, 256},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 0, 384},
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, 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(0), 256},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(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}, uint64ToFixed64(1), 128},
|
||||
}
|
||||
for _, c := range cases {
|
||||
c.input.addExp(c.another)
|
||||
|
|
@ -99,13 +99,13 @@ func TestExpiredValueSubtraction(t *testing.T) {
|
|||
var cases = []struct {
|
||||
input expiredValue
|
||||
another expiredValue
|
||||
timeOffset float64
|
||||
timeOffset fixed64
|
||||
expect uint64
|
||||
}{
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 0, 0},
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, 0, 0},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 0, 128},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 1, 64},
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(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}, uint64ToFixed64(0), 128},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, uint64ToFixed64(1), 64},
|
||||
}
|
||||
for _, c := range cases {
|
||||
c.input.subExp(c.another)
|
||||
|
|
|
|||
Loading…
Reference in a new issue