-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add validation feature, Add benchmark (#5)
- Loading branch information
Showing
22 changed files
with
1,299 additions
and
192 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Benchmark comparison | ||
|
||
This sample will compare Ore (current commit of Nov 2024) to [samber/do/v2 v2.0.0-beta.7](https://github.com/samber/do). | ||
We registered the below dependency graphs to both Ore and SamberDo, then ask them to create the concrete `A`. | ||
|
||
We will only benchmark the creation, not the registration. Because registration usually happens only once on application startup => | ||
not very interesting to benchmark. | ||
|
||
## Data Model | ||
|
||
- This data model has only 2 singletons `F` and `Gb` => they will be created only once | ||
- Other concretes are `Transient` => they will be created each time the container create a new `A` concrete. | ||
- We don't test the "Scoped" lifetime in this excercise because SamberDo doesn't have equivalent support for it. [The "Scoped" functionality of SamberDo](https://do.samber.dev/docs/container/scope) means "Sub Module" rather than a lifetime. | ||
|
||
```mermaid | ||
flowchart TD | ||
A["A<br><sup></sup>"] | ||
B["B<br><sup></sup>"] | ||
C["C<br><sup></sup>"] | ||
D["D<br><sup><br></sup>"] | ||
E["E<br><sup><br></sup>"] | ||
F["F<br><sup>Singleton</sup>"] | ||
G(["G<br><sup>(interface)</sup>"]) | ||
Gb("Gb<br><sup>Singleton</sup>") | ||
Ga("Ga<br><sup></sup>") | ||
DGa("DGa<br><sup>(decorator)</sup>") | ||
H(["H<br><sup>(interface)<br></sup>"]) | ||
Hr["Hr<br><sup>(real)</sup>"] | ||
Hm["Hm<br><sup>(mock)</sup>"] | ||
A-->B | ||
A-->C | ||
B-->D | ||
B-->E | ||
D-->H | ||
D-->F | ||
Hr -.implement..-> H | ||
Hm -.implement..-> H | ||
E-->DGa | ||
E-->Gb | ||
E-->Gc | ||
DGa-->|decorate| Ga | ||
Ga -.implement..-> G | ||
Gb -.implement..-> G | ||
Gc -.implement..-> G | ||
DGa -.implement..-> G | ||
``` | ||
|
||
## Run the benchmark by yourself | ||
|
||
```sh | ||
go test -benchmem -bench . | ||
``` | ||
|
||
## Sample results | ||
|
||
On my machine, Ore always perform faster and use less memory than Samber/Do: | ||
|
||
```text | ||
Benchmark_Ore-12 415822 2565 ns/op 2089 B/op 57 allocs/op | ||
Benchmark_SamberDo-12 221941 4954 ns/op 2184 B/op 70 allocs/op | ||
``` | ||
|
||
And with `ore.DisableValidation = true` | ||
|
||
```text | ||
Benchmark_Ore-12 785088 1668 ns/op 1080 B/op 30 allocs/op | ||
Benchmark_SamberDo-12 227851 4940 ns/op 2184 B/op 70 allocs/op | ||
``` | ||
|
||
As any benchmarks, please take these number "relatively" as a general idea: | ||
|
||
- These numbers are probably outdated at the moment you are reading them | ||
- You might got a very different numbers when running them on your machine or on production machine. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
i "examples/benchperf/internal" | ||
"testing" | ||
|
||
"github.com/firasdarwish/ore" | ||
"github.com/samber/do/v2" | ||
) | ||
|
||
// func Benchmark_Ore_NoValidation(b *testing.B) { | ||
// i.BuildContainerOre() | ||
// ore.DisableValidation = true | ||
// ctx := context.Background() | ||
// b.ResetTimer() | ||
// for n := 0; n < b.N; n++ { | ||
// _, ctx = ore.Get[*i.A](ctx) | ||
// } | ||
// } | ||
|
||
var _ = i.BuildContainerOre() | ||
var injector = i.BuildContainerDo() | ||
var ctx = context.Background() | ||
|
||
func Benchmark_Ore(b *testing.B) { | ||
for n := 0; n < b.N; n++ { | ||
_, ctx = ore.Get[*i.A](ctx) | ||
} | ||
} | ||
|
||
func Benchmark_SamberDo(b *testing.B) { | ||
for n := 0; n < b.N; n++ { | ||
_ = do.MustInvoke[*i.A](injector) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package internal | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/firasdarwish/ore" | ||
) | ||
|
||
func BuildContainerOre() bool { | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (*A, context.Context) { | ||
b, ctx := ore.Get[*B](ctx) | ||
c, ctx := ore.Get[*C](ctx) | ||
return NewA(b, c), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (*B, context.Context) { | ||
d, ctx := ore.Get[*D](ctx) | ||
e, ctx := ore.Get[*E](ctx) | ||
return NewB(d, e), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (*C, context.Context) { | ||
return NewC(), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (*D, context.Context) { | ||
f, ctx := ore.Get[*F](ctx) | ||
h, ctx := ore.Get[H](ctx) | ||
return NewD(f, h), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (*E, context.Context) { | ||
gs, ctx := ore.GetList[G](ctx) | ||
return NewE(gs), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Singleton, func(ctx context.Context) (*F, context.Context) { | ||
return NewF(), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (*Ga, context.Context) { | ||
return NewGa(), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Singleton, func(ctx context.Context) (G, context.Context) { | ||
return NewGb(), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (G, context.Context) { | ||
return NewGc(), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (G, context.Context) { | ||
ga, ctx := ore.Get[*Ga](ctx) | ||
return NewDGa(ga), ctx | ||
}) | ||
ore.RegisterLazyFunc(ore.Transient, func(ctx context.Context) (H, context.Context) { | ||
return NewHr(), ctx | ||
}) | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package internal | ||
|
||
import ( | ||
"github.com/samber/do/v2" | ||
) | ||
|
||
func BuildContainerDo() do.Injector { | ||
injector := do.New() | ||
do.ProvideTransient(injector, func(inj do.Injector) (*A, error) { | ||
return NewA(do.MustInvoke[*B](inj), do.MustInvoke[*C](inj)), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (*B, error) { | ||
return NewB(do.MustInvoke[*D](inj), do.MustInvoke[*E](inj)), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (*C, error) { | ||
return NewC(), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (*D, error) { | ||
return NewD(do.MustInvoke[*F](inj), do.MustInvoke[H](inj)), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (*E, error) { | ||
gs := []G{ | ||
do.MustInvoke[*DGa](inj), | ||
do.MustInvoke[*Gb](inj), | ||
do.MustInvoke[*Gc](inj), | ||
} | ||
return NewE(gs), nil | ||
}) | ||
do.Provide(injector, func(inj do.Injector) (*F, error) { | ||
return NewF(), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (*Ga, error) { | ||
return NewGa(), nil | ||
}) | ||
do.Provide(injector, func(inj do.Injector) (*Gb, error) { | ||
return NewGb(), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (*Gc, error) { | ||
return NewGc(), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (*DGa, error) { | ||
return NewDGa(do.MustInvoke[*Ga](inj)), nil | ||
}) | ||
do.ProvideTransient(injector, func(inj do.Injector) (H, error) { | ||
return NewHr(), nil | ||
}) | ||
return injector | ||
} |
Oops, something went wrong.