diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 7194a54719..a4a61fbe74 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -631,7 +631,7 @@ buildvariants: - name: perf display_name: "Performance" run_on: - - ubuntu1604-test + - ubuntu1604-build expansions: GO_DIST: "/opt/golang/go1.9" tasks: diff --git a/benchmark/harness.go b/benchmark/harness.go index 2e63b70b1e..13a2f9bd0c 100644 --- a/benchmark/harness.go +++ b/benchmark/harness.go @@ -203,5 +203,23 @@ func getAllCases() []*CaseDefinition { Size: 27310890, Runtime: StandardRuntime, }, + { + Bench: MultiFindMany, + Count: tenThousand, + Size: 16220000, + Runtime: StandardRuntime, + }, + { + Bench: MultiInsertSmallDocument, + Count: tenThousand, + Size: 2750000, + Runtime: StandardRuntime, + }, + { + Bench: MultiInsertLargeDocument, + Count: ten, + Size: 27310890, + Runtime: StandardRuntime, + }, } } diff --git a/benchmark/harness_case.go b/benchmark/harness_case.go index ad0c854e24..d7fc9f0f6c 100644 --- a/benchmark/harness_case.go +++ b/benchmark/harness_case.go @@ -40,9 +40,12 @@ func (c *CaseDefinition) StopTimer() { if c.startAt.IsZero() { return } - - c.startAt = time.Time{} c.Runtime += time.Since(c.startAt) + c.startAt = time.Time{} +} + +func (c *CaseDefinition) roundedRuntime() time.Duration { + return roundDurationMS(c.Runtime) } func (c *CaseDefinition) Run(ctx context.Context) *BenchResult { @@ -58,6 +61,7 @@ func (c *CaseDefinition) Run(ctx context.Context) *BenchResult { fmt.Println("=== RUN", out.Name) c.startAt = time.Now() + for { if time.Since(c.startAt) > c.Runtime { if out.Trials >= MinIterations { @@ -66,10 +70,10 @@ func (c *CaseDefinition) Run(ctx context.Context) *BenchResult { break } } - res := Result{ Iterations: c.Count, } + c.StartTimer() res.Error = c.Bench(ctx, c, c.Count) c.StopTimer() @@ -85,9 +89,9 @@ func (c *CaseDefinition) Run(ctx context.Context) *BenchResult { out.Duration = out.totalDuration() if out.HasErrors() { - fmt.Printf("--- FAIL: %s (%s)\n", out.Name, out.Duration.Round(time.Millisecond)) + fmt.Printf("--- FAIL: %s (%s)\n", out.Name, out.roundedRuntime()) } else { - fmt.Printf("--- PASS: %s (%s)\n", out.Name, out.Duration.Round(time.Millisecond)) + fmt.Printf("--- PASS: %s (%s)\n", out.Name, out.roundedRuntime()) } return out @@ -100,7 +104,6 @@ func (c *CaseDefinition) String() string { } func (c *CaseDefinition) Name() string { return getName(c.Bench) } - func getName(i interface{}) string { n := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() parts := strings.Split(n, ".") diff --git a/benchmark/harness_results.go b/benchmark/harness_results.go index d352e2225c..653689bcdd 100644 --- a/benchmark/harness_results.go +++ b/benchmark/harness_results.go @@ -80,14 +80,15 @@ func (r *BenchResult) timings() []float64 { func (r *BenchResult) totalDuration() time.Duration { var out time.Duration - for _, r := range r.Raw { - out += r.Duration + for _, trial := range r.Raw { + out += trial.Duration } return out } func (r *BenchResult) adjustResults(data float64) float64 { return float64(r.DataSize) / data } func (r *BenchResult) getThroughput(data float64) float64 { return float64(r.Operations) / data } +func (r *BenchResult) roundedRuntime() time.Duration { return roundDurationMS(r.Duration) } func (r *BenchResult) String() string { return fmt.Sprintf("name=%s, trials=%d, secs=%s", r.Name, r.Trials, r.Duration) @@ -113,3 +114,11 @@ type Result struct { Iterations int Error error } + +func roundDurationMS(d time.Duration) time.Duration { + rounded := d.Round(time.Millisecond) + if rounded == 1<<63-1 { + return 0 + } + return rounded +} diff --git a/benchmark/multi.go b/benchmark/multi.go new file mode 100644 index 0000000000..f8bc8dcc17 --- /dev/null +++ b/benchmark/multi.go @@ -0,0 +1,137 @@ +package benchmark + +import ( + "context" + "errors" + + "github.com/mongodb/mongo-go-driver/bson" +) + +func MultiFindMany(ctx context.Context, tm TimerManager, iters int) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + db, err := getClientDB(ctx) + if err != nil { + return err + } + + db = db.Client().Database("perftest") + if err = db.Drop(ctx); err != nil { + return err + } + + doc, err := loadSourceDocument(getProjectRoot(), perfDataDir, singleAndMultiDataDir, tweetData) + if err != nil { + return err + } + + coll := db.Collection("corpus") + + for i := 0; i < iters; i++ { + if _, err = coll.InsertOne(ctx, doc); err != nil { + return err + } + + // TODO: should be remove after resolving GODRIVER-468 + _ = doc.Delete("_id") + } + + tm.ResetTimer() + + cursor, err := coll.Find(ctx, bson.NewDocument()) + if err != nil { + return err + } + + counter := 0 + for cursor.Next(ctx) { + err = cursor.Err() + if err != nil { + return err + } + r, err := cursor.DecodeBytes() + if err != nil { + return err + } + if len(r) == 0 { + return errors.New("error retrieving document") + } + + counter++ + } + + if counter != iters { + return errors.New("problem iterating cursors") + + } + + tm.StopTimer() + + if err = cursor.Close(ctx); err != nil { + return err + } + + if err = db.Drop(ctx); err != nil { + return err + } + + return nil +} + +func multiInsertCase(ctx context.Context, tm TimerManager, iters int, data string) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + db, err := getClientDB(ctx) + if err != nil { + return err + } + + db = db.Client().Database("perftest") + if err = db.Drop(ctx); err != nil { + return err + } + + doc, err := loadSourceDocument(getProjectRoot(), perfDataDir, singleAndMultiDataDir, data) + if err != nil { + return err + } + + _, err = db.RunCommand(ctx, bson.NewDocument(bson.EC.String("create", "corpus"))) + if err != nil { + return err + } + + payload := make([]interface{}, iters) + for idx := range payload { + payload[idx] = *doc + } + + coll := db.Collection("corpus") + + tm.ResetTimer() + res, err := coll.InsertMany(ctx, payload) + if err != nil { + return err + } + tm.StopTimer() + + if len(res.InsertedIDs) != iters { + return errors.New("bulk operation did not complete") + } + + if err = db.Drop(ctx); err != nil { + return err + } + + return nil +} + +func MultiInsertSmallDocument(ctx context.Context, tm TimerManager, iters int) error { + return multiInsertCase(ctx, tm, iters, smallData) +} + +func MultiInsertLargeDocument(ctx context.Context, tm TimerManager, iters int) error { + return multiInsertCase(ctx, tm, iters, largeData) +} diff --git a/benchmark/multi_test.go b/benchmark/multi_test.go new file mode 100644 index 0000000000..2fe503f228 --- /dev/null +++ b/benchmark/multi_test.go @@ -0,0 +1,7 @@ +package benchmark + +import "testing" + +func BenchmarkMultiFindMany(b *testing.B) { WrapCase(MultiFindMany)(b) } +func BenchmarkMultiInsertSmallDocument(b *testing.B) { WrapCase(MultiInsertSmallDocument)(b) } +func BenchmarkMultiInsertLargeDocument(b *testing.B) { WrapCase(MultiInsertLargeDocument)(b) }