2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file stream/perf_stream_api.c
23 * @brief performance benchmarks for stream api
24 * @author Sree Harsha Totakura
27 #define LOG(kind, ...) \
28 GNUNET_log (kind, __VA_ARGS__);
30 /****************************************************************************************/
31 /* Test is setup into the following major steps: */
32 /* 1. Measurements over loopback (1 hop). i.e. we use only one peer and open */
33 /* stream connections over loopback. Messages will go through */
34 /* STREAM_API->MESH_API->MESH_SERVICE->MESH_API->STREAM_API. */
35 /* 2. Measurements over 2 peers (2 hops). We use testbed to create 2 peers, */
36 /* connect them and then create stream connections. Messages will go through */
37 /* STREAM_API->MESH_API->MESH_SERVICE->CORE1.....CORE2->MESH_API->STREAM_API */
38 /* 3. Measurements over 3 peers (3 hops). We use testbed to create 3 peers, */
39 /* connect them in a line topology: peer1->peer2->peer3. Messages will go */
41 /* STREAM_API->MESH_API->MESH_SERVICE->CORE1..CORE2..CORE3->MESH_API->STREAM_API. */
42 /****************************************************************************************/
45 #include "gnunet_common.h"
46 #include "gnunet_util_lib.h"
47 #include "gnunet_testing_lib.h"
48 #include "gnunet_testbed_service.h"
49 #include "gnunet_stream_lib.h"
54 * Simple struct to keep track of progress, and print a
55 * nice little percentage meter for long running tasks.
65 unsigned int completed;
79 * Single hop loopback testing
84 * Testing with 2 peers
89 * Testing with 3 peers
96 * Structure for holding peer's sockets and IO Handles
101 * Peer's stream socket
103 struct GNUNET_STREAM_Socket *socket;
105 struct GNUNET_PeerIdentity self;
108 * Peer's io write handle
110 struct GNUNET_STREAM_IOWriteHandle *io_write_handle;
113 * Peer's io read handle
115 struct GNUNET_STREAM_IOReadHandle *io_read_handle;
118 * Bytes the peer has written
120 unsigned int bytes_wrote;
123 * Byte the peer has read
125 unsigned int bytes_read;
130 * Maximum size of the data which we will transfer during tests
132 #define DATA_SIZE 5000000 /* 5mB */
135 * Listen socket of peer2
137 struct GNUNET_STREAM_ListenSocket *peer2_listen_socket;
140 * Handle to configuration during TEST_STEP_1_HOP
142 const struct GNUNET_CONFIGURATION_Handle *config;
145 * Handle for the progress meter
147 static struct ProgressMeter *meter;
150 * Placeholder for peer data
152 static struct PeerData peer_data[3];
155 * Task ID for abort task
157 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
160 * Task ID for write task
162 static GNUNET_SCHEDULER_TaskIdentifier write_task;
165 * Task ID for read task
167 static GNUNET_SCHEDULER_TaskIdentifier read_task;
170 * Absolute time when profiling starts
172 static struct GNUNET_TIME_Absolute prof_start_time;
175 * Test time taken for sending the data
177 static struct GNUNET_TIME_Relative prof_time;
180 * Random data block. Should generate data first
182 static uint32_t data[DATA_SIZE / 4];
185 * Payload sizes to test each major test with
187 static uint16_t payload_size[] =
188 { 20, 500, 2000, 7000, 13000, 25000, 50000, 60000, 63000, 64000 };
191 * Handle for the progress meter
193 static struct ProgressMeter *meter;
196 * Current step of testing
198 static enum TestStep test_step;
201 * Index for choosing payload size
203 static unsigned int payload_size_index;
206 * Testing result of a major test
211 * Create a meter to keep track of the progress of some task.
213 * @param total the total number of items to complete
214 * @param start_string a string to prefix the meter with (if printing)
215 * @param print GNUNET_YES to print the meter, GNUNET_NO to count
218 * @return the progress meter
220 static struct ProgressMeter *
221 create_meter (unsigned int total, char *start_string, int print)
223 struct ProgressMeter *ret;
225 ret = GNUNET_malloc (sizeof (struct ProgressMeter));
228 ret->modnum = total / 4;
229 if (ret->modnum == 0) /* Divide by zero check */
231 ret->dotnum = (total / 50) + 1;
232 if (start_string != NULL)
233 ret->startup_string = GNUNET_strdup (start_string);
235 ret->startup_string = GNUNET_strdup ("");
242 * Update progress meter (increment by one).
244 * @param meter the meter to update and print info for
246 * @return GNUNET_YES if called the total requested,
247 * GNUNET_NO if more items expected
250 update_meter (struct ProgressMeter *meter)
252 if (meter->print == GNUNET_YES)
254 if (meter->completed % meter->modnum == 0)
256 if (meter->completed == 0)
258 FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string);
261 FPRINTF (stdout, "%d%%",
262 (int) (((float) meter->completed / meter->total) * 100));
264 else if (meter->completed % meter->dotnum == 0)
265 FPRINTF (stdout, "%s", ".");
267 if (meter->completed + 1 == meter->total)
268 FPRINTF (stdout, "%d%%]\n", 100);
273 if (meter->completed == meter->total)
275 if (meter->completed > meter->total)
276 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n");
282 * Reset progress meter.
284 * @param meter the meter to reset
286 * @return GNUNET_YES if meter reset,
287 * GNUNET_SYSERR on error
290 reset_meter (struct ProgressMeter *meter)
293 return GNUNET_SYSERR;
295 meter->completed = 0;
301 * Release resources for meter
303 * @param meter the meter to free
306 free_meter (struct ProgressMeter *meter)
308 GNUNET_free_non_null (meter->startup_string);
317 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
319 GNUNET_STREAM_close (peer_data[1].socket);
320 if (NULL != peer_data[2].socket)
321 GNUNET_STREAM_close (peer_data[2].socket);
322 if (NULL != peer2_listen_socket)
323 GNUNET_STREAM_listen_close (peer2_listen_socket); /* Close listen socket */
324 if (GNUNET_SCHEDULER_NO_TASK != abort_task)
325 GNUNET_SCHEDULER_cancel (abort_task);
326 if (GNUNET_SCHEDULER_NO_TASK != write_task)
327 GNUNET_SCHEDULER_cancel (write_task);
328 GNUNET_SCHEDULER_shutdown (); /* Shutdown this testcase */
333 * Something went wrong and timed out. Kill everything and set error flag
336 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
338 abort_task = GNUNET_SCHEDULER_NO_TASK;
339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: ABORT\n");
340 if (GNUNET_SCHEDULER_NO_TASK != read_task)
341 GNUNET_SCHEDULER_cancel (read_task);
342 result = GNUNET_SYSERR;
343 do_shutdown (cls, tc);
348 * The write completion function; called upon writing some data to stream or
351 * @param cls the closure from GNUNET_STREAM_write/read
352 * @param status the status of the stream at the time this function is called
353 * @param size the number of bytes read or written
356 write_completion (void *cls, enum GNUNET_STREAM_Status status, size_t size)
358 struct PeerData *pdata = cls;
360 GNUNET_assert (GNUNET_STREAM_OK == status);
361 GNUNET_assert (size <= DATA_SIZE);
362 pdata->bytes_wrote += size;
363 if (pdata->bytes_wrote < DATA_SIZE) /* Have more data to send */
365 pdata->io_write_handle =
366 GNUNET_STREAM_write (pdata->socket,
367 ((void *) data) + pdata->bytes_wrote,
368 sizeof (data) - pdata->bytes_wrote,
369 GNUNET_TIME_UNIT_FOREVER_REL, &write_completion,
371 GNUNET_assert (NULL != pdata->io_write_handle);
375 prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
377 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
379 for (;size > 0; size--)
380 update_meter (meter);
385 * Task for calling STREAM_write with a chunk of random data
387 * @param cls the peer data entity
388 * @param tc the task context
391 stream_write_task (void *cls,
392 const struct GNUNET_SCHEDULER_TaskContext *tc)
394 struct PeerData *pdata = cls;
396 write_task = GNUNET_SCHEDULER_NO_TASK;
397 prof_start_time = GNUNET_TIME_absolute_get ();
398 pdata->bytes_wrote = 0;
399 pdata->io_write_handle = GNUNET_STREAM_write (pdata->socket, data,
401 GNUNET_TIME_UNIT_FOREVER_REL,
402 &write_completion, pdata);
403 GNUNET_assert (NULL != pdata->io_write_handle);
408 * Scheduler call back; to be executed when a new stream is connected
409 * Called from listen connect for peer2
412 stream_read_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
419 * @param status the status of the stream at the time this function is called
420 * @param data traffic from the other side
421 * @param size the number of bytes available in data read
422 * @return number of bytes of processed from 'data' (any data remaining should be
423 * given to the next time the read processor is called).
426 input_processor (void *cls, enum GNUNET_STREAM_Status status,
427 const void *input_data, size_t size)
429 struct PeerData *pdata = cls;
431 GNUNET_assert (GNUNET_STREAM_OK == status);
432 GNUNET_assert (size < DATA_SIZE);
433 GNUNET_assert (0 == memcmp (((void *)data ) + pdata->bytes_read,
435 pdata->bytes_read += size;
437 if (pdata->bytes_read < DATA_SIZE)
439 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == read_task);
440 read_task = GNUNET_SCHEDULER_add_now (&stream_read_task, pdata);
444 /* Peer2 has completed reading*/
445 LOG (GNUNET_ERROR_TYPE_DEBUG, "Reading finished successfully\n");
452 * Scheduler call back; to be executed when a new stream is connected
453 * Called from listen connect for peer2
456 stream_read_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
458 struct PeerData *pdata = cls;
460 read_task = GNUNET_SCHEDULER_NO_TASK;
461 pdata->io_read_handle =
462 GNUNET_STREAM_read (pdata->socket, GNUNET_TIME_UNIT_FOREVER_REL,
463 &input_processor, pdata);
464 GNUNET_assert (NULL != pdata->io_read_handle);
469 * Functions of this type are called upon new stream connection from other peers
471 * @param cls the closure from GNUNET_STREAM_listen
472 * @param socket the socket representing the stream
473 * @param initiator the identity of the peer who wants to establish a stream
475 * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
476 * stream (the socket will be invalid after the call)
479 stream_listen_cb (void *cls, struct GNUNET_STREAM_Socket *socket,
480 const struct GNUNET_PeerIdentity *initiator)
482 struct PeerData *pdata = cls;
484 GNUNET_assert (NULL != socket);
485 GNUNET_assert (pdata == &peer_data[2]);
486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer connected: %s\n",
487 GNUNET_i2s(initiator));
488 pdata->socket = socket;
489 pdata->bytes_read = 0;
490 read_task = GNUNET_SCHEDULER_add_now (&stream_read_task, pdata);
496 * Function executed after stream has been established
498 * @param cls the closure from GNUNET_STREAM_open
499 * @param socket socket to use to communicate with the other side (read/write)
502 stream_open_cb (void *cls,
503 struct GNUNET_STREAM_Socket *socket)
505 struct PeerData *pdata = cls;
507 GNUNET_assert (socket == pdata->socket);
508 write_task = GNUNET_SCHEDULER_add_now (&stream_write_task, pdata);
513 * Listen success callback; connects a peer to stream as client
516 stream_connect (void)
518 peer_data[1].socket =
519 GNUNET_STREAM_open (config, &peer_data[2].self, 10, &stream_open_cb,
521 GNUNET_STREAM_OPTION_MAX_PAYLOAD_SIZE,
522 payload_size[payload_size_index],
523 GNUNET_STREAM_OPTION_END);
524 GNUNET_assert (NULL != peer_data[1].socket);
529 * Initialize framework and start test
532 * @param cfg configuration of the peer that was started
533 * @param peer identity of the peer that was created
537 const struct GNUNET_CONFIGURATION_Handle *cfg,
538 struct GNUNET_TESTING_Peer *peer)
540 struct GNUNET_PeerIdentity self;
542 GNUNET_TESTING_peer_get_identity (peer, &self);
544 peer2_listen_socket =
545 GNUNET_STREAM_listen (config, 10, &stream_listen_cb, &peer_data[2],
546 GNUNET_STREAM_OPTION_SIGNAL_LISTEN_SUCCESS,
547 &stream_connect, GNUNET_STREAM_OPTION_END);
548 GNUNET_assert (NULL != peer2_listen_socket);
549 peer_data[1].self = self;
550 peer_data[2].self = self;
552 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
553 (GNUNET_TIME_UNIT_SECONDS, 300), &do_abort,
561 int main (int argc, char **argv)
564 char *test_name = "perf_stream_api";
565 char *cfg_file = "test_stream_local.conf";
567 double prof_time_sec;
571 meter = create_meter ((sizeof (data) / 4), "Generating random data\n", GNUNET_YES);
572 for (count=0; count < (sizeof (data) / 4); count++)
574 data[count] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
576 update_meter (meter);
580 test_step = TEST_STEP_1_HOP;
581 for (payload_size_index = 0;
582 payload_size_index < (sizeof (payload_size) / sizeof (uint16_t));
583 payload_size_index++)
585 GNUNET_asprintf (&pmsg, "\nTesting over loopback with payload size %hu\n",
586 payload_size[payload_size_index]);
587 meter = create_meter (sizeof (data), pmsg, GNUNET_YES);
589 result = GNUNET_SYSERR;
590 ret = GNUNET_TESTING_peer_run (test_name, cfg_file, &run, NULL);
592 if ((0 != ret) || (GNUNET_OK != result))
594 prof_time_sec = (((double) prof_time.rel_value)/ ((double) 1000));
595 throughput = (((float) sizeof (data)) / prof_time_sec);
596 //PRINTF ("Profiling time %llu ms = %.2f sec\n", prof_time.rel_value, prof_time_sec);
597 PRINTF ("Throughput %.2f kB/sec\n", throughput / 1000.00);
599 test_step = TEST_STEP_2_HOP;
600 for (payload_size_index = 0;
601 payload_size_index < (sizeof (payload_size) / sizeof (uint16_t));
602 payload_size_index++)
604 /* Initialize testbed here */
606 test_step = TEST_STEP_3_HOP;
607 for (payload_size_index = 0;
608 payload_size_index < (sizeof (payload_size) / sizeof (uint16_t));
609 payload_size_index++)
611 /* Initialize testbed here */
616 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test failed\n");
620 /* end of perf_stream_api.c */