On 11/4/22 23:02, Adrian Moreno wrote:
> Currently, things like the number of handler and revalidator threads are
> calculated based on the number of available CPUs. However, this number
> is considered static and only calculated once, hence ignoring events
> such as cpus being hotplugged, switched on/off or affinity mask
> changing.
> 
> On the other hand, checking the number of available CPUs multiple times
> per second seems like an overkill.
> Affinity should not change that often and, even if it does, the impact
> of destroying and recreating all the threads so often is probably a
> price too expensive to pay.
> 
> This patch makes the number of cpus be calculated every time 5 seconds
> which seems a reasonable middle point.
> It generates an impact in the main loop duration of <1% and a worst-case
> scenario impact in throughput of < 5% [1].

Thanks, Adrian!  See some comments below.

> 
> As a result of these changes (assuming the patch is backported):
> - >=2.16: a change in the cpu affinity reflects on the number of threads
>   in (at most) 5 seconds.
> - < 2.16: a change in the cpu affinity will be reflected on
>   the number of threads the next time there is a call to
>   bridge_reconfigure() (e.g: on the next DB change), and 5 seconds
>   have passed.
> 
> The difference in behavior is because on older versions the thread
> number calculation was done on bridge reconfiguration while newer
> versions moved this logic down to the dpif layer and is run on
> dpif->run() stage.
> Considering it has not been a huge problem up to today and that the
> cpu change would be reflected sooner or later (e.g the user could
> force a recalculation with a simple ovs-vsctl command), I think it
> might be OK to leave like that.

I don't think we should actually backport that change as it changing
the behavior of the ovs-vswitchd in a way that may not be expected
the the user.  It's more like a new feature.  So, above paragraphs can
be dropped from the commit message.

And since it's a new behavior change, it should have a record in the
NEWS file.  Something like this, maybe:

  - ovs-vswitchd now detects changes in CPU affinity and adjusts the number
    of handler and revalidator threads if necessary.

> 
> [1] Tested in the worst-case scenario of disabling the kernel cache
> (other_config:flow-size=0), modifying ovs-vswithd's affinity so the
> number of handlers go up and down every 5 seconds and calculated the
> difference in netperf's ops/sec.
> 
> Fixes: be15ec48d766 ("lib: Use a more accurate value for CPU count 
> (sched_getaffinity).")

And this tag is not really needed.

> Cc: [email protected]
> Signed-off-by: Adrian Moreno <[email protected]>
> ---
>  lib/ovs-thread.c | 61 +++++++++++++++++++++++++++++-------------------
>  1 file changed, 37 insertions(+), 24 deletions(-)
> 
> diff --git a/lib/ovs-thread.c b/lib/ovs-thread.c
> index 78ed3e970..d1deb9c52 100644
> --- a/lib/ovs-thread.c
> +++ b/lib/ovs-thread.c
> @@ -31,6 +31,7 @@
>  #include "openvswitch/poll-loop.h"
>  #include "seq.h"
>  #include "socket-util.h"
> +#include "timeval.h"
>  #include "util.h"
>  
>  #ifdef __CHECKER__
> @@ -627,42 +628,54 @@ ovs_thread_stats_next_bucket(const struct 
> ovsthread_stats *stats, size_t i)
>  }
>  
>  
> -/* Returns the total number of cores available to this process, or 0 if the
> - * number cannot be determined. */
> -int
> -count_cpu_cores(void)
> +static int
> +count_cpu_cores__(void)
>  {
> -    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
> -    static long int n_cores;
> +    long int n_cores;
>  
> -    if (ovsthread_once_start(&once)) {
>  #ifndef _WIN32
> -        n_cores = sysconf(_SC_NPROCESSORS_ONLN);
> +    n_cores = sysconf(_SC_NPROCESSORS_ONLN);
> +#else
> +    SYSTEM_INFO sysinfo;
> +    GetSystemInfo(&sysinfo);
> +    n_cores = sysinfo.dwNumberOfProcessors;
> +#endif
>  #ifdef __linux__
> -        if (n_cores > 0) {
> -            cpu_set_t *set = CPU_ALLOC(n_cores);
> +    if (n_cores > 0) {
> +        cpu_set_t *set = CPU_ALLOC(n_cores);
>  
> -            if (set) {
> -                size_t size = CPU_ALLOC_SIZE(n_cores);
> +        if (set) {
> +            size_t size = CPU_ALLOC_SIZE(n_cores);
>  
> -                if (!sched_getaffinity(0, size, set)) {
> -                    n_cores = CPU_COUNT_S(size, set);
> -                }
> -                CPU_FREE(set);
> +            if (!sched_getaffinity(0, size, set)) {
> +                n_cores = CPU_COUNT_S(size, set);
>              }
> +            CPU_FREE(set);
>          }
> -#endif
> -#else
> -        SYSTEM_INFO sysinfo;
> -        GetSystemInfo(&sysinfo);
> -        n_cores = sysinfo.dwNumberOfProcessors;
> -#endif
> -        ovsthread_once_done(&once);
>      }
> -
> +#endif
>      return n_cores > 0 ? n_cores : 0;
>  }
>  
> +/* It's unlikely that the available cpus change several times per second and
> + * even if it does, it's not needed (or desired) to react to such changes so
> + * quickly.*/
> +#define COUNT_CPU_UPDATE_TIME_MS 5000
> +/* Returns the current total number of cores available to this process, or 0
> + * if the number cannot be determined.
> + * It is assumed that this function is only called from the main thread.*/

There is a missing space at the end.  But, more importantly, this
assumption is not correct.  The function is called from the system
stats thread, i.e. system_stats_thread_func().  So, updates inside
should be guarded by some form of a mutex.

> +int count_cpu_cores(void) {

Braces should be on a new line.

> +    static long long int last_updated = 0;
> +    long long int now = time_msec();
> +    static int cpu_cores;
> +
> +    if (now - last_updated >= COUNT_CPU_UPDATE_TIME_MS) {
> +        last_updated = now;
> +        cpu_cores = count_cpu_cores__();
> +    }
> +    return cpu_cores;
> +}
> +
>  /* Returns the total number of cores on the system, or 0 if the
>   * number cannot be determined. */
>  int

Bets regards, Ilya Maximets.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to