/* * 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 using namespace apache::thrift; using namespace apache::thrift::test; class DummyCallback : public RequestCallback { public: explicit DummyCallback(ClientReceiveState& state, folly::Baton<>& baton) : state_(state), baton_(baton) {} void requestSent() override {} void replyReceived(ClientReceiveState&& state) override { state_ = std::move(state); baton_.post(); } void requestError(ClientReceiveState&&) override {} bool isInlineSafe() const override { return true; } ClientReceiveState& state_; private: folly::Baton<>& baton_; }; class DummyChannel : public apache::thrift::RequestChannel { void sendRequestResponse( const RpcOptions&, apache::thrift::MethodMetadata&&, SerializedRequest&&, std::shared_ptr, RequestClientCallback::Ptr cb) override { static ClientReceiveState staticState = [&] { ClientReceiveState state; folly::Baton<> baton; auto client = makeTestClient( std::make_shared()); client->echoInt(std::make_unique(state, baton), 0); baton.wait(); state.serializedResponse().buffer->coalesce(); return state; }(); cb.release()->onResponse(ClientReceiveState( staticState.protocolId(), staticState.messageType(), SerializedResponse(staticState.serializedResponse().buffer->clone()), nullptr, nullptr, RpcTransportStats())); } void sendRequestNoResponse( const RpcOptions&, apache::thrift::MethodMetadata&&, SerializedRequest&&, std::shared_ptr, RequestClientCallback::Ptr cb) override { cb.release()->onResponse({}); } void setCloseCallback(CloseCallback*) override { std::terminate(); } folly::EventBase* getEventBase() const override { return nullptr; } uint16_t getProtocolId() override { return 2; // COMPACT } }; class MockClient : public TestServiceAsyncClient { public: using TestServiceAsyncClient::TestServiceAsyncClient; template folly::coro::Task co_old_echoInt(int x) { auto task = semifuture_echoInt(x); const folly::CancellationToken& cancelToken = co_await folly::coro::co_current_cancellation_token; if (cancelToken.canBeCancelled()) { co_yield folly::coro::co_result(co_await folly::coro::co_awaitTry( folly::coro::detachOnCancel(std::move(task)))); } else { co_yield folly::coro::co_result( co_await folly::coro::co_awaitTry(std::move(task))); } } template folly::coro::Task co_old_noResponse(int x) { auto task = semifuture_noResponse(x); const folly::CancellationToken& cancelToken = co_await folly::coro::co_current_cancellation_token; if (cancelToken.canBeCancelled()) { co_yield folly::coro::co_result(co_await folly::coro::co_awaitTry( folly::coro::detachOnCancel(std::move(task)))); } else { co_yield folly::coro::co_result( co_await folly::coro::co_awaitTry(std::move(task))); } } }; enum class Flavor { Sync, Future, Semifuture, Co, Co_old, Co_cancellable, Co_cancellable_old, }; template void bench(size_t iters) { folly::BenchmarkSuspender susp; MockClient client(std::make_shared()); if (!oneway) { client.sync_echoInt(0); // prime DummyChannel } folly::CancellationSource cs; susp.dismiss(); while (iters--) { if (!oneway) { switch (flavor) { case Flavor::Sync: client.sync_echoInt(0); break; case Flavor::Future: client.future_echoInt(0).get(); break; case Flavor::Semifuture: client.semifuture_echoInt(0).get(); break; case Flavor::Co: folly::coro::blockingWait(client.co_echoInt(0)); break; case Flavor::Co_old: folly::coro::blockingWait(client.co_old_echoInt(0)); break; case Flavor::Co_cancellable: folly::coro::blockingWait(folly::coro::co_withCancellation( cs.getToken(), client.co_echoInt(0))); break; case Flavor::Co_cancellable_old: folly::coro::blockingWait(folly::coro::co_withCancellation( cs.getToken(), client.co_old_echoInt(0))); break; } } else { switch (flavor) { case Flavor::Sync: client.sync_noResponse(0); break; case Flavor::Future: client.future_noResponse(0).get(); break; case Flavor::Semifuture: client.semifuture_noResponse(0).get(); break; case Flavor::Co: folly::coro::blockingWait(client.co_noResponse(0)); break; case Flavor::Co_old: folly::coro::blockingWait(client.co_old_noResponse(0)); break; case Flavor::Co_cancellable: folly::coro::blockingWait(folly::coro::co_withCancellation( cs.getToken(), client.co_noResponse(0))); break; case Flavor::Co_cancellable_old: folly::coro::blockingWait(folly::coro::co_withCancellation( cs.getToken(), client.co_old_noResponse(0))); break; } } } susp.rehire(); } #define NAME_true _oneway_ #define NAME_false _ #define ONEWAY(x) NAME_##x #define CAT(x, y) x##y #define EVALUATOR(x, y) CAT(x, y) #define FLAVOR(flavor, oneway) EVALUATOR(flavor, ONEWAY(oneway)) #define X1(flavor, oneway) \ BENCHMARK(EVALUATOR(FLAVOR(flavor, oneway), Client_Bench), iters) { \ bench(iters); \ } #define X2(oneway) \ X1(Sync, oneway) \ X1(Future, oneway) \ X1(Semifuture, oneway) \ X1(Co, oneway) \ X1(Co_old, oneway) \ X1(Co_cancellable, oneway) \ X1(Co_cancellable_old, oneway) X2(false) X2(true) int main(int argc, char** argv) { folly::Init init(&argc, &argv); folly::runBenchmarks(); return 0; }