/* 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 . 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 #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, ¬ify_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; }