Details
-
Improvement
-
Resolution: Unresolved
-
Unknown
-
None
-
None
-
None
Description
The proposed solution for GODRIVER-2582 involves using reflection. This is very inefficient and non-idiomatic. A more optimized solution would be to defer the logic for parsing the document interface to the collection.insert method. The idea would be to marshal the documents in one operation like this:
func _switch(documents interface{}) {
|
bytes, err := bson.Marshal(struct {
|
Arr any `bson:"arr"`
|
}{Arr: documents})
|
if err != nil {
|
panic(err)
|
}
|
|
|
raw := bson.Raw(bytes).Lookup("arr")
|
|
|
var docSlice []bson.Raw
|
switch raw.Type {
|
case bsontype.Array:
|
elems, err := raw.Array().Elements()
|
if err != nil {
|
panic(err)
|
}
|
|
|
docSlice = make([]bson.Raw, len(elems))
|
for idx, elem := range elems {
|
docSlice[idx] = elem.Value().Document()
|
}
|
}
|
}
|
Here is a script:
package benchmark
|
|
|
import (
|
"reflect"
|
"testing"
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson/bsontype"
|
)
|
|
|
func reflection(documents interface{}) {
|
dv := reflect.ValueOf(documents)
|
if dv.Kind() != reflect.Slice {
|
return
|
}
|
|
|
if dv.Len() == 0 {
|
return
|
}
|
|
|
docSlice := make([]interface{}, 0, dv.Len())
|
for i := 0; i < dv.Len(); i++ {
|
docSlice = append(docSlice, dv.Index(i).Interface())
|
}
|
}
|
|
|
func _switch(documents interface{}) {
|
bytes, err := bson.Marshal(struct {
|
Arr any `bson:"arr"`
|
}{Arr: documents})
|
if err != nil {
|
panic(err)
|
}
|
|
|
raw := bson.Raw(bytes).Lookup("arr")
|
|
|
var docSlice []bson.Raw
|
switch raw.Type {
|
case bsontype.Array:
|
elems, err := raw.Array().Elements()
|
if err != nil {
|
panic(err)
|
}
|
|
|
docSlice = make([]bson.Raw, len(elems))
|
for idx, elem := range elems {
|
docSlice[idx] = elem.Value().Document()
|
}
|
}
|
|
|
if len(docSlice) == 0 {
|
panic(2)
|
}
|
}
|
|
|
type Document struct {
|
A, B string
|
}
|
|
|
func TestSwitch(t *testing.T) {
|
docs := []Document{{A: "A", B: "B"}}
|
_switch(docs)
|
}
|
|
|
func BenchmarkReflection(b *testing.B) {
|
// Prepare a sample input for the reflection function
|
var data []Document
|
for i := 0; i < 1000; i++ {
|
data = append(data, Document{A: "A", B: "B"})
|
}
|
|
|
b.ReportAllocs()
|
b.ResetTimer()
|
for i := 0; i < b.N; i++ {
|
reflection(data)
|
}
|
}
|
|
|
func BenchmarkSwitch(b *testing.B) {
|
// Prepare a sample input for the _switch function
|
var data []interface{}
|
for i := 0; i < 1000; i++ {
|
data = append(data, Document{A: "A"})
|
}
|
|
|
b.ReportAllocs()
|
b.ResetTimer()
|
for i := 0; i < b.N; i++ {
|
_switch(data)
|
}
|
}
|
Here are the results of the benchmark:
goos: darwin
|
goarch: arm64
|
pkg: github.com/prestonvasquez/technical/workshop/mongo/insertmany/benchmark2582
|
BenchmarkReflection-10 42393 27440 ns/op 48408 B/op 1002 allocs/op
|
BenchmarkSwitch-10 5612 212016 ns/op 157499 B/op 17 allocs/op
|
PASS
|
Attachments
Issue Links
- related to
-
GODRIVER-2582 Accept any value for "documents" in InsertMany
-
- Closed
-