summaryrefslogtreecommitdiff
path: root/perf
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2017-01-05 12:38:09 -0800
committerGarrett D'Amore <garrett@damore.org>2017-01-05 12:38:09 -0800
commitbc9aec8395edb2ae9ce219892a1e99a6e114860c (patch)
treed80d07864fadfb958af8a847a308ba8365ba67d4 /perf
parent4ff449b6b1d5b571997f328ecdc7b0042c6de4e1 (diff)
downloadnng-bc9aec8395edb2ae9ce219892a1e99a6e114860c.tar.gz
nng-bc9aec8395edb2ae9ce219892a1e99a6e114860c.tar.bz2
nng-bc9aec8395edb2ae9ce219892a1e99a6e114860c.zip
Add initial performanced tests.
Diffstat (limited to 'perf')
-rw-r--r--perf/CMakeLists.txt43
-rw-r--r--perf/perf.c225
2 files changed, 268 insertions, 0 deletions
diff --git a/perf/CMakeLists.txt b/perf/CMakeLists.txt
new file mode 100644
index 00000000..44c4c607
--- /dev/null
+++ b/perf/CMakeLists.txt
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2012 Martin Sustrik All rights reserved.
+# Copyright (c) 2013 GoPivotal, Inc. All rights reserved.
+# Copyright (c) 2015-2016 Jack R. Dunaway. All rights reserved.
+# Copyright 2016 Garrett D'Amore <garrett@damore.org>
+# Copyright 2016 Franklin "Snaipe" Mathieu <franklinmathieu@gmail.com>
+#
+# 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:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# 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.
+#
+
+# Build unit tests.
+
+include_directories(AFTER SYSTEM ${PROJECT_SOURCE_DIR}/src)
+
+if (NNG_TESTS)
+ macro (add_nng_perf NAME)
+ add_executable (${NAME} perf.c)
+ target_link_libraries (${NAME} ${PROJECT_NAME})
+ endmacro (add_nng_perf)
+
+else ()
+ macro (add_nng_perf NAME)
+ endmacro (add_nng_perf)
+endif ()
+
+add_nng_perf(remote_lat)
+add_nng_perf(local_lat)
diff --git a/perf/perf.c b/perf/perf.c
new file mode 100644
index 00000000..169a2119
--- /dev/null
+++ b/perf/perf.c
@@ -0,0 +1,225 @@
+//
+// Copyright 2016 Garrett D'Amore <garrett@damore.org>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#include "nng.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+// We steal access to the clock and thread functions so that we can
+// work on Windows too. These functions are *not* part of nng's public
+// API, so don't be lazy like this! All nni_ symbols are subject to
+// change without notice, and not part of the stable API or ABI.
+#include "core/nng_impl.h"
+
+static void latency_client(const char *, int, int);
+static void latency_server(const char *, int, int);
+static void do_remote_lat(int argc, char **argv);
+static void do_local_lat(int argc, char **argv);
+static void die(const char *, ...);
+
+// perf implements the same performance tests found in the standard
+// nanomsg & mangos performance tests. As with mangos, the decision
+// about which test to run is determined by the program name (ARGV[0}])
+// that it is run under.
+//
+// Options are:
+//
+// - remote_lat - remote latency side (client, aka latency_client)
+// - local_lat - local latency side (server, aka latency_server)
+// - local_thr - local throughput side
+// - remote_thr - remote throughput side
+// - inproc_lat - inproc latency
+// - inproc_thr - inproc throughput
+//
+
+int
+main(int argc, char **argv)
+{
+ char *prog;
+
+ // Allow -m <remote_late> or whatever to override argv[0].
+ if ((argc >= 3) && (strcmp(argv[1], "-m") == 0)) {
+ prog = argv[1];
+ argv += 3;
+ argc -= 3;
+ } else {
+ if (((prog = strrchr(argv[0], '/')) != NULL) ||
+ ((prog = strrchr(argv[0], '\\')) != NULL) ||
+ ((prog = strrchr(argv[0], ':')) != NULL)) {
+ prog++;
+ } else {
+ prog = argv[0];
+ }
+ argc--;
+ argv++;
+ }
+ if ((strcmp(prog, "remote_lat") == 0) ||
+ (strcmp(prog, "latency_client") == 0)) {
+ do_remote_lat(argc, argv);
+ } else if ((strcmp(prog, "local_lat") == 0) ||
+ (strcmp(prog, "latency_server") == 0)) {
+ do_local_lat(argc, argv);
+ } else {
+ die("Unknown program mode? Use -m <mode>.");
+ }
+}
+
+
+static void
+die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ exit(2);
+}
+
+
+static int
+parse_int(const char *arg, const char *what)
+{
+ long val;
+ char *eptr;
+
+ val = strtol(arg, &eptr, 10);
+ // Must be a postive number less than around a billion.
+ if ((val < 0) || (val > (1<<30)) || (*eptr != 0) || (eptr == arg)) {
+ die("Invalid %s", what);
+ }
+ return ((int) val);
+}
+
+
+void
+do_local_lat(int argc, char **argv)
+{
+ long int msgsize;
+ long int trips;
+
+ if (argc != 3) {
+ die("Usage: local_lat <listen-addr> <msg-size> <roundtrips>");
+ }
+
+ msgsize = parse_int(argv[1], "message size");
+ trips = parse_int(argv[2], "round-trips");
+
+ latency_server(argv[0], msgsize, trips);
+}
+
+
+void
+do_remote_lat(int argc, char **argv)
+{
+ long int msgsize;
+ long int trips;
+
+ if (argc != 3) {
+ die("Usage: remote_lat <connect-to> <msg-size> <roundtrips>");
+ }
+
+ msgsize = parse_int(argv[1], "message size");
+ trips = parse_int(argv[2], "round-trips");
+
+ latency_client(argv[0], msgsize, trips);
+}
+
+
+void
+latency_client(const char *addr, int msgsize, int trips)
+{
+ nng_socket *s;
+ nng_msg *msg;
+ nni_time start, end;
+ int rv;
+ int i;
+ float total;
+ float latency;
+
+ if ((rv = nng_open(&s, NNG_PROTO_PAIR)) != 0) {
+ die("nng_socket: %s", nng_strerror(rv));
+ }
+
+ // XXX: set no delay
+ // XXX: other options (TLS in the future?, Linger?)
+
+ if ((rv = nng_dial(s, addr, NULL, NNG_FLAG_SYNCH)) != 0) {
+ die("nng_dial: %s", nng_strerror(rv));
+ }
+
+ if (nng_msg_alloc(&msg, msgsize) != 0) {
+ die("nng_msg_alloc: %s", nng_strerror(rv));
+ }
+
+ start = nni_clock();
+ for (i = 0; i < trips; i++) {
+ if ((rv = nng_sendmsg(s, msg, 0)) != 0) {
+ die("nng_sendmsg: %s", nng_strerror(rv));
+ }
+
+ if ((rv = nng_recvmsg(s, &msg, 0)) != 0) {
+ die("nng_recvmsg: %s", nng_strerror(rv));
+ }
+ }
+ end = nni_clock();
+
+ nni_msg_free(msg);
+
+ total = (end - start) / 1.0;
+ latency = (total / (trips * 2));
+ printf("total time: %.3f [s]\n", total / 1000000.0);
+ printf("message size: %d [B]\n", msgsize);
+ printf("round trip count: %d\n", trips);
+ printf("average latency: %.3f [us]\n", latency);
+}
+
+
+void
+latency_server(const char *addr, int msgsize, int trips)
+{
+ nng_socket *s;
+ nng_msg *msg;
+ int rv;
+ int i;
+ size_t len;
+
+ if ((rv = nng_open(&s, NNG_PROTO_PAIR)) != 0) {
+ die("nng_socket: %s", nng_strerror(rv));
+ }
+
+ // XXX: set no delay
+ // XXX: other options (TLS in the future?, Linger?)
+
+ if ((rv = nng_listen(s, addr, NULL, NNG_FLAG_SYNCH)) != 0) {
+ die("nng_listen: %s", nng_strerror(rv));
+ }
+
+ for (i = 0; i < trips; i++) {
+ if ((rv = nng_recvmsg(s, &msg, 0)) != 0) {
+ die("nng_recvmsg: %s", nng_strerror(rv));
+ }
+ nng_msg_body(msg, &len);
+ if (len != msgsize) {
+ die("wrong message size: %d != %d", len, msgsize);
+ }
+ if ((rv = nng_sendmsg(s, msg, 0)) != 0) {
+ die("nng_sendmsg: %s", nng_strerror(rv));
+ }
+ }
+
+ // Wait a bit for things to drain... linger should do this.
+ // 100ms ought to be enough.
+ nni_usleep(100000);
+}