perf: add cache for gas permium estimation
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
parent
897c5c7d82
commit
5f672c2ed0
@ -329,6 +329,8 @@ var ChainNode = Options(
|
||||
Override(new(storagemarket.StorageClientNode), storageadapter.NewClientNodeAdapter),
|
||||
Override(HandleMigrateClientFundsKey, modules.HandleMigrateClientFunds),
|
||||
|
||||
Override(new(*full.GasPriceCache), full.NewGasPriceCache),
|
||||
|
||||
// Lite node API
|
||||
ApplyIf(isLiteNode,
|
||||
Override(new(messagesigner.MpoolNonceAPI), From(new(modules.MpoolNonceAPI))),
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
"go.uber.org/fx"
|
||||
"golang.org/x/xerrors"
|
||||
@ -39,6 +40,8 @@ type GasModule struct {
|
||||
Chain *store.ChainStore
|
||||
Mpool *messagepool.MessagePool
|
||||
GetMaxFee dtypes.DefaultMaxFeeFunc
|
||||
|
||||
PriceCache *GasPriceCache
|
||||
}
|
||||
|
||||
var _ GasModuleAPI = (*GasModule)(nil)
|
||||
@ -51,6 +54,53 @@ type GasAPI struct {
|
||||
Stmgr *stmgr.StateManager
|
||||
Chain *store.ChainStore
|
||||
Mpool *messagepool.MessagePool
|
||||
|
||||
PriceCache *GasPriceCache
|
||||
}
|
||||
|
||||
func NewGasPriceCache() *GasPriceCache {
|
||||
// 50 because we usually won't access more than 40
|
||||
c, err := lru.New2Q(50)
|
||||
if err != nil {
|
||||
// err only if parameter is bad
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &GasPriceCache{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
type GasPriceCache struct {
|
||||
c *lru.TwoQueueCache
|
||||
}
|
||||
|
||||
type GasMeta struct {
|
||||
Price big.Int
|
||||
Limit int64
|
||||
}
|
||||
|
||||
func (g *GasPriceCache) GetTSGasStats(cstore *store.ChainStore, ts *types.TipSet) ([]GasMeta, error) {
|
||||
i, has := g.c.Get(ts.Key())
|
||||
if has {
|
||||
return i.([]GasMeta), nil
|
||||
}
|
||||
|
||||
var prices []GasMeta
|
||||
msgs, err := cstore.MessagesForTipset(ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading messages: %w", err)
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
prices = append(prices, GasMeta{
|
||||
Price: msg.VMMessage().GasPremium,
|
||||
Limit: msg.VMMessage().GasLimit,
|
||||
})
|
||||
}
|
||||
|
||||
g.c.Add(ts.Key(), prices)
|
||||
|
||||
return prices, nil
|
||||
}
|
||||
|
||||
const MinGasPremium = 100e3
|
||||
@ -88,24 +138,19 @@ func gasEstimateFeeCap(cstore *store.ChainStore, msg *types.Message, maxqueueblk
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type gasMeta struct {
|
||||
price big.Int
|
||||
limit int64
|
||||
}
|
||||
|
||||
// finds 55th percntile instead of median to put negative pressure on gas price
|
||||
func medianGasPremium(prices []gasMeta, blocks int) abi.TokenAmount {
|
||||
func medianGasPremium(prices []GasMeta, blocks int) abi.TokenAmount {
|
||||
sort.Slice(prices, func(i, j int) bool {
|
||||
// sort desc by price
|
||||
return prices[i].price.GreaterThan(prices[j].price)
|
||||
return prices[i].Price.GreaterThan(prices[j].Price)
|
||||
})
|
||||
|
||||
at := build.BlockGasTarget * int64(blocks) / 2 // 50th
|
||||
at += build.BlockGasTarget * int64(blocks) / (2 * 20) // move 5% further
|
||||
prev1, prev2 := big.Zero(), big.Zero()
|
||||
for _, price := range prices {
|
||||
prev1, prev2 = price.price, prev1
|
||||
at -= price.limit
|
||||
prev1, prev2 = price.Price, prev1
|
||||
at -= price.Limit
|
||||
if at < 0 {
|
||||
break
|
||||
}
|
||||
@ -126,7 +171,7 @@ func (a *GasAPI) GasEstimateGasPremium(
|
||||
gaslimit int64,
|
||||
_ types.TipSetKey,
|
||||
) (types.BigInt, error) {
|
||||
return gasEstimateGasPremium(a.Chain, nblocksincl)
|
||||
return gasEstimateGasPremium(a.Chain, a.PriceCache, nblocksincl)
|
||||
}
|
||||
func (m *GasModule) GasEstimateGasPremium(
|
||||
ctx context.Context,
|
||||
@ -135,14 +180,14 @@ func (m *GasModule) GasEstimateGasPremium(
|
||||
gaslimit int64,
|
||||
_ types.TipSetKey,
|
||||
) (types.BigInt, error) {
|
||||
return gasEstimateGasPremium(m.Chain, nblocksincl)
|
||||
return gasEstimateGasPremium(m.Chain, m.PriceCache, nblocksincl)
|
||||
}
|
||||
func gasEstimateGasPremium(cstore *store.ChainStore, nblocksincl uint64) (types.BigInt, error) {
|
||||
func gasEstimateGasPremium(cstore *store.ChainStore, cache *GasPriceCache, nblocksincl uint64) (types.BigInt, error) {
|
||||
if nblocksincl == 0 {
|
||||
nblocksincl = 1
|
||||
}
|
||||
|
||||
var prices []gasMeta
|
||||
var prices []GasMeta
|
||||
var blocks int
|
||||
|
||||
ts := cstore.GetHeaviestTipSet()
|
||||
@ -157,17 +202,11 @@ func gasEstimateGasPremium(cstore *store.ChainStore, nblocksincl uint64) (types.
|
||||
}
|
||||
|
||||
blocks += len(pts.Blocks())
|
||||
|
||||
msgs, err := cstore.MessagesForTipset(pts)
|
||||
meta, err := cache.GetTSGasStats(cstore, pts)
|
||||
if err != nil {
|
||||
return types.BigInt{}, xerrors.Errorf("loading messages: %w", err)
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
prices = append(prices, gasMeta{
|
||||
price: msg.VMMessage().GasPremium,
|
||||
limit: msg.VMMessage().GasLimit,
|
||||
})
|
||||
return types.BigInt{}, err
|
||||
}
|
||||
prices = append(prices, meta...)
|
||||
|
||||
ts = pts
|
||||
}
|
||||
|
@ -12,27 +12,27 @@ import (
|
||||
)
|
||||
|
||||
func TestMedian(t *testing.T) {
|
||||
require.Equal(t, types.NewInt(5), medianGasPremium([]gasMeta{
|
||||
require.Equal(t, types.NewInt(5), medianGasPremium([]GasMeta{
|
||||
{big.NewInt(5), build.BlockGasTarget},
|
||||
}, 1))
|
||||
|
||||
require.Equal(t, types.NewInt(10), medianGasPremium([]gasMeta{
|
||||
require.Equal(t, types.NewInt(10), medianGasPremium([]GasMeta{
|
||||
{big.NewInt(5), build.BlockGasTarget},
|
||||
{big.NewInt(10), build.BlockGasTarget},
|
||||
}, 1))
|
||||
|
||||
require.Equal(t, types.NewInt(15), medianGasPremium([]gasMeta{
|
||||
require.Equal(t, types.NewInt(15), medianGasPremium([]GasMeta{
|
||||
{big.NewInt(10), build.BlockGasTarget / 2},
|
||||
{big.NewInt(20), build.BlockGasTarget / 2},
|
||||
}, 1))
|
||||
|
||||
require.Equal(t, types.NewInt(25), medianGasPremium([]gasMeta{
|
||||
require.Equal(t, types.NewInt(25), medianGasPremium([]GasMeta{
|
||||
{big.NewInt(10), build.BlockGasTarget / 2},
|
||||
{big.NewInt(20), build.BlockGasTarget / 2},
|
||||
{big.NewInt(30), build.BlockGasTarget / 2},
|
||||
}, 1))
|
||||
|
||||
require.Equal(t, types.NewInt(15), medianGasPremium([]gasMeta{
|
||||
require.Equal(t, types.NewInt(15), medianGasPremium([]GasMeta{
|
||||
{big.NewInt(10), build.BlockGasTarget / 2},
|
||||
{big.NewInt(20), build.BlockGasTarget / 2},
|
||||
{big.NewInt(30), build.BlockGasTarget / 2},
|
||||
|
Loading…
Reference in New Issue
Block a user