Skip to content

Commit

Permalink
Expand tests and fix mode computation
Browse files Browse the repository at this point in the history
  • Loading branch information
2opremio committed May 16, 2024
1 parent 0469068 commit d9b9bbc
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 2 deletions.
8 changes: 7 additions & 1 deletion cmd/soroban-rpc/internal/feewindow/feewindow.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,17 @@ func computeFeeDistribution(fees []uint64, ledgerCount uint32) FeeDistribution {

if localRepetitions > maxRepetitions {
maxRepetitions = localRepetitions
mode = fees[i]
mode = lastVal
}
lastVal = fees[i]
localRepetitions = 0
}

if localRepetitions > maxRepetitions {
// the last cluster of values was the longest
mode = fees[len(fees)-1]
}

count := uint64(len(fees))
// nearest-rank percentile
percentile := func(p uint64) uint64 {
Expand Down
193 changes: 192 additions & 1 deletion cmd/soroban-rpc/internal/feewindow/feewindow_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package feewindow

import (
"fmt"
"math/rand"
"slices"
"testing"

"github.com/montanaflynn/stats"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestComputeFeeDistribution(t *testing.T) {
func TestBasicComputeFeeDistribution(t *testing.T) {
for _, testCase := range []struct {
name string
input []uint64
Expand Down Expand Up @@ -94,7 +99,193 @@ func TestComputeFeeDistribution(t *testing.T) {
FeeCount: 2,
},
},
{"random distribution with a repetition",
[]uint64{515, 245, 245, 530, 221, 262, 927},
FeeDistribution{
Max: 927,
Min: 221,
Mode: 245,
P10: 221,
P20: 245,
P30: 245,
P40: 245,
P50: 262,
P60: 515,
P70: 515,
P80: 530,
P90: 927,
P95: 927,
P99: 927,
FeeCount: 7,
},
},
{"random distribution with a repetition of its largest value",
[]uint64{515, 245, 530, 221, 262, 927, 927},
FeeDistribution{
Max: 927,
Min: 221,
Mode: 927,
P10: 221,
P20: 245,
P30: 262,
P40: 262,
P50: 515,
P60: 530,
P70: 530,
P80: 927,
P90: 927,
P95: 927,
P99: 927,
FeeCount: 7,
},
},
} {
assert.Equal(t, computeFeeDistribution(testCase.input, 0), testCase.output, testCase.name)
}
}

func TestComputeFeeDistributionAgainstAlternative(t *testing.T) {

for i := 0; i < 100_000; i++ {
fees := generateFees(nil)
feesCopy1 := make([]uint64, len(fees))
feesCopy2 := make([]uint64, len(fees))
for i := 0; i < len(fees); i++ {
feesCopy1[i] = fees[i]
feesCopy2[i] = fees[i]
}
actual := computeFeeDistribution(feesCopy2, 0)
expected, err := alternativeComputeFeeDistribution(feesCopy2, 0)
require.NoError(t, err)
assert.Equal(t, expected, actual, fmt.Sprintf("input fees: %v", fees))
}
}

func generateFees(l *int) []uint64 {
var length int
if l == nil {
length = *l
} else {
// Generate sequences with a length between 0 and 1000
length = rand.Intn(100)
}
result := make([]uint64, length)
lastFee := uint64(0)
for i := 0; i < length; i++ {
if lastFee != 0 && rand.Intn(100) <= 25 {
// To test the Mode correctly, generate a repetition with a chance of 25%
result[i] = lastFee
} else {
// generate fees between 100 and 1000
lastFee = uint64(rand.Intn(900) + 100)
result[i] = lastFee
}
}
return result
}

func BenchmarkComputeFeeDistribution(b *testing.B) {
length := 5000
fees := generateFees(&length)
b.Run("computeFeeDistribution", func(b *testing.B) {
for i := 0; i < b.N; i++ {
computeFeeDistribution(fees, 0)
}
})
b.Run("alternativeComputeFeeDistribution", func(b *testing.B) {
for i := 0; i < b.N; i++ {
alternativeComputeFeeDistribution(fees, 0)
}
})
}

func alternativeComputeFeeDistribution(fees []uint64, ledgerCount uint32) (FeeDistribution, error) {
if len(fees) == 0 {
return FeeDistribution{}, nil
}
input := stats.LoadRawData(fees)
max, err := input.Max()
if err != nil {
return FeeDistribution{}, err
}
min, err := input.Min()
if err != nil {
return FeeDistribution{}, err
}
modeSeq, err := input.Mode()
if err != nil {
return FeeDistribution{}, err
}
var mode uint64
if len(modeSeq) == 0 {
// mode can have length 0 if no value is repeated more than the rest
slices.Sort(fees)
mode = fees[0]
} else {
mode = uint64(modeSeq[0])
}
p10, err := input.PercentileNearestRank(float64(10))
if err != nil {
return FeeDistribution{}, err
}
p20, err := input.PercentileNearestRank(float64(20))
if err != nil {
return FeeDistribution{}, err
}
p30, err := input.PercentileNearestRank(float64(30))
if err != nil {
return FeeDistribution{}, err
}
p40, err := input.PercentileNearestRank(float64(40))
if err != nil {
return FeeDistribution{}, err
}
p50, err := input.PercentileNearestRank(float64(50))
if err != nil {
return FeeDistribution{}, err
}
p60, err := input.PercentileNearestRank(float64(60))
if err != nil {
return FeeDistribution{}, err
}
p70, err := input.PercentileNearestRank(float64(70))
if err != nil {
return FeeDistribution{}, err
}
p80, err := input.PercentileNearestRank(float64(80))
if err != nil {
return FeeDistribution{}, err
}
p90, err := input.PercentileNearestRank(float64(90))
if err != nil {
return FeeDistribution{}, err
}
p95, err := input.PercentileNearestRank(float64(95))
if err != nil {
return FeeDistribution{}, err
}
p99, err := input.PercentileNearestRank(float64(99))
if err != nil {
return FeeDistribution{}, err
}

result := FeeDistribution{
Max: uint64(max),
Min: uint64(min),
Mode: mode,
P10: uint64(p10),
P20: uint64(p20),
P30: uint64(p30),
P40: uint64(p40),
P50: uint64(p50),
P60: uint64(p60),
P70: uint64(p70),
P80: uint64(p80),
P90: uint64(p90),
P95: uint64(p95),
P99: uint64(p99),
FeeCount: uint64(len(fees)),
LedgerCount: ledgerCount,
}
return result, nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/creachadair/jrpc2 v1.2.0
github.com/go-chi/chi v4.1.2+incompatible
github.com/mattn/go-sqlite3 v1.14.17
github.com/montanaflynn/stats v0.7.1
github.com/pelletier/go-toml v1.9.5
github.com/prometheus/client_golang v1.17.0
github.com/rs/cors v1.10.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvls
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/moul/http2curl v0.0.0-20161031194548-4e24498b31db h1:eZgFHVkk9uOTaOQLC6tgjkzdp7Ays8eEVecBcfHZlJQ=
github.com/moul/http2curl v0.0.0-20161031194548-4e24498b31db/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
Expand Down

0 comments on commit d9b9bbc

Please sign in to comment.