From 783095e138b7257dfcbf4e9aa2a48685d14c7f16 Mon Sep 17 00:00:00 2001 From: David Bar-On Date: Sun, 11 Aug 2024 16:14:42 +0300 Subject: [PATCH 1/2] Fix #1741 - reduce CPU usage when test baud rate is limited --- configure.ac | 9 ++++++ src/iperf_api.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-- src/iperf_locale.c | 2 ++ 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index a23c3bcf4..073d07dcc 100644 --- a/configure.ac +++ b/configure.ac @@ -342,5 +342,14 @@ AC_SEARCH_LIBS(clock_gettime, [rt posix4]) # Check for clock_gettime support AC_CHECK_FUNCS([clock_gettime]) +# Check if we need -lrt for nanosleep +AC_SEARCH_LIBS(nanosleep, [rt posix4]) +# Check for nanosleep support +AC_CHECK_FUNCS([nanosleep]) +# Check if we need -lrt for clock_nanosleep +AC_SEARCH_LIBS(clock_nanosleep, [rt posix4]) +# Check for clock_nanosleep support +AC_CHECK_FUNCS([clock_nanosleep]) + AC_CONFIG_FILES([Makefile src/Makefile src/version.h examples/Makefile iperf3.spec]) AC_OUTPUT diff --git a/src/iperf_api.c b/src/iperf_api.c index daa157cac..c2d2b7bf6 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1639,10 +1639,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->use_pkcs1_padding = 1; break; #endif /* HAVE_SSL */ +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) case OPT_PACING_TIMER: test->settings->pacing_timer = unit_atoi(optarg); client_flag = 1; break; +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */ case OPT_CONNECT_TIMEOUT: test->settings->connect_timeout = unit_atoi(optarg); client_flag = 1; @@ -1881,17 +1883,73 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) struct iperf_time temp_time; double seconds; uint64_t bits_per_second; + int64_t missing_rate; + uint64_t bits_sent; + +#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP) + struct timespec nanosleep_time; + int64_t time_to_green_ligh, delta_bits; + int ret; +#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP) */ +#if defined(HAVE_CLOCK_NANOSLEEP) + int64_t ns; +#endif /* HAVE_CLOCK_NANOSLEEP */ if (sp->test->done || sp->test->settings->rate == 0) return; iperf_time_diff(&sp->result->start_time_fixed, nowP, &temp_time); seconds = iperf_time_in_secs(&temp_time); - bits_per_second = sp->result->bytes_sent * 8 / seconds; - if (bits_per_second < sp->test->settings->rate) { + bits_sent = sp->result->bytes_sent * 8; + bits_per_second = bits_sent / seconds; + missing_rate = sp->test->settings->rate - bits_per_second; + + if (missing_rate > 0) { sp->green_light = 1; } else { sp->green_light = 0; } + +#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP) + // If estimated time to next send is large enough, sleep instead of just CPU looping until green light is set + if (missing_rate < 0) { + delta_bits = bits_sent - (seconds * sp->test->settings->rate); + // Calclate time until next data send is required + time_to_green_ligh = (SEC_TO_NS * delta_bits / sp->test->settings->rate); + // Whether shouuld wait before next send + if (time_to_green_ligh >= 0) { +#if defined(HAVE_CLOCK_NANOSLEEP) + if (clock_gettime(CLOCK_MONOTONIC, &nanosleep_time) == 0) { + // Calculate absolute end of sleep time + ns = nanosleep_time.tv_nsec + time_to_green_ligh; + if (ns < SEC_TO_NS) { + nanosleep_time.tv_nsec = ns; + } else { + nanosleep_time.tv_sec += ns / SEC_TO_NS; + nanosleep_time.tv_nsec = ns % SEC_TO_NS; + } + // Sleep until average baud rate reaches the target value + while((ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &nanosleep_time, NULL)) == EINTR); + if (ret == 0) { + sp->green_light = 1; + } + } + +#else /* HAVE_NANOSLEEP */ + nanosleep_time.tv_sec = 0; + // Sleep until average baud rate reaches the target value or intrupt / error + do { + // nansleep() time should be less than 1 sec + nanosleep_time.tv_nsec = (time_to_green_ligh >= SEC_TO_NS) ? SEC_TO_NS - 1 : time_to_green_ligh; + time_to_green_ligh -= nanosleep_time.tv_nsec; + ret = nanosleep(&nanosleep_time, NULL); + } while (ret == 0 && time_to_green_ligh > 0); + if (ret == 0) { + sp->green_light = 1; + } +#endif /* HAVE_CLOCK_NANOSLEEP else HAVE_NANOSLEEP */ + } + } +#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP */ } /* Verify that average traffic is not greater than the specified limit */ @@ -1982,7 +2040,11 @@ iperf_send_mt(struct iperf_stream *sp) if (!streams_active) break; } +#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP) + if (!sp->green_light) { /* Should check if green ligh can be set, as pacing timer is not supported in this case */ +#else /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */ if (!no_throttle_check) { /* Throttle check if was not checked for each send */ +#endif /* HAVE_CLOCK_NANOSLEEP, HAVE_NANOSLEEP */ iperf_time_now(&now); if (sp->sender) iperf_check_throttle(sp, &now); @@ -2032,6 +2094,7 @@ iperf_init_test(struct iperf_test *test) return 0; } +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) static void send_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { @@ -2043,20 +2106,25 @@ send_timer_proc(TimerClientData client_data, struct iperf_time *nowP) */ iperf_check_throttle(sp, nowP); } +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP) */ int iperf_create_send_timers(struct iperf_test * test) { - struct iperf_time now; struct iperf_stream *sp; +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) TimerClientData cd; + struct iperf_time now; if (iperf_time_now(&now) < 0) { i_errno = IEINITTEST; return -1; } +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP) */ + SLIST_FOREACH(sp, &test->streams, streams) { sp->green_light = 1; +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) if (test->settings->rate != 0 && sp->sender) { cd.p = sp; sp->send_timer = tmr_create(NULL, send_timer_proc, cd, test->settings->pacing_timer, 1); @@ -2065,6 +2133,7 @@ iperf_create_send_timers(struct iperf_test * test) return -1; } } +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP) */ } return 0; } diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 9d94e0234..ae4454074 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -163,7 +163,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " -b, --bitrate #[KMG][/#] target bitrate in bits/sec (0 for unlimited)\n" " (default %d Mbit/sec for UDP, unlimited for TCP)\n" " (optional slash and packet count for burst mode)\n" +#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) " --pacing-timer #[KMG] set the timing for pacing, in microseconds (default %d)\n" +#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */ #if defined(HAVE_SO_MAX_PACING_RATE) " --fq-rate #[KMG] enable fair-queuing based socket pacing in\n" " bits/sec (Linux only)\n" From af81adc9bf27604df33e5e39e57450f7e7c6c52b Mon Sep 17 00:00:00 2001 From: DavidBar-On Date: Thu, 29 Aug 2024 10:24:20 +0300 Subject: [PATCH 2/2] Changes per reviewer comments --- src/iperf_api.c | 16 +++++++--------- src/iperf_locale.c | 3 +++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/iperf_api.c b/src/iperf_api.c index c2d2b7bf6..f8acd9237 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1639,12 +1639,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->use_pkcs1_padding = 1; break; #endif /* HAVE_SSL */ -#if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) case OPT_PACING_TIMER: test->settings->pacing_timer = unit_atoi(optarg); client_flag = 1; break; -#endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */ case OPT_CONNECT_TIMEOUT: test->settings->connect_timeout = unit_atoi(optarg); client_flag = 1; @@ -1888,7 +1886,7 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) #if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP) struct timespec nanosleep_time; - int64_t time_to_green_ligh, delta_bits; + int64_t time_to_green_light, delta_bits; int ret; #endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP) */ #if defined(HAVE_CLOCK_NANOSLEEP) @@ -1914,13 +1912,13 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) if (missing_rate < 0) { delta_bits = bits_sent - (seconds * sp->test->settings->rate); // Calclate time until next data send is required - time_to_green_ligh = (SEC_TO_NS * delta_bits / sp->test->settings->rate); + time_to_green_light = (SEC_TO_NS * delta_bits / sp->test->settings->rate); // Whether shouuld wait before next send - if (time_to_green_ligh >= 0) { + if (time_to_green_light >= 0) { #if defined(HAVE_CLOCK_NANOSLEEP) if (clock_gettime(CLOCK_MONOTONIC, &nanosleep_time) == 0) { // Calculate absolute end of sleep time - ns = nanosleep_time.tv_nsec + time_to_green_ligh; + ns = nanosleep_time.tv_nsec + time_to_green_light; if (ns < SEC_TO_NS) { nanosleep_time.tv_nsec = ns; } else { @@ -1939,10 +1937,10 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) // Sleep until average baud rate reaches the target value or intrupt / error do { // nansleep() time should be less than 1 sec - nanosleep_time.tv_nsec = (time_to_green_ligh >= SEC_TO_NS) ? SEC_TO_NS - 1 : time_to_green_ligh; - time_to_green_ligh -= nanosleep_time.tv_nsec; + nanosleep_time.tv_nsec = (time_to_green_light >= SEC_TO_NS) ? SEC_TO_NS - 1 : time_to_green_light; + time_to_green_light -= nanosleep_time.tv_nsec; ret = nanosleep(&nanosleep_time, NULL); - } while (ret == 0 && time_to_green_ligh > 0); + } while (ret == 0 && time_to_green_light > 0); if (ret == 0) { sp->green_light = 1; } diff --git a/src/iperf_locale.c b/src/iperf_locale.c index ae4454074..dbbb72fec 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -165,6 +165,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " (optional slash and packet count for burst mode)\n" #if !defined(HAVE_CLOCK_NANOSLEEP) && !defined(HAVE_NANOSLEEP) " --pacing-timer #[KMG] set the timing for pacing, in microseconds (default %d)\n" +#else + " --pacing-timer #[KMG] set the Server timing for pacing, in microseconds (default %d)\n" + " (used by the server only if this option is in its help message)\n" #endif /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */ #if defined(HAVE_SO_MAX_PACING_RATE) " --fq-rate #[KMG] enable fair-queuing based socket pacing in\n"