Just re-noticed this discussions, thanks for helping Mirco!

Answering in place, given some updates recently.

> As mentioned above, this would be also a good opportunity to cleanup the 
proto generation code using buf.

We can move to *buf* straight away, even now in all places. It works with 
or without gogoproto option fields in the proto format. No need to change 
proto format (although it would be cleaner without those options once ppl 
move out of gogo). See the generation with buf 
<https://github.com/bwplotka/benchmarks/blob/main/benchmarks/metrics-streaming/buf.gen.yaml#L5>from
 
proto that has "gogo" fields. Note one (trivial) hack 
<https://github.com/bwplotka/benchmarks/blob/38a20655422b0152a4953edc288c1edfcca25500/benchmarks/metrics-streaming/Makefile#L14>,
 
discussed here 
<https://github.com/bwplotka/benchmarks/blob/38a20655422b0152a4953edc288c1edfcca25500/benchmarks/metrics-streaming/Makefile#L14>
.

> P.S.: This would depend on a change in prometheus/client_model, but would 
allow removing the duplicate proto definition in the repository.

It's unfortunate we have the same client model proto file (and different 
generated code) in (1) Prometheus 
<https://github.com/prometheus/prometheus/blob/main/prompb/io/prometheus/client/metrics.proto>
 (with 
gogo fields) and in (2) client_model 
<https://github.com/prometheus/client_model/blob/master/io/prometheus/client/metrics.proto>.
 
Recently we decided to introduce a completely custom unmarshal for 
textparse proto parser as even gogo was too slow (due to parser interface 
mostly). <https://github.com/prometheus/prometheus/pull/15731> I planned to 
write a blog post and perhaps even a Go generator for this, but this means 
we can move the (1) generated code to an internal package (make it internal 
to textparse only). For proto file (1) is what is shipped to buf registry 
as a source of truth <https://buf.build/prometheus/client-model> so I would 
propose:

1. We make (1) source of truth for proto file.
2. We deprecate (2) client_model and ask for all users to generate 
themselves the Go code using buf and plugin of their choice for future uses 
(even gogo if they want). This also means moving out of (2) client_model in 
client_golang which I think is possible even in 1.x client_golang (e.g. 
Write2(*ourown.Metric), see a quick exploration here 
<https://github.com/prometheus/client_golang/pull/1746#issue-2865853407>.).

I will create an official proposal for this.

> I tried benchmarking BenchmarkSampleSend and the results seem awful, from 
> ~150000 
B/op and ~250 allocs/op to ~1400000 B/op to ~170000 allocs/op. Profiling 
the code, it seems like labelsToLabelsProto is the offender, as it 
allocates a *prompb.Label per label. I experimented with using a 
map[string]string instead, but it seems like the results aren't that much 
different.

We don't have this problem with Remote Write 2.0 BTW, but indeed 1.0 would 
suffer for now.

> One potential solution to this - which would require a relatively big 
refactor - would be to replace repeated Label labels with the raw byte 
representation that is held inside of labels_stringlabels, and switch from 
an array of Sample to two arrays of Timestamp and Value. Just doing the 
former seems to speed up the benchmarks by 30% (while allocations are still 
huge because of the *prompb.Sample).

It's tempting to make a tailored protocol (proto file) with optimizations 
that work well for Go. But it's not a silver bullet as we might be making 
it worse for other languages. Ideally we work in the community on Go 
decoders and encoders that work well with the current protocol and as we 
saw with gogo protobuf--it's possible to deliver the optimizations, just 
it's hard to sustain a generic generator that works for all small cases.

During the custom decoder work for client_model proto parsing 
<https://github.com/prometheus/prometheus/pull/15731> (which is 
streaming(!), have reusable structs, non-nullable by default (gogo style 
with less objects) and custom parsing to native types) I learned that:

1) It's not that difficult to write a decoding and encoding of the protobuf 
<https://github.com/prometheus/prometheus/blob/main/prompb/io/prometheus/client/decoder.go#L173>
 
(by hand or generation). We might exaggerate the amount of work to maintain 
simple decoding/encoding generation for simple use cases, given gogo trauma.
2) I'm skeptical the main golang protobuf generation will suit high 
efficiency needs for the data-intensive cases like we have in streaming 
observability data. With extra plugins or not (vtproto, csproto), the 
direction of the main golang protobuf generator is to be stable, 
generically usable and efficient enough for masses. This is why with the new 
OpaqueAPI <https://go.dev/blog/protobuf-opaque> we have even more pointers 
(e.g. []*Elem is not *[]*Array, lazy decoding adds more fields and data to 
the structs), we can't customise unmarshal/marshal easily and generally 
generates structs are bigger themselves (unknown fields, lazy decoding, 
metadata handling) 
<https://github.com/bwplotka/benchmarks/blob/db942ed12cc1b0ab160fbfb882fc3c1b81c0bd98/benchmarks/metrics-streaming/io/prometheus/write/v2/types.pb.go#L509>
 
which is probably fine generally, but for us it's an overhead. What was the 
last time our decoding code "checked" what unknown fields we received in 
the proto? Never - so why we have this fat?

I think we should write slim Go generator for data intensive proto use and 
make it easy to write generators here, long term. FYI: OpenTelemetry is 
discussing similar. 
<https://github.com/open-telemetry/opentelemetry-collector/issues/7095>

Kind Regards,
Bartek Plotka (@bwplotka)


On Thursday, May 16, 2024 at 3:58:58 PM UTC+1 Bryan Boreham wrote:

> > Any reason samples are still encoded as a list of pairs? Using two 
> arrays would both reduce the number of objects and allow using varint 
> encoding for timestamps.
>
> Just to note that Prometheus is coded to send just one sample per series 
> in a remote-write message.
> In normal usage we want to send data straight away after it was scraped, 
> so that fits.
>
> Other clients, or Prometheus in some different mode, could benefit as you 
> say.
>
> Bryan
>
> On Wednesday 6 March 2024 at 11:26:50 UTC bjo...@rabenste.in wrote:
>
>> On 02.03.24 08:00, Mirco wrote: 
>> > Any reason samples are still encoded as a list of pairs? Using two 
>> arrays 
>> > would both reduce the number of objects and allow using varint encoding 
>> for 
>> > timestamps. 
>>
>> I assume you are referring to the protobuf messages for TimeSeries and 
>> Sample? 
>>
>> I'm not an expert for remote write (and even less so for remote read), 
>> but I think it's safe to say that a change in the protobuf layout 
>> would mean a new major version of the protocols. v2 is just underway, 
>> so that would require the next major version bump to v3, which would 
>> be a big deal given how widely used the protocol is. 
>>
>> Having said that, at least for remote write, there are usually not a 
>> lot of samples in a TimeSeries message. The most common number is 
>> AFAIK one. Mileage might vary for remote read, but that's also far 
>> less used. 
>>
>> WRT varint: In my understanding of protobuf, varint will always be 
>> used for an int64, even if it is a field in another message. 
>>
>> -- 
>> Björn Rabenstein 
>> [PGP-ID] 0x851C3DA17D748D03 
>> [email] bjo...@rabenste.in 
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Prometheus Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to prometheus-developers+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/prometheus-developers/66529c70-6514-4ba8-9d1d-b842f7711ebdn%40googlegroups.com.

Reply via email to