Skip to content

Commit

Permalink
Feat: Memory Management Optimization (#73)
Browse files Browse the repository at this point in the history
* Remove useless make commands

* Split logic components in zero runner

* Add benchmakrs to go test

* Add generic max function

* Optimize memory management

* reflect optimization on mem manager

* Add new tests for memory

* Bug fix

* Trim test segments
  • Loading branch information
rodrigo-pino committed Sep 27, 2023
1 parent fc47c8c commit 49f0c59
Show file tree
Hide file tree
Showing 11 changed files with 540 additions and 210 deletions.
8 changes: 0 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,3 @@ integration:
testall:
@echo "Running all tests..."
@go test ./...

format:
@gofumpt -l -w .

staticcheck:
@staticcheck ./...

pre-commit: format staticcheck build test clean
1 change: 0 additions & 1 deletion pkg/hintrunner/hint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ func TestAllocSegment(t *testing.T) {
alloc2 := AllocSegment{fp}

err := alloc1.Execute(vm)
t.Log(err)
require.Nil(t, err)
require.Equal(t, 3, len(vm.MemoryManager.Memory.Segments))
require.Equal(
Expand Down
118 changes: 118 additions & 0 deletions pkg/runners/zero/program.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package zero

import (
"errors"
"fmt"

"github.com/NethermindEth/cairo-vm-go/pkg/parsers/zero"
f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
)

type Program struct {
// the bytecode in string format
Bytecode []*f.Element
// given a string it returns the pc for that function call
Entrypoints map[string]uint64
// it stores the start and end label pcs
Labels map[string]uint64
}

func LoadCairoZeroProgram(content []byte) (*Program, error) {
cairoZeroJson, err := zero.ZeroProgramFromJSON(content)
if err != nil {
return nil, err
}

// bytecode
bytecode := make([]*f.Element, len(cairoZeroJson.Data))
for i := range cairoZeroJson.Data {
felt, err := new(f.Element).SetString(cairoZeroJson.Data[i])
if err != nil {
return nil, fmt.Errorf(
"cannot read bytecode %s at position %d: %w",
cairoZeroJson.Data[i], i, err,
)
}
bytecode[i] = felt
}

entrypoints, err := extractEntrypoints(cairoZeroJson)
if err != nil {
return nil, err
}

labels, err := extractLabels(cairoZeroJson)
if err != nil {
return nil, err
}

return &Program{
Bytecode: bytecode,
Entrypoints: entrypoints,
Labels: labels,
}, nil
}

func extractEntrypoints(json *zero.ZeroProgram) (map[string]uint64, error) {
result := make(map[string]uint64)
err := scanIdentifiers(
json,
func(key string, typex string, value map[string]any) error {
if typex == "function" {
pc, ok := value["pc"].(float64)
if !ok {
return fmt.Errorf("%s: unknown entrypoint pc", key)
}
name := key[len(json.MainScope)+1:]
result[name] = uint64(pc)
}
return nil
},
)

if err != nil {
return nil, fmt.Errorf("extracting entrypoints: %w", err)
}
return result, nil
}

func extractLabels(json *zero.ZeroProgram) (map[string]uint64, error) {
labels := make(map[string]uint64, 2)
err := scanIdentifiers(
json,
func(key string, typex string, value map[string]any) error {
if typex == "label" {
pc, ok := value["pc"].(float64)
if !ok {
return fmt.Errorf("%s: unknown entrypoint pc", key)
}
name := key[len(json.MainScope)+1:]
labels[name] = uint64(pc)
}
return nil
},
)
if err != nil {
return nil, fmt.Errorf("extracting labels: %w", err)
}

return labels, nil
}

func scanIdentifiers(
json *zero.ZeroProgram,
f func(key string, typex string, value map[string]any) error,
) error {
for key, value := range json.Identifiers {
properties := value.(map[string]any)

typex, ok := properties["type"].(string)
if !ok {
return errors.New("unnespecified identifier type")
}
if err := f(key, typex, properties); err != nil {
return err
}
}
return nil
}
60 changes: 60 additions & 0 deletions pkg/runners/zero/program_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package zero

import (
f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
"github.com/stretchr/testify/require"
"testing"
)

func TestLoadCairoZeroProgram(t *testing.T) {
content := []byte(`
{
"data": [
"0x0000001",
"0x0000002",
"0x0000003",
"0x0000004"
],
"main_scope": "__main__",
"identifiers": {
"__main__.main": {
"decorators": [],
"pc": 0,
"type": "function"
},
"__main__.fib": {
"decorators": [],
"pc": 4,
"type": "function"
}
}
}
`)

stringToFelt := func(bytecode string) *f.Element {
felt, err := new(f.Element).SetString(bytecode)
if err != nil {
panic(err)
}
return felt
}

program, err := LoadCairoZeroProgram(content)
require.NoError(t, err)

require.Equal(t, &Program{
Bytecode: []*f.Element{
stringToFelt("0x01"),
stringToFelt("0x02"),
stringToFelt("0x03"),
stringToFelt("0x04"),
},
Entrypoints: map[string]uint64{
"main": 0,
"fib": 4,
},
Labels: map[string]uint64{},
},
program,
)
}
110 changes: 0 additions & 110 deletions pkg/runners/zero/zero.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,123 +6,13 @@ import (
"fmt"

"github.com/NethermindEth/cairo-vm-go/pkg/hintrunner"
"github.com/NethermindEth/cairo-vm-go/pkg/parsers/zero"
"github.com/NethermindEth/cairo-vm-go/pkg/safemath"
"github.com/NethermindEth/cairo-vm-go/pkg/vm"
VM "github.com/NethermindEth/cairo-vm-go/pkg/vm"
"github.com/NethermindEth/cairo-vm-go/pkg/vm/memory"
f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
)

type Program struct {
// the bytecode in string format
Bytecode []*f.Element
// given a string it returns the pc for that function call
Entrypoints map[string]uint64
// it stores the start and end label pcs
Labels map[string]uint64
}

func LoadCairoZeroProgram(content []byte) (*Program, error) {
cairoZeroJson, err := zero.ZeroProgramFromJSON(content)
if err != nil {
return nil, err
}

// bytecode
bytecode := make([]*f.Element, len(cairoZeroJson.Data))
for i := range cairoZeroJson.Data {
felt, err := new(f.Element).SetString(cairoZeroJson.Data[i])
if err != nil {
return nil, fmt.Errorf(
"cannot read bytecode %s at position %d: %w",
cairoZeroJson.Data[i], i, err,
)
}
bytecode[i] = felt
}

entrypoints, err := extractEntrypoints(cairoZeroJson)
if err != nil {
return nil, err
}

labels, err := extractLabels(cairoZeroJson)
if err != nil {
return nil, err
}

return &Program{
Bytecode: bytecode,
Entrypoints: entrypoints,
Labels: labels,
}, nil
}

func extractEntrypoints(json *zero.ZeroProgram) (map[string]uint64, error) {
result := make(map[string]uint64)
err := scanIdentifiers(
json,
func(key string, typex string, value map[string]any) error {
if typex == "function" {
pc, ok := value["pc"].(float64)
if !ok {
return fmt.Errorf("%s: unknown entrypoint pc", key)
}
name := key[len(json.MainScope)+1:]
result[name] = uint64(pc)
}
return nil
},
)

if err != nil {
return nil, fmt.Errorf("extracting entrypoints: %w", err)
}
return result, nil
}

func extractLabels(json *zero.ZeroProgram) (map[string]uint64, error) {
labels := make(map[string]uint64, 2)
err := scanIdentifiers(
json,
func(key string, typex string, value map[string]any) error {
if typex == "label" {
pc, ok := value["pc"].(float64)
if !ok {
return fmt.Errorf("%s: unknown entrypoint pc", key)
}
name := key[len(json.MainScope)+1:]
labels[name] = uint64(pc)
}
return nil
},
)
if err != nil {
return nil, fmt.Errorf("extracting labels: %w", err)
}

return labels, nil
}

func scanIdentifiers(
json *zero.ZeroProgram,
f func(key string, typex string, value map[string]any) error,
) error {
for key, value := range json.Identifiers {
properties := value.(map[string]any)

typex, ok := properties["type"].(string)
if !ok {
return errors.New("unnespecified identifier type")
}
if err := f(key, typex, properties); err != nil {
return err
}
}
return nil
}

type ZeroRunner struct {
// core components
program *Program
Expand Down
Loading

0 comments on commit 49f0c59

Please sign in to comment.