ZeroCopy.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. * Copyright 2017-present Facebook, Inc.
  3. *
  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
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  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.
  15. */
  16. #pragma once
  17. #include <folly/ExceptionWrapper.h>
  18. #include <folly/SocketAddress.h>
  19. #include <folly/io/IOBufQueue.h>
  20. #include <folly/io/async/AsyncServerSocket.h>
  21. #include <folly/io/async/AsyncSocket.h>
  22. #include <folly/io/async/EventBase.h>
  23. namespace folly {
  24. class ZeroCopyTestAsyncSocket {
  25. public:
  26. explicit ZeroCopyTestAsyncSocket(
  27. size_t* counter,
  28. folly::EventBase* evb,
  29. int numLoops,
  30. size_t bufferSize,
  31. bool zeroCopy)
  32. : counter_(counter),
  33. evb_(evb),
  34. numLoops_(numLoops),
  35. sock_(new folly::AsyncSocket(evb)),
  36. callback_(this),
  37. client_(true) {
  38. setBufferSize(bufferSize);
  39. setZeroCopy(zeroCopy);
  40. }
  41. explicit ZeroCopyTestAsyncSocket(
  42. size_t* counter,
  43. folly::EventBase* evb,
  44. int fd,
  45. int numLoops,
  46. size_t bufferSize,
  47. bool zeroCopy)
  48. : counter_(counter),
  49. evb_(evb),
  50. numLoops_(numLoops),
  51. sock_(new folly::AsyncSocket(evb, fd)),
  52. callback_(this),
  53. client_(false) {
  54. setBufferSize(bufferSize);
  55. setZeroCopy(zeroCopy);
  56. // enable reads
  57. if (sock_) {
  58. sock_->setReadCB(&callback_);
  59. }
  60. }
  61. ~ZeroCopyTestAsyncSocket() {
  62. clearBuffers();
  63. }
  64. void connect(const folly::SocketAddress& remote) {
  65. if (sock_) {
  66. sock_->connect(&callback_, remote);
  67. }
  68. }
  69. bool isZeroCopyWriteInProgress() const {
  70. return sock_->isZeroCopyWriteInProgress();
  71. }
  72. private:
  73. void setZeroCopy(bool enable) {
  74. zeroCopy_ = enable;
  75. if (sock_) {
  76. sock_->setZeroCopy(zeroCopy_);
  77. }
  78. }
  79. void setBufferSize(size_t bufferSize) {
  80. clearBuffers();
  81. bufferSize_ = bufferSize;
  82. readBuffer_ = new char[bufferSize_];
  83. }
  84. class Callback : public folly::AsyncSocket::ReadCallback,
  85. public folly::AsyncSocket::ConnectCallback {
  86. public:
  87. explicit Callback(ZeroCopyTestAsyncSocket* parent) : parent_(parent) {}
  88. void connectSuccess() noexcept override {
  89. parent_->sock_->setReadCB(this);
  90. parent_->onConnected();
  91. }
  92. void connectErr(const folly::AsyncSocketException& ex) noexcept override {
  93. LOG(ERROR) << "Connect error: " << ex.what();
  94. parent_->onDataFinish(folly::exception_wrapper(ex));
  95. }
  96. void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
  97. parent_->getReadBuffer(bufReturn, lenReturn);
  98. }
  99. void readDataAvailable(size_t len) noexcept override {
  100. parent_->readDataAvailable(len);
  101. }
  102. void readEOF() noexcept override {
  103. parent_->onDataFinish(folly::exception_wrapper());
  104. }
  105. void readErr(const folly::AsyncSocketException& ex) noexcept override {
  106. parent_->onDataFinish(folly::exception_wrapper(ex));
  107. }
  108. private:
  109. ZeroCopyTestAsyncSocket* parent_{nullptr};
  110. };
  111. void clearBuffers() {
  112. if (readBuffer_) {
  113. delete[] readBuffer_;
  114. }
  115. }
  116. void getReadBuffer(void** bufReturn, size_t* lenReturn) {
  117. *bufReturn = readBuffer_ + readOffset_;
  118. *lenReturn = bufferSize_ - readOffset_;
  119. }
  120. void readDataAvailable(size_t len) noexcept {
  121. readOffset_ += len;
  122. if (readOffset_ == bufferSize_) {
  123. readOffset_ = 0;
  124. onDataReady();
  125. }
  126. }
  127. void onConnected() {
  128. setZeroCopy(zeroCopy_);
  129. writeBuffer();
  130. }
  131. void onDataReady() {
  132. currLoop_++;
  133. if (client_ && currLoop_ >= numLoops_) {
  134. evb_->runInLoop(
  135. [this] {
  136. if (counter_ && (0 == --(*counter_))) {
  137. evb_->terminateLoopSoon();
  138. }
  139. },
  140. false /*thisIteration*/);
  141. return;
  142. }
  143. writeBuffer();
  144. }
  145. void onDataFinish(folly::exception_wrapper) {
  146. if (client_) {
  147. if (counter_ && (0 == --(*counter_))) {
  148. evb_->terminateLoopSoon();
  149. }
  150. }
  151. }
  152. bool writeBuffer() {
  153. // use calloc to make sure the memory is touched
  154. // if the memory is just malloc'd, running the zeroCopyOn
  155. // and the zeroCopyOff back to back on a system that does not support
  156. // zerocopy leads to the second test being much slower
  157. writeBuffer_ =
  158. folly::IOBuf::takeOwnership(::calloc(1, bufferSize_), bufferSize_);
  159. if (sock_ && writeBuffer_) {
  160. sock_->writeChain(
  161. nullptr,
  162. std::move(writeBuffer_),
  163. zeroCopy_ ? WriteFlags::WRITE_MSG_ZEROCOPY : WriteFlags::NONE);
  164. }
  165. return true;
  166. }
  167. size_t* counter_{nullptr};
  168. folly::EventBase* evb_;
  169. int numLoops_{0};
  170. int currLoop_{0};
  171. bool zeroCopy_{false};
  172. folly::AsyncSocket::UniquePtr sock_;
  173. Callback callback_;
  174. size_t bufferSize_{0};
  175. size_t readOffset_{0};
  176. char* readBuffer_{nullptr};
  177. std::unique_ptr<folly::IOBuf> writeBuffer_;
  178. bool client_;
  179. };
  180. class ZeroCopyTestServer : public folly::AsyncServerSocket::AcceptCallback {
  181. public:
  182. explicit ZeroCopyTestServer(
  183. folly::EventBase* evb,
  184. int numLoops,
  185. size_t bufferSize,
  186. bool zeroCopy)
  187. : evb_(evb),
  188. numLoops_(numLoops),
  189. bufferSize_(bufferSize),
  190. zeroCopy_(zeroCopy) {}
  191. void addCallbackToServerSocket(folly::AsyncServerSocket& sock) {
  192. sock.addAcceptCallback(this, evb_);
  193. }
  194. void connectionAccepted(
  195. int fd,
  196. const folly::SocketAddress& /* unused */) noexcept override {
  197. auto client = std::make_shared<ZeroCopyTestAsyncSocket>(
  198. nullptr, evb_, fd, numLoops_, bufferSize_, zeroCopy_);
  199. clients_[client.get()] = client;
  200. }
  201. void acceptError(const std::exception&) noexcept override {}
  202. private:
  203. folly::EventBase* evb_;
  204. int numLoops_;
  205. size_t bufferSize_;
  206. bool zeroCopy_;
  207. std::unique_ptr<ZeroCopyTestAsyncSocket> client_;
  208. std::unordered_map<
  209. ZeroCopyTestAsyncSocket*,
  210. std::shared_ptr<ZeroCopyTestAsyncSocket>>
  211. clients_;
  212. };
  213. class ZeroCopyTest {
  214. public:
  215. explicit ZeroCopyTest(
  216. size_t numClients,
  217. int numLoops,
  218. bool zeroCopy,
  219. size_t bufferSize);
  220. bool run();
  221. private:
  222. void connectAll() {
  223. SocketAddress addr = listenSock_->getAddress();
  224. for (auto& client : clients_) {
  225. client->connect(addr);
  226. }
  227. }
  228. size_t numClients_;
  229. size_t counter_;
  230. int numLoops_;
  231. bool zeroCopy_;
  232. size_t bufferSize_;
  233. EventBase evb_;
  234. std::vector<std::unique_ptr<ZeroCopyTestAsyncSocket>> clients_;
  235. folly::AsyncServerSocket::UniquePtr listenSock_;
  236. ZeroCopyTestServer server_;
  237. };
  238. } // namespace folly