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
|
// 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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,8 @@ type clientPool struct {
|
||||||
// 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
|
||||||
|
posExpTCi, negExpTCi float64 // already inverted (logMultiplier/time)
|
||||||
freeRatioLastUpdate mclock.AbsTime
|
freeRatioLastUpdate mclock.AbsTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue