You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by jd...@apache.org on 2020/05/12 11:52:54 UTC
[qpid-proton] branch master updated: PROTON-220: Google Benchmark
proton-c and proton-cpp microbenchmarks (#211)
This is an automated email from the ASF dual-hosted git repository.
jdanek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git
The following commit(s) were added to refs/heads/master by this push:
new 7abcfc7 PROTON-220: Google Benchmark proton-c and proton-cpp microbenchmarks (#211)
7abcfc7 is described below
commit 7abcfc7aba6cfd1c938324cf34a51555c845a589
Author: Jirka Daněk <dn...@redhat.com>
AuthorDate: Tue May 12 13:52:43 2020 +0200
PROTON-220: Google Benchmark proton-c and proton-cpp microbenchmarks (#211)
---
.travis.yml | 11 +-
CMakeLists.txt | 6 +-
c/CMakeLists.txt | 4 +
c/benchmarks/CMakeLists.txt | 33 +++
c/benchmarks/README.md | 26 ++
c/benchmarks/benchmarks_main.cpp | 3 +
c/benchmarks/connection-driver.cpp | 487 +++++++++++++++++++++++++++++++++
c/benchmarks/message-encoding_list.cpp | 134 +++++++++
c/benchmarks/message-encoding_map.cpp | 164 +++++++++++
cpp/CMakeLists.txt | 4 +
cpp/benchmarks/CMakeLists.txt | 33 +++
cpp/benchmarks/README.md | 3 +
cpp/benchmarks/benchmarks_main.cpp | 3 +
cpp/benchmarks/container.cpp | 60 ++++
14 files changed, 969 insertions(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index e722316..bfc0d82 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,6 +29,15 @@ matrix:
include:
- os: linux
dist: bionic
+ sudo: true
+ compiler:
+ - gcc
+ env:
+ - QPID_PROTON_CMAKE_ARGS='-DENABLE_BENCHMARKS=ON -DRUNTIME_CHECK=OFF'
+ before_install:
+ - sudo apt-get install -y libbenchmark-dev
+ - os: linux
+ dist: bionic
compiler: gcc
env:
# python-tox-test fails and ruby tests segfault
@@ -96,7 +105,7 @@ addons:
- swig
# update:true workaround as of Feb. 22/20 as per
# https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296/10
- update: true
+ update: true
install:
- python -m pip install --user --upgrade pip
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f36ad5..c596219 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -116,9 +116,12 @@ if (APPLE)
set (NOENABLE_WARNING_ERROR ON)
endif (APPLE)
+# Benchmarks should be disabled by default
+set (NOENABLE_BENCHMARKS ON)
+
# Add options here called <whatever> they will turn into "ENABLE_<whatever" and can be
# overridden on a platform specific basis above by NOENABLE_<whatever>
-set (OPTIONS WARNING_ERROR UNDEFINED_ERROR LINKTIME_OPTIMIZATION HIDE_UNEXPORTED_SYMBOLS FUZZ_TESTING)
+set (OPTIONS WARNING_ERROR UNDEFINED_ERROR LINKTIME_OPTIMIZATION HIDE_UNEXPORTED_SYMBOLS FUZZ_TESTING BENCHMARKS)
foreach (OPTION ${OPTIONS})
if (NOT NOENABLE_${OPTION})
@@ -132,6 +135,7 @@ option(ENABLE_UNDEFINED_ERROR "Check for unresolved library symbols" ${DEFAULT_U
option(ENABLE_LINKTIME_OPTIMIZATION "Perform link time optimization" ${DEFAULT_LINKTIME_OPTIMIZATION})
option(ENABLE_HIDE_UNEXPORTED_SYMBOLS "Only export library symbols that are explicitly requested" ${DEFAULT_HIDE_UNEXPORTED_SYMBOLS})
option(ENABLE_FUZZ_TESTING "Enable building fuzzers and regression testing with libFuzzer" ${DEFAULT_FUZZ_TESTING})
+option(ENABLE_BENCHMARKS "Enable building and running benchmarks with Google Benchmark" ${DEFAULT_BENCHMARKS})
# Set any additional compiler specific flags
if (CMAKE_COMPILER_IS_GNUCC)
diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt
index d2e2888..44a9745 100644
--- a/c/CMakeLists.txt
+++ b/c/CMakeLists.txt
@@ -576,6 +576,10 @@ install (FILES
${CMAKE_CURRENT_BINARY_DIR}/ProtonConfigVersion.cmake
DESTINATION ${LIB_INSTALL_DIR}/cmake/Proton)
+if (ENABLE_BENCHMARKS)
+ add_subdirectory(benchmarks)
+endif (ENABLE_BENCHMARKS)
+
add_subdirectory(docs)
add_subdirectory(examples)
add_subdirectory(tests)
diff --git a/c/benchmarks/CMakeLists.txt b/c/benchmarks/CMakeLists.txt
new file mode 100644
index 0000000..e2c2a62
--- /dev/null
+++ b/c/benchmarks/CMakeLists.txt
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+find_package(benchmark REQUIRED)
+
+# incompatibility in older libbenchmark, https://github.com/google/benchmark/issues/494#issuecomment-502444478
+get_source_file_property(COMPILE_FLAGS benchmarks_main.cpp current_compile_flags)
+set_source_files_properties(benchmarks_main.cpp PROPERTIES COMPILE_FLAGS "${current_compile_flags} -Wno-pedantic")
+
+add_executable(c-benchmarks benchmarks_main.cpp connection-driver.cpp message-encoding_list.cpp message-encoding_map.cpp)
+target_link_libraries(c-benchmarks benchmark pthread qpid-proton)
+
+add_test(NAME c-benchmarks COMMAND c-benchmarks)
diff --git a/c/benchmarks/README.md b/c/benchmarks/README.md
new file mode 100644
index 0000000..91caab4
--- /dev/null
+++ b/c/benchmarks/README.md
@@ -0,0 +1,26 @@
+# Microbenchmarks
+
+## Compiling
+
+ -DENABLE_BENCHMARKS
+
+## Running
+
+Suppress CPU frequency scaling
+
+ # https://www.kernel.org/doc/html/latest/admin-guide/pm/intel_pstate.html
+ echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
+
+Maybe reserve specific cores for the benchmark and and possibly also switch off Multi Threading (on Intel).
+This makes the results less noisy.
+
+ # https://llvm.org/docs/Benchmarking.html
+
+Let Google Benchmark compute average, median, stddev of time from multiple executions
+
+ --benchmark_repetitions=9
+
+## Profiling
+
+ sudo sh -c 'echo 1 > /proc/sys/kernel/perf_event_paranoid'
+ sudo sh -c 'echo 0 > /proc/sys/kernel/kptr_restrict'
diff --git a/c/benchmarks/benchmarks_main.cpp b/c/benchmarks/benchmarks_main.cpp
new file mode 100644
index 0000000..71fefa0
--- /dev/null
+++ b/c/benchmarks/benchmarks_main.cpp
@@ -0,0 +1,3 @@
+#include <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/c/benchmarks/connection-driver.cpp b/c/benchmarks/connection-driver.cpp
new file mode 100644
index 0000000..e2fdad1
--- /dev/null
+++ b/c/benchmarks/connection-driver.cpp
@@ -0,0 +1,487 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wait.h>
+
+#include <benchmark/benchmark.h>
+
+#include "proton/connection_driver.h"
+#include "proton/engine.h"
+#include "proton/log.h"
+#include "proton/message.h"
+#include "proton/transport.h"
+#include <proton/listener.h>
+#include <proton/netaddr.h>
+#include <proton/proactor.h>
+#include <proton/sasl.h>
+
+
+// variant of the receive.c proactor example
+// borrowed things from the quiver arrow, too
+
+#define MAX_SIZE 1024
+
+typedef char str[MAX_SIZE];
+
+typedef struct app_data_t {
+ str container_id;
+ pn_rwbytes_t message_buffer{};
+ int message_count;
+ int received = 0;
+ pn_message_t *message;
+ int sent = 0;
+ pn_rwbytes_t msgout;
+ int credit_window = 5000;
+ int acknowledged = 0;
+ int closed = 0;
+} app_data_t;
+
+static void decode_message(pn_delivery_t *dlv);
+static void check_condition(pn_event_t *e, pn_condition_t *cond);
+static void shovel(pn_connection_driver_t &sender, pn_connection_driver_t &receiver);
+
+static void handle_receiver(app_data_t *app, pn_event_t *event);
+static void handle_sender(app_data_t *app, pn_event_t *event);
+
+const bool VERBOSE = false;
+const bool ERRORS = true;
+
+// useful for debugging
+static void turn_on_transport_logging(pn_transport_t& transport) {
+ pn_log_enable(true);
+ pn_transport_trace(&transport,
+ PN_TRACE_FRM | PN_TRACE_EVT);
+}
+
+static void BM_InitCloseSender(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_InitCloseSender\n");
+
+ for (auto _ : state) {
+ pn_connection_driver_t sender;
+ if (pn_connection_driver_init(&sender, NULL, NULL) != 0) {
+ printf("sender: pn_connection_driver_init failed\n");
+ exit(1);
+ }
+
+ pn_connection_driver_close(&sender);
+ pn_connection_driver_destroy(&sender);
+ }
+
+ if (VERBOSE)
+ printf("END BM_InitCloseSender\n");
+}
+
+BENCHMARK(BM_InitCloseSender)->Unit(benchmark::kMicrosecond);
+
+static void BM_InitCloseReceiver(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_InitCloseReceiver\n");
+
+ for (auto _ : state) {
+ pn_connection_driver_t receiver;
+ if (pn_connection_driver_init(&receiver, NULL, NULL) != 0) {
+ printf("receiver: pn_connection_driver_init failed\n");
+ exit(1);
+ }
+
+ pn_connection_driver_close(&receiver);
+ pn_connection_driver_destroy(&receiver);
+ }
+
+ if (VERBOSE)
+ printf("END BM_InitCloseReceiver\n");
+}
+
+BENCHMARK(BM_InitCloseReceiver)->Unit(benchmark::kMicrosecond);
+
+// sends single message and closes the connection
+static void BM_EstablishConnection(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_EstablishConnection\n");
+
+ for (auto _ : state) {
+ app_data_t app = {};
+ app.message_count = 1;
+ app.message = pn_message();
+ sprintf(app.container_id, "%s:%06x", "BM_EstablishConnection",
+ rand() & 0xffffff);
+
+ pn_connection_driver_t receiver;
+ if (pn_connection_driver_init(&receiver, NULL, NULL) != 0) {
+ printf("receiver: pn_connection_driver_init failed\n");
+ exit(1);
+ }
+
+ pn_connection_driver_t sender;
+ if (pn_connection_driver_init(&sender, NULL, NULL) != 0) {
+ printf("sender: pn_connection_driver_init failed\n");
+ exit(1);
+ }
+
+ do {
+ pn_event_t *event;
+ while ((event = pn_connection_driver_next_event(&sender)) != NULL) {
+ handle_sender(&app, event);
+ }
+ shovel(sender, receiver);
+ while ((event = pn_connection_driver_next_event(&receiver)) != NULL) {
+ handle_receiver(&app, event);
+ }
+ shovel(receiver, sender);
+ } while (app.closed < 2); // until both sender and receiver are closed
+
+ pn_message_free(app.message);
+
+ pn_connection_driver_close(&receiver);
+ pn_connection_driver_close(&sender);
+
+ shovel(receiver, sender);
+ shovel(sender, receiver);
+
+ // this can take long time, up to 500 ms
+ pn_connection_driver_destroy(&receiver);
+ pn_connection_driver_destroy(&sender);
+ }
+
+ state.SetLabel("connections");
+ state.SetItemsProcessed(state.iterations());
+
+ if (VERBOSE)
+ printf("END BM_EstablishConnection\n");
+}
+
+BENCHMARK(BM_EstablishConnection)->Unit(benchmark::kMicrosecond);
+
+static void BM_SendReceiveMessages(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_SendReceiveMessages\n");
+
+ app_data_t app = {};
+ app.message_count = -1; // unlimited
+ app.message = pn_message();
+ app.credit_window = state.range(0);
+ sprintf(app.container_id, "%s:%06x", "BM_SendReceiveMessages",
+ rand() & 0xffffff);
+
+ pn_connection_driver_t receiver;
+ if (pn_connection_driver_init(&receiver, NULL, NULL) != 0) {
+ printf("receiver: pn_connection_driver_init failed\n");
+ exit(1);
+ }
+
+ pn_connection_driver_t sender;
+ if (pn_connection_driver_init(&sender, NULL, NULL) != 0) {
+ printf("sender: pn_connection_driver_init failed\n");
+ exit(1);
+ }
+
+ for (auto _ : state) {
+ pn_event_t *event;
+ while ((event = pn_connection_driver_next_event(&sender)) != NULL) {
+ handle_sender(&app, event);
+ }
+ shovel(sender, receiver);
+ while ((event = pn_connection_driver_next_event(&receiver)) != NULL) {
+ handle_receiver(&app, event);
+ }
+ shovel(receiver, sender);
+ }
+
+ pn_connection_driver_close(&receiver);
+ pn_connection_driver_close(&sender);
+
+ shovel(receiver, sender);
+ shovel(sender, receiver);
+
+ // this can take long time, up to 500 ms
+ pn_connection_driver_destroy(&receiver);
+ pn_connection_driver_destroy(&sender);
+
+ state.SetLabel("messages");
+ state.SetItemsProcessed(app.acknowledged);
+
+ if (VERBOSE)
+ printf("END BM_SendReceiveMessages\n");
+}
+
+BENCHMARK(BM_SendReceiveMessages)
+ ->RangeMultiplier(3)
+ ->Range(1, 200000)
+ ->ArgName("creditWindow")
+ ->Arg(1000)
+ ->Unit(benchmark::kMillisecond);
+
+
+///* Create a message with a map { "sequence" : number } encode it and return
+/// the encoded buffer. */
+static void send_message(app_data_t *app, pn_link_t *sender) {
+ /* Construct a message with the map { "sequence": 42 } */
+ pn_data_t *body;
+ pn_message_clear(app->message);
+ body = pn_message_body(app->message);
+ pn_data_put_int(pn_message_id(app->message),
+ app->sent); /* Set the message_id also */
+ pn_data_put_map(body);
+ pn_data_enter(body);
+ pn_data_put_string(body, pn_bytes(sizeof("sequence") - 1, "sequence"));
+ pn_data_put_int(body, 42); /* The sequence number */
+ pn_data_exit(body);
+ if (pn_message_send(app->message, sender, &app->message_buffer) < 0) {
+ fprintf(stderr, "error sending message: %s\n",
+ pn_error_text(pn_message_error(app->message)));
+ exit(1);
+ }
+}
+
+static void handle_sender(app_data_t *app, pn_event_t *event) {
+ switch (pn_event_type(event)) {
+
+ case PN_CONNECTION_INIT: {
+ pn_connection_t *c = pn_event_connection(event);
+ pn_connection_set_container(c, "sendercid");
+ pn_connection_open(c);
+ pn_session_t *s = pn_session(c);
+ pn_session_open(s);
+
+ pn_link_t *l = pn_sender(s, "my_sender");
+ pn_terminus_set_address(pn_link_target(l), "example");
+ pn_link_set_snd_settle_mode(l, PN_SND_UNSETTLED);
+ pn_link_set_rcv_settle_mode(l, PN_RCV_FIRST);
+ pn_link_open(l);
+ } break;
+
+ case PN_LINK_FLOW: {
+ if (VERBOSE)
+ printf("BEGIN handle_sender: PN_LINK_FLOW\n");
+ /* The peer has given us some credit, now we can send messages */
+ pn_link_t *sender = pn_event_link(event);
+ while (pn_link_credit(sender) > 0 && app->sent != app->message_count) {
+ ++app->sent;
+ /* Use sent counter as unique delivery tag. */
+ pn_delivery(sender, pn_dtag((const char *)&app->sent, sizeof(app->sent)));
+ send_message(app, sender);
+ }
+ break;
+ }
+
+ case PN_DELIVERY: {
+ /* We received acknowledgement from the peer that a message was delivered. */
+ pn_delivery_t *d = pn_event_delivery(event);
+ if (pn_delivery_remote_state(d) == PN_ACCEPTED) {
+ if (VERBOSE)
+ printf("got PN_ACCEPTED\n");
+ if (++app->acknowledged == app->message_count) {
+ if (VERBOSE)
+ printf("%d messages sent and acknowledged\n", app->acknowledged);
+ pn_connection_close(pn_event_connection(event));
+ /* Continue handling events till we receive TRANSPORT_CLOSED */
+ }
+ } else {
+ fprintf(stderr, "unexpected delivery state %d\n", (int) pn_delivery_remote_state(d));
+ pn_connection_close(pn_event_connection(event));
+ exit(EXIT_FAILURE);
+ }
+ break;
+ }
+
+ case PN_CONNECTION_REMOTE_OPEN:
+ pn_connection_open(pn_event_connection(event)); /* Complete the open */
+ break;
+
+ case PN_SESSION_REMOTE_OPEN:
+ pn_session_open(pn_event_session(event));
+ break;
+
+ case PN_TRANSPORT_ERROR:
+ check_condition(event, pn_transport_condition(pn_event_transport(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_CONNECTION_REMOTE_CLOSE:
+ check_condition(event,
+ pn_connection_remote_condition(pn_event_connection(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_SESSION_REMOTE_CLOSE:
+ check_condition(event,
+ pn_session_remote_condition(pn_event_session(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_LINK_REMOTE_CLOSE:
+ case PN_LINK_REMOTE_DETACH:
+ check_condition(event, pn_link_remote_condition(pn_event_link(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_TRANSPORT_CLOSED:
+ app->closed++;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void handle_receiver(app_data_t *app, pn_event_t *event) {
+ switch (pn_event_type(event)) {
+
+ case PN_CONNECTION_INIT: {
+ pn_connection_t *c = pn_event_connection(event);
+ pn_connection_set_container(c, app->container_id);
+ // pn_connection_open(c);
+ pn_session_t *s = pn_session(c);
+ pn_session_open(s);
+
+ pn_link_t *l = pn_receiver(s, "my_receiver");
+ pn_terminus_set_address(pn_link_source(l), "example");
+ pn_link_open(l);
+ } break;
+
+ case PN_LINK_REMOTE_OPEN: {
+ pn_link_t *l = pn_event_link(event);
+ pn_terminus_t *t = pn_link_target(l);
+ pn_terminus_t *rt = pn_link_remote_target(l);
+ pn_terminus_set_address(t, pn_terminus_get_address(rt));
+ pn_link_open(l);
+ if (pn_link_is_receiver(l)) {
+ pn_link_flow(l, app->credit_window);
+ }
+ break;
+ }
+
+ case PN_DELIVERY: {
+ /* A message has been received */
+ pn_link_t *link = NULL;
+ pn_delivery_t *dlv = pn_event_delivery(event);
+ if (pn_delivery_readable(dlv) && !pn_delivery_partial(dlv)) {
+ link = pn_delivery_link(dlv);
+ decode_message(dlv);
+ /* Accept the delivery */
+ pn_delivery_update(dlv, PN_ACCEPTED);
+ /* done with the delivery, move to the next and free it */
+ pn_link_advance(link);
+ pn_delivery_settle(dlv); /* dlv is now freed */
+ }
+ pn_link_flow(link, app->credit_window - pn_link_credit(link));
+ } break;
+
+ case PN_CONNECTION_REMOTE_OPEN:
+ pn_connection_open(pn_event_connection(event)); /* Complete the open */
+ break;
+
+ case PN_SESSION_REMOTE_OPEN:
+ pn_session_open(pn_event_session(event));
+ break;
+
+ case PN_TRANSPORT_ERROR:
+ check_condition(event, pn_transport_condition(pn_event_transport(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_CONNECTION_REMOTE_CLOSE:
+ check_condition(event,
+ pn_connection_remote_condition(pn_event_connection(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_SESSION_REMOTE_CLOSE:
+ check_condition(event,
+ pn_session_remote_condition(pn_event_session(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_LINK_REMOTE_CLOSE:
+ case PN_LINK_REMOTE_DETACH:
+ check_condition(event, pn_link_remote_condition(pn_event_link(event)));
+ pn_connection_close(pn_event_connection(event));
+ break;
+
+ case PN_TRANSPORT_CLOSED:
+ app->closed++;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void check_condition(pn_event_t *e, pn_condition_t *cond) {
+ if (VERBOSE)
+ printf("beginning check_condition\n");
+ if (pn_condition_is_set(cond)) {
+ if (VERBOSE || ERRORS)
+ fprintf(stderr, "%s: %s: %s\n", pn_event_type_name(pn_event_type(e)),
+ pn_condition_get_name(cond), pn_condition_get_description(cond));
+ }
+}
+
+///* Copies output from first connection driver into input of the second. */
+static void shovel(pn_connection_driver_t &sender, pn_connection_driver_t &receiver) {
+ pn_bytes_t wbuf = pn_connection_driver_write_buffer(&receiver);
+ if (wbuf.size == 0) {
+ pn_connection_driver_write_done(&receiver, 0);
+ return;
+ }
+
+ pn_rwbytes_t rbuf = pn_connection_driver_read_buffer(&sender);
+ if (rbuf.start == NULL) {
+ printf("shovel: rbuf.start is null\n");
+ fflush(stdout);
+ fflush(stderr);
+ exit(1);
+ }
+
+ size_t s = rbuf.size < wbuf.size ? rbuf.size : wbuf.size;
+ memcpy(rbuf.start, wbuf.start, s);
+
+ pn_connection_driver_read_done(&sender, s);
+ pn_connection_driver_write_done(&receiver, s);
+}
+
+static void decode_message(pn_delivery_t *dlv) {
+ static char buffer[MAX_SIZE];
+ ssize_t len;
+ // try to decode the message body
+ if (pn_delivery_pending(dlv) < MAX_SIZE) {
+ // read in the raw data
+ len = pn_link_recv(pn_delivery_link(dlv), buffer, MAX_SIZE);
+ if (len > 0) {
+ // decode it into a proton message
+ pn_message_t *m = pn_message();
+ if (PN_OK == pn_message_decode(m, buffer, len)) {
+ pn_string_t *s = pn_string(NULL);
+ pn_inspect(pn_message_body(m), s);
+ if (VERBOSE)
+ printf("%s\n", pn_string_get(s));
+ pn_free(s);
+ }
+ pn_message_free(m);
+ }
+ }
+}
diff --git a/c/benchmarks/message-encoding_list.cpp b/c/benchmarks/message-encoding_list.cpp
new file mode 100644
index 0000000..51e8ceb
--- /dev/null
+++ b/c/benchmarks/message-encoding_list.cpp
@@ -0,0 +1,134 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <unistd.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "proton/connection_driver.h"
+#include "proton/engine.h"
+#include "proton/log.h"
+#include "proton/message.h"
+
+#include <benchmark/benchmark.h>
+#include <proton/listener.h>
+#include <proton/netaddr.h>
+#include <proton/proactor.h>
+#include <proton/sasl.h>
+#include <wait.h>
+
+const bool VERBOSE = false;
+const bool ERRORS = true;
+
+static void decode_message_buffer(pn_rwbytes_t data) {
+ pn_message_t *m = pn_message();
+ int err = pn_message_decode(m, data.start, data.size);
+ if (!err) {
+ /* Print the decoded message */
+ pn_string_t *s = pn_string(NULL);
+ pn_inspect(pn_message_body(m), s);
+ if (VERBOSE) {
+ printf("%s\n", pn_string_get(s));
+ fflush(stdout);
+ }
+ pn_free(s);
+ pn_message_free(m);
+ free(data.start);
+ } else {
+ fprintf(stderr, "decode error: %s\n", pn_error_text(pn_message_error(m)));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void BM_EncodeListMessage(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_EncodeListMessage\n");
+
+ uint64_t entries = 0;
+
+ pn_message_t *message = pn_message();
+ for (auto _ : state) {
+ pn_message_clear(message);
+ pn_data_t *body;
+ body = pn_message_body(message);
+ pn_data_put_list(body);
+ pn_data_enter(body);
+ for (size_t i = 0; i < state.range(0); i += 2) {
+ pn_data_put_string(
+ body, pn_bytes(sizeof("some list value") - 1, "some list value"));
+ pn_data_put_int(body, 42);
+ entries += 2;
+ }
+ pn_data_exit(body);
+ pn_rwbytes_t buf{};
+ if (pn_message_encode2(message, &buf) == 0) {
+ state.SkipWithError(pn_error_text(pn_message_error(message)));
+ }
+ if (buf.start)
+ free(buf.start);
+ }
+ pn_message_free(message);
+
+ state.SetLabel("entries");
+ state.SetItemsProcessed(entries);
+
+ if (VERBOSE)
+ printf("END BM_EncodeListMessage\n");
+}
+
+BENCHMARK(BM_EncodeListMessage)
+ ->Arg(10)
+ ->Arg(100)
+ ->Arg(1000)
+ ->Arg(10000)
+ ->ArgName("entries")
+ ->Unit(benchmark::kMillisecond);
+
+static void BM_CreateListMessage(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_CreateListMessage\n");
+
+ uint64_t entries = 0;
+
+ pn_message_t *message = pn_message();
+ pn_data_t *body;
+ body = pn_message_body(message);
+ pn_data_put_list(body);
+ pn_data_enter(body);
+ for (auto _ : state) {
+ pn_data_put_string(
+ body, pn_bytes(sizeof("some list value") - 1, "some list value"));
+ pn_data_put_int(body, 42);
+ entries += 2;
+ }
+ pn_data_exit(body);
+ pn_message_free(message);
+
+ state.SetLabel("entries");
+ state.SetItemsProcessed(entries);
+
+ if (VERBOSE)
+ printf("END BM_CreateListMessage\n");
+}
+
+BENCHMARK(BM_CreateListMessage);
diff --git a/c/benchmarks/message-encoding_map.cpp b/c/benchmarks/message-encoding_map.cpp
new file mode 100644
index 0000000..86f8e1d
--- /dev/null
+++ b/c/benchmarks/message-encoding_map.cpp
@@ -0,0 +1,164 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <unistd.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "proton/connection_driver.h"
+#include "proton/engine.h"
+#include "proton/log.h"
+#include "proton/message.h"
+
+#include <benchmark/benchmark.h>
+#include <proton/listener.h>
+#include <proton/netaddr.h>
+#include <proton/proactor.h>
+#include <proton/sasl.h>
+#include <wait.h>
+
+#define MAX_SIZE 1024
+
+typedef char str[MAX_SIZE];
+
+const bool VERBOSE = false;
+const bool ERRORS = true;
+
+static void decode_message_buffer(pn_rwbytes_t data) {
+ pn_message_t *m = pn_message();
+ int err = pn_message_decode(m, data.start, data.size);
+ if (!err) {
+ /* Print the decoded message */
+ pn_string_t *s = pn_string(NULL);
+ pn_inspect(pn_message_body(m), s);
+ if (VERBOSE) {
+ printf("%s\n", pn_string_get(s));
+ fflush(stdout);
+ }
+ pn_free(s);
+ pn_message_free(m);
+ free(data.start);
+ } else {
+ fprintf(stderr, "decode error: %s\n", pn_error_text(pn_message_error(m)));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void BM_EncodeMapMessage(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_EncodeMapMessage\n");
+
+ uint64_t entries = 0;
+
+ pn_message_t *message = pn_message();
+
+ for (auto _ : state) {
+ pn_message_clear(message);
+ pn_data_t *body;
+ body = pn_message_body(message);
+ pn_data_put_map(body);
+ pn_data_enter(body);
+ for (size_t i = 0; i < state.range(0); ++i) {
+ pn_data_put_int(body, i);
+ pn_data_put_string(
+ body, pn_bytes(sizeof("some key value") - 1, "some key value"));
+ entries += 2;
+ }
+ pn_data_exit(body);
+ pn_rwbytes_t buf{};
+ if (pn_message_encode2(message, &buf) == 0) {
+ state.SkipWithError(pn_error_text(pn_message_error(message)));
+ }
+ if (buf.start)
+ free(buf.start);
+ }
+ pn_message_free(message);
+
+ state.SetLabel("entries");
+ state.SetItemsProcessed(entries);
+
+ if (VERBOSE)
+ printf("END BM_EncodeMapMessage\n");
+}
+
+BENCHMARK(BM_EncodeMapMessage)
+ ->Arg(10)
+ ->Arg(100)
+ ->Arg(1000)
+ ->Arg(10000)
+ ->ArgName("entries")
+ ->Unit(benchmark::kMillisecond);
+
+static void BM_CreateMapMessage(benchmark::State &state) {
+ if (VERBOSE)
+ printf("BEGIN BM_CreateMapMessage\n");
+
+ uint64_t entries = 0;
+
+ pn_message_t *message = pn_message();
+
+ pn_message_clear(message);
+ pn_data_t *body;
+ body = pn_message_body(message);
+ pn_data_put_map(body);
+ pn_data_enter(body);
+ for (auto _ : state) {
+ pn_data_put_int(body, entries);
+ pn_data_put_string(
+ body, pn_bytes(sizeof("some key value") - 1, "some key value"));
+ entries += 2;
+ }
+ pn_data_exit(body);
+ pn_message_free(message);
+
+ state.SetLabel("entries");
+ state.SetItemsProcessed(entries);
+
+ if (VERBOSE)
+ printf("END BM_CreateMapMessage\n");
+}
+
+BENCHMARK(BM_CreateMapMessage);
+
+
+static void decode_message(pn_delivery_t *dlv) {
+ static char buffer[MAX_SIZE];
+ ssize_t len;
+ // try to decode the message body
+ if (pn_delivery_pending(dlv) < MAX_SIZE) {
+ // read in the raw data
+ len = pn_link_recv(pn_delivery_link(dlv), buffer, MAX_SIZE);
+ if (len > 0) {
+ // decode it into a proton message
+ pn_message_t *m = pn_message();
+ if (PN_OK == pn_message_decode(m, buffer, len)) {
+ pn_string_t *s = pn_string(NULL);
+ pn_inspect(pn_message_body(m), s);
+ if (VERBOSE)
+ printf("%s\n", pn_string_get(s));
+ pn_free(s);
+ }
+ pn_message_free(m);
+ }
+ }
+}
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 2b5730e..59b6a01 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -331,3 +331,7 @@ endmacro(add_core_catch_test)
add_catch_test(url)
add_core_catch_test(object)
+
+if (ENABLE_BENCHMARKS)
+ add_subdirectory(benchmarks)
+endif (ENABLE_BENCHMARKS)
diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt
new file mode 100644
index 0000000..63a5bd7
--- /dev/null
+++ b/cpp/benchmarks/CMakeLists.txt
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+find_package(benchmark REQUIRED)
+
+# incompatibility in older libbenchmark, https://github.com/google/benchmark/issues/494#issuecomment-502444478
+get_source_file_property(COMPILE_FLAGS benchmarks_main.cpp current_compile_flags)
+set_source_files_properties(benchmarks_main.cpp PROPERTIES COMPILE_FLAGS "${current_compile_flags} -Wno-pedantic")
+
+add_executable(cpp-benchmarks benchmarks_main.cpp container.cpp)
+target_link_libraries(cpp-benchmarks benchmark pthread qpid-proton-cpp)
+
+add_test(NAME cpp-benchmarks COMMAND cpp-benchmarks)
diff --git a/cpp/benchmarks/README.md b/cpp/benchmarks/README.md
new file mode 100644
index 0000000..4ef5306
--- /dev/null
+++ b/cpp/benchmarks/README.md
@@ -0,0 +1,3 @@
+# Microbenchmarks
+
+See c/benchmarks for instructions
\ No newline at end of file
diff --git a/cpp/benchmarks/benchmarks_main.cpp b/cpp/benchmarks/benchmarks_main.cpp
new file mode 100644
index 0000000..71fefa0
--- /dev/null
+++ b/cpp/benchmarks/benchmarks_main.cpp
@@ -0,0 +1,3 @@
+#include <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/cpp/benchmarks/container.cpp b/cpp/benchmarks/container.cpp
new file mode 100644
index 0000000..21a9ba3
--- /dev/null
+++ b/cpp/benchmarks/container.cpp
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 <iostream>
+
+#include <benchmark/benchmark.h>
+
+#include <proton/connection_options.hpp>
+#include <proton/container.hpp>
+#include <proton/messaging_handler.hpp>
+
+// PROTON-2137 [cpp] Performance regression found in 0.29.0
+// proton would spend long time initializing ssl when not needed
+
+class handler : public proton::messaging_handler {
+ std::string url;
+
+public:
+ explicit handler(const std::string &u) : url(u) {}
+
+private:
+ void on_container_start(proton::container &c) override {
+ c.connect(url, proton::connection_options().sasl_enabled(false));
+ }
+
+ void on_connection_open(proton::connection &c) override { c.close(); }
+};
+
+// expected to run in < 1 ms, for me it is 160 us
+static void BM_UnsuccessfulContainerRun(benchmark::State &state) {
+ for (auto _ : state) {
+ try {
+ handler h("127.0.0.1:0"); // wrong port
+ proton::container(h).run();
+ state.SkipWithError("Exception expected");
+ } catch (std::exception &e) {
+ // expected
+ }
+ }
+}
+
+BENCHMARK(BM_UnsuccessfulContainerRun)->Unit(benchmark::kMicrosecond);
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org