[v4,3/6] build: optional NUMA and cpu counts detection

Message ID 1603464488-25493-4-git-send-email-juraj.linkes@pantheon.tech (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series Arm build options rework |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Juraj Linkeš Oct. 23, 2020, 2:48 p.m. UTC
  Add an option to automatically discover the host's numa and cpu counts
and use those values for a non cross-build.
Give users the option to override the per-arch default values or values
from cross files by specifying them on the command line with -Dmax_lcores
and -Dmax_numa_nodes.

Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
---
 buildtools/get_cpu_count.py  |  7 ++++++
 buildtools/get_numa_count.py | 22 +++++++++++++++++
 buildtools/meson.build       |  2 ++
 config/meson.build           | 48 ++++++++++++++++++++++++++++++++++--
 meson_options.txt            |  8 +++---
 5 files changed, 81 insertions(+), 6 deletions(-)
 create mode 100644 buildtools/get_cpu_count.py
 create mode 100644 buildtools/get_numa_count.py
  

Comments

Bruce Richardson Oct. 27, 2020, 11:20 a.m. UTC | #1
On Fri, Oct 23, 2020 at 04:48:05PM +0200, Juraj Linkeš wrote:
> Add an option to automatically discover the host's numa and cpu counts
> and use those values for a non cross-build.
> Give users the option to override the per-arch default values or values
> from cross files by specifying them on the command line with -Dmax_lcores
> and -Dmax_numa_nodes.
> 
> Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
> ---
>  buildtools/get_cpu_count.py  |  7 ++++++
>  buildtools/get_numa_count.py | 22 +++++++++++++++++
>  buildtools/meson.build       |  2 ++
>  config/meson.build           | 48 ++++++++++++++++++++++++++++++++++--
>  meson_options.txt            |  8 +++---
>  5 files changed, 81 insertions(+), 6 deletions(-)
>  create mode 100644 buildtools/get_cpu_count.py
>  create mode 100644 buildtools/get_numa_count.py
> 
> diff --git a/buildtools/get_cpu_count.py b/buildtools/get_cpu_count.py
> new file mode 100644
> index 000000000..386f85f8b
> --- /dev/null
> +++ b/buildtools/get_cpu_count.py
> @@ -0,0 +1,7 @@
> +#!/usr/bin/python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright (c) 2020 PANTHEON.tech s.r.o.
> +
> +import os
> +
> +print(os.cpu_count())
> diff --git a/buildtools/get_numa_count.py b/buildtools/get_numa_count.py
> new file mode 100644
> index 000000000..f0c49973a
> --- /dev/null
> +++ b/buildtools/get_numa_count.py
> @@ -0,0 +1,22 @@
> +#!/usr/bin/python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright (c) 2020 PANTHEON.tech s.r.o.
> +
> +import ctypes
> +import glob
> +import os
> +import subprocess
> +
> +if os.name == 'posix':
> +    if os.path.isdir('/sys/devices/system/node'):
> +        print(len(glob.glob('/sys/devices/system/node/node*')))
> +    else:
> +        print(subprocess.run(['sysctl', 'vm.ndomains'], capture_output=True).stdout)

I think you can shorten this, by just calling subprocess.run and not
capturing anything, in which case the stdout will be printed as normal.

	subprocess.run(['sysctl', 'vm.ndomains'])

> +
> +elif os.name == 'nt':
> +    libkernel32 = ctypes.windll.kernel32
> +
> +    count = ctypes.c_ulong()
> +
> +    libkernel32.GetNumaHighestNodeNumber(ctypes.pointer(count))
> +    print(count.value + 1)
> diff --git a/buildtools/meson.build b/buildtools/meson.build
> index 04808dabc..925e733b1 100644
> --- a/buildtools/meson.build
> +++ b/buildtools/meson.build
> @@ -17,3 +17,5 @@ else
>  endif
>  map_to_win_cmd = py3 + files('map_to_win.py')
>  sphinx_wrapper = py3 + files('call-sphinx-build.py')
> +get_cpu_count_cmd = py3 + files('get_cpu_count.py')
> +get_numa_count_cmd = py3 + files('get_numa_count.py')
> diff --git a/config/meson.build b/config/meson.build
> index 918ca2dfb..5c6fdcc7d 100644
> --- a/config/meson.build
> +++ b/config/meson.build
> @@ -230,8 +230,14 @@ foreach arg: warning_flags
>  endforeach
>  
>  # set other values pulled from the build options
> -dpdk_conf.set('RTE_MAX_LCORE', get_option('max_lcores'))
> -dpdk_conf.set('RTE_MAX_NUMA_NODES', get_option('max_numa_nodes'))
> +if not meson.is_cross_build()
> +	# set default values
> +	# these defaults may be overwritten by meson.build in an arch subdir
> +	# or later overwritten if a user specifies a value on the command line
> +	# or the user chooses to use values discovered from the build machine
> +	dpdk_conf.set('RTE_MAX_LCORE', 128)
> +	dpdk_conf.set('RTE_MAX_NUMA_NODES', 4)
> +endif

I think it might be better to remove this block, and instead set the values
below where you do the additional checks. It's confusing having these
settings managed in multiple places.

>  dpdk_conf.set('RTE_MAX_ETHPORTS', get_option('max_ethports'))
>  dpdk_conf.set('RTE_LIBEAL_USE_HPET', get_option('use_hpet'))
>  dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp'))
> @@ -250,6 +256,44 @@ compile_time_cpuflags = []
>  subdir(arch_subdir)
>  dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags))
>  
> +max_lcores = get_option('max_lcores')
> +max_numa_nodes = get_option('max_numa_nodes')
> +if max_lcores > 0
> +	dpdk_conf.set('RTE_MAX_LCORE', max_lcores)
> +elif max_lcores == -1
> +	if not meson.is_cross_build()
> +		max_lcores = run_command(get_cpu_count_cmd).stdout().to_int()
> +		min_lcores = 2
> +		if max_lcores < min_lcores
> +			message('Found less than @0@ cores, building for @0@ cores'.format(min_lcores))
> +			max_lcores = min_lcores
> +		else
> +			message('Found @0@ cores'.format(max_lcores))
> +		endif
> +		dpdk_conf.set('RTE_MAX_LCORE', max_lcores)
> +	endif
> +endif
> +
> +if max_numa_nodes > 0
> +	dpdk_conf.set('RTE_MAX_NUMA_NODES', max_numa_nodes)
> +elif max_numa_nodes == -1
> +	if not meson.is_cross_build()
> +		max_numa_nodes = run_command(get_numa_count_cmd).stdout().to_int()
> +		message('Found @0@ numa nodes'.format(max_numa_nodes))
> +		dpdk_conf.set('RTE_MAX_NUMA_NODES', max_numa_nodes)
> +	endif
> +endif
> +
> +# check that cpu and numa count is set in cross builds
> +if meson.is_cross_build()
> +	if not dpdk_conf.has('RTE_MAX_LCORE')
> +		error('Number of cores for cross build not specified in @0@ subdir (e.g. in a cross-file) nor on the cmdline'.format(arch_subdir))
> +	endif
> +	if not dpdk_conf.has('RTE_MAX_NUMA_NODES')
> +		error('Number of numa nodes for cross build not specified in @0@ subdir (e.g. in a cross-file) nor on the cmdline'.format(arch_subdir))
> +	endif
> +endif
> +

I think we need some comments explaining all the logic here, and probably
some documentation updates.

>  # set the install path for the drivers
>  dpdk_conf.set_quoted('RTE_EAL_PMD_PATH', eal_pmd_path)
>  
> diff --git a/meson_options.txt b/meson_options.txt
> index 9bf18ab6b..a78c21b0d 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -26,10 +26,10 @@ option('machine', type: 'string', value: 'native',
>  	description: 'set the target machine type')
>  option('max_ethports', type: 'integer', value: 32,
>  	description: 'maximum number of Ethernet devices')
> -option('max_lcores', type: 'integer', value: 128,
> -	description: 'maximum number of cores/threads supported by EAL')
> -option('max_numa_nodes', type: 'integer', value: 4,
> -	description: 'maximum number of NUMA nodes supported by EAL')
> +option('max_lcores', type: 'integer', value: 0,
> +	description: 'maximum number of cores/threads supported by EAL. Set to positive integer to overwrite per-arch or cross-compilation defaults. Set to -1 to use number of cores on the build machine.')
> +option('max_numa_nodes', type: 'integer', value: 0,
> +	description: 'maximum number of NUMA nodes supported by EAL. Set to positive integer to overwrite per-arch or cross-compilation defaults. Set to -1 to use number of numa nodes on the build machine.')
>  option('enable_trace_fp', type: 'boolean', value: false,
>  	description: 'enable fast path trace points.')
>  option('tests', type: 'boolean', value: true,
> -- 
> 2.20.1
>
  
Juraj Linkeš Oct. 27, 2020, 3:50 p.m. UTC | #2
> -----Original Message-----
> From: Bruce Richardson <bruce.richardson@intel.com>
> Sent: Tuesday, October 27, 2020 12:21 PM
> To: Juraj Linkeš <juraj.linkes@pantheon.tech>
> Cc: Ruifeng.Wang@arm.com; Honnappa.Nagarahalli@arm.com;
> Phil.Yang@arm.com; vcchunga@amazon.com; Dharmik.Thakkar@arm.com;
> jerinjacobk@gmail.com; hemant.agrawal@nxp.com; dev@dpdk.org
> Subject: Re: [PATCH v4 3/6] build: optional NUMA and cpu counts detection
> 
> On Fri, Oct 23, 2020 at 04:48:05PM +0200, Juraj Linkeš wrote:
> > Add an option to automatically discover the host's numa and cpu counts
> > and use those values for a non cross-build.
> > Give users the option to override the per-arch default values or
> > values from cross files by specifying them on the command line with
> > -Dmax_lcores and -Dmax_numa_nodes.
> >
> > Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
> > ---
> >  buildtools/get_cpu_count.py  |  7 ++++++
> > buildtools/get_numa_count.py | 22 +++++++++++++++++
> >  buildtools/meson.build       |  2 ++
> >  config/meson.build           | 48 ++++++++++++++++++++++++++++++++++--
> >  meson_options.txt            |  8 +++---
> >  5 files changed, 81 insertions(+), 6 deletions(-)  create mode 100644
> > buildtools/get_cpu_count.py  create mode 100644
> > buildtools/get_numa_count.py
> >
> > diff --git a/buildtools/get_cpu_count.py b/buildtools/get_cpu_count.py
> > new file mode 100644 index 000000000..386f85f8b
> > --- /dev/null
> > +++ b/buildtools/get_cpu_count.py
> > @@ -0,0 +1,7 @@
> > +#!/usr/bin/python3
> > +# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2020
> > +PANTHEON.tech s.r.o.
> > +
> > +import os
> > +
> > +print(os.cpu_count())
> > diff --git a/buildtools/get_numa_count.py
> > b/buildtools/get_numa_count.py new file mode 100644 index
> > 000000000..f0c49973a
> > --- /dev/null
> > +++ b/buildtools/get_numa_count.py
> > @@ -0,0 +1,22 @@
> > +#!/usr/bin/python3
> > +# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2020
> > +PANTHEON.tech s.r.o.
> > +
> > +import ctypes
> > +import glob
> > +import os
> > +import subprocess
> > +
> > +if os.name == 'posix':
> > +    if os.path.isdir('/sys/devices/system/node'):
> > +        print(len(glob.glob('/sys/devices/system/node/node*')))
> > +    else:
> > +        print(subprocess.run(['sysctl', 'vm.ndomains'],
> > +capture_output=True).stdout)
> 
> I think you can shorten this, by just calling subprocess.run and not capturing
> anything, in which case the stdout will be printed as normal.
> 
> 	subprocess.run(['sysctl', 'vm.ndomains'])
> 

This will also print out the resulting object (e.g. CompletedProcess(args=['ls', '-ls', '/sys/devices/system/node'], returncode=0)), but an assignment will take care of that. I'll make the change.

> > +
> > +elif os.name == 'nt':
> > +    libkernel32 = ctypes.windll.kernel32
> > +
> > +    count = ctypes.c_ulong()
> > +
> > +    libkernel32.GetNumaHighestNodeNumber(ctypes.pointer(count))
> > +    print(count.value + 1)
> > diff --git a/buildtools/meson.build b/buildtools/meson.build index
> > 04808dabc..925e733b1 100644
> > --- a/buildtools/meson.build
> > +++ b/buildtools/meson.build
> > @@ -17,3 +17,5 @@ else
> >  endif
> >  map_to_win_cmd = py3 + files('map_to_win.py')  sphinx_wrapper = py3 +
> > files('call-sphinx-build.py')
> > +get_cpu_count_cmd = py3 + files('get_cpu_count.py')
> > +get_numa_count_cmd = py3 + files('get_numa_count.py')
> > diff --git a/config/meson.build b/config/meson.build index
> > 918ca2dfb..5c6fdcc7d 100644
> > --- a/config/meson.build
> > +++ b/config/meson.build
> > @@ -230,8 +230,14 @@ foreach arg: warning_flags  endforeach
> >
> >  # set other values pulled from the build options
> > -dpdk_conf.set('RTE_MAX_LCORE', get_option('max_lcores'))
> > -dpdk_conf.set('RTE_MAX_NUMA_NODES', get_option('max_numa_nodes'))
> > +if not meson.is_cross_build()
> > +	# set default values
> > +	# these defaults may be overwritten by meson.build in an arch subdir
> > +	# or later overwritten if a user specifies a value on the command line
> > +	# or the user chooses to use values discovered from the build machine
> > +	dpdk_conf.set('RTE_MAX_LCORE', 128)
> > +	dpdk_conf.set('RTE_MAX_NUMA_NODES', 4) endif
> 
> I think it might be better to remove this block, and instead set the values below
> where you do the additional checks. It's confusing having these settings
> managed in multiple places.
> 

It needs to be set before subdir(arch_subdir) which is why it's here.

The order goes like this:
Global defaults (or x86 defaults)
arch_subdir defaults overwrite those (or non-x86 defaults)
cmdline options or optional discovery overwrite those

What about I move these to config/x86/meson.build? It looks like the place where per-arch defaults should be. Then we'd only have the second part after arch_subdir.

> >  dpdk_conf.set('RTE_MAX_ETHPORTS', get_option('max_ethports'))
> > dpdk_conf.set('RTE_LIBEAL_USE_HPET', get_option('use_hpet'))
> > dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp')) @@
> > -250,6 +256,44 @@ compile_time_cpuflags = []
> >  subdir(arch_subdir)
> >  dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS',
> > ','.join(compile_time_cpuflags))
> >
> > +max_lcores = get_option('max_lcores') max_numa_nodes =
> > +get_option('max_numa_nodes') if max_lcores > 0
> > +	dpdk_conf.set('RTE_MAX_LCORE', max_lcores) elif max_lcores == -1
> > +	if not meson.is_cross_build()
> > +		max_lcores =
> run_command(get_cpu_count_cmd).stdout().to_int()
> > +		min_lcores = 2
> > +		if max_lcores < min_lcores
> > +			message('Found less than @0@ cores, building for
> @0@ cores'.format(min_lcores))
> > +			max_lcores = min_lcores
> > +		else
> > +			message('Found @0@ cores'.format(max_lcores))
> > +		endif
> > +		dpdk_conf.set('RTE_MAX_LCORE', max_lcores)
> > +	endif
> > +endif
> > +
> > +if max_numa_nodes > 0
> > +	dpdk_conf.set('RTE_MAX_NUMA_NODES', max_numa_nodes) elif
> > +max_numa_nodes == -1
> > +	if not meson.is_cross_build()
> > +		max_numa_nodes =
> run_command(get_numa_count_cmd).stdout().to_int()
> > +		message('Found @0@ numa nodes'.format(max_numa_nodes))
> > +		dpdk_conf.set('RTE_MAX_NUMA_NODES', max_numa_nodes)
> > +	endif
> > +endif
> > +
> > +# check that cpu and numa count is set in cross builds if
> > +meson.is_cross_build()
> > +	if not dpdk_conf.has('RTE_MAX_LCORE')
> > +		error('Number of cores for cross build not specified in @0@
> subdir (e.g. in a cross-file) nor on the cmdline'.format(arch_subdir))
> > +	endif
> > +	if not dpdk_conf.has('RTE_MAX_NUMA_NODES')
> > +		error('Number of numa nodes for cross build not specified in
> @0@ subdir (e.g. in a cross-file) nor on the cmdline'.format(arch_subdir))
> > +	endif
> > +endif
> > +
> 
> I think we need some comments explaining all the logic here, and probably some
> documentation updates.
> 

Will add comments. Where is this documented?

> >  # set the install path for the drivers
> > dpdk_conf.set_quoted('RTE_EAL_PMD_PATH', eal_pmd_path)
> >
> > diff --git a/meson_options.txt b/meson_options.txt index
> > 9bf18ab6b..a78c21b0d 100644
> > --- a/meson_options.txt
> > +++ b/meson_options.txt
> > @@ -26,10 +26,10 @@ option('machine', type: 'string', value: 'native',
> >  	description: 'set the target machine type')  option('max_ethports',
> > type: 'integer', value: 32,
> >  	description: 'maximum number of Ethernet devices')
> > -option('max_lcores', type: 'integer', value: 128,
> > -	description: 'maximum number of cores/threads supported by EAL')
> > -option('max_numa_nodes', type: 'integer', value: 4,
> > -	description: 'maximum number of NUMA nodes supported by EAL')
> > +option('max_lcores', type: 'integer', value: 0,
> > +	description: 'maximum number of cores/threads supported by EAL. Set
> > +to positive integer to overwrite per-arch or cross-compilation defaults. Set to
> -1 to use number of cores on the build machine.') option('max_numa_nodes',
> type: 'integer', value: 0,
> > +	description: 'maximum number of NUMA nodes supported by EAL. Set to
> > +positive integer to overwrite per-arch or cross-compilation defaults.
> > +Set to -1 to use number of numa nodes on the build machine.')
> >  option('enable_trace_fp', type: 'boolean', value: false,
> >  	description: 'enable fast path trace points.')  option('tests',
> > type: 'boolean', value: true,
> > --
> > 2.20.1
> >
  
Bruce Richardson Oct. 27, 2020, 4:04 p.m. UTC | #3
On Tue, Oct 27, 2020 at 03:50:46PM +0000, Juraj Linkeš wrote:
> 
> 
> > -----Original Message-----
> > From: Bruce Richardson <bruce.richardson@intel.com>
> > Sent: Tuesday, October 27, 2020 12:21 PM
> > To: Juraj Linkeš <juraj.linkes@pantheon.tech>
> > Cc: Ruifeng.Wang@arm.com; Honnappa.Nagarahalli@arm.com;
> > Phil.Yang@arm.com; vcchunga@amazon.com; Dharmik.Thakkar@arm.com;
> > jerinjacobk@gmail.com; hemant.agrawal@nxp.com; dev@dpdk.org
> > Subject: Re: [PATCH v4 3/6] build: optional NUMA and cpu counts detection
> > 
> > On Fri, Oct 23, 2020 at 04:48:05PM +0200, Juraj Linkeš wrote:
> > > Add an option to automatically discover the host's numa and cpu counts
> > > and use those values for a non cross-build.
> > > Give users the option to override the per-arch default values or
> > > values from cross files by specifying them on the command line with
> > > -Dmax_lcores and -Dmax_numa_nodes.
> > >
> > > Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
> > > ---
> > >  buildtools/get_cpu_count.py  |  7 ++++++
> > > buildtools/get_numa_count.py | 22 +++++++++++++++++
> > >  buildtools/meson.build       |  2 ++
> > >  config/meson.build           | 48 ++++++++++++++++++++++++++++++++++--
> > >  meson_options.txt            |  8 +++---
> > >  5 files changed, 81 insertions(+), 6 deletions(-)  create mode 100644
> > > buildtools/get_cpu_count.py  create mode 100644
> > > buildtools/get_numa_count.py
> > >
> > > diff --git a/buildtools/get_cpu_count.py b/buildtools/get_cpu_count.py
> > > new file mode 100644 index 000000000..386f85f8b
> > > --- /dev/null
> > > +++ b/buildtools/get_cpu_count.py
> > > @@ -0,0 +1,7 @@
> > > +#!/usr/bin/python3
> > > +# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2020
> > > +PANTHEON.tech s.r.o.
> > > +
> > > +import os
> > > +
> > > +print(os.cpu_count())
> > > diff --git a/buildtools/get_numa_count.py
> > > b/buildtools/get_numa_count.py new file mode 100644 index
> > > 000000000..f0c49973a
> > > --- /dev/null
> > > +++ b/buildtools/get_numa_count.py
> > > @@ -0,0 +1,22 @@
> > > +#!/usr/bin/python3
> > > +# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2020
> > > +PANTHEON.tech s.r.o.
> > > +
> > > +import ctypes
> > > +import glob
> > > +import os
> > > +import subprocess
> > > +
> > > +if os.name == 'posix':
> > > +    if os.path.isdir('/sys/devices/system/node'):
> > > +        print(len(glob.glob('/sys/devices/system/node/node*')))
> > > +    else:
> > > +        print(subprocess.run(['sysctl', 'vm.ndomains'],
> > > +capture_output=True).stdout)
> > 
> > I think you can shorten this, by just calling subprocess.run and not capturing
> > anything, in which case the stdout will be printed as normal.
> > 
> > 	subprocess.run(['sysctl', 'vm.ndomains'])
> > 
> 
> This will also print out the resulting object (e.g. CompletedProcess(args=['ls', '-ls', '/sys/devices/system/node'], returncode=0)), but an assignment will take care of that. I'll make the change.
> 

Not unless you are running interactively in the python3 REPL.
For example:

$ cat test_meminfo.py
#! /usr/bin/env python3

from subprocess import run
run(['cat', '/proc/meminfo'])

$ python3 test_meminfo.py | tail -n 5
Hugepagesize:       2048 kB
Hugetlb:        17825792 kB
DirectMap4k:     1056788 kB
DirectMap2M:     9758720 kB
DirectMap1G:    88080384 kB

$
  

Patch

diff --git a/buildtools/get_cpu_count.py b/buildtools/get_cpu_count.py
new file mode 100644
index 000000000..386f85f8b
--- /dev/null
+++ b/buildtools/get_cpu_count.py
@@ -0,0 +1,7 @@ 
+#!/usr/bin/python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 PANTHEON.tech s.r.o.
+
+import os
+
+print(os.cpu_count())
diff --git a/buildtools/get_numa_count.py b/buildtools/get_numa_count.py
new file mode 100644
index 000000000..f0c49973a
--- /dev/null
+++ b/buildtools/get_numa_count.py
@@ -0,0 +1,22 @@ 
+#!/usr/bin/python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (c) 2020 PANTHEON.tech s.r.o.
+
+import ctypes
+import glob
+import os
+import subprocess
+
+if os.name == 'posix':
+    if os.path.isdir('/sys/devices/system/node'):
+        print(len(glob.glob('/sys/devices/system/node/node*')))
+    else:
+        print(subprocess.run(['sysctl', 'vm.ndomains'], capture_output=True).stdout)
+
+elif os.name == 'nt':
+    libkernel32 = ctypes.windll.kernel32
+
+    count = ctypes.c_ulong()
+
+    libkernel32.GetNumaHighestNodeNumber(ctypes.pointer(count))
+    print(count.value + 1)
diff --git a/buildtools/meson.build b/buildtools/meson.build
index 04808dabc..925e733b1 100644
--- a/buildtools/meson.build
+++ b/buildtools/meson.build
@@ -17,3 +17,5 @@  else
 endif
 map_to_win_cmd = py3 + files('map_to_win.py')
 sphinx_wrapper = py3 + files('call-sphinx-build.py')
+get_cpu_count_cmd = py3 + files('get_cpu_count.py')
+get_numa_count_cmd = py3 + files('get_numa_count.py')
diff --git a/config/meson.build b/config/meson.build
index 918ca2dfb..5c6fdcc7d 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -230,8 +230,14 @@  foreach arg: warning_flags
 endforeach
 
 # set other values pulled from the build options
-dpdk_conf.set('RTE_MAX_LCORE', get_option('max_lcores'))
-dpdk_conf.set('RTE_MAX_NUMA_NODES', get_option('max_numa_nodes'))
+if not meson.is_cross_build()
+	# set default values
+	# these defaults may be overwritten by meson.build in an arch subdir
+	# or later overwritten if a user specifies a value on the command line
+	# or the user chooses to use values discovered from the build machine
+	dpdk_conf.set('RTE_MAX_LCORE', 128)
+	dpdk_conf.set('RTE_MAX_NUMA_NODES', 4)
+endif
 dpdk_conf.set('RTE_MAX_ETHPORTS', get_option('max_ethports'))
 dpdk_conf.set('RTE_LIBEAL_USE_HPET', get_option('use_hpet'))
 dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp'))
@@ -250,6 +256,44 @@  compile_time_cpuflags = []
 subdir(arch_subdir)
 dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags))
 
+max_lcores = get_option('max_lcores')
+max_numa_nodes = get_option('max_numa_nodes')
+if max_lcores > 0
+	dpdk_conf.set('RTE_MAX_LCORE', max_lcores)
+elif max_lcores == -1
+	if not meson.is_cross_build()
+		max_lcores = run_command(get_cpu_count_cmd).stdout().to_int()
+		min_lcores = 2
+		if max_lcores < min_lcores
+			message('Found less than @0@ cores, building for @0@ cores'.format(min_lcores))
+			max_lcores = min_lcores
+		else
+			message('Found @0@ cores'.format(max_lcores))
+		endif
+		dpdk_conf.set('RTE_MAX_LCORE', max_lcores)
+	endif
+endif
+
+if max_numa_nodes > 0
+	dpdk_conf.set('RTE_MAX_NUMA_NODES', max_numa_nodes)
+elif max_numa_nodes == -1
+	if not meson.is_cross_build()
+		max_numa_nodes = run_command(get_numa_count_cmd).stdout().to_int()
+		message('Found @0@ numa nodes'.format(max_numa_nodes))
+		dpdk_conf.set('RTE_MAX_NUMA_NODES', max_numa_nodes)
+	endif
+endif
+
+# check that cpu and numa count is set in cross builds
+if meson.is_cross_build()
+	if not dpdk_conf.has('RTE_MAX_LCORE')
+		error('Number of cores for cross build not specified in @0@ subdir (e.g. in a cross-file) nor on the cmdline'.format(arch_subdir))
+	endif
+	if not dpdk_conf.has('RTE_MAX_NUMA_NODES')
+		error('Number of numa nodes for cross build not specified in @0@ subdir (e.g. in a cross-file) nor on the cmdline'.format(arch_subdir))
+	endif
+endif
+
 # set the install path for the drivers
 dpdk_conf.set_quoted('RTE_EAL_PMD_PATH', eal_pmd_path)
 
diff --git a/meson_options.txt b/meson_options.txt
index 9bf18ab6b..a78c21b0d 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -26,10 +26,10 @@  option('machine', type: 'string', value: 'native',
 	description: 'set the target machine type')
 option('max_ethports', type: 'integer', value: 32,
 	description: 'maximum number of Ethernet devices')
-option('max_lcores', type: 'integer', value: 128,
-	description: 'maximum number of cores/threads supported by EAL')
-option('max_numa_nodes', type: 'integer', value: 4,
-	description: 'maximum number of NUMA nodes supported by EAL')
+option('max_lcores', type: 'integer', value: 0,
+	description: 'maximum number of cores/threads supported by EAL. Set to positive integer to overwrite per-arch or cross-compilation defaults. Set to -1 to use number of cores on the build machine.')
+option('max_numa_nodes', type: 'integer', value: 0,
+	description: 'maximum number of NUMA nodes supported by EAL. Set to positive integer to overwrite per-arch or cross-compilation defaults. Set to -1 to use number of numa nodes on the build machine.')
 option('enable_trace_fp', type: 'boolean', value: false,
 	description: 'enable fast path trace points.')
 option('tests', type: 'boolean', value: true,