Create rule S6908: "tf.function" should not be recursive (#3607)
* Create rule S6908 * Create rule S6908: "tf.function" should not be recursive. --------- Co-authored-by: joke1196 <joke1196@users.noreply.github.com> Co-authored-by: David Kunzmann <david.kunzmann@sonarsource.com>
This commit is contained in:
parent
9489f1c4a9
commit
15d9064b86
2
rules/S6908/metadata.json
Normal file
2
rules/S6908/metadata.json
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
23
rules/S6908/python/metadata.json
Normal file
23
rules/S6908/python/metadata.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"title": "\"tensorflow.function\" should not be recursive",
|
||||||
|
"type": "CODE_SMELL",
|
||||||
|
"status": "ready",
|
||||||
|
"remediation": {
|
||||||
|
"func": "Constant\/Issue",
|
||||||
|
"constantCost": "5min"
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
],
|
||||||
|
"defaultSeverity": "Major",
|
||||||
|
"ruleSpecification": "RSPEC-6908",
|
||||||
|
"sqKey": "S6908",
|
||||||
|
"scope": "All",
|
||||||
|
"defaultQualityProfiles": ["Sonar way"],
|
||||||
|
"quickfix": "unknown",
|
||||||
|
"code": {
|
||||||
|
"impacts": {
|
||||||
|
"RELIABILITY": "HIGH"
|
||||||
|
},
|
||||||
|
"attribute": "CONVENTIONAL"
|
||||||
|
}
|
||||||
|
}
|
53
rules/S6908/python/rule.adoc
Normal file
53
rules/S6908/python/rule.adoc
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
This rule raises an issue when a `tensorflow.function` is recursive.
|
||||||
|
|
||||||
|
== Why is this an issue?
|
||||||
|
|
||||||
|
When defining a `tensorflow.function` it is generally a bad practice to make this function recursive. TensorFlow does not
|
||||||
|
support recursive `tensorflow.function` and will in the majority of cases throw an exception. However it is possible as
|
||||||
|
well that the execution of such function succeeds, but with multiple tracings which has strong performance implications.
|
||||||
|
When executing `tensorflow.function`, the code is split into two distinct stages.
|
||||||
|
The first stage call `tracing` creates a new `tensorflow.Graph`, runs the Python code normally,
|
||||||
|
but defers the execution of TensorFlow operations (i.e. adding two Tensors). These operations are added to the graph without being ran.
|
||||||
|
The second stage which is much faster than the first, runs everything that was deferred previously.
|
||||||
|
Depending on the input of the `tensorflow.function` the first stage may not be needed, see: https://www.tensorflow.org/guide/function#rules_of_tracing[Rules of tracing].
|
||||||
|
Skipping this first stage is what provides the user with TensorFlow's high performance.
|
||||||
|
|
||||||
|
Having a recursive `tensorflow.function` prevents the user from benefiting of TensorFlow's capabilities.
|
||||||
|
|
||||||
|
== How to fix it
|
||||||
|
|
||||||
|
To fix this issue, refactor the `tensorflow.function` so that is it not recursive.
|
||||||
|
|
||||||
|
=== Code examples
|
||||||
|
|
||||||
|
==== Noncompliant code example
|
||||||
|
|
||||||
|
[source,python,diff-id=1,diff-type=noncompliant]
|
||||||
|
----
|
||||||
|
import tensorflow as tf
|
||||||
|
|
||||||
|
@tf.function
|
||||||
|
def factorial(n):
|
||||||
|
if n == 1:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return (n * factorial(n-1)) # Noncompliant: the function is recursive
|
||||||
|
----
|
||||||
|
|
||||||
|
==== Compliant solution
|
||||||
|
|
||||||
|
[source,python,diff-id=1,diff-type=compliant]
|
||||||
|
----
|
||||||
|
import tensorflow as tf
|
||||||
|
|
||||||
|
@tf.function
|
||||||
|
def factorial(n):
|
||||||
|
return tf.exp(tf.lgamma(n + 1)) # Compliant
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
== Resources
|
||||||
|
=== Documentation
|
||||||
|
|
||||||
|
* TensorFlow Documentation - https://www.tensorflow.org/guide/function#recursive_tffunctions_are_not_supported[Recursive tf.functions are not supported]
|
||||||
|
* TensorFlow Documentation - https://www.tensorflow.org/guide/function#rules_of_tracing[Rules of tracing]
|
Loading…
x
Reference in New Issue
Block a user