capnproto-common.h 14.9 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
21

Kenton Varda's avatar
Kenton Varda committed
22 23
#ifndef CAPNP_BENCHMARK_CAPNP_COMMON_H_
#define CAPNP_BENCHMARK_CAPNP_COMMON_H_
24

25
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
26 27 28
#pragma GCC system_header
#endif

29
#include "common.h"
30 31
#include <capnp/serialize.h>
#include <capnp/serialize-packed.h>
Kenton Varda's avatar
Kenton Varda committed
32
#include <kj/debug.h>
33
#if HAVE_SNAPPY
34
#include <capnp/serialize-snappy.h>
35
#endif  // HAVE_SNAPPY
36 37
#include <thread>

38
namespace capnp {
39 40 41
namespace benchmark {
namespace capnp {

42
class CountingOutputStream: public kj::FdOutputStream {
Kenton Varda's avatar
Kenton Varda committed
43 44 45 46 47 48 49 50 51 52
public:
  CountingOutputStream(int fd): FdOutputStream(fd), throughput(0) {}

  uint64_t throughput;

  void write(const void* buffer, size_t size) override {
    FdOutputStream::write(buffer, size);
    throughput += size;
  }

53
  void write(kj::ArrayPtr<const kj::ArrayPtr<const byte>> pieces) override {
Kenton Varda's avatar
Kenton Varda committed
54 55 56 57 58 59 60 61 62 63
    FdOutputStream::write(pieces);
    for (auto& piece: pieces) {
      throughput += piece.size();
    }
  }
};

// =======================================================================================

struct Uncompressed {
64
  typedef kj::FdInputStream& BufferedInput;
65 66 67 68
  typedef InputStreamMessageReader MessageReader;

  class ArrayMessageReader: public FlatArrayMessageReader {
  public:
69
    ArrayMessageReader(kj::ArrayPtr<const byte> array,
70
                       ReaderOptions options = ReaderOptions(),
71 72
                       kj::ArrayPtr<word> scratchSpace = nullptr)
      : FlatArrayMessageReader(kj::arrayPtr(
73 74 75
          reinterpret_cast<const word*>(array.begin()),
          reinterpret_cast<const word*>(array.end())), options) {}
  };
Kenton Varda's avatar
Kenton Varda committed
76

77
  static inline void write(kj::OutputStream& output, MessageBuilder& builder) {
Kenton Varda's avatar
Kenton Varda committed
78 79 80 81
    writeMessage(output, builder);
  }
};

82
struct Packed {
83
  typedef kj::BufferedInputStreamWrapper BufferedInput;
84 85
  typedef PackedMessageReader MessageReader;

86
  class ArrayMessageReader: private kj::ArrayInputStream, public PackedMessageReader {
87
  public:
88
    ArrayMessageReader(kj::ArrayPtr<const byte> array,
89
                       ReaderOptions options = ReaderOptions(),
90
                       kj::ArrayPtr<word> scratchSpace = nullptr)
91 92 93 94
      : ArrayInputStream(array),
        PackedMessageReader(*this, options, scratchSpace) {}
  };

95
  static inline void write(kj::OutputStream& output, MessageBuilder& builder) {
96 97 98
    writePackedMessage(output, builder);
  }

99
  static inline void write(kj::BufferedOutputStream& output, MessageBuilder& builder) {
100 101 102 103
    writePackedMessage(output, builder);
  }
};

104
#if HAVE_SNAPPY
Kenton Varda's avatar
Kenton Varda committed
105 106 107 108
static byte snappyReadBuffer[SNAPPY_BUFFER_SIZE];
static byte snappyWriteBuffer[SNAPPY_BUFFER_SIZE];
static byte snappyCompressedBuffer[SNAPPY_COMPRESSED_BUFFER_SIZE];

Kenton Varda's avatar
Kenton Varda committed
109
struct SnappyCompressed {
Kenton Varda's avatar
Kenton Varda committed
110 111
  typedef BufferedInputStreamWrapper BufferedInput;
  typedef SnappyPackedMessageReader MessageReader;
112

Kenton Varda's avatar
Kenton Varda committed
113
  class ArrayMessageReader: private ArrayInputStream, public SnappyPackedMessageReader {
114
  public:
115
    ArrayMessageReader(kj::ArrayPtr<const byte> array,
116
                       ReaderOptions options = ReaderOptions(),
117
                       kj::ArrayPtr<word> scratchSpace = nullptr)
118
      : ArrayInputStream(array),
Kenton Varda's avatar
Kenton Varda committed
119
        SnappyPackedMessageReader(static_cast<ArrayInputStream&>(*this), options, scratchSpace,
120
                                  kj::arrayPtr(snappyReadBuffer, SNAPPY_BUFFER_SIZE)) {}
121
  };
Kenton Varda's avatar
Kenton Varda committed
122 123

  static inline void write(OutputStream& output, MessageBuilder& builder) {
Kenton Varda's avatar
Kenton Varda committed
124
    writeSnappyPackedMessage(output, builder,
125 126
        kj::arrayPtr(snappyWriteBuffer, SNAPPY_BUFFER_SIZE),
        kj::arrayPtr(snappyCompressedBuffer, SNAPPY_COMPRESSED_BUFFER_SIZE));
127
  }
Kenton Varda's avatar
Kenton Varda committed
128
};
129
#endif  // HAVE_SNAPPY
Kenton Varda's avatar
Kenton Varda committed
130 131 132

// =======================================================================================

133 134 135
struct NoScratch {
  struct ScratchSpace {};

136
  template <typename Compression>
Kenton Varda's avatar
Kenton Varda committed
137
  class MessageReader: public Compression::MessageReader {
138
  public:
139 140 141 142 143 144 145
    inline MessageReader(typename Compression::BufferedInput& input, ScratchSpace& scratch)
        : Compression::MessageReader(input) {}
  };

  template <typename Compression>
  class ArrayMessageReader: public Compression::ArrayMessageReader {
  public:
146
    inline ArrayMessageReader(kj::ArrayPtr<const byte> input, ScratchSpace& scratch)
147
        : Compression::ArrayMessageReader(input) {}
148 149 150 151 152 153
  };

  class MessageBuilder: public MallocMessageBuilder {
  public:
    inline MessageBuilder(ScratchSpace& scratch): MallocMessageBuilder() {}
  };
Kenton Varda's avatar
Kenton Varda committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

  class ObjectSizeCounter {
  public:
    ObjectSizeCounter(uint64_t iters): counter(0) {}

    template <typename RequestBuilder, typename ResponseBuilder>
    void add(RequestBuilder& request, ResponseBuilder& response) {
      for (auto segment: request.getSegmentsForOutput()) {
        counter += segment.size() * sizeof(word);
      }
      for (auto segment: response.getSegmentsForOutput()) {
        counter += segment.size() * sizeof(word);
      }
    }

    uint64_t get() { return counter; }

  private:
    uint64_t counter;
  };
174 175
};

Kenton Varda's avatar
Kenton Varda committed
176
constexpr size_t SCRATCH_SIZE = 128 * 1024;
177
word scratchSpace[6 * SCRATCH_SIZE];
Kenton Varda's avatar
Kenton Varda committed
178 179
int scratchCounter = 0;

180 181
struct UseScratch {
  struct ScratchSpace {
Kenton Varda's avatar
Kenton Varda committed
182 183 184
    word* words;

    ScratchSpace() {
185
      KJ_REQUIRE(scratchCounter < 6, "Too many scratch spaces needed at once.");
Kenton Varda's avatar
Kenton Varda committed
186 187
      words = scratchSpace + scratchCounter++ * SCRATCH_SIZE;
    }
188
    ~ScratchSpace() noexcept {
Kenton Varda's avatar
Kenton Varda committed
189 190
      --scratchCounter;
    }
191 192
  };

193
  template <typename Compression>
Kenton Varda's avatar
Kenton Varda committed
194
  class MessageReader: public Compression::MessageReader {
195
  public:
196 197
    inline MessageReader(typename Compression::BufferedInput& input, ScratchSpace& scratch)
        : Compression::MessageReader(
198
            input, ReaderOptions(), kj::arrayPtr(scratch.words, SCRATCH_SIZE)) {}
199 200 201 202 203
  };

  template <typename Compression>
  class ArrayMessageReader: public Compression::ArrayMessageReader {
  public:
204
    inline ArrayMessageReader(kj::ArrayPtr<const byte> input, ScratchSpace& scratch)
205
        : Compression::ArrayMessageReader(
206
            input, ReaderOptions(), kj::arrayPtr(scratch.words, SCRATCH_SIZE)) {}
207 208 209 210 211
  };

  class MessageBuilder: public MallocMessageBuilder {
  public:
    inline MessageBuilder(ScratchSpace& scratch)
212
        : MallocMessageBuilder(kj::arrayPtr(scratch.words, SCRATCH_SIZE)) {}
Kenton Varda's avatar
Kenton Varda committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
  };

  class ObjectSizeCounter {
  public:
    ObjectSizeCounter(uint64_t iters): iters(iters), maxSize(0) {}

    template <typename RequestBuilder, typename ResponseBuilder>
    void add(RequestBuilder& request, ResponseBuilder& response) {
      size_t counter = 0;
      for (auto segment: request.getSegmentsForOutput()) {
        counter += segment.size() * sizeof(word);
      }
      for (auto segment: response.getSegmentsForOutput()) {
        counter += segment.size() * sizeof(word);
      }
      maxSize = std::max(counter, maxSize);
    }

    uint64_t get() { return iters * maxSize; }

  private:
    uint64_t iters;
    size_t maxSize;
236 237 238 239 240
  };
};

// =======================================================================================

Kenton Varda's avatar
Kenton Varda committed
241
template <typename TestCase, typename ReuseStrategy, typename Compression>
242 243
struct BenchmarkMethods {
  static uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
244
    kj::FdInputStream inputStream(inputFd);
245 246
    typename Compression::BufferedInput bufferedInput(inputStream);

247
    CountingOutputStream output(outputFd);
248 249
    typename ReuseStrategy::ScratchSpace builderScratch;
    typename ReuseStrategy::ScratchSpace readerScratch;
250 251 252 253

    for (; iters > 0; --iters) {
      typename TestCase::Expectation expected;
      {
254
        typename ReuseStrategy::MessageBuilder builder(builderScratch);
255 256 257 258 259 260
        expected = TestCase::setupRequest(
            builder.template initRoot<typename TestCase::Request>());
        Compression::write(output, builder);
      }

      {
261 262
        typename ReuseStrategy::template MessageReader<Compression> reader(
            bufferedInput, readerScratch);
263 264 265 266 267 268
        if (!TestCase::checkResponse(
            reader.template getRoot<typename TestCase::Response>(), expected)) {
          throw std::logic_error("Incorrect response.");
        }
      }
    }
269

270 271 272 273 274 275 276 277 278 279
    return output.throughput;
  }

  static uint64_t asyncClientSender(
      int outputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
      uint64_t iters) {
    CountingOutputStream output(outputFd);
    typename ReuseStrategy::ScratchSpace scratch;

    for (; iters > 0; --iters) {
280
      typename ReuseStrategy::MessageBuilder builder(scratch);
281 282
      expectations->post(TestCase::setupRequest(
          builder.template initRoot<typename TestCase::Request>()));
Kenton Varda's avatar
Kenton Varda committed
283
      Compression::write(output, builder);
284
    }
285

286 287 288 289 290 291
    return output.throughput;
  }

  static void asyncClientReceiver(
      int inputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
      uint64_t iters) {
292
    kj::FdInputStream inputStream(inputFd);
293 294
    typename Compression::BufferedInput bufferedInput(inputStream);

295 296 297 298
    typename ReuseStrategy::ScratchSpace scratch;

    for (; iters > 0; --iters) {
      typename TestCase::Expectation expected = expectations->next();
299
      typename ReuseStrategy::template MessageReader<Compression> reader(bufferedInput, scratch);
300 301 302 303
      if (!TestCase::checkResponse(
          reader.template getRoot<typename TestCase::Response>(), expected)) {
        throw std::logic_error("Incorrect response.");
      }
304 305
    }
  }
Kenton Varda's avatar
Kenton Varda committed
306

307 308 309 310 311 312
  static uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
    ProducerConsumerQueue<typename TestCase::Expectation> expectations;
    std::thread receiverThread(asyncClientReceiver, inputFd, &expectations, iters);
    uint64_t throughput = asyncClientSender(outputFd, &expectations, iters);
    receiverThread.join();
    return throughput;
313
  }
Kenton Varda's avatar
Kenton Varda committed
314

315
  static uint64_t server(int inputFd, int outputFd, uint64_t iters) {
316
    kj::FdInputStream inputStream(inputFd);
317 318
    typename Compression::BufferedInput bufferedInput(inputStream);

319 320 321
    CountingOutputStream output(outputFd);
    typename ReuseStrategy::ScratchSpace builderScratch;
    typename ReuseStrategy::ScratchSpace readerScratch;
322

323 324
    for (; iters > 0; --iters) {
      typename ReuseStrategy::MessageBuilder builder(builderScratch);
325 326
      typename ReuseStrategy::template MessageReader<Compression> reader(
          bufferedInput, readerScratch);
327 328 329
      TestCase::handleRequest(reader.template getRoot<typename TestCase::Request>(),
                              builder.template initRoot<typename TestCase::Response>());
      Compression::write(output, builder);
330 331
    }

332
    return output.throughput;
333
  }
Kenton Varda's avatar
Kenton Varda committed
334

335 336 337
  static uint64_t passByObject(uint64_t iters, bool countObjectSize) {
    typename ReuseStrategy::ScratchSpace requestScratch;
    typename ReuseStrategy::ScratchSpace responseScratch;
338

339
    typename ReuseStrategy::ObjectSizeCounter counter(iters);
340

341 342 343 344
    for (; iters > 0; --iters) {
      typename ReuseStrategy::MessageBuilder requestMessage(requestScratch);
      auto request = requestMessage.template initRoot<typename TestCase::Request>();
      typename TestCase::Expectation expected = TestCase::setupRequest(request);
Kenton Varda's avatar
Kenton Varda committed
345

346 347 348
      typename ReuseStrategy::MessageBuilder responseMessage(responseScratch);
      auto response = responseMessage.template initRoot<typename TestCase::Response>();
      TestCase::handleRequest(request.asReader(), response);
349

350 351 352
      if (!TestCase::checkResponse(response.asReader(), expected)) {
        throw std::logic_error("Incorrect response.");
      }
353

354 355 356
      if (countObjectSize) {
        counter.add(requestMessage, responseMessage);
      }
357
    }
Kenton Varda's avatar
Kenton Varda committed
358

359
    return counter.get();
360
  }
Kenton Varda's avatar
Kenton Varda committed
361

362 363
  static uint64_t passByBytes(uint64_t iters) {
    uint64_t throughput = 0;
364 365 366 367 368 369
    typename ReuseStrategy::ScratchSpace clientRequestScratch;
    UseScratch::ScratchSpace requestBytesScratch;
    typename ReuseStrategy::ScratchSpace serverRequestScratch;
    typename ReuseStrategy::ScratchSpace serverResponseScratch;
    UseScratch::ScratchSpace responseBytesScratch;
    typename ReuseStrategy::ScratchSpace clientResponseScratch;
Kenton Varda's avatar
Kenton Varda committed
370

371
    for (; iters > 0; --iters) {
372
      typename ReuseStrategy::MessageBuilder requestBuilder(clientRequestScratch);
373 374
      typename TestCase::Expectation expected = TestCase::setupRequest(
          requestBuilder.template initRoot<typename TestCase::Request>());
375

376
      kj::ArrayOutputStream requestOutput(kj::arrayPtr(
377
          reinterpret_cast<byte*>(requestBytesScratch.words), SCRATCH_SIZE * sizeof(word)));
378 379 380 381 382 383
      Compression::write(requestOutput, requestBuilder);
      throughput += requestOutput.getArray().size();
      typename ReuseStrategy::template ArrayMessageReader<Compression> requestReader(
          requestOutput.getArray(), serverRequestScratch);

      typename ReuseStrategy::MessageBuilder responseBuilder(serverResponseScratch);
384 385
      TestCase::handleRequest(requestReader.template getRoot<typename TestCase::Request>(),
                              responseBuilder.template initRoot<typename TestCase::Response>());
386

387
      kj::ArrayOutputStream responseOutput(
388 389
          kj::arrayPtr(reinterpret_cast<byte*>(responseBytesScratch.words),
                       SCRATCH_SIZE * sizeof(word)));
390 391 392 393 394
      Compression::write(responseOutput, responseBuilder);
      throughput += responseOutput.getArray().size();
      typename ReuseStrategy::template ArrayMessageReader<Compression> responseReader(
          responseOutput.getArray(), clientResponseScratch);

395 396 397 398 399
      if (!TestCase::checkResponse(
          responseReader.template getRoot<typename TestCase::Response>(), expected)) {
        throw std::logic_error("Incorrect response.");
      }
     }
Kenton Varda's avatar
Kenton Varda committed
400 401

    return throughput;
402
  }
403
};
404

405 406
struct BenchmarkTypes {
  typedef capnp::Uncompressed Uncompressed;
407
  typedef capnp::Packed Packed;
408
#if HAVE_SNAPPY
409
  typedef capnp::SnappyCompressed SnappyCompressed;
410
#endif  // HAVE_SNAPPY
411

412 413
  typedef capnp::UseScratch ReusableResources;
  typedef capnp::NoScratch SingleUseResources;
Kenton Varda's avatar
Kenton Varda committed
414

415 416 417
  template <typename TestCase, typename ReuseStrategy, typename Compression>
  struct BenchmarkMethods: public capnp::BenchmarkMethods<TestCase, ReuseStrategy, Compression> {};
};
418

419
}  // namespace capnp
420
}  // namespace benchmark
421
}  // namespace capnp
422

Kenton Varda's avatar
Kenton Varda committed
423
#endif  // CAPNP_BENCHMARK_CAPNP_COMMON_H_