For simple protocols protobuf decoding has no taken branches. I.e. if you only use the first 15 field numbers (all your tags are 1 byte) and if all the types are the expected types, and if all the variable-length items are < 128 bytes long then you can decode the message without taking any branches. In C++. Most of the other languages have simpler and slower codecs.
This is the hot path in C++[1]. A really large amount of work has gone into protobuf C++ performance in the last 3 years or so.
1: https://github.com/protocolbuffers/protobuf/blob/master/src/...