Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Ops and Storage Benchmarks #2073

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions benchmarking/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package main

import (
"encoding/binary"
"flag"
"fmt"
"os"
"sync"

"github.com/gnolang/gno/benchmarking"
"github.com/gnolang/gno/gnovm/pkg/gnolang"
)

const recordSize int = 10

var pathFlag = flag.String("path", "", "the path to the benchmark file")

func main() {
flag.Parse()

file, err := os.Open(*pathFlag)
if err != nil {
panic("could not create benchmark file: " + err.Error())
}
defer file.Close()

inputCh := make(chan []byte, 10000)
outputCh := make(chan string, 10000)
wg := sync.WaitGroup{}
numWorkers := 1
wg.Add(numWorkers)

doneCh := make(chan struct{})

for i := 0; i < numWorkers; i++ {
go func() {
for {
record, ok := <-inputCh
if !ok {
break
}

opName := gnolang.Op(record[0]).String()
if record[1] != 0 {
opName = benchmarking.StoreCodeString(record[1])
}

elapsedTime := binary.LittleEndian.Uint32(record[2:])
size := binary.LittleEndian.Uint32(record[6:])
outputCh <- opName + "," + fmt.Sprint(elapsedTime) + "," + fmt.Sprint(size)
}
wg.Done()
}()
}

go func() {
out, err := os.Create("results.csv")
if err != nil {
panic("could not create readable output file: " + err.Error())
}
defer out.Close()
fmt.Fprintln(out, "op,elapsedTime,diskIOBytes")

for {
output, ok := <-outputCh
if !ok {
break
}

fmt.Fprintln(out, output)
}

out.Close()
doneCh <- struct{}{}
}()

var i int

bufSize := recordSize * 100000
buf := make([]byte, bufSize)

for {
nbytes, err := file.Read(buf)

if err != nil && nbytes == 0 {
break
}
n := nbytes / recordSize

for j := 0; j < n; j++ {
inputCh <- buf[j*recordSize : (j+1)*recordSize]
}

i += bufSize / recordSize
if i%1000 == 0 {
fmt.Println(i)
}
}

close(inputCh)
wg.Wait()
close(outputCh)
<-doneCh
close(doneCh)

fmt.Println("done")
}
46 changes: 46 additions & 0 deletions benchmarking/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package benchmarking

import (
"encoding/binary"
"fmt"
"os"
"time"
)

var fileWriter *exporter

func initExporter(fileName string) {
file, err := os.Create(fileName)
if err != nil {
panic("could not create benchmark file: " + err.Error())

Check warning on line 15 in benchmarking/exporter.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/exporter.go#L12-L15

Added lines #L12 - L15 were not covered by tests
}

fileWriter = &exporter{
file: file,

Check warning on line 19 in benchmarking/exporter.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/exporter.go#L18-L19

Added lines #L18 - L19 were not covered by tests
}
}

type exporter struct {
file *os.File
}

func (e *exporter) export(code Code, elapsedTime time.Duration, size uint32) {
buf := []byte{code[0], code[1], 0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint32(buf[2:], uint32(elapsedTime))
binary.LittleEndian.PutUint32(buf[6:], size)
_, err := e.file.Write(buf)
if err != nil {
panic("could not write to benchmark file: " + err.Error())

Check warning on line 33 in benchmarking/exporter.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/exporter.go#L27-L33

Added lines #L27 - L33 were not covered by tests
}
}

func (e *exporter) close() {
e.file.Sync()
e.file.Close()

Check warning on line 39 in benchmarking/exporter.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/exporter.go#L37-L39

Added lines #L37 - L39 were not covered by tests
}

func Finish() {
fmt.Println("## StackSize: ", stackSize)

Check warning on line 43 in benchmarking/exporter.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/exporter.go#L42-L43

Added lines #L42 - L43 were not covered by tests

fileWriter.close()

Check warning on line 45 in benchmarking/exporter.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/exporter.go#L45

Added line #L45 was not covered by tests
}
6 changes: 6 additions & 0 deletions benchmarking/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package benchmarking

func Init(filepath string) {
initExporter(filepath)
initStack()

Check warning on line 5 in benchmarking/init.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/init.go#L3-L5

Added lines #L3 - L5 were not covered by tests
}
37 changes: 37 additions & 0 deletions benchmarking/measurement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package benchmarking

import (
"time"
)

type measurement struct {
*timer
code Code
allocation uint32
}

func startNewMeasurement(code Code) *measurement {
return &measurement{
timer: &timer{startTime: time.Now()},
code: code,

Check warning on line 16 in benchmarking/measurement.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/measurement.go#L13-L16

Added lines #L13 - L16 were not covered by tests
}
}

func (m *measurement) pause() {
m.stop()

Check warning on line 21 in benchmarking/measurement.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/measurement.go#L20-L21

Added lines #L20 - L21 were not covered by tests
}

func (m *measurement) resume() {
m.start()

Check warning on line 25 in benchmarking/measurement.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/measurement.go#L24-L25

Added lines #L24 - L25 were not covered by tests
}

func (m *measurement) end(size uint32) {
m.stop()
if size != 0 && m.allocation != 0 {
panic("measurement cannot have both allocation and size")
} else if size == 0 {
size = m.allocation

Check warning on line 33 in benchmarking/measurement.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/measurement.go#L28-L33

Added lines #L28 - L33 were not covered by tests
}

fileWriter.export(m.code, m.elapsedTime, size)

Check warning on line 36 in benchmarking/measurement.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/measurement.go#L36

Added line #L36 was not covered by tests
}
62 changes: 62 additions & 0 deletions benchmarking/ops.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package benchmarking

// store code
const (
StoreGetObject byte = 0x01 // get value and unmarshl to object from store
StoreSetObject byte = 0x02 // marshal object and set value in store
StoreDeleteObject byte = 0x03 // delete value from store
StoreGetPackage byte = 0x04 // get package from store
StoreSetPackage byte = 0x05 // get package from store
StoreGetType byte = 0x06 // get type from store
StoreSetType byte = 0x07 // set type in store
StoreGetBlockNode byte = 0x08 // get block node from store
StoreSetBlockNode byte = 0x09 // set block node in store
StoreAddMemPackage byte = 0x0A // add mempackage to store
StoreGetMemPackage byte = 0x0B // get mempackage from store
FinalizeTx byte = 0x0C // finalize realm transaction

AminoMarshal byte = 0x0D // marshal go object to binary value
AminoUnMarshal byte = 0x0E // unmarshl binary value to go object

StoreGet byte = 0x0F // Get binary value by key
StoreSet byte = 0x10 // Set binary value by key

invalidStoreCode string = "StoreInvalid"
)

var storeCodeNames = []string{
invalidStoreCode,
"StoreGetObject",
"StoreSetObject",
"StoreDeleteObject",
"StoreGetPackage",
"StoreSetPackage",
"StoreGetType",
"StoreSetType",
"StoreGetBlockNode",
"StoreSetBlockNode",
"StoreAddMemPackage",
"StoreGetMemPackage",
"FinalizeTx",
"AminoMarshal",
"AminoUnMarshal",
"StoreGet",
"StoreSet",
}

type Code [2]byte

func VMOpCode(opCode byte) Code {
return [2]byte{opCode, 0x00}

Check warning on line 50 in benchmarking/ops.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/ops.go#L49-L50

Added lines #L49 - L50 were not covered by tests
}

func StoreCode(storeCode byte) Code {
return [2]byte{0x00, storeCode}

Check warning on line 54 in benchmarking/ops.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/ops.go#L53-L54

Added lines #L53 - L54 were not covered by tests
}

func StoreCodeString(storeCode byte) string {
if int(storeCode) >= len(storeCodeNames) {
return invalidStoreCode

Check warning on line 59 in benchmarking/ops.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/ops.go#L57-L59

Added lines #L57 - L59 were not covered by tests
}
return storeCodeNames[storeCode]

Check warning on line 61 in benchmarking/ops.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/ops.go#L61

Added line #L61 was not covered by tests
}
5 changes: 5 additions & 0 deletions benchmarking/ops_disabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !benchmarkingops

package benchmarking

const OpsEnabled = false
5 changes: 5 additions & 0 deletions benchmarking/ops_enabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build benchmarkingops

package benchmarking

const OpsEnabled = true
66 changes: 66 additions & 0 deletions benchmarking/stack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package benchmarking

const initStackSize int = 64

var (
measurementStack []*measurement
stackSize int
)

func initStack() {
measurementStack = make([]*measurement, initStackSize)

Check warning on line 11 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L10-L11

Added lines #L10 - L11 were not covered by tests
}

func StartMeasurement(code Code) {
if stackSize != 0 {
measurementStack[stackSize-1].pause()

Check warning on line 16 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L14-L16

Added lines #L14 - L16 were not covered by tests
}

if stackSize == len(measurementStack) {
newStack := make([]*measurement, stackSize*2)
copy(newStack, measurementStack)
measurementStack = newStack

Check warning on line 22 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L19-L22

Added lines #L19 - L22 were not covered by tests
}

measurementStack[stackSize] = startNewMeasurement(code)
stackSize++

Check warning on line 26 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L25-L26

Added lines #L25 - L26 were not covered by tests
}

// Pause pauses current measurement on the stack
func Pause() {
if stackSize != 0 {
measurementStack[stackSize-1].pause()

Check warning on line 32 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L30-L32

Added lines #L30 - L32 were not covered by tests
}
}

// Resume resumes current measurement on the stack
func Resume() {
if stackSize != 0 {
measurementStack[stackSize-1].resume()

Check warning on line 39 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L37-L39

Added lines #L37 - L39 were not covered by tests
}
}

// StopMeasurement ends the current measurement and resumes the previous one
// if one exists. It accepts the number of bytes that were read/written to/from
// the store. This value is zero if the operation is not a read or write.
func StopMeasurement(size uint32) {
if stackSize == 0 {
return

Check warning on line 48 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L46-L48

Added lines #L46 - L48 were not covered by tests
}

measurementStack[stackSize-1].end(size)

Check warning on line 51 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L51

Added line #L51 was not covered by tests

stackSize--

Check warning on line 53 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L53

Added line #L53 was not covered by tests

if stackSize != 0 {
measurementStack[stackSize-1].resume()

Check warning on line 56 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L55-L56

Added lines #L55 - L56 were not covered by tests
}
}

func RecordAllocation(size uint32) {
if stackSize == 0 {
return

Check warning on line 62 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L60-L62

Added lines #L60 - L62 were not covered by tests
}

measurementStack[stackSize-1].allocation += size

Check warning on line 65 in benchmarking/stack.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/stack.go#L65

Added line #L65 was not covered by tests
}
5 changes: 5 additions & 0 deletions benchmarking/storage_disabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !benchmarkingstorage

package benchmarking

const StorageEnabled = false
5 changes: 5 additions & 0 deletions benchmarking/storage_enabled.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build benchmarkingstorage

package benchmarking

const StorageEnabled = true
22 changes: 22 additions & 0 deletions benchmarking/timer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package benchmarking

import "time"

type timer struct {
startTime time.Time
elapsedTime time.Duration
isStopped bool
}

func (t *timer) start() {
t.startTime = time.Now()

Check warning on line 12 in benchmarking/timer.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/timer.go#L11-L12

Added lines #L11 - L12 were not covered by tests
}

func (t *timer) stop() {
if t.isStopped {
return

Check warning on line 17 in benchmarking/timer.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/timer.go#L15-L17

Added lines #L15 - L17 were not covered by tests
}

t.elapsedTime += time.Since(t.startTime)
t.isStopped = true

Check warning on line 21 in benchmarking/timer.go

View check run for this annotation

Codecov / codecov/patch

benchmarking/timer.go#L20-L21

Added lines #L20 - L21 were not covered by tests
}
5 changes: 5 additions & 0 deletions gno.land/cmd/gnoland/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"strings"
"time"

bm "github.com/gnolang/gno/benchmarking"
"github.com/gnolang/gno/gno.land/pkg/gnoland"
"github.com/gnolang/gno/gno.land/pkg/log"
"github.com/gnolang/gno/gnovm/pkg/gnoenv"
Expand Down Expand Up @@ -201,6 +202,10 @@
}

func execStart(c *startCfg, io commands.IO) error {
if bm.OpsEnabled || bm.StorageEnabled {
bm.Init("benchmarks.bin")

Check warning on line 206 in gno.land/cmd/gnoland/start.go

View check run for this annotation

Codecov / codecov/patch

gno.land/cmd/gnoland/start.go#L206

Added line #L206 was not covered by tests
}

// Get the absolute path to the node's data directory
nodeDir, err := filepath.Abs(c.dataDir)
if err != nil {
Expand Down
Loading
Loading