diff --git a/doc/main/tbb_userguide/design_patterns/Design_Patterns.rst b/doc/main/tbb_userguide/design_patterns/Design_Patterns.rst index 48d04618db..5daa696b5b 100644 --- a/doc/main/tbb_userguide/design_patterns/Design_Patterns.rst +++ b/doc/main/tbb_userguide/design_patterns/Design_Patterns.rst @@ -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 diff --git a/doc/main/tbb_userguide/design_patterns/Images/image008a.jpg b/doc/main/tbb_userguide/design_patterns/Images/image008a.jpg new file mode 100644 index 0000000000..f1199dbaef Binary files /dev/null and b/doc/main/tbb_userguide/design_patterns/Images/image008a.jpg differ diff --git a/doc/main/tbb_userguide/design_patterns/Images/image009a.jpg b/doc/main/tbb_userguide/design_patterns/Images/image009a.jpg new file mode 100644 index 0000000000..02cd196067 Binary files /dev/null and b/doc/main/tbb_userguide/design_patterns/Images/image009a.jpg differ diff --git a/doc/main/tbb_userguide/design_patterns/Lazy_Initialization.rst b/doc/main/tbb_userguide/design_patterns/Lazy_Initialization.rst new file mode 100644 index 0000000000..ccba0d2499 --- /dev/null +++ b/doc/main/tbb_userguide/design_patterns/Lazy_Initialization.rst @@ -0,0 +1,109 @@ +.. _Lazy_Initialization: + +Lazy Initialization +================== + + +.. container:: section + + + .. rubric:: Problem + :class: sectiontitle + + Delay the creation of an object, potentially expensive, until it is accessed. + In parallel programming, initialization must also be guarded against race conditions. + + +.. 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 needed. Lazy initialization is + the common tactic that allows implementing such an approach. + + +.. 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 + + This example illustrates the implementation of lazy initialization + for the calculation of the Fibonacci numbers. 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. + + + An implementation without the use of lazy initialization would have *O(2^N)* time complexity due to + the full recursion tree traversal and recalculation of values. Since all the nodes are traversed once, + the tree becomes a list, making the time complexity *O(N)*. + + + |image1| + + + Here you can see the code for the implementation. Already calculated values are stored in a buffer + paired with ``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::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 + :width: 744px + :height: 367px +.. |image1| image:: Images/image009a.jpg + :width: 744px + :height: 367px