From eda7de1dd2d5849f3fd4747c492a4ce6e264787b Mon Sep 17 00:00:00 2001 From: Jeanette Tan Date: Fri, 27 Jan 2023 17:25:50 +0800 Subject: [PATCH] Start to use json-iterator to improve marshalling of HistogramBucket Signed-off-by: Jeanette Tan --- go.mod | 3 ++ go.sum | 3 ++ model/value_histogram.go | 38 ++++++++++---------- model/value_marshal.go | 78 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 model/value_marshal.go diff --git a/go.mod b/go.mod index 5c9ea467..56ec4039 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/go-kit/log v0.2.1 github.com/golang/protobuf v1.5.2 + github.com/json-iterator/go v1.1.12 github.com/julienschmidt/httprouter v1.3.0 github.com/matttproud/golang_protobuf_extensions v1.0.4 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f @@ -23,6 +24,8 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/jpillora/backoff v1.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/stretchr/testify v1.8.0 // indirect golang.org/x/sys v0.3.0 // indirect diff --git a/go.sum b/go.sum index 2a6dc91c..b6f83ba2 100644 --- a/go.sum +++ b/go.sum @@ -139,6 +139,7 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -159,9 +160,11 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= diff --git a/model/value_histogram.go b/model/value_histogram.go index d20b1b74..5b9724f4 100644 --- a/model/value_histogram.go +++ b/model/value_histogram.go @@ -18,8 +18,15 @@ import ( "fmt" "strconv" "strings" + "unsafe" + + jsoniter "github.com/json-iterator/go" ) +func init() { + jsoniter.RegisterTypeEncoderFunc("model.HistogramBucket", marshalHistogramBucketJSON, marshalHistogramBucketJSONIsEmpty) +} + type FloatString float64 func (v FloatString) String() string { @@ -49,24 +56,14 @@ type HistogramBucket struct { Count FloatString } -func (s HistogramBucket) MarshalJSON() ([]byte, error) { - b, err := json.Marshal(s.Boundaries) - if err != nil { - return nil, err - } - l, err := json.Marshal(s.Lower) - if err != nil { - return nil, err - } - u, err := json.Marshal(s.Upper) - if err != nil { - return nil, err - } - c, err := json.Marshal(s.Count) - if err != nil { - return nil, err - } - return []byte(fmt.Sprintf("[%s,%s,%s,%s]", b, l, u, c)), nil +// marshalHistogramBucketJSON writes fmt.Sprintf("[%s,%s,%s,%s]", b.Boundaries, b.Lower, b.Upper, b.Count). +func marshalHistogramBucketJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { + b := *((*HistogramBucket)(ptr)) + MarshalHistogramBucket(b, stream) +} + +func marshalHistogramBucketJSONIsEmpty(ptr unsafe.Pointer) bool { + return false } func (s *HistogramBucket) UnmarshalJSON(buf []byte) error { @@ -140,14 +137,15 @@ type SampleHistogramPair struct { } func (s SampleHistogramPair) MarshalJSON() ([]byte, error) { - t, err := json.Marshal(s.Timestamp) + jsoni := jsoniter.ConfigCompatibleWithStandardLibrary + t, err := jsoni.Marshal(s.Timestamp) if err != nil { return nil, err } if s.Histogram == nil { return nil, fmt.Errorf("histogram is nil") } - v, err := json.Marshal(s.Histogram) + v, err := jsoni.Marshal(s.Histogram) if err != nil { return nil, err } diff --git a/model/value_marshal.go b/model/value_marshal.go new file mode 100644 index 00000000..febc1399 --- /dev/null +++ b/model/value_marshal.go @@ -0,0 +1,78 @@ +// Copyright 2013 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "math" + "strconv" + + jsoniter "github.com/json-iterator/go" +) + +// from https://github.com/prometheus/prometheus/blob/main/util/jsonutil/marshal.go +// MarshalTimestamp marshals a point timestamp using the passed jsoniter stream. +func MarshalTimestamp(t int64, stream *jsoniter.Stream) { + // Write out the timestamp as a float divided by 1000. + // This is ~3x faster than converting to a float. + if t < 0 { + stream.WriteRaw(`-`) + t = -t + } + stream.WriteInt64(t / 1000) + fraction := t % 1000 + if fraction != 0 { + stream.WriteRaw(`.`) + if fraction < 100 { + stream.WriteRaw(`0`) + } + if fraction < 10 { + stream.WriteRaw(`0`) + } + stream.WriteInt64(fraction) + } +} + +// adapted from https://github.com/prometheus/prometheus/blob/main/util/jsonutil/marshal.go +// MarshalValue marshals a point value using the passed jsoniter stream. +func MarshalValue(f FloatString, stream *jsoniter.Stream) { + v := float64(f) + stream.WriteRaw(`"`) + // Taken from https://github.com/json-iterator/go/blob/master/stream_float.go#L71 as a workaround + // to https://github.com/json-iterator/go/issues/365 (jsoniter, to follow json standard, doesn't allow inf/nan). + buf := stream.Buffer() + abs := math.Abs(v) + fmt := byte('f') + // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. + if abs != 0 { + if abs < 1e-6 || abs >= 1e21 { + fmt = 'e' + } + } + buf = strconv.AppendFloat(buf, v, fmt, -1, 64) + stream.SetBuffer(buf) + stream.WriteRaw(`"`) +} + +// adapted from https://github.com/prometheus/prometheus/blob/main/web/api/v1/api.go +func MarshalHistogramBucket(b HistogramBucket, stream *jsoniter.Stream) { + stream.WriteArrayStart() + stream.WriteInt32(b.Boundaries) + stream.WriteMore() + MarshalValue(b.Lower, stream) + stream.WriteMore() + MarshalValue(b.Upper, stream) + stream.WriteMore() + MarshalValue(b.Count, stream) + stream.WriteArrayEnd() +}