Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use more real world examples in BenchmarkSerializeObject #99192

Merged
merged 1 commit into from Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -13,14 +13,17 @@ go_test(
"status_test.go",
"writers_test.go",
],
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
Expand Down
@@ -0,0 +1,162 @@
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"creationTimestamp": "2021-02-18T09:46:18Z",
"generateName": "nginx-deployment-7b88ccfd76-",
"labels": {
"app": "nginx",
"pod-template-hash": "7b88ccfd76"
},
"managedFields": [
{
"apiVersion": "v1",
"fieldsType": "FieldsV1",
"fieldsV1": {"f:metadata":{"f:generateName":{},"f:labels":{".":{},"f:app":{},"f:pod-template-hash":{}},"f:ownerReferences":{".":{},"k:{\"uid\":\"c9f927f9-8f7a-43c4-a100-b4bdf306dd93\"}":{".":{},"f:apiVersion":{},"f:blockOwnerDeletion":{},"f:controller":{},"f:kind":{},"f:name":{},"f:uid":{}}}},"f:spec":{"f:containers":{"k:{\"name\":\"nginx\"}":{".":{},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":80,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{".":{},"f:requests":{".":{},"f:cpu":{}}},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:enableServiceLinks":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}},
"manager": "kube-controller-manager",
"operation": "Update",
"time": "2021-02-18T09:46:18Z"
},
{
"apiVersion": "v1",
"fieldsType": "FieldsV1",
"fieldsV1": {"f:status":{"f:conditions":{"k:{\"type\":\"ContainersReady\"}":{".":{},"f:lastProbeTime":{},"f:lastTransitionTime":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Initialized\"}":{".":{},"f:lastProbeTime":{},"f:lastTransitionTime":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Ready\"}":{".":{},"f:lastProbeTime":{},"f:lastTransitionTime":{},"f:status":{},"f:type":{}}},"f:containerStatuses":{},"f:hostIP":{},"f:phase":{},"f:podIP":{},"f:podIPs":{".":{},"k:{\"ip\":\"10.224.98.9\"}":{".":{},"f:ip":{}}},"f:startTime":{}}},
"manager": "kubelet",
"operation": "Update",
"time": "2021-02-18T09:46:19Z"
}
],
"name": "nginx-deployment-7b88ccfd76-24jf7",
"namespace": "default",
"ownerReferences": [
{
"apiVersion": "apps/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "ReplicaSet",
"name": "nginx-deployment-7b88ccfd76",
"uid": "c9f927f9-8f7a-43c4-a100-b4bdf306dd93"
}
],
"resourceVersion": "10345330",
"uid": "3be4f64b-3687-47ff-9910-b72366ea798e"
},
"spec": {
"containers": [
{
"image": "nginx:1.14.2",
"imagePullPolicy": "IfNotPresent",
"name": "nginx",
"ports": [
{
"containerPort": 80,
"protocol": "TCP"
}
],
"resources": {
"requests": {
"cpu": "1m"
}
},
"terminationMessagePath": "/dev/termination-log",
"terminationMessagePolicy": "File",
"volumeMounts": [
{
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
"name": "default-token-hz58m",
"readOnly": true
}
]
}
],
"dnsPolicy": "ClusterFirst",
"enableServiceLinks": true,
"nodeName": "gke-pf-default-pool-ad46e28b-kdh9",
"preemptionPolicy": "PreemptLowerPriority",
"priority": 0,
"restartPolicy": "Always",
"schedulerName": "default-scheduler",
"securityContext": {},
"serviceAccount": "default",
"serviceAccountName": "default",
"terminationGracePeriodSeconds": 30,
"tolerations": [
{
"effect": "NoExecute",
"key": "node.kubernetes.io/not-ready",
"operator": "Exists",
"tolerationSeconds": 300
},
{
"effect": "NoExecute",
"key": "node.kubernetes.io/unreachable",
"operator": "Exists",
"tolerationSeconds": 300
}
],
"volumes": [
{
"name": "default-token-hz58m",
"secret": {
"defaultMode": 420,
"secretName": "default-token-hz58m"
}
}
]
},
"status": {
"conditions": [
{
"lastProbeTime": null,
"lastTransitionTime": "2021-02-18T09:46:18Z",
"status": "True",
"type": "Initialized"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2021-02-18T09:46:19Z",
"status": "True",
"type": "Ready"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2021-02-18T09:46:19Z",
"status": "True",
"type": "ContainersReady"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2021-02-18T09:46:18Z",
"status": "True",
"type": "PodScheduled"
}
],
"containerStatuses": [
{
"containerID": "docker://cea8a81981cd4780fd0b705049a533ca9d83c4596b9f7f4e67915863a2ca76a2",
"image": "nginx:1.14.2",
"imageID": "docker-pullable://nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d",
"lastState": {},
"name": "nginx",
"ready": true,
"restartCount": 0,
"started": true,
"state": {
"running": {
"startedAt": "2021-02-18T09:46:19Z"
}
}
}
],
"hostIP": "10.223.96.100",
"phase": "Running",
"podIP": "10.224.98.9",
"podIPs": [
{
"ip": "10.224.98.9"
}
],
"qosClass": "Burstable",
"startTime": "2021-02-18T09:46:18Z"
}
}
Expand Up @@ -20,25 +20,34 @@ import (
"bytes"
"compress/gzip"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/http/httptest"
"net/url"
"os"
"reflect"
"strconv"
"testing"
"time"

v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
)

const benchmarkSeed = 100

func TestSerializeObjectParallel(t *testing.T) {
largePayload := bytes.Repeat([]byte("0123456789abcdef"), defaultGzipThresholdBytes/16+1)
type test struct {
Expand Down Expand Up @@ -366,8 +375,88 @@ func TestSerializeObject(t *testing.T) {
}
}

func benchmarkSerializeObject(b *testing.B, size int) {
largePayload := bytes.Repeat([]byte("0123456789abcdef"), size/16+1)
func randTime(t *time.Time, r *rand.Rand) {
*t = time.Unix(r.Int63n(1000*365*24*60*60), r.Int63())
}

func randIP(s *string, r *rand.Rand) {
*s = fmt.Sprintf("10.20.%d.%d", r.Int31n(256), r.Int31n(256))
}

// randPod changes fields in pod to mimic another pod from the same replicaset.
// The list fields here has been generated by picking two pods in the same replicaset
// and checking diff of their jsons.
func randPod(b *testing.B, pod *v1.Pod, r *rand.Rand) {
pod.Name = fmt.Sprintf("%s-%x", pod.GenerateName, r.Int63n(1000))
pod.UID = uuid.NewUUID()
pod.ResourceVersion = strconv.Itoa(r.Int())
pod.Spec.NodeName = fmt.Sprintf("some-node-prefix-%x", r.Int63n(1000))

randTime(&pod.CreationTimestamp.Time, r)
randTime(&pod.Status.StartTime.Time, r)
for i := range pod.Status.Conditions {
randTime(&pod.Status.Conditions[i].LastTransitionTime.Time, r)
}
for i := range pod.Status.ContainerStatuses {
containerStatus := &pod.Status.ContainerStatuses[i]
state := &containerStatus.State
if state.Running != nil {
randTime(&state.Running.StartedAt.Time, r)
}
containerStatus.ContainerID = fmt.Sprintf("docker://%x%x%x%x", r.Int63(), r.Int63(), r.Int63(), r.Int63())
}
for i := range pod.ManagedFields {
randTime(&pod.ManagedFields[i].Time.Time, r)
}

randIP(&pod.Status.HostIP, r)
randIP(&pod.Status.PodIP, r)
}

func benchmarkItems(b *testing.B, file string, n int) *v1.PodList {
pod := v1.Pod{}
f, err := os.Open(file)
if err != nil {
b.Fatalf("Failed to open %q: %v", file, err)
}
defer f.Close()
err = json.NewDecoder(f).Decode(&pod)
if err != nil {
b.Fatalf("Failed to decode %q: %v", file, err)
}

list := &v1.PodList{
Items: make([]v1.Pod, n),
}

r := rand.New(rand.NewSource(benchmarkSeed))
for i := 0; i < n; i++ {
list.Items[i] = *pod.DeepCopy()
randPod(b, &list.Items[i], r)
}
return list
}

func toProtoBuf(b *testing.B, list *v1.PodList) []byte {
out, err := list.Marshal()
if err != nil {
b.Fatalf("Failed to marshal list to protobuf: %v", err)
}
return out
}

func toJSON(b *testing.B, list *v1.PodList) []byte {
out, err := json.Marshal(list)
if err != nil {
b.Fatalf("Failed to marshal list to json: %v", err)
}
return out
}

func benchmarkSerializeObject(b *testing.B, payload []byte) {
input, output := len(payload), len(gzipContent(payload, defaultGzipContentEncodingLevel))
b.Logf("Payload size: %d, expected output size: %d, ratio: %.2f", input, output, float64(output)/float64(input))

req := &http.Request{
Header: http.Header{
"Accept-Encoding": []string{"gzip"},
Expand All @@ -377,7 +466,7 @@ func benchmarkSerializeObject(b *testing.B, size int) {
defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.APIResponseCompression, true)()

encoder := &fakeEncoder{
buf: largePayload,
buf: payload,
}

b.ResetTimer()
Expand All @@ -391,18 +480,24 @@ func benchmarkSerializeObject(b *testing.B, size int) {
}
}

func BenchmarkSerializeObject10KB(b *testing.B) {
benchmarkSerializeObject(b, 10*1024)
func BenchmarkSerializeObject1000PodsPB(b *testing.B) {
benchmarkSerializeObject(b, toProtoBuf(b, benchmarkItems(b, "testdata/pod.json", 1000)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to reset the time after computing benchmark items, something like:

items := benchmarkItems(...)
b.ResetTimer()
benchmarkSerializeObject(...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok - it's done later anyway. Never mind.

}
func BenchmarkSerializeObject10000PodsPB(b *testing.B) {
benchmarkSerializeObject(b, toProtoBuf(b, benchmarkItems(b, "testdata/pod.json", 10000)))
}
func BenchmarkSerializeObject100000PodsPB(b *testing.B) {
benchmarkSerializeObject(b, toProtoBuf(b, benchmarkItems(b, "testdata/pod.json", 100000)))
}

func BenchmarkSerializeObject10MB(b *testing.B) {
benchmarkSerializeObject(b, 10*1024*1024)
func BenchmarkSerializeObject1000PodsJSON(b *testing.B) {
benchmarkSerializeObject(b, toJSON(b, benchmarkItems(b, "testdata/pod.json", 1000)))
}
func BenchmarkSerializeObject100MB(b *testing.B) {
benchmarkSerializeObject(b, 100*1024*1024)
func BenchmarkSerializeObject10000PodsJSON(b *testing.B) {
benchmarkSerializeObject(b, toJSON(b, benchmarkItems(b, "testdata/pod.json", 10000)))
}
func BenchmarkSerializeObject1GB(b *testing.B) {
benchmarkSerializeObject(b, 1024*1024*1024)
func BenchmarkSerializeObject100000PodsJSON(b *testing.B) {
benchmarkSerializeObject(b, toJSON(b, benchmarkItems(b, "testdata/pod.json", 100000)))
}

type fakeResponseRecorder struct {
Expand Down