/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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 #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace folly; using namespace apache::thrift; using namespace thrift::benchmark; template struct SerializerTraits; template struct SerializerTraits> { using Reader = ReaderType; using Writer = WriterType; }; template using GetReader = typename SerializerTraits::Reader; template using GetWriter = typename SerializerTraits::Writer; struct FrozenSerializer { template static void serialize(const T& obj, folly::IOBufQueue* out) { auto p = new string(frozen::freezeToString(obj)); out->append(folly::IOBuf::takeOwnership( p->data(), p->size(), [](void*, void* p) { delete static_cast(p); }, static_cast(p))); } template static size_t deserialize(folly::IOBuf* iobuf, T& t) { auto view = frozen::mapFrozen(iobuf->coalesce()); t = view.thaw(); return 0; } }; enum class SerializerMethod { Codegen, Object, }; // The benckmark is to measure single struct use case, the iteration here is // more like a benchmark artifact, so avoid doing optimizationon iteration // usecase in this benchmark (e.g. move string definition out of while loop) template < SerializerMethod kSerializerMethod, typename Serializer, typename Struct, typename Counter> void writeBench(size_t iters, Counter&&) { BenchmarkSuspender susp; auto strct = create(); protocol::Object obj; if constexpr (kSerializerMethod == SerializerMethod::Object) { IOBufQueue q; Serializer::serialize(strct, &q); obj = protocol::parseObject>(*q.move()); } susp.dismiss(); IOBufQueue q; while (iters--) { if constexpr (kSerializerMethod == SerializerMethod::Object) { protocol::serializeObject>(obj, q); folly::doNotOptimizeAway(q); } else { Serializer::serialize(strct, &q); folly::doNotOptimizeAway(q); } // Reuse the queue across iterations to avoid allocating a new buffer for // each struct (which would dominate the measurement), but keep only the // tail to avoid unbounded growth. if (auto head = q.front(); head && head->isChained()) { q.append(q.move()->prev()->unlink()); } } susp.rehire(); } template < SerializerMethod kSerializerMethod, typename Serializer, typename Struct, typename Counter> void readBench(size_t iters, Counter&& counter) { BenchmarkSuspender susp; auto strct = create(); IOBufQueue q; Serializer::serialize(strct, &q); auto buf = q.move(); // coalesce the IOBuf chain to test fast path buf->coalesce(); susp.dismiss(); while (iters--) { if constexpr (kSerializerMethod == SerializerMethod::Object) { auto obj = protocol::parseObject>(*buf); folly::doNotOptimizeAway(obj); } else { Struct data; Serializer::deserialize(buf.get(), data); folly::doNotOptimizeAway(data); } } susp.rehire(); counter["serialized_size"] = buf->computeChainDataLength(); } constexpr SerializerMethod getSerializerMethod(std::string_view prefix) { return prefix == "" || prefix == "OpEncode" ? SerializerMethod::Codegen : prefix == "Object" ? SerializerMethod::Object : throw std::invalid_argument(std::string(prefix) + " is invalid"); } #define X1(Prefix, proto, rdwr, bench) \ BENCHMARK_COUNTERS( \ Prefix##proto##Protocol_##rdwr##_##bench, counter, iters) { \ rdwr##Bench( \ iters, counter); \ } #define OpEncodeX1(Prefix, proto, rdwr, bench) \ BENCHMARK_COUNTERS( \ Prefix##proto##Protocol_##rdwr##_##bench, counter, iters) { \ rdwr##Bench( \ iters, counter); \ } // clang-format off #define X2(Prefix, proto, bench) \ X1(Prefix, proto, write, bench) \ X1(Prefix, proto, read, bench) #define OpEncodeX2(Prefix, proto, bench) \ OpEncodeX1(Prefix, proto, write, bench) \ OpEncodeX1(Prefix, proto, read, bench) #define X(Prefix, proto) \ X2(Prefix, proto, Empty) \ X2(Prefix, proto, SmallInt) \ X2(Prefix, proto, BigInt) \ X2(Prefix, proto, SmallString) \ X2(Prefix, proto, BigString) \ X2(Prefix, proto, BigBinary) \ X2(Prefix, proto, LargeBinary) \ X2(Prefix, proto, Mixed) \ X2(Prefix, proto, MixedInt) \ X2(Prefix, proto, SmallListInt) \ X2(Prefix, proto, BigListInt) \ X2(Prefix, proto, BigListMixed) \ X2(Prefix, proto, BigListMixedInt) \ X2(Prefix, proto, LargeListMixed) \ X2(Prefix, proto, LargeSetInt) \ X2(Prefix, proto, UnorderedSetInt) \ X2(Prefix, proto, SortedVecSetInt) \ X2(Prefix, proto, LargeMapInt) \ X2(Prefix, proto, LargeMapMixed) \ X2(Prefix, proto, LargeUnorderedMapMixed) \ X2(Prefix, proto, LargeSortedVecMapMixed) \ X2(Prefix, proto, UnorderedMapInt) \ X2(Prefix, proto, NestedMap) \ X2(Prefix, proto, SortedVecNestedMap) \ X2(Prefix, proto, ComplexStruct) #define OpEncodeX(Prefix, proto) \ OpEncodeX2(Prefix, proto, Empty) \ OpEncodeX2(Prefix, proto, SmallInt) \ OpEncodeX2(Prefix, proto, BigInt) \ OpEncodeX2(Prefix, proto, SmallString) \ OpEncodeX2(Prefix, proto, BigString) \ OpEncodeX2(Prefix, proto, Mixed) \ OpEncodeX2(Prefix, proto, MixedInt) \ OpEncodeX2(Prefix, proto, SmallListInt) \ OpEncodeX2(Prefix, proto, BigListInt) \ OpEncodeX2(Prefix, proto, BigListMixed) \ OpEncodeX2(Prefix, proto, BigListMixedInt) \ OpEncodeX2(Prefix, proto, LargeListMixed) \ OpEncodeX2(Prefix, proto, LargeSetInt) \ OpEncodeX2(Prefix, proto, UnorderedSetInt) \ OpEncodeX2(Prefix, proto, SortedVecSetInt) \ OpEncodeX2(Prefix, proto, LargeMapInt) \ OpEncodeX2(Prefix, proto, LargeMapMixed) \ OpEncodeX2(Prefix, proto, LargeUnorderedMapMixed) \ OpEncodeX2(Prefix, proto, LargeSortedVecMapMixed) \ OpEncodeX2(Prefix, proto, UnorderedMapInt) \ OpEncodeX2(Prefix, proto, NestedMap) \ OpEncodeX2(Prefix, proto, SortedVecNestedMap) \ OpEncodeX2(Prefix, proto, ComplexStruct) X(, Binary) X(, Compact) X(, SimpleJSON) X(, JSON) X(, Frozen) X(Object, Binary) X(Object, Compact) OpEncodeX(OpEncode, Binary) OpEncodeX(OpEncode, Compact) int main(int argc, char** argv) { folly::Init init(&argc, &argv); runBenchmarks(); return 0; } // clang-format on