Skip to content

Commit 2c794d1

Browse files
committed
Address comments
1 parent d7c8d14 commit 2c794d1

File tree

8 files changed

+77
-87
lines changed

8 files changed

+77
-87
lines changed

docs/configure.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ You can view logger's log level as a general cut off.
194194
For example, if we have set it to ``warning``, no debug or informational messages would ever be printed.
195195

196196
Finally, there is a special set of handlers for handling performance log messages.
197-
Performance log messages are generated *only* for `performance tests <tutorial_basics.html#writing-a-performance-test>`__, i.e., tests defining the :attr:`perf_variables <reframe.core.pipeline.RegressionTest.perf_variables>` attribute.
197+
Performance log messages are generated *only* for `performance tests <tutorial_basics.html#writing-a-performance-test>`__, i.e., tests defining the :attr:`~reframe.core.pipeline.RegressionTest.perf_variables` or the :attr:`~reframe.core.pipeline.RegressionTest.perf_patterns` attributes.
198198
The performance log handlers are stored in the ``handlers_perflog`` property.
199199
The ``filelog`` handler used in this example will create a file per test and per system/partition combination (``./<system>/<partition>/<testname>.log``) and will append to it the obtained performance data every time a performance test is run.
200200
Notice how the message to be logged is structured in the ``format`` property, such that it can be easily parsed from post processing tools.

docs/tutorial_advanced.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ Let's inspect the build script generated by ReFrame:
161161
162162
trap _onerror ERR
163163
164-
make -j 1 CPPFLAGS="-DELEM_TYPE=float"
164+
make -j 1 CPPFLAGS="-DELEM_TYPE=float" CC=cc CXX=CC
165165
166166
167167
The compiler variables (``CC``, ``CXX`` etc.) are set based on the corresponding values specified in the `configuration <config_reference.html#environment-configuration>`__ of the current environment.
@@ -175,7 +175,7 @@ In this case, ``make`` will be invoked as follows:
175175

176176
.. code::
177177
178-
make -j 1 CPPFLAGS="-DELEM_TYPE=float"
178+
make -j 1 CPPFLAGS="-DELEM_TYPE=float" CC=cc CXX=CC
179179
180180
Notice that the ``-j 1`` option is always generated.
181181
We can increase the build concurrency by setting the :attr:`~reframe.core.buildsystems.Make.max_concurrency` attribute.
@@ -681,8 +681,8 @@ The test will verify that all the nodes print the expected host name:
681681
:emphasize-lines: 10-
682682

683683
The first thing to notice in this test is that :attr:`~reframe.core.pipeline.RegressionTest.num_tasks` is set to zero as default, which is a requirement for flexible tests.
684-
However, this value is set to the actual number of tasks during the ``run`` pipeline stage.
685-
Lastly, the sanity check of this test counts the host names printed and verifies that the total count equals :attr:`~reframe.core.pipeline.RegressionTest.num_tasks`.
684+
However, with flexible tests, this value is updated right after the job completes to the actual number of tasks that were used.
685+
Consequenly, this allows the sanity function of the tests to assert that the number host names printed matches :attr:`~reframe.core.pipeline.RegressionTest.num_tasks`.
686686

687687
.. |--flex-alloc-nodes| replace:: :attr:`--flex-alloc-nodes`
688688
.. _--flex-alloc-nodes: manpage.html#cmdoption-flex-alloc-nodes

docs/tutorial_basics.rst

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -664,11 +664,13 @@ For running the benchmark, we need to set the OpenMP number of threads and pin t
664664
You can set environment variables in a ReFrame test through the :attr:`~reframe.core.pipeline.RegressionTest.variables` dictionary.
665665

666666
What makes a ReFrame test a performance test is the definition of at least one :ref:`performance function<deferrable-performance-functions>`.
667-
Similarly to a test's :func:`@sanity_function<reframe.core.pipeline.RegressionMixin.sanity_function>`, a performance function is simply a member function decorated with the :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` decorator, which is responsible for extracting a specified performance quantity from a regression test.
668-
The :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` decorator must be passed the units of the quantity to be extracted, and it also takes the optional argument ``perf_key`` to customize the name of the extracted performance variable.
669-
If ``perf_key`` is not provided, the performance variable will take the name of the decorated performance function.
667+
Similarly to a test's :func:`@sanity_function<reframe.core.pipeline.RegressionMixin.sanity_function>`, a performance function is a member function decorated with the :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` decorator, which binds the decorated function to a given unit.
668+
These functions can be used by the regression test to extract, measure or compute a given quantity of interest; where in this context, the values returned by a performance function are referred to as performance variables.
669+
Alternatively, performance functions can also be thought as `tools` available to the regression test for extracting performance variables.
670+
By default, ReFrame will attempt to execute all the available performance functions during the test's ``performance`` stage, producing a single performance variable out of each of the available performance functions.
671+
These default-generated performance variables are defined in the regression test's attribute :attr:`~reframe.core.pipeline.RegressionTest.perf_variables` during class instantiation, and their default name matches the name of their associated performance function.
672+
However, one could customize the default-generated performance variable's name by passing the ``perf-key`` argument to the :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` decorator of the associated performance function.
670673

671-
ReFrame identifies all member functions that use the :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` decorator, and will automatically schedule them for execution during the ``performance`` pipeline stage of the test.
672674
In this example, we extract four performance variables, namely the memory bandwidth values for each of the "Copy", "Scale", "Add" and "Triad" sub-benchmarks of STREAM, where each of the performance functions use the :func:`~reframe.utility.sanity.extractsingle` utility function.
673675
For each of the sub-benchmarks we extract the "Best Rate MB/s" column of the output (see below) and we convert that to a float.
674676

@@ -731,6 +733,45 @@ The :option:`--performance-report` will generate a short report at the end for e
731733
Log file(s) saved in: '/var/folders/h7/k7cgrdl13r996m4dmsvjq7v80000gp/T/rfm-gczplnic.log'
732734
733735
736+
---------------------------------------------------
737+
Setting explicitly the test's performance variables
738+
---------------------------------------------------
739+
740+
In the above STREAM example, all four performance functions were almost identical except for a small part of the regex pattern, which led to some code repetition.
741+
Even though the performance functions were rather simple and the code repetition was not much in that case, this is still not a good practice and it is certainly an approach that would not scale when using more complex performance functions.
742+
Hence, in this example, we show how to collapse all these four performance functions into a single function and how to reuse this single performance function to create multiple performance variables.
743+
744+
.. code-block:: console
745+
746+
cat tutorials/basics/stream/stream2.py
747+
748+
.. literalinclude:: ../tutorials/basics/stream/stream2.py
749+
:lines: 6-
750+
:emphasize-lines: 28-
751+
752+
As shown in the highlighted lines, this example collapses the four performance functions from the previous example into the :func:`extract_bw` function, which is also decorated with the :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` decorator with the units set to ``'MB/s'``.
753+
However, the :func:`extract_bw` function now takes the optional argument ``kind`` which selects the STREAM benchmark to extract.
754+
By default, this argument is set to ``'Copy'`` because functions decorated with :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` are only allowed to have ``self`` as a non-default argument.
755+
Thus, from this performance function definition, ReFrame will default-generate a single performance variable during the test instantiation under the name ``extract_bw``, where this variable will report the performance results from the ``Copy`` benchmark.
756+
With no further action from our side, ReFrame would just report the performance of the test based on this default-generated performance variable, but that is not what we are after here.
757+
Therefore, we must modify these default performance variables so that this version of the STREAM test produces the same results as in the previous example.
758+
As mentioned before, the performance variables (also the default-generated ones) are stored in the :attr:`~reframe.core.pipeline.RegressionTest.perf_variables` dictionary, so all we need to do is to redefine this mapping with our desired performance variables as done in the pre-performance pipeline hook :func:`set_perf_variables`.
759+
760+
.. tip::
761+
Performance functions may also be generated inline using the :func:`~reframe.utility.sanity.make_performance_function` utility as shown below.
762+
763+
.. code-block:: python
764+
765+
@run_before('performance')
766+
def set_perf_vars(self):
767+
self.perf_variables = {
768+
'Copy': sn.make_performance_function(
769+
sn.extractsingle(r'Copy:\s+(\S+)\s+.*',
770+
self.stdout, 1, float),
771+
'MB/s'
772+
)
773+
}
774+
734775
-----------------------
735776
Adding reference values
736777
-----------------------
@@ -747,22 +788,23 @@ In the following example, we set the reference values for all the STREAM sub-ben
747788

748789
.. code-block:: console
749790
750-
cat tutorials/basics/stream/stream2.py
791+
cat tutorials/basics/stream/stream3.py
751792
752793
753-
.. literalinclude:: ../tutorials/basics/stream/stream2.py
794+
.. literalinclude:: ../tutorials/basics/stream/stream3.py
754795
:lines: 6-
755796
:emphasize-lines: 18-25
756797

757798

758799
The performance reference tuple consists of the reference value, the lower and upper thresholds expressed as fractional numbers relative to the reference value, and the unit of measurement.
759800
If any of the thresholds is not relevant, :class:`None` may be used instead.
801+
Also, the units in this :attr:`~reframe.core.pipeline.RegressionTest.reference` variable are entirely optional, since they were already provided through the :attr:`@performance_function<reframe.core.pipeline.RegressionMixin.performance_function>` decorator.
760802

761803
If any obtained performance value is beyond its respective thresholds, the test will fail with a summary as shown below:
762804

763805
.. code-block:: console
764806
765-
./bin/reframe -c tutorials/basics/stream/stream2.py -r --performance-report
807+
./bin/reframe -c tutorials/basics/stream/stream3.py -r --performance-report
766808
767809
768810
.. code-block:: none
@@ -779,30 +821,6 @@ If any obtained performance value is beyond its respective thresholds, the test
779821
* Rerun with '-n StreamWithRefTest -p gnu --system catalina:default'
780822
* Reason: performance error: failed to meet reference: Copy=24586.5, expected 55200 (l=52440.0, u=57960.0)
781823
782-
Also, note how the performance syntax for this example is far more compact in comparison to our first iteration of the STREAM test.
783-
In that first STREAM example, all four performance functions were almost identical, except for a small part of the regex pattern, which led to a lot of code repetition.
784-
Hence, this example collapses all four performance functions into a single performance function, which now takes an optional argument to select the quantity to extract.
785-
Then, the performance variables of the test can be defined by setting the respective entries in the :attr:`~reframe.core.pipeline.RegressionTest.perf_variables` dictionary.
786-
787-
.. literalinclude:: ../tutorials/basics/stream/stream2.py
788-
:lines: 41-
789-
790-
.. note::
791-
Performance functions may also be generated inline using the :func:`~reframe.utility.sanity.make_performance_function` utility as shown below.
792-
793-
.. code-block:: python
794-
795-
@run_before('performance')
796-
def set_perf_vars(self):
797-
self.perf_variables = {
798-
'Copy': sn.make_performance_function(
799-
sn.extractsingle(r'Copy:\s+(\S+)\s+.*',
800-
self.stdout, 1, float),
801-
'MB/s'
802-
)
803-
}
804-
805-
806824
------------------------------
807825
Examining the performance logs
808826
------------------------------
@@ -1122,9 +1140,9 @@ Let's see and comment the changes:
11221140

11231141
.. code-block:: console
11241142
1125-
cat tutorials/basics/stream/stream3.py
1143+
cat tutorials/basics/stream/stream4.py
11261144
1127-
.. literalinclude:: ../tutorials/basics/stream/stream3.py
1145+
.. literalinclude:: ../tutorials/basics/stream/stream4.py
11281146
:lines: 6-
11291147
:emphasize-lines: 8, 27-41, 46-56
11301148

@@ -1155,18 +1173,18 @@ Let's run our adapted test now:
11551173

11561174
.. code-block:: console
11571175
1158-
./bin/reframe -c tutorials/basics/stream/stream3.py -r --performance-report
1176+
./bin/reframe -c tutorials/basics/stream/stream4.py -r --performance-report
11591177
11601178
11611179
.. code-block:: none
11621180
11631181
[ReFrame Setup]
11641182
version: 3.3-dev0 (rev: cb974c13)
1165-
command: './bin/reframe -C tutorials/config/settings.py -c tutorials/basics/stream/stream3.py -r --performance-report'
1183+
command: './bin/reframe -C tutorials/config/settings.py -c tutorials/basics/stream/stream4.py -r --performance-report'
11661184
launched by: user@dom101
11671185
working directory: '/users/user/Devel/reframe'
11681186
settings file: 'tutorials/config/settings.py'
1169-
check search path: '/users/user/Devel/reframe/tutorials/basics/stream/stream3.py'
1187+
check search path: '/users/user/Devel/reframe/tutorials/basics/stream/stream4.py'
11701188
stage directory: '/users/user/Devel/reframe/stage'
11711189
output directory: '/users/user/Devel/reframe/output'
11721190

docs/tutorial_tips_tricks.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ As suggested by the warning message, passing :option:`-v` will give you the stac
9090
Debugging deferred expressions
9191
==============================
9292

93-
Although deferred expression that are used in sanity and performance functions behave similarly to normal Python expressions, you need to understand their `implicit evaluation rules <deferrable_functions_reference.html#implicit-evaluation-of-sanity-functions>`__.
93+
Although deferred expressions that are used in sanity and performance functions behave similarly to normal Python expressions, you need to understand their `implicit evaluation rules <deferrable_functions_reference.html#implicit-evaluation-of-sanity-functions>`__.
9494
One of the rules is that :func:`str` triggers the implicit evaluation, so trying to use the standard :func:`print` function with a deferred expression, you might get unexpected results if that expression is not yet to be evaluated.
9595
For this reason, ReFrame offers a sanity function counterpart of :func:`print`, which allows you to safely print deferred expressions.
9696

tutorials/basics/hellomp/hellomp2.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ def set_compilation_flags(self):
2424

2525
@sanity_function
2626
def assert_num_messages(self):
27-
num_messages = len(sn.findall(r'\[\s?\d+\] Hello, World\!',
28-
self.stdout).evaluate())
29-
return num_messages == 16
27+
num_messages = sn.len(sn.findall(r'\[\s?\d+\] Hello, World\!',
28+
self.stdout))
29+
return sn.assert_eq(num_messages, 16)

tutorials/basics/hellomp/hellomp3.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ def set_compilation_flags(self):
2525

2626
@sanity_function
2727
def assert_num_messages(self):
28-
num_messages = len(sn.findall(r'\[\s?\d+\] Hello, World\!',
29-
self.stdout).evaluate())
30-
return num_messages == 16
28+
num_messages = sn.len(sn.findall(r'\[\s?\d+\] Hello, World\!',
29+
self.stdout))
30+
return sn.assert_eq(num_messages, 16)

tutorials/basics/stream/stream2.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,6 @@ class StreamWithRefTest(rfm.RegressionTest):
2020
'OMP_NUM_THREADS': '4',
2121
'OMP_PLACES': 'cores'
2222
}
23-
reference = {
24-
'catalina': {
25-
'Copy': (25200, -0.05, 0.05, 'MB/s'),
26-
'Scale': (16800, -0.05, 0.05, 'MB/s'),
27-
'Add': (18500, -0.05, 0.05, 'MB/s'),
28-
'Triad': (18800, -0.05, 0.05, 'MB/s')
29-
}
30-
}
3123

3224
@run_before('compile')
3325
def set_compiler_flags(self):
@@ -41,7 +33,8 @@ def validate_solution(self):
4133
@performance_function('MB/s')
4234
def extract_bw(self, kind='Copy'):
4335
'''Generic performance extraction function.'''
44-
if kind not in {'Copy', 'Scale', 'Add', 'Triad'}:
36+
37+
if kind not in ('Copy', 'Scale', 'Add', 'Triad'):
4538
raise ValueError(f'illegal value in argument kind ({kind!r})')
4639

4740
return sn.extractsingle(rf'{kind}:\s+(\S+)\s+.*',
@@ -50,6 +43,7 @@ def extract_bw(self, kind='Copy'):
5043
@run_before('performance')
5144
def set_perf_variables(self):
5245
'''Build the dictionary with all the performance variables.'''
46+
5347
self.perf_variables = {
5448
'Copy': self.extract_bw(),
5549
'Scale': self.extract_bw('Scale'),

tutorials/basics/stream/stream3.py

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99

1010
@rfm.simple_test
11-
class StreamMultiSysTest(rfm.RegressionTest):
11+
class StreamWithRefTest(rfm.RegressionTest):
1212
valid_systems = ['*']
13-
valid_prog_environs = ['cray', 'gnu', 'intel', 'pgi']
13+
valid_prog_environs = ['gnu']
1414
prebuild_cmds = [
1515
'wget http://www.cs.virginia.edu/stream/FTP/Code/stream.c',
1616
]
@@ -29,51 +29,29 @@ class StreamMultiSysTest(rfm.RegressionTest):
2929
}
3030
}
3131

32-
# Flags per programming environment
33-
flags = variable(dict, value={
34-
'cray': ['-fopenmp', '-O3', '-Wall'],
35-
'gnu': ['-fopenmp', '-O3', '-Wall'],
36-
'intel': ['-qopenmp', '-O3', '-Wall'],
37-
'pgi': ['-mp', '-O3']
38-
})
39-
40-
# Number of cores for each system
41-
cores = variable(dict, value={
42-
'catalina:default': 4,
43-
'daint:gpu': 12,
44-
'daint:mc': 36,
45-
'daint:login': 10
46-
})
47-
4832
@run_before('compile')
4933
def set_compiler_flags(self):
5034
self.build_system.cppflags = ['-DSTREAM_ARRAY_SIZE=$((1 << 25))']
51-
environ = self.current_environ.name
52-
self.build_system.cflags = self.flags.get(environ, [])
53-
54-
@run_before('run')
55-
def set_num_threads(self):
56-
num_threads = self.cores.get(self.current_partition.fullname, 1)
57-
self.num_cpus_per_task = num_threads
58-
self.variables = {
59-
'OMP_NUM_THREADS': str(num_threads),
60-
'OMP_PLACES': 'cores'
61-
}
35+
self.build_system.cflags = ['-fopenmp', '-O3', '-Wall']
6236

6337
@sanity_function
6438
def validate_solution(self):
6539
return sn.assert_found(r'Solution Validates', self.stdout)
6640

6741
@performance_function('MB/s')
6842
def extract_bw(self, kind='Copy'):
69-
if kind not in {'Copy', 'Scale', 'Add', 'Triad'}:
43+
'''Generic performance extraction function.'''
44+
45+
if kind not in ('Copy', 'Scale', 'Add', 'Triad'):
7046
raise ValueError(f'illegal value in argument kind ({kind!r})')
7147

7248
return sn.extractsingle(rf'{kind}:\s+(\S+)\s+.*',
7349
self.stdout, 1, float)
7450

7551
@run_before('performance')
7652
def set_perf_variables(self):
53+
'''Build the dictionary with all the performance variables.'''
54+
7755
self.perf_variables = {
7856
'Copy': self.extract_bw(),
7957
'Scale': self.extract_bw('Scale'),

0 commit comments

Comments
 (0)