Skip to content
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

Add topic about "Lazy Initiliazation" pattern to Design patterns #790

Merged
merged 15 commits into from
Mar 9, 2022
1 change: 1 addition & 0 deletions doc/main/tbb_userguide/design_patterns/Design_Patterns.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ expressions into equivalent C++03 code.
../../tbb_userguide/design_patterns/Divide_and_Conquer
../../tbb_userguide/design_patterns/GUI_Thread
../../tbb_userguide/design_patterns/Non-Preemptive_Priorities
../../tbb_userguide/design_patterns/Lazy_Initialization
../../tbb_userguide/design_patterns/Local_Serializer
../../tbb_userguide/design_patterns/Fenced_Data_Transfer
../../tbb_userguide/design_patterns/Reference_Counting
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
97 changes: 97 additions & 0 deletions doc/main/tbb_userguide/design_patterns/Lazy_Initialization.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
.. _Lazy_Initialization:

Lazy Initialization
==================


.. container:: section


.. rubric:: Problem
:class: sectiontitle

Delay the creation of an object (potentially expensive) until it is accessed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Delay the creation of an object (potentially expensive) until it is accessed.
Delay the creation of an object, potentially expensive, until it is accessed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

In parallel programming initialization also must be guarded against race conditions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In parallel programming initialization also must be guarded against race conditions.
In parallel programming, initialization must also be guarded against race conditions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.



.. container:: section


.. rubric:: Context
:class: sectiontitle

The cost of operations that take place during the initialization
of the object may be considerably high. In that case, the object
should be initialized only when it is needed. Lazy initialization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
should be initialized only when it is needed. Lazy initialization
should be initialized only when needed. Lazy initialization

Copy link
Contributor Author

@isaevil isaevil Mar 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (also applicable to the comments below).

is the common tactic that allows implementing such approach.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
is the common tactic that allows implementing such approach.
is the common tactic that allows implementing such an approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.



.. container:: section


.. rubric:: Solution
:class: sectiontitle

Using ``oneapi::tbb::collaborative_call_once`` with ``oneapi::tbb::collaborative_once_flag``
helps to implement thread-safe lazy initialization for a user object.


In addition, ``collaborative_call_once`` allows other thread blocked on
the same ``collaborative_once_flag`` to join other |short_name|
parallel constructions called within the initializing function.


.. container:: section


.. rubric:: Example
:class: sectiontitle

The example presented here illustrate implementation of "lazy initialization" for Fibonacci numbers calculation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The example presented here illustrate implementation of "lazy initialization" for Fibonacci numbers calculation.
This example illustrates the implementation of "lazy initialization" for the calculation of the Fibonacci numbers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is lazy initialization in quotes here but not in the cases above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed quotes.

Here is a graphical representation of the Fibonacci recursion tree for N=4.


|image0|


As seen in the diagram, some elements are recalculated more than once. These operations are redundant,
so the "lazy initialized" Fibonacci numbers are relevant here.


The code for the implementation is shown below. Already calculated values are stored in a buffer paired with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The code for the implementation is shown below. Already calculated values are stored in a buffer paired with
Here you can see the code for the implementation. Already calculated values are stored in a buffer paired with

``collaborative_once_flag`` and won't be recalculated when ``collaborative_call_once`` is invoked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
``collaborative_once_flag`` and won't be recalculated when ``collaborative_call_once`` is invoked
``collaborative_once_flag`` and will not be recalculated when ``collaborative_call_once`` is invoked

when initialization has already been done.


::


using FibBuffer = std::vector<std::pair<oneapi::tbb::collaborative_once_flag, std::uint64_t>>;

std::uint64_t LazyFibHelper(int n, FibBuffer& buffer) {
// Base case
if (n <= 1) {
return n;
}
// Calculate nth value only once and store it in the buffer.
// Other threads won't be blocked on already taken collaborative_once_flag
// but join parallelism inside functor
oneapi::tbb::collaborative_call_once(buffer[n].first, [&]() {
std::uint64_t a, b;
oneapi::tbb::parallel_invoke([&] { a = LazyFibHelper(n - 2, buffer); },
[&] { b = LazyFibHelper(n - 1, buffer); });
buffer[n].second = a + b;
});

return buffer[n].second;
}

std::uint64_t Fib(int n) {
FibBuffer buffer(n+1);
return LazyFibHelper(n, buffer);
}


.. |image0| image:: Images/image008a.jpg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remake screenshots so it would have a space above and beneath the figures?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated images.

:width: 740px
:height: 344px