-
Notifications
You must be signed in to change notification settings - Fork 117
[feat] New performance syntax #2083
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hello @jjotero, Thank you for updating!
Do see the ReFrame Coding Style Guide Comment last updated at 2021-08-23 11:48:31 UTC |
Codecov Report
@@ Coverage Diff @@
## master #2083 +/- ##
==========================================
+ Coverage 86.00% 86.15% +0.15%
==========================================
Files 53 53
Lines 9368 9465 +97
==========================================
+ Hits 8057 8155 +98
+ Misses 1311 1310 -1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jjotero, I really loved this PR! This syntax is very clear, extensible, and very powerful.
But I have a question why is the make_performance_function a regression test method? Would it make sense to have it as an independent module that can be used by regression classes? I think that the module approach is more "modular".
And last but not least, I loved, really loved the bananas examples! I do not know if the b-1 example was intentional, but I missed a b-2!
|
@victorusu I was not aware of that show! 😂 |
jjotero
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have introduced the variable perf_variables of type typ.Dict[str, _DeferredPerformanceExpression]. A _DeferredPerformanceExpression is derived from the _DeferredExpression class with the addition that it has a member unit holding the performance units of each performance variable.
As a result of this internal reorg, I have moved the make_performance_function utility into the pipeline. This function is basically an external convert-constructor for the _DeferredPerformanceExpression class that can take either a callable with its arguments and the units, or just a deferred expression and the units. This could be integrated somehow into the typed descriptor, and it probably makes sense to think of this also in relation to #563 (which also needs some type of conversion function).
| return ret | ||
|
|
||
|
|
||
| def is_trivially_callable(fn, *, non_def_args=0): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that someone might want to enforce some function to take 2 or more positional arguments without a default value. We don't have such thing in the code right now, but since this is in the utilities, I thought I'd make sense to make it generic.
|
I've added the unit tests for all this new syntax. Also, I've moved the |
|
|
||
|
|
||
| def test_performance_function_errors(MyMeta): | ||
| with pytest.raises(ReframeSyntaxError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the feeling that here we can throw a TypeError and the error would still be traced back to the right user line. Similarly for anything in the performance_function decorator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to TypeError, but now I'm confused 😅. I thought the plan was to raise ReframeSyntaxError for everything that we impose and control.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also added a few other checks in here to ensure that the inheritance of performance functions adheres to python's MRO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to TypeError, but now I'm confused 😅. I thought the plan was to raise ReframeSyntaxError for everything that we impose and control.
If a programming error can be traced back to the user frame that triggered it, then ReFrame can avoid a stack trace automatically. In this case, it can be traced because it's a direct call chain from the decorator call. The problem has been with the variables and parameters, where we do several checks at a later point. In these cases, the user line information is lost and we can't trace back the error to the user code; the stack trace has no user frame. So to avoid printing a stack trace for such user errors, we invented the ReframeSyntaxError. That's the rule of thumb, I hope it makes sense...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Got it! Yeah, that makes total sense. Should we move this explanation somewhere in the docs? I think it would be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know where would be the right place to be honest, because that's mostly a developer's documentation, isn't it?
|
|
||
|
|
||
| def test_performance_function_errors(MyMeta): | ||
| with pytest.raises(ReframeSyntaxError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to TypeError, but now I'm confused 😅. I thought the plan was to raise ReframeSyntaxError for everything that we impose and control.
If a programming error can be traced back to the user frame that triggered it, then ReFrame can avoid a stack trace automatically. In this case, it can be traced because it's a direct call chain from the decorator call. The problem has been with the variables and parameters, where we do several checks at a later point. In these cases, the user line information is lost and we can't trace back the error to the user code; the stack trace has no user frame. So to avoid printing a stack trace for such user errors, we invented the ReframeSyntaxError. That's the rule of thumb, I hope it makes sense...
vkarak
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Just minor corrections and some fine tuning to the docs. I could also take care of some of those.
vkarak
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Latm! Goes in! I just made a bit of fine tuning of the docs.
This PR introduces a new syntax to capture and report performance data from a reframe check. In order to facilitate writing generic and composable tests, the
referencevariable is now entirely optional and each performance variable is extracted with a member function decorated with theperformance_functiondecorator:Reframe will pickup these functions marked as
@performance_functionsand run them all by default:============================================================================== PERFORMANCE REPORT ------------------------------------------------------------------------------ Base - dom:login - PrgEnv-gnu * num_tasks: 1 * perf0: 40 bananas * b-1: 40 bananas ------------------------------------------------------------------------------Note that the units are attached to the decorated function, and the name of the resulting performance variable can also be customised with the
perf_keydecorator argument. If any decorated performance function has more than one argument without a default value (theselfplaceholder), an error is raised:So now, what if someone derives a test and wants to change the performance variables from the base test? We have the
perf_variablesattribute for that. During class instantiation, ReFrame will initialiseperf_variableswith all the functions decorated with theperformance_functiondecorator. However, if one wants to modify this mapping, one can do so in a similarly to when dealing with the oldperf_patternsas:With this syntax, the functions that go into the
perf_variablesdictionary are guaranteed to be suitable performance functions, otherwise an error is raised. Note the addition of thesn.make_performance_functionmethod, which allows to add more performance functions inline (it works with both a generic callable and a deferred expression). The performance report for thisDerivedtest looks as follows:============================================================================== PERFORMANCE REPORT ------------------------------------------------------------------------------ Base - dom:login - PrgEnv-gnu * num_tasks: 1 * a: 40 bananas * b: 40 more_bananas * c: 40 other_bananas ------------------------------------------------------------------------------Closes #2064
Closes #2113