Skip to content

Commit

Permalink
Expose and add some docs
Browse files Browse the repository at this point in the history
Signed-off-by: Krzysztof Lecki <klecki@nvidia.com>
  • Loading branch information
klecki committed Jan 3, 2024
1 parent 436777e commit 60aec25
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 7 deletions.
1 change: 1 addition & 0 deletions dali/python/nvidia/dali/_autograph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from nvidia.dali._autograph.impl.api import convert
from nvidia.dali._autograph.impl.api import converted_call
from nvidia.dali._autograph.impl.api import do_not_convert
from nvidia.dali._autograph.impl.api import is_autograph_artifact as _is_autograph_artifact

# from nvidia.dali._autograph.impl.api import StackTraceMapper
from nvidia.dali._autograph.impl.api import to_code
Expand Down
9 changes: 4 additions & 5 deletions dali/python/nvidia/dali/_autograph/impl/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,11 +446,11 @@ def _fall_back_unconverted(f, args, kwargs, options, exc):
"""Falls back to calling the function unconverted, in case of error."""
# TODO(mdan): Consider adding an internal metric.
warning_template = (
"AutoGraph could not transform %s and will run it as-is.\n" "%s" "Cause: %s\n"
"AutoGraph could not transform %s and will run it as-is.\n"
"%s"
"Cause: %s\n"
"To silence this warning, decorate the function with @nvidia.dali.pipeline.do_not_convert"
)
# TODO(klecki): Expose the do_not_convert in DALI
# 'To silence this warning, decorate the function with'
# ' @tf.autograph.experimental.do_not_convert')
if isinstance(exc, errors.InaccessibleSourceCodeError):
if ag_ctx.INSPECT_SOURCE_SUPPORTED:
logging.warning(warning_template, f, "", exc)
Expand Down Expand Up @@ -598,7 +598,6 @@ def _log_callargs(f, args, kwargs):
#


@export_symbol("autograph.experimental.do_not_convert")
def do_not_convert(func=None):
"""Decorator that suppresses the conversion of a function.
Expand Down
20 changes: 19 additions & 1 deletion dali/python/nvidia/dali/external_source.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# Copyright (c) 2020-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,7 @@
from nvidia.dali import backend as _b
from nvidia.dali import tensors as _tensors
from nvidia.dali import types as _types
from nvidia.dali import _conditionals
from nvidia.dali._multiproc.messages import TaskArgs as _TaskArgs, SampleRange as _SampleRange
import nvidia.dali.types
from nvidia.dali._utils.external_source_impl import (
Expand Down Expand Up @@ -783,6 +784,23 @@ def __call__(
"(specify `batch=True` in the external source definition and make sure "
"your source returns batches)".format(what)
)
if _conditionals._is_autograph_artifact(source_desc.source):
raise ValueError(
"The `source` parameter that was passed to external source was created "
"in a scope that was converted with AutoGraph. To allow the `source` to be "
"correctly used with parallel external source, it must remain unconverted. "
"To prevent conversion, two steps may need to be taken:\n"
"1. The `source` or a factory creating the parameter passed to `source` must "
"be defined at global scope (or at least outside of `pipeline_def` scope).\n"
"2. If the `source` is created by some factory function that is invoked within "
"pipeline definition, that function must be decorated with "
"`@nvidia.dali.pipeline.do_not_convert`, otherwise it will be recursively "
"converted when the pipeline definition is traced.\n"
"You can read more details and see examples in the "
"`@nvidia.dali.pipeline.do_not_convert` decorator documentation. The AutoGraph "
"conversion is part of conditional execution in DALI."
)

else:
for kwarg_value, kwarg_name in (
(prefetch_queue_depth, "prefetch_queue_depth"),
Expand Down
45 changes: 44 additions & 1 deletion dali/python/nvidia/dali/pipeline.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# Copyright (c) 2017-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -1884,6 +1884,49 @@ def create_pipeline(*args, **kwargs):
return actual_decorator(fn) if fn else actual_decorator


def do_not_convert(func=None):
"""Decorator that suppresses the conversion of a function by AutoGraph.
In conditional mode DALI uses fork of [TensorFlow's AutoGraph](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/autograph/g3doc/reference/index.md)
to rewrite `if` statements, so they can be detected and used in processing DALI graph.
When used with [Parallel External Source](...), this may interfere with the serialization of
the provided callback. To prevent this, functions that are used to create the `source`
parameter, should be marked with @do_not_convert.
The AutoGraph conversion is applied to any top-level function or method called within the
pipeline definition (as well as the pipeline definition itself).
When a function is converted, all functions defined within its syntactical scope are also
converted.
To prevent a function from being converted, its top-level encompassing function must be marked
with this decorator.
Note that typically only functions that do not process DataNodes (so don't use DALI operators
directly) should be marked with this decorator.
Parameters
----------
func : _type_, optional
_description_, by default None
Returns
-------
_type_
_description_
"""

if func is None:
return do_not_convert

if getattr(func, "_is_pipeline_def", False):
# TODO(klecki): The other way round as well?
raise ValueError("Pipeline definition cannot be marked with @do_not_convert.")

# TODO(klecki): Verify if we don't have any DataNodes in the returned structure?
return _conditionals.do_not_convert(func)


def _collect_ops(output_nodes):
"""
Traverses the pipeline graph starting from the outputs to collect all reachable operators.
Expand Down

0 comments on commit 60aec25

Please sign in to comment.