proxy/extensions/common/proto_util_speed_test.cc

221 lines
7.7 KiB
C++

/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* 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.
*/
#include "benchmark/benchmark.h"
#include "extensions/common/metadata_object.h"
#include "extensions/common/node_info_generated.h"
#include "extensions/common/proto_util.h"
#include "extensions/common/util.h"
#include "google/protobuf/util/json_util.h"
#include "source/common/common/base64.h"
#include "source/common/stream_info/filter_state_impl.h"
#include "source/extensions/filters/common/expr/cel_state.h"
#include "test/test_common/status_utility.h"
// WASM_PROLOG
#ifdef NULL_PLUGIN
namespace Wasm {
#endif // NULL_PLUGIN
// END WASM_PROLOG
namespace Common {
using namespace google::protobuf::util;
constexpr std::string_view node_metadata_json = R"###(
{
"NAME":"test_pod",
"NAMESPACE":"test_namespace",
"LABELS": {
"app": "productpage",
"version": "v1",
"pod-template-hash": "84975bc778"
},
"OWNER":"test_owner",
"WORKLOAD_NAME":"test_workload",
"PLATFORM_METADATA":{
"gcp_project":"test_project",
"gcp_cluster_location":"test_location",
"gcp_cluster_name":"test_cluster"
},
"ISTIO_VERSION":"istio-1.4",
"MESH_ID":"test-mesh"
}
)###";
constexpr std::string_view metadata_id_key = "envoy.wasm.metadata_exchange.downstream_id";
constexpr std::string_view metadata_key = "envoy.wasm.metadata_exchange.downstream";
constexpr std::string_view node_id = "test_pod.test_namespace";
static void setData(Envoy::StreamInfo::FilterStateImpl& filter_state, std::string_view key,
std::string_view value) {
Envoy::Extensions::Filters::Common::Expr::CelStatePrototype prototype;
auto state_ptr = std::make_unique<Envoy::Extensions::Filters::Common::Expr::CelState>(prototype);
state_ptr->setValue(toAbslStringView(value));
filter_state.setData(toAbslStringView(key), std::move(state_ptr),
Envoy::StreamInfo::FilterState::StateType::Mutable);
}
static const std::string& getData(Envoy::StreamInfo::FilterStateImpl& filter_state,
std::string_view key) {
return filter_state
.getDataReadOnly<Envoy::Extensions::Filters::Common::Expr::CelState>(toAbslStringView(key))
->value();
}
static void BM_ReadFlatBuffer(benchmark::State& state) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options));
auto out = extractNodeFlatBufferFromStruct(metadata_struct);
Envoy::StreamInfo::FilterStateImpl filter_state{
Envoy::StreamInfo::FilterState::LifeSpan::TopSpan};
setData(filter_state, metadata_key,
std::string_view(reinterpret_cast<const char*>(out.data()), out.size()));
size_t size = 0;
for (auto _ : state) {
auto buf = getData(filter_state, metadata_key);
auto peer = flatbuffers::GetRoot<FlatNode>(buf.data());
size += peer->workload_name()->size() + peer->namespace_()->size() +
peer->labels()->LookupByKey("app")->value()->size() +
peer->labels()->LookupByKey("version")->value()->size();
benchmark::DoNotOptimize(size);
}
}
BENCHMARK(BM_ReadFlatBuffer);
static void BM_WriteRawBytes(benchmark::State& state) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options));
auto bytes = metadata_struct.SerializeAsString();
Envoy::StreamInfo::FilterStateImpl filter_state{
Envoy::StreamInfo::FilterState::LifeSpan::TopSpan};
for (auto _ : state) {
setData(filter_state, metadata_id_key, node_id);
setData(filter_state, metadata_key, bytes);
}
}
BENCHMARK(BM_WriteRawBytes);
static void BM_WriteFlatBufferWithCache(benchmark::State& state) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options));
auto bytes = metadata_struct.SerializeAsString();
Envoy::StreamInfo::FilterStateImpl filter_state{
Envoy::StreamInfo::FilterState::LifeSpan::TopSpan};
std::unordered_map<std::string, std::string> cache;
for (auto _ : state) {
// lookup cache by key
auto nodeinfo_it = cache.find(std::string(node_id));
std::string node_info;
if (nodeinfo_it == cache.end()) {
google::protobuf::Struct test_struct;
test_struct.ParseFromArray(bytes.data(), bytes.size());
benchmark::DoNotOptimize(test_struct);
auto out = extractNodeFlatBufferFromStruct(test_struct);
node_info =
cache.emplace(node_id, std::string(reinterpret_cast<const char*>(out.data()), out.size()))
.first->second;
} else {
node_info = nodeinfo_it->second;
}
setData(filter_state, metadata_id_key, node_id);
setData(filter_state, metadata_key, node_info);
}
}
BENCHMARK(BM_WriteFlatBufferWithCache);
constexpr std::string_view node_flatbuffer_json = R"###(
{
"NAME":"test_pod",
"NAMESPACE":"default",
"CLUSTER_ID": "client-cluster",
"LABELS": {
"app": "productpage",
"version": "v1",
"service.istio.io/canonical-name": "productpage-v1",
"service.istio.io/canonical-revision": "version-1"
},
"OWNER": "kubernetes://apis/apps/v1/namespaces/default/deployments/productpage-v1",
"WORKLOAD_NAME":"productpage-v1"
}
)###";
// Measure decoding performance of x-envoy-peer-metadata.
static void BM_DecodeFlatBuffer(benchmark::State& state) {
// Construct a header from sample value.
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_flatbuffer_json), &metadata_struct, json_parse_options));
std::string metadata_bytes;
::Wasm::Common::serializeToStringDeterministic(metadata_struct, &metadata_bytes);
const std::string header_value =
Envoy::Base64::encode(metadata_bytes.data(), metadata_bytes.size());
size_t size = 0;
for (auto _ : state) {
auto bytes = Envoy::Base64::decodeWithoutPadding(header_value);
google::protobuf::Struct metadata;
metadata.ParseFromString(bytes);
auto fb = ::Wasm::Common::extractNodeFlatBufferFromStruct(metadata);
size += fb.size();
benchmark::DoNotOptimize(size);
}
}
BENCHMARK(BM_DecodeFlatBuffer);
// Measure decoding performance of baggage.
static void BM_DecodeBaggage(benchmark::State& state) {
// Construct a header from sample value.
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_flatbuffer_json), &metadata_struct, json_parse_options));
auto fb = ::Wasm::Common::extractNodeFlatBufferFromStruct(metadata_struct);
const auto& node = *flatbuffers::GetRoot<Wasm::Common::FlatNode>(fb.data());
const std::string baggage = Istio::Common::convertFlatNodeToWorkloadMetadata(node).baggage();
size_t size = 0;
for (auto _ : state) {
auto obj = Istio::Common::WorkloadMetadataObject::fromBaggage(baggage);
size += obj.namespace_name_.size();
benchmark::DoNotOptimize(size);
}
}
BENCHMARK(BM_DecodeBaggage);
} // namespace Common
// WASM_EPILOG
#ifdef NULL_PLUGIN
} // namespace Wasm
#endif