diff --git a/pkg/api/testing/unstructured_test.go b/pkg/api/testing/unstructured_test.go index 6d352b48bf..33d7d480b6 100644 --- a/pkg/api/testing/unstructured_test.go +++ b/pkg/api/testing/unstructured_test.go @@ -19,11 +19,13 @@ package testing import ( "math/rand" "reflect" + "sort" "testing" - "github.com/google/gofuzz" + fuzz "github.com/google/gofuzz" + jsoniter "github.com/json-iterator/go" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/meta" @@ -99,7 +101,7 @@ func doRoundTrip(t *testing.T, internalVersion schema.GroupVersion, externalVers return } - newUnstr, err := runtime.NewTestUnstructuredConverter(apiequality.Semantic).ToUnstructured(item) + newUnstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item) if err != nil { t.Errorf("ToUnstructured failed: %v", err) return @@ -163,7 +165,7 @@ func TestRoundTripWithEmptyCreationTimestamp(t *testing.T) { // attempt to re-convert unstructured object - conversion should not fail // based on empty metadata fields, such as creationTimestamp newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) - err = runtime.NewTestUnstructuredConverter(apiequality.Semantic).FromUnstructured(unstructObj.Object, newObj) + err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.Object, newObj) if err != nil { t.Fatalf("FromUnstructured failed: %v", err) } @@ -171,44 +173,160 @@ func TestRoundTripWithEmptyCreationTimestamp(t *testing.T) { } } -func BenchmarkToFromUnstructured(b *testing.B) { +func BenchmarkToUnstructured(b *testing.B) { items := benchmarkItems(b) size := len(items) + convertor := runtime.DefaultUnstructuredConverter b.ResetTimer() for i := 0; i < b.N; i++ { - unstr, err := runtime.NewTestUnstructuredConverter(apiequality.Semantic).ToUnstructured(&items[i%size]) - if err != nil { - b.Fatalf("unexpected error: %v", err) - } - obj := v1.Pod{} - if err := runtime.NewTestUnstructuredConverter(apiequality.Semantic).FromUnstructured(unstr, &obj); err != nil { + unstr, err := convertor.ToUnstructured(&items[i%size]) + if err != nil || unstr == nil { b.Fatalf("unexpected error: %v", err) } } b.StopTimer() } -func BenchmarkToFromUnstructuredViaJSON(b *testing.B) { +func BenchmarkFromUnstructured(b *testing.B) { items := benchmarkItems(b) + convertor := runtime.DefaultUnstructuredConverter + var unstr []map[string]interface{} + for i := range items { + item, err := convertor.ToUnstructured(&items[i]) + if err != nil || item == nil { + b.Fatalf("unexpected error: %v", err) + } + unstr = append(unstr, item) + } size := len(items) b.ResetTimer() for i := 0; i < b.N; i++ { - data, err := json.Marshal(&items[i%size]) - if err != nil { - b.Fatalf("unexpected error: %v", err) - } - unstr := map[string]interface{}{} - if err := json.Unmarshal(data, &unstr); err != nil { - b.Fatalf("unexpected error: %v", err) - } - data, err = json.Marshal(unstr) - if err != nil { - b.Fatalf("unexpected error: %v", err) - } obj := v1.Pod{} - if err := json.Unmarshal(data, &obj); err != nil { + if err := convertor.FromUnstructured(unstr[i%size], &obj); err != nil { b.Fatalf("unexpected error: %v", err) } } b.StopTimer() } + +func BenchmarkToUnstructuredViaJSON(b *testing.B) { + items := benchmarkItems(b) + var data [][]byte + for i := range items { + item, err := json.Marshal(&items[i]) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + data = append(data, item) + } + size := len(items) + b.ResetTimer() + for i := 0; i < b.N; i++ { + unstr := map[string]interface{}{} + if err := json.Unmarshal(data[i%size], &unstr); err != nil { + b.Fatalf("unexpected error: %v", err) + } + } + b.StopTimer() +} + +func BenchmarkFromUnstructuredViaJSON(b *testing.B) { + items := benchmarkItems(b) + var unstr []map[string]interface{} + for i := range items { + data, err := json.Marshal(&items[i]) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + item := map[string]interface{}{} + if err := json.Unmarshal(data, &item); err != nil { + b.Fatalf("unexpected error: %v", err) + } + unstr = append(unstr, item) + } + size := len(items) + b.ResetTimer() + for i := 0; i < b.N; i++ { + item, err := json.Marshal(unstr[i%size]) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + obj := v1.Pod{} + if err := json.Unmarshal(item, &obj); err != nil { + b.Fatalf("unexpected error: %v", err) + } + } + b.StopTimer() +} + +func BenchmarkToUnstructuredViaJSONIter(b *testing.B) { + items := benchmarkItems(b) + size := len(items) + var keys []string + for k := range jsonIterConfig { + keys = append(keys, k) + } + sort.Strings(keys) + for _, name := range keys { + c := jsonIterConfig[name] + b.Run(name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + data, err := c.Marshal(&items[i%size]) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + unstr := map[string]interface{}{} + if err := c.Unmarshal(data, &unstr); err != nil { + b.Fatalf("unexpected error: %v", err) + } + } + b.StopTimer() + }) + } +} + +var jsonIterConfig = map[string]jsoniter.API{ + "default": jsoniter.ConfigDefault, + "fastest": jsoniter.ConfigFastest, + "compat": jsoniter.ConfigCompatibleWithStandardLibrary, +} + +func BenchmarkFromUnstructuredViaJSONIter(b *testing.B) { + items := benchmarkItems(b) + var unstr []map[string]interface{} + for i := range items { + data, err := jsoniter.Marshal(&items[i]) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + item := map[string]interface{}{} + if err := json.Unmarshal(data, &item); err != nil { + b.Fatalf("unexpected error: %v", err) + } + unstr = append(unstr, item) + } + size := len(items) + var keys []string + for k := range jsonIterConfig { + keys = append(keys, k) + } + sort.Strings(keys) + for _, name := range keys { + c := jsonIterConfig[name] + b.Run(name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + item, err := c.Marshal(unstr[i%size]) + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + obj := v1.Pod{} + if err := c.Unmarshal(item, &obj); err != nil { + b.Fatalf("unexpected error: %v", err) + } + } + b.StopTimer() + }) + } +} diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go b/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go index dff56e0340..80343081f5 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go @@ -746,7 +746,7 @@ func isZero(v reflect.Value) bool { func structToUnstructured(sv, dv reflect.Value) error { st, dt := sv.Type(), dv.Type() if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 { - dv.Set(reflect.MakeMap(mapStringInterfaceType)) + dv.Set(reflect.MakeMapWithSize(mapStringInterfaceType, st.NumField())) dv = dv.Elem() dt = dv.Type() }