2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/Benchmark.h>
19 #include <folly/ExceptionWrapper.h>
20 #include <folly/SocketAddress.h>
21 #include <folly/io/IOBufQueue.h>
22 #include <folly/io/async/AsyncServerSocket.h>
23 #include <folly/io/async/AsyncSocket.h>
24 #include <folly/io/async/EventBase.h>
26 #include <folly/portability/GFlags.h>
28 using namespace folly;
30 class TestAsyncSocket {
32 explicit TestAsyncSocket(
33 folly::EventBase* evb,
39 sock_(new folly::AsyncSocket(evb)),
42 setBufferSize(bufferSize);
43 setZeroCopy(zeroCopy);
46 explicit TestAsyncSocket(
47 folly::EventBase* evb,
54 sock_(new folly::AsyncSocket(evb, fd)),
57 setBufferSize(bufferSize);
58 setZeroCopy(zeroCopy);
61 sock_->setReadCB(&callback_);
69 void connect(const folly::SocketAddress& remote) {
71 sock_->connect(&callback_, remote);
76 void setZeroCopy(bool enable) {
79 sock_->setZeroCopy(zeroCopy_);
83 void setBufferSize(size_t bufferSize) {
85 bufferSize_ = bufferSize;
87 readBuffer_ = new char[bufferSize_];
90 class Callback : public folly::AsyncSocket::ReadCallback,
91 public folly::AsyncSocket::ConnectCallback {
93 explicit Callback(TestAsyncSocket* parent) : parent_(parent) {}
95 void connectSuccess() noexcept override {
96 parent_->sock_->setReadCB(this);
97 parent_->onConnected();
100 void connectErr(const folly::AsyncSocketException& ex) noexcept override {
101 LOG(ERROR) << "Connect error: " << ex.what();
102 parent_->onDataFinish(folly::exception_wrapper(ex));
105 void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
106 parent_->getReadBuffer(bufReturn, lenReturn);
109 void readDataAvailable(size_t len) noexcept override {
110 parent_->readDataAvailable(len);
113 void readEOF() noexcept override {
114 parent_->onDataFinish(folly::exception_wrapper());
117 void readErr(const folly::AsyncSocketException& ex) noexcept override {
118 parent_->onDataFinish(folly::exception_wrapper(ex));
122 TestAsyncSocket* parent_{nullptr};
125 void clearBuffers() {
127 delete[] readBuffer_;
131 void getReadBuffer(void** bufReturn, size_t* lenReturn) {
132 *bufReturn = readBuffer_ + readOffset_;
133 *lenReturn = bufferSize_ - readOffset_;
136 void readDataAvailable(size_t len) noexcept {
138 if (readOffset_ == bufferSize_) {
145 setZeroCopy(zeroCopy_);
151 if (client_ && currLoop_ >= numLoops_) {
152 evb_->terminateLoopSoon();
158 void onDataFinish(folly::exception_wrapper) {
160 evb_->terminateLoopSoon();
166 folly::IOBuf::takeOwnership(::malloc(bufferSize_), bufferSize_);
168 if (sock_ && writeBuffer_) {
171 std::move(writeBuffer_),
172 zeroCopy_ ? WriteFlags::WRITE_MSG_ZEROCOPY : WriteFlags::NONE);
178 folly::EventBase* evb_;
181 bool zeroCopy_{false};
183 folly::AsyncSocket::UniquePtr sock_;
186 size_t bufferSize_{0};
187 size_t readOffset_{0};
188 char* readBuffer_{nullptr};
189 std::unique_ptr<folly::IOBuf> writeBuffer_;
194 class TestServer : public folly::AsyncServerSocket::AcceptCallback {
197 folly::EventBase* evb,
203 bufferSize_(bufferSize),
204 zeroCopy_(zeroCopy) {}
206 void addCallbackToServerSocket(folly::AsyncServerSocket& sock) {
207 sock.addAcceptCallback(this, evb_);
210 void connectionAccepted(
212 const folly::SocketAddress& /* unused */) noexcept override {
213 auto client = std::make_shared<TestAsyncSocket>(
214 evb_, fd, numLoops_, bufferSize_, zeroCopy_);
215 clients_[client.get()] = client;
218 void acceptError(const std::exception&) noexcept override {}
221 folly::EventBase* evb_;
225 std::unique_ptr<TestAsyncSocket> client_;
226 std::unordered_map<TestAsyncSocket*, std::shared_ptr<TestAsyncSocket>>
232 explicit Test(int numLoops, bool zeroCopy, size_t bufferSize)
233 : numLoops_(numLoops),
235 bufferSize_(bufferSize),
236 client_(new TestAsyncSocket(&evb_, numLoops_, bufferSize_, zeroCopy)),
237 listenSock_(new folly::AsyncServerSocket(&evb_)),
238 server_(&evb_, numLoops_, bufferSize_, zeroCopy) {
240 server_.addCallbackToServerSocket(*listenSock_);
245 evb_.runInEventBaseThread([this]() {
248 listenSock_->bind(0);
249 listenSock_->setZeroCopy(zeroCopy_);
250 listenSock_->listen(10);
251 listenSock_->startAccepting();
262 SocketAddress addr = listenSock_->getAddress();
263 client_->connect(addr);
271 std::unique_ptr<TestAsyncSocket> client_;
272 folly::AsyncServerSocket::UniquePtr listenSock_;
277 const std::string& host,
282 LOG(INFO) << "Running client. host = " << host << " port = " << port
283 << " numLoops = " << numLoops << " zeroCopy = " << zeroCopy
284 << " bufferSize = " << bufferSize;
287 std::unique_ptr<TestAsyncSocket> client(
288 new TestAsyncSocket(&evb, numLoops, bufferSize, zeroCopy));
289 SocketAddress addr(host, port);
290 evb.runInEventBaseThread([&]() { client->connect(addr); });
295 void runServer(uint16_t port, int numLoops, bool zeroCopy, size_t bufferSize) {
296 LOG(INFO) << "Running server. port = " << port << " numLoops = " << numLoops
297 << " zeroCopy = " << zeroCopy << " bufferSize = " << bufferSize;
300 folly::AsyncServerSocket::UniquePtr listenSock(
301 new folly::AsyncServerSocket(&evb));
302 TestServer server(&evb, numLoops, bufferSize, zeroCopy);
304 server.addCallbackToServerSocket(*listenSock);
306 evb.runInEventBaseThread([&]() {
307 listenSock->bind(port);
308 listenSock->setZeroCopy(zeroCopy);
309 listenSock->listen(10);
310 listenSock->startAccepting();
316 static auto constexpr kMaxLoops = 200000;
318 void zeroCopyOn(unsigned /* unused */, size_t bufferSize) {
319 Test test(kMaxLoops, true, bufferSize);
323 void zeroCopyOff(unsigned /* unused */, size_t bufferSize) {
324 Test test(kMaxLoops, false, bufferSize);
328 BENCHMARK_PARAM(zeroCopyOn, 4096);
329 BENCHMARK_PARAM(zeroCopyOff, 4096);
330 BENCHMARK_DRAW_LINE()
331 BENCHMARK_PARAM(zeroCopyOn, 8192);
332 BENCHMARK_PARAM(zeroCopyOff, 8192);
333 BENCHMARK_DRAW_LINE()
334 BENCHMARK_PARAM(zeroCopyOn, 16384);
335 BENCHMARK_PARAM(zeroCopyOff, 16384);
336 BENCHMARK_DRAW_LINE()
337 BENCHMARK_PARAM(zeroCopyOn, 32768);
338 BENCHMARK_PARAM(zeroCopyOff, 32768);
339 BENCHMARK_DRAW_LINE()
340 BENCHMARK_PARAM(zeroCopyOn, 65536);
341 BENCHMARK_PARAM(zeroCopyOff, 65536);
342 BENCHMARK_DRAW_LINE()
343 BENCHMARK_PARAM(zeroCopyOn, 131072);
344 BENCHMARK_PARAM(zeroCopyOff, 131072);
345 BENCHMARK_DRAW_LINE()
346 BENCHMARK_PARAM(zeroCopyOn, 262144);
347 BENCHMARK_PARAM(zeroCopyOff, 262144);
348 BENCHMARK_DRAW_LINE()
349 BENCHMARK_PARAM(zeroCopyOn, 524288);
350 BENCHMARK_PARAM(zeroCopyOff, 524288);
351 BENCHMARK_DRAW_LINE()
352 BENCHMARK_PARAM(zeroCopyOn, 1048576);
353 BENCHMARK_PARAM(zeroCopyOff, 1048576);
354 BENCHMARK_DRAW_LINE()
356 DEFINE_bool(client, false, "client mode");
357 DEFINE_bool(server, false, "server mode");
358 DEFINE_bool(zeroCopy, false, "use zerocopy");
359 DEFINE_int32(numLoops, kMaxLoops, "number of loops");
360 DEFINE_int32(bufferSize, 524288, "buffer size");
361 DEFINE_int32(port, 33130, "port");
362 DEFINE_string(host, "::1", "host");
364 int main(int argc, char** argv) {
365 gflags::ParseCommandLineFlags(&argc, &argv, true);
374 } else if (FLAGS_server) {
375 runServer(FLAGS_port, FLAGS_numLoops, FLAGS_zeroCopy, FLAGS_bufferSize);