diff --git a/.github/make-benchmark-table.rb b/.github/make-benchmark-table.rb index 3b20847e..1287b723 100755 --- a/.github/make-benchmark-table.rb +++ b/.github/make-benchmark-table.rb @@ -27,6 +27,17 @@ suites.uniq! +def full_suite_name(s) + case s + when 'st' + return 'Single Threaded' + when 'mt' + return 'Multithreaded' + else + return s + end +end + def row(arr) puts '| ' + arr.join(' | ') + ' |' end @@ -34,10 +45,10 @@ def row(arr) Algorithm execution times in seconds """ -row ['Algorithm', *suites.map{|suite|suite.gsub /_/, ' '}] +row ['Algorithm', *suites.map{|suite|full_suite_name suite}] row (suites.size+1).times.map{ |i| '---' } -results.each do |algo_name, algo_results| +results.sort.each do |algo_name, algo_results| times = suites.map{ |suite| algo_results[suite] } row ["`#{algo_name}`", *times] end diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6afecd38..2521ac6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,10 +197,10 @@ jobs: - name: benchmark algorithms if: ${{ matrix.id == 'cpp' }} run: | - for suite in single_threaded memoize; do + for suite in st mt; do meson test --benchmark --suite $suite -C iguana_build | tee benchmark_$suite.txt done - iguana_src/.github/make-benchmark-table.rb benchmark_{single_threaded,memoize}.txt | xargs -0 -I{} echo {} >> $GITHUB_STEP_SUMMARY + iguana_src/.github/make-benchmark-table.rb benchmark_{st,mt}.txt | xargs -0 -I{} echo {} >> $GITHUB_STEP_SUMMARY ### reproducible build test - name: reproducible build test if: ${{ matrix.id == 'cpp' }} diff --git a/bind/python/README.md b/bind/python/README.md index ca9158dd..dbff9624 100644 --- a/bind/python/README.md +++ b/bind/python/README.md @@ -36,7 +36,7 @@ For Python to be able to find and use these bindings, you need to set some envir ## Running the Examples -Example Python scripts are found in this directory as `iguana_ex_*.py`; they will be installed in the `bin/` subdirectory. +Example Python scripts are found in the [`examples/` directory as `iguana_ex_python*.py`](/examples); they will be installed in the `bin/` subdirectory. Most of them are analogous to the C++ examples, but some may be specific to the Python bindings. diff --git a/bind/python/meson.build b/bind/python/meson.build index b55d22d1..d11240e0 100644 --- a/bind/python/meson.build +++ b/bind/python/meson.build @@ -1,12 +1 @@ install_subdir('pyiguana', install_dir: project_pythondir) - -if (get_option('install_examples')) - python_examples = [ - 'iguana_ex_python_00_run_functions.py', - 'iguana_ex_python_01_action_functions.py', - 'iguana_ex_python_hipopy.py', - ] - foreach example : python_examples - install_data(example, install_dir: get_option('bindir'), install_mode: 'rwxr-xr-x') - endforeach -endif diff --git a/bind/python/requirements.txt b/bind/python/requirements.txt index a9ea7c10..fdd7c52e 100644 --- a/bind/python/requirements.txt +++ b/bind/python/requirements.txt @@ -1,6 +1,3 @@ cppyy==3.1.2 -cppyy-backend==1.15.2 -cppyy-cling==6.30.0 -CPyCppyy==1.12.16 pkgconfig==1.5.5 hipopy==2.0.1 diff --git a/bind/python/iguana_ex_python_00_run_functions.py b/examples/iguana_ex_python_00_run_functions.py similarity index 100% rename from bind/python/iguana_ex_python_00_run_functions.py rename to examples/iguana_ex_python_00_run_functions.py diff --git a/bind/python/iguana_ex_python_01_action_functions.py b/examples/iguana_ex_python_01_action_functions.py similarity index 100% rename from bind/python/iguana_ex_python_01_action_functions.py rename to examples/iguana_ex_python_01_action_functions.py diff --git a/bind/python/iguana_ex_python_hipopy.py b/examples/iguana_ex_python_hipopy.py similarity index 100% rename from bind/python/iguana_ex_python_hipopy.py rename to examples/iguana_ex_python_hipopy.py diff --git a/examples/meson.build b/examples/meson.build index f13e20f3..f54cd84b 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -2,22 +2,36 @@ example_config_files_prefix = project_etcdir / 'examples' install_subdir('config' / 'examples', install_dir: example_config_files_prefix, strip_directory: true) -# example source information +# compiled examples example_sources = { - 'iguana_ex_cpp_00_run_functions': {'sources': [ 'iguana_ex_cpp_00_run_functions.cc' ]}, - 'iguana_ex_cpp_00_run_functions_with_banks': {'sources': [ 'iguana_ex_cpp_00_run_functions_with_banks.cc' ]}, - 'iguana_ex_cpp_01_action_functions': {'sources': [ 'iguana_ex_cpp_01_action_functions.cc' ]}, + # C++ + 'iguana_ex_cpp_00_run_functions': { + 'sources': [ 'iguana_ex_cpp_00_run_functions.cc' ], + 'suite': 'cpp', + }, + 'iguana_ex_cpp_00_run_functions_with_banks': { + 'sources': [ 'iguana_ex_cpp_00_run_functions_with_banks.cc' ], + 'suite': 'cpp', + }, + 'iguana_ex_cpp_01_action_functions': { + 'sources': [ 'iguana_ex_cpp_01_action_functions.cc' ], + 'suite': 'cpp', + }, 'iguana_ex_cpp_dataframes': { 'sources': [ 'iguana_ex_cpp_dataframes.cc' ], + 'suite': 'cpp', 'build_this': ROOT_dep.found() and hipo_dep_dataframes_found, 'test_this': false, # FIXME: disabled for now, see https://github.com/JeffersonLab/iguana/issues/384 }, 'iguana_ex_cpp_config_files': { 'sources': [ 'iguana_ex_cpp_config_files.cc' ], + 'suite': 'cpp', 'test_args': [ get_option('prefix') / example_config_files_prefix ], }, + # Fortran 'iguana_ex_fortran_01_action_functions': { 'sources': [ 'iguana_ex_fortran_01_action_functions.f' ], + 'suite': 'fortran', 'build_this': get_option('bind_fortran') and ROOT_dep.found(), # depends on physics::InclusiveKinematics, which depends on ROOT }, } @@ -37,7 +51,7 @@ foreach example, info : example_sources test( example, example_exe, - suite: [ 'example', 'single_threaded' ], + suite: [ 'example', info.get('suite') ], args: info.get( 'test_args', [ get_option('test_data_file'), '100' ], # don't run too many events, or meson test log will be huge @@ -48,3 +62,30 @@ foreach example, info : example_sources endif endif endforeach + +# Python examples +if (get_option('bind_python')) + python_examples = [ + 'iguana_ex_python_00_run_functions.py', + 'iguana_ex_python_01_action_functions.py', + 'iguana_ex_python_hipopy.py', + ] + foreach example : python_examples + install_data( + example, + install_dir: get_option('bindir'), + install_mode: 'rwxr-xr-x' + ) + if fs.is_file(get_option('test_data_file')) + test( + example, + prog_python, + suite: [ 'example', 'python' ], + args: [ example, get_option('test_data_file'), '100' ], # don't run too many events, or meson test log will be huge + workdir: meson.current_source_dir(), + env: project_test_env, + timeout: 240, + ) + endif + endforeach +endif diff --git a/meson.build b/meson.build index 28d21364..46d2fd15 100644 --- a/meson.build +++ b/meson.build @@ -78,6 +78,11 @@ prog_ruby = find_program( version: '>=3.0.0', required: use_chameleon, ) +prog_python = find_program( + 'python3', + version: '>=3.0.0', + required: get_option('bind_python'), +) rcdb_dep = dependency( 'rcdb', fallback: ['rcdb', 'rcdb_dep'], @@ -179,6 +184,16 @@ project_test_env.set( 'suppressions=' + meson.project_source_root() / 'meson' / 'lsan.supp', ) +# additional test environment variables +project_test_env.prepend( + 'PKG_CONFIG_PATH', + get_option('prefix') / get_option('libdir') / 'pkgconfig' +) +project_test_env.prepend( + 'PYTHONPATH', + get_option('prefix') / 'python' +) + # set preprocessor macros add_project_arguments('-DIGUANA_ETCDIR="' + get_option('prefix') / project_etcdir + '"', language: [ 'cpp' ]) if ROOT_dep.found() diff --git a/meson/setup_python_venv.sh b/meson/setup_python_venv.sh new file mode 100755 index 00000000..22920a1b --- /dev/null +++ b/meson/setup_python_venv.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail +python -m venv .venv +source .venv/bin/activate +python -m pip install -r bind/python/requirements.txt +python -m pip install -r doc/mkdocs/requirements.txt +echo "DONE: now source one of the activate scripts in '.venv/bin/'" diff --git a/src/iguana/tests/meson.build b/src/iguana/tests/meson.build index 91f88524..a2005919 100644 --- a/src/iguana/tests/meson.build +++ b/src/iguana/tests/meson.build @@ -39,9 +39,9 @@ foreach algo : algo_dict # single threaded tests test( - '-'.join(['algorithm', test_name_algo]), + '-'.join(['st', 'algorithm', test_name_algo]), test_exe, - suite: [ 'algorithm', 'single_threaded' ], + suite: [ 'algorithm', 'st' ], args: [ 'algorithm', '-n', get_option('test_num_events').to_string() ] + test_args, env: project_test_env, timeout: 0, @@ -49,9 +49,9 @@ foreach algo : algo_dict ) if not should_fail benchmark( - '-'.join(['benchmark', 'single_threaded', test_name_algo]), + '-'.join(['benchmark', 'st', test_name_algo]), test_exe, - suite: [ 'algorithm', 'single_threaded' ], + suite: [ 'algorithm', 'st' ], args: [ 'algorithm', '-n', '0' ] + test_args, # benchmark all the events env: project_test_env, timeout: 0, @@ -60,34 +60,32 @@ foreach algo : algo_dict # multithreaded tests if get_option('z_test_multithreading') - foreach concurrency_model : [ 'memoize' ] # note: we used to have more, so leave this array in case we want more - multithreading_args = [ - 'multithreading', - '-j', get_option('test_num_threads').to_string(), - '-m', concurrency_model, - '-V', # vary run number - ] - test( - '-'.join(['multithreading', concurrency_model, test_name_algo]), + multithreading_args = [ + 'multithreading', + '-j', get_option('test_num_threads').to_string(), + '-m', 'memoize', + '-V', # vary run number + ] + test( + '-'.join(['mt', 'algorithm', test_name_algo]), + test_exe, + suite: [ 'algorithm', 'mt' ], + args: multithreading_args + [ '-n', get_option('test_num_events').to_string() ] + test_args, + is_parallel: false, + env: project_test_env, + timeout: 0, + should_fail: should_fail, + ) + if not should_fail + benchmark( + '-'.join(['benchmark', 'mt', test_name_algo]), test_exe, - suite: [ 'algorithm', 'multithreading', concurrency_model ], - args: multithreading_args + [ '-n', get_option('test_num_events').to_string() ] + test_args, - is_parallel: false, + suite: [ 'algorithm', 'mt' ], + args: multithreading_args + [ '-n', '0' ] + test_args, # benchmark all the events env: project_test_env, timeout: 0, - should_fail: should_fail, ) - if not should_fail - benchmark( - '-'.join(['benchmark', f'multithreading_@concurrency_model@', test_name_algo]), - test_exe, - suite: [ 'algorithm', 'multithreading', concurrency_model ], - args: multithreading_args + [ '-n', '0' ] + test_args, # benchmark all the events - env: project_test_env, - timeout: 0, - ) - endif - endforeach + endif endif endif @@ -110,7 +108,7 @@ foreach algo : algo_dict test( 'validator-' + test_name_algo, test_exe, - suite: [ 'validator', 'single_threaded' ], + suite: [ 'validator' ], args: test_args, env: project_test_env, timeout: get_option('test_validator_all_stats') ? 0 : 120, @@ -126,7 +124,7 @@ foreach command : [ '', 'unknown', 'algorithm', 'validator', 'unit', 'config' ] test( 'iguana_test-usage-guide' + (command == '' ? '' : '-' + command), test_exe, - suite: [ 'misc', 'single_threaded' ], + suite: [ 'misc' ], args: command == '' ? [] : [ command ], env: project_test_env, should_fail: true, @@ -140,7 +138,7 @@ foreach test_num : [ '1', '2', '3' ] test( 'config-test_' + test_num, test_exe, - suite: [ 'misc', 'single_threaded' ], + suite: [ 'misc' ], args: [ 'config', '-t', test_num, '-v' ], env: project_test_env, ) @@ -150,7 +148,7 @@ endforeach test( 'logger', test_exe, - suite: [ 'misc', 'single_threaded' ], + suite: [ 'misc' ], args: [ 'logger' ], env: project_test_env ) @@ -160,7 +158,7 @@ if fs.is_file(get_option('test_data_file')) test( 'banklist', test_exe, - suite: [ 'misc', 'single_threaded' ], + suite: [ 'misc' ], args: [ 'banklist', '-f', get_option('test_data_file') ], env: project_test_env )