adding MQ benchmark
authorChristian Grothoff <christian@grothoff.org>
Tue, 4 Feb 2020 18:11:40 +0000 (19:11 +0100)
committerChristian Grothoff <christian@grothoff.org>
Tue, 4 Feb 2020 18:11:40 +0000 (19:11 +0100)
src/util/Makefile.am
src/util/perf_mq.c [new file with mode: 0644]

index 60b845414c5432c59bb30ee807c66013982c6a71..ffe95a24f51db468c84de2a7144be4a757411fa8 100644 (file)
@@ -246,6 +246,7 @@ if HAVE_BENCHMARKS
   perf_crypto_symmetric \
   perf_crypto_asymmetric \
   perf_malloc \
+  perf_mq \
   perf_scheduler
 endif
 
@@ -609,6 +610,11 @@ perf_malloc_SOURCES = \
 perf_malloc_LDADD = \
  libgnunetutil.la
 
+perf_mq_SOURCES = \
+ perf_mq.c
+perf_mq_LDADD = \
+ libgnunetutil.la
+
 perf_scheduler_SOURCES = \
  perf_scheduler.c
 perf_scheduler_LDADD = \
diff --git a/src/util/perf_mq.c b/src/util/perf_mq.c
new file mode 100644 (file)
index 0000000..f6e3d78
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2018, 2020 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file util/perf_mq.c
+ * @brief benchmark for mq
+ * @author Florian Dold
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include <gauger.h>
+
+#define NUM_TRANSMISSIONS 1000000
+
+/**
+ * How long does the receiver take per message?
+ */
+#define RECEIVER_THROTTLE GNUNET_TIME_relative_multiply ( \
+    GNUNET_TIME_UNIT_MILLISECONDS, 1)
+
+static unsigned int received_cnt;
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct MyMessage
+{
+  struct GNUNET_MessageHeader header;
+  uint32_t x GNUNET_PACKED;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+static int global_ret;
+
+static struct GNUNET_SCHEDULER_Task *task;
+
+static struct GNUNET_MQ_Handle *cmq;
+
+
+static void
+do_shutdown (void *cls)
+{
+  (void) cls;
+  if (NULL != task)
+  {
+    GNUNET_SCHEDULER_cancel (task);
+    task = NULL;
+  }
+  if (NULL != cmq)
+  {
+    GNUNET_MQ_destroy (cmq);
+    cmq = NULL;
+  }
+}
+
+
+/**
+ * Generic error handler, called with the appropriate
+ * error code and the same closure specified at the creation of
+ * the message queue.
+ * Not every message queue implementation supports an error handler.
+ *
+ * @param cls closure
+ * @param error error code
+ */
+static void
+error_cb (void *cls,
+          enum GNUNET_MQ_Error error)
+{
+  GNUNET_break (0);
+  global_ret = 3;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+handle_dummy (void *cls,
+              const struct MyMessage *msg)
+{
+  struct GNUNET_SERVICE_Client *c = cls;
+
+  GNUNET_SERVICE_client_continue (c);
+  if (received_cnt != ntohl (msg->x))
+  {
+    GNUNET_break (0);
+    global_ret = 4;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  received_cnt++;
+}
+
+
+static void
+handle_dummy2 (void *cls,
+               const struct MyMessage *msg)
+{
+  struct GNUNET_SERVICE_Client *c = cls;
+
+  GNUNET_SERVICE_client_continue (c);
+  if (NUM_TRANSMISSIONS != received_cnt)
+  {
+    GNUNET_break (0);
+    global_ret = 5;
+  }
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+do_send (void *cls);
+
+
+/**
+ * Function called whenever MQ has sent a message.
+ */
+static void
+notify_sent_cb (void *cls)
+{
+  static unsigned int seen;
+  unsigned int *cnt = cls;
+
+  if (seen != *cnt)
+  {
+    GNUNET_break (0);
+    global_ret = 6;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  seen++;
+  GNUNET_free (cnt);
+  task = GNUNET_SCHEDULER_add_now (&do_send,
+                                   NULL);
+}
+
+
+static void
+do_send (void *cls)
+{
+  static unsigned int i = 0;
+  unsigned int *cnt;
+  struct GNUNET_MQ_Envelope *env;
+  struct MyMessage *m;
+
+  task = NULL;
+  if (NUM_TRANSMISSIONS == i)
+  {
+    env = GNUNET_MQ_msg (m,
+                         GNUNET_MESSAGE_TYPE_DUMMY2);
+    GNUNET_MQ_send (cmq,
+                    env);
+    return;
+  }
+  cnt = GNUNET_new (unsigned int);
+  *cnt = i;
+  env = GNUNET_MQ_msg (m,
+                       GNUNET_MESSAGE_TYPE_DUMMY);
+  GNUNET_MQ_notify_sent (env,
+                         &notify_sent_cb,
+                         cnt);
+  m->x = htonl (i);
+  GNUNET_MQ_send (cmq,
+                  env);
+  i++;
+}
+
+
+/**
+ * Start running the actual test.
+ *
+ * @param cls closure passed to #GNUNET_SERVICE_MAIN
+ * @param cfg configuration to use for this service
+ * @param sh handle to the newly create service
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *cfg,
+     struct GNUNET_SERVICE_Handle *sh)
+{
+  struct GNUNET_MQ_MessageHandler ch[] = {
+    GNUNET_MQ_handler_end ()
+  };
+
+  (void) cls;
+  (void) sh;
+  cmq = GNUNET_CLIENT_connect (cfg,
+                               "test_client",
+                               ch,
+                               &error_cb,
+                               NULL);
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+                                 NULL);
+  task = GNUNET_SCHEDULER_add_now (&do_send,
+                                   NULL);
+}
+
+
+/**
+ * Callback to be called when a client connects to the service.
+ *
+ * @param cls closure for the service
+ * @param c the new client that connected to the service
+ * @param mq the message queue used to send messages to the client
+ * @return the client-specific (`internal') closure
+ */
+static void *
+connect_cb (void *cls,
+            struct GNUNET_SERVICE_Client *c,
+            struct GNUNET_MQ_Handle *mq)
+{
+  (void) cls;
+  (void) mq;
+  return c;
+}
+
+
+/**
+ * Callback to be called when a client disconnected from the service
+ *
+ * @param cls closure for the service
+ * @param c the client that disconnected
+ * @param internal_cls the client-specific (`internal') closure
+ */
+static void
+disconnect_cb (void *cls,
+               struct GNUNET_SERVICE_Client *c,
+               void *internal_cls)
+{
+  (void) cls;
+  (void) c;
+  (void) internal_cls;
+}
+
+
+int
+main (int argc, char **argv)
+{
+  struct GNUNET_TIME_Absolute start;
+  char *test_argv[] = {
+    (char *) "test_client",
+    "-c",
+    "test_client_data.conf",
+    NULL
+  };
+  struct GNUNET_MQ_MessageHandler mh[] = {
+    GNUNET_MQ_hd_fixed_size (dummy,
+                             GNUNET_MESSAGE_TYPE_DUMMY,
+                             struct MyMessage,
+                             NULL),
+    GNUNET_MQ_hd_fixed_size (dummy2,
+                             GNUNET_MESSAGE_TYPE_DUMMY2,
+                             struct MyMessage,
+                             NULL),
+    GNUNET_MQ_handler_end ()
+  };
+
+  (void) argc;
+  (void) argv;
+  GNUNET_log_setup ("perf-mq",
+                    "INFO",
+                    NULL);
+  start = GNUNET_TIME_absolute_get ();
+  if (0 !=
+      GNUNET_SERVICE_run_ (3,
+                           test_argv,
+                           "test_client",
+                           GNUNET_SERVICE_OPTION_NONE,
+                           &run,
+                           &connect_cb,
+                           &disconnect_cb,
+                           NULL,
+                           mh))
+    return 1;
+  printf ("Scheduler perf took %s\n",
+          GNUNET_STRINGS_relative_time_to_string (
+            GNUNET_TIME_absolute_get_duration (start),
+            GNUNET_YES));
+  GAUGER ("UTIL", "Scheduler",
+          received_cnt / 1024 / (1
+                                 + GNUNET_TIME_absolute_get_duration
+                                   (start).rel_value_us / 1000LL), "kmsg/ms");
+  return global_ret;
+}