Skip to content

Commit

Permalink
Merge pull request #14 from saantiaguilera/feature/sa/docs
Browse files Browse the repository at this point in the history
Add bench tests
  • Loading branch information
saantiaguilera committed May 26, 2022
2 parents ca8c7f3 + 1aa4e49 commit 79d5751
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 112 deletions.
31 changes: 31 additions & 0 deletions concurrent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,37 @@ func ExampleConcurrentStep_different() {
// {true true} <nil>
}

// Benchmark for traversing a concurrent step. This is simply used so that future changes can
// easily reflect how they affected the performance
//
// goos: darwin
// goarch: amd64
// pkg: github.com/saantiaguilera/go-pipeline
// cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
// BenchmarkConcurrentStep-8 559716 1990 ns/op 384 B/op 4 allocs/op
func BenchmarkConcurrentStep(b *testing.B) {
var err error
s := pipeline.NewConcurrentStep(
[]pipeline.Step[any, any]{noopStep[any]{}, noopStep[any]{}},
func(ctx context.Context, a1, a2 any) (any, error) {
return a1, nil
},
)
ctx := context.Background()
in := 0

b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StartTimer()
_, err = s.Run(ctx, in)
b.StopTimer()

if err != nil {
b.Fail()
}
}
}

func TestConcurrentStep_GivenStepsWithoutErrors_WhenRun_ThenAllStepsAreRunConcurrently(t *testing.T) {
arr := &[]int{}
var expectedArr []int
Expand Down
32 changes: 32 additions & 0 deletions conditional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,38 @@ func ExampleConditionalStep() {
// true <nil>
}

// Benchmark for traversing a conditional step. This is simply used so that future changes can
// easily reflect how they affected the performance
//
// goos: darwin
// goarch: amd64
// pkg: github.com/saantiaguilera/go-pipeline
// cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
// BenchmarkConditionalStep-8 9185359 150.2 ns/op 0 B/op 0 allocs/op
func BenchmarkConditionalStep(b *testing.B) {
var err error
s := pipeline.NewConditionalStep[any, any](
pipeline.NewAnonymousStatement(func(ctx context.Context, a any) bool {
return a != nil
}),
noopStep[any]{},
noopStep[any]{},
)
ctx := context.Background()
in := 0

b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StartTimer()
_, err = s.Run(ctx, in)
b.StopTimer()

if err != nil {
b.Fail()
}
}
}

func TestConditionalStep_GivenNilStatement_WhenRun_FalseIsRun(t *testing.T) {
run := false
falseStep := pipeline.NewUnitStep("", func(ctx context.Context, t any) (any, error) {
Expand Down
31 changes: 31 additions & 0 deletions optional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,37 @@ func ExampleOptionalStep_default() {
// true <nil>
}

// Benchmark for traversing a optional step. This is simply used so that future changes can
// easily reflect how they affected the performance
//
// goos: darwin
// goarch: amd64
// pkg: github.com/saantiaguilera/go-pipeline
// cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
// BenchmarkOptionalStep-8 7752477 175.9 ns/op 0 B/op 0 allocs/op
func BenchmarkOptionalStep(b *testing.B) {
var err error
s := pipeline.NewOptionalStep[any](
pipeline.NewAnonymousStatement(func(ctx context.Context, a any) bool {
return a != nil
}),
noopStep[any]{},
)
ctx := context.Background()
in := 0

b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StartTimer()
_, err = s.Run(ctx, in)
b.StopTimer()

if err != nil {
b.Fail()
}
}
}

func TestOptionalStep_GivenNilStatement_WhenRun_ThenDefaults(t *testing.T) {
run := false
step := pipeline.NewOptionalStep[any](pipeline.NewAnonymousStatement[any](nil), pipeline.NewUnitStep("", func(_ context.Context, _ any) (any, error) {
Expand Down
124 changes: 12 additions & 112 deletions pipeline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,111 +2,11 @@ package pipeline_test

import (
"context"
"flag"
"fmt"
"os"
"testing"

"github.com/stretchr/testify/assert"

"github.com/saantiaguilera/go-pipeline"
)

var render = flag.Bool("pipeline.render", false, "render pipeline")

func newDummyStep() pipeline.Step[int, int] {
return pipeline.NewUnitStep("dummy step", func(ctx context.Context, in int) (int, error) {
return in, nil
})
}

func dummyReducer(_ context.Context, a, _ int) (int, error) {
return a, nil
}

func NewImmenseGraph() pipeline.Step[int, int] {
step := newDummyStep()

innerJob := pipeline.NewSequentialStep[int, int, int](
pipeline.NewSequentialStep[int, int, int](
pipeline.NewSequentialStep(step, step),
pipeline.NewSequentialStep(step, step),
),
pipeline.NewConcurrentStep(
[]pipeline.Step[int, int]{step, step, step, step, step, step}, dummyReducer,
),
)

return pipeline.NewSequentialStep[int, int, int](
pipeline.NewConditionalStep[int, int](
pipeline.NewAnonymousStatement(
func(ctx context.Context, t int) bool {
return true
},
),
pipeline.NewConcurrentStep(
[]pipeline.Step[int, int]{innerJob, step, innerJob, step, innerJob, step, innerJob, step},
dummyReducer,
),
nil,
),
pipeline.NewSequentialStep[int, int, int](
pipeline.NewSequentialStep[int, int, int](
pipeline.NewSequentialStep(step, step),
pipeline.NewSequentialStep(step, step),
),
pipeline.NewConcurrentStep(
[]pipeline.Step[int, int]{step, step, step, step, step, step}, dummyReducer,
),
),
)
}

func Test_GraphRendering(t *testing.T) {
if *render {
diagram := pipeline.NewUMLGraph()
renderer := pipeline.NewUMLRenderer(pipeline.UMLOptions{
Type: pipeline.UMLFormatSVG,
})
file, _ := os.Create("pipeline_test.svg")

NewImmenseGraph().Draw(diagram)

err := renderer.Render(diagram, file)

assert.Nil(t, err)
}
}

// Benchmark for getting input if traversing a graph costs too much
//
// Steps are stubbed so that we measure only the cost of walking the whole graph with a simple pipeline
// Also, the graph contains all conditionals returning the "worse" possible path (the largest way)
//
// The current graph has 26 steps, the UML can be seen at pipeline_benchmark_test.svg
// goos: darwin
// goarch: amd64
// cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
// BenchmarkPipeline_Run-8 44532 24716 ns/op 5140 B/op 50 allocs/op
// Given this graph magnitude, the cost of traversing it is negligible (~0.025ms) in comparison to a step operation.
func BenchmarkPipeline_Run(b *testing.B) {
var err error
graph := NewImmenseGraph()
ctx := context.Background()
in := 0

b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StartTimer()
_, err = graph.Run(ctx, in)
b.StopTimer()

if err != nil {
b.Fail()
}
}
}

// Example basic showcases a simple graph that uses the basic API steps to produce a simple result
// based on a given input.
//
Expand All @@ -119,31 +19,31 @@ func BenchmarkPipeline_Run(b *testing.B) {
//
// In the examples directory you can find more elaborate samples on how to do this better.
func Example_basic() {
inc := pipeline.NewUnitStep[int, int](
inc := pipeline.NewUnitStep( // int -> int
"increase_number",
func(ctx context.Context, i int) (int, error) {
return i + 20, nil
},
)
double := pipeline.NewUnitStep[int, int](
double := pipeline.NewUnitStep( // int -> int
"double_number",
func(ctx context.Context, i int) (int, error) {
return i * 2, nil
},
)
toString := pipeline.NewUnitStep[int, string](
toString := pipeline.NewUnitStep( // int -> string
"to_string",
func(ctx context.Context, i int) (string, error) {
return fmt.Sprintf("%d", i), nil
},
)
threeDigit := pipeline.NewUnitStep[string, bool](
threeDigit := pipeline.NewUnitStep( // string -> bool
"number_is_three_digit",
func(ctx context.Context, s string) (bool, error) {
return len(s) == 3, nil
},
)
print := pipeline.NewUnitStep[bool, bool](
print := pipeline.NewUnitStep( // bool -> bool
"print",
func(ctx context.Context, b bool) (bool, error) {
fmt.Println(b)
Expand Down Expand Up @@ -176,46 +76,46 @@ func Example_basic() {
//
// In the examples directory you can find more elaborate samples on how to do this better.
func Example_complex() {
inc := pipeline.NewUnitStep[int, int](
inc := pipeline.NewUnitStep( // int -> int
"increase_number",
func(ctx context.Context, i int) (int, error) {
return i + 1, nil
},
)
double := pipeline.NewUnitStep[int, int](
double := pipeline.NewUnitStep( // int -> int
"double_number",
func(ctx context.Context, i int) (int, error) {
return i * 2, nil
},
)
toString := pipeline.NewUnitStep[int, string](
toString := pipeline.NewUnitStep( // int -> string
"to_string",
func(ctx context.Context, i int) (string, error) {
return fmt.Sprintf("%d", i), nil
},
)
threeDigit := pipeline.NewUnitStep[string, bool](
threeDigit := pipeline.NewUnitStep( // string -> bool
"number_is_three_digit",
func(ctx context.Context, s string) (bool, error) {
return len(s) == 3, nil
},
)
cond := pipeline.NewOptionalStep[int](
pipeline.NewStatement[int](
pipeline.NewStatement(
"multiply_if_even",
func(ctx context.Context, i int) bool {
return i%2 == 0
},
),
double,
)
concurrentInc := pipeline.NewConcurrentStep[int, int](
concurrentInc := pipeline.NewConcurrentStep( // int -> int
[]pipeline.Step[int, int]{inc, inc, inc, inc, inc, inc, inc, inc, inc, inc},
func(ctx context.Context, i1, i2 int) (int, error) {
return i1 + i2, nil
},
)
print := pipeline.NewUnitStep[bool, bool](
print := pipeline.NewUnitStep( // bool -> bool
"print",
func(ctx context.Context, b bool) (bool, error) {
fmt.Println(b)
Expand Down
29 changes: 29 additions & 0 deletions sequential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,35 @@ func ExampleSequentialStep() {
// 25 <nil>
}

// Benchmark for traversing a sequential step. This is simply used so that future changes can
// easily reflect how they affected the performance
//
// goos: darwin
// goarch: amd64
// pkg: github.com/saantiaguilera/go-pipeline
// cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
// BenchmarkSequentialStep-8 7136264 172.5 ns/op 0 B/op 0 allocs/op
func BenchmarkSequentialStep(b *testing.B) {
var err error
s := pipeline.NewSequentialStep[any, any, any](
noopStep[any]{},
noopStep[any]{},
)
ctx := context.Background()
in := 0

b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StartTimer()
_, err = s.Run(ctx, in)
b.StopTimer()

if err != nil {
b.Fail()
}
}
}

func TestSequentialStep_GivenTwoSteps_WhenRun_ThenBehavesSequentially(t *testing.T) {
start := new(mockStep[int, string])
start.On("Run", mock.Anything, 1).Return("test", nil)
Expand Down
Loading

0 comments on commit 79d5751

Please sign in to comment.