Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_withLocalAccess.hpp
Go to the documentation of this file.
1 /*
2 // @HEADER
3 // ***********************************************************************
4 //
5 // Tpetra: Templated Linear Algebra Services Package
6 // Copyright (2008) Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // ************************************************************************
39 // @HEADER
40 */
41 
42 #ifndef TPETRA_WITHLOCALACCESS_HPP
43 #define TPETRA_WITHLOCALACCESS_HPP
44 
45 #include "TpetraCore_config.h"
46 #include <functional>
47 #include <type_traits>
48 
52 
53 namespace Tpetra {
54 
56  // Generic classes needed to implement withLocalAccess
58 
59  namespace Details {
64  enum class EAccess {
65  ReadOnly,
66  WriteOnly,
67  ReadWrite
68  };
69 
76  template<const EAccess accessMode>
77  class Access {
84  };
85 
86  template<class T> struct is_access_mode : public std::false_type {};
87  template<EAccess am>
88  struct is_access_mode<Access<am>> : public std::true_type {};
89 
90  using read_only = Access<EAccess::ReadOnly>;
91  using write_only = Access<EAccess::WriteOnly>;
92  using read_write = Access<EAccess::ReadWrite>;
93 
100  template<class GlobalObjectType>
102  using type = typename GlobalObjectType::device_type::memory_space;
103 
104  // Given a global object, get its (default) memory space instance.
105  static type space (const GlobalObjectType& /* G */) {
106  // This stub just assumes that 'type' is default constructible.
107  // In Kokkos, default-constructing a memory space instance just
108  // gives the default memory space.
109  return type ();
110  }
111  };
112 
119  template<class GlobalObjectType>
121  using type = typename GlobalObjectType::device_type::execution_space;
122 
123  // Given a global object, get its (default) execution space instance.
124  static type space (const GlobalObjectType& /* G */) {
125  // This stub just assumes that 'type' is default constructible.
126  // In Kokkos, default-constructing a execution space instance just
127  // gives the default execution space.
128  return type ();
129  }
130  };
131 
132 #ifndef DOXYGEN_SHOULD_SKIP_THIS
133  template<class GlobalObjectType, class ... Args>
134  class LocalAccess; // forward declaration
135 #endif // DOXYGEN_SHOULD_SKIP_THIS
136 
154  template<class LocalAccessType>
156 
175  template<class LocalAccessType>
177  master_local_object_type
178  getMasterLocalObject (LocalAccessType LA) {
180  }
181 
206  template<class LocalAccessType>
208 
219  template<class LocalAccessType>
221  nonowning_local_object_type
222  getNonowningLocalObject(LocalAccessType LA,
224  master_local_object_type& master)
225  {
227  return impl_type::get(LA, master);
228  }
229  } // namespace Details
230 
245  template<class LocalAccessType>
247  typename Details::GetNonowningLocalObject<LocalAccessType>::
248  nonowning_local_object_type;
249 
251  // Users call readOnly, writeOnly, and readWrite, in order to declare
252  // how they intend to access a global object's local data.
254 
257  template<class GlobalObjectType>
259  GlobalObjectType,
261  readOnly (GlobalObjectType&);
262 
266  template<class GlobalObjectType>
268  GlobalObjectType,
270  readOnly (const GlobalObjectType&);
271 
274  template<class GlobalObjectType>
276  GlobalObjectType,
278  writeOnly (GlobalObjectType&);
279 
282  template<class GlobalObjectType>
284  GlobalObjectType,
286  readWrite (GlobalObjectType&);
287 
289  // LocalAccess struct
291 
292  namespace Details {
294  struct unspecified_type {};
295 
304  template<class ... Args>
305  struct LocalAccessTraits {};
306 
307  // An empty list of template arguments constrains nothing.
308  template<>
309  struct LocalAccessTraits<> {
310  using execution_space = unspecified_type;
311  using memory_space = unspecified_type;
312  using access_mode = unspecified_type;
313  };
314 
315  // Skip over any instances of unspecified_type. This makes it
316  // easy to define the return types of LocalAccess::on and
317  // LocalAccess::at (see below).
318  template<class ... Rest>
319  struct LocalAccessTraits<unspecified_type, Rest...> {
320  private:
321  using rest_type = LocalAccessTraits<Rest...>;
322  public:
323  using execution_space = typename rest_type::execution_space;
324  using memory_space = typename rest_type::memory_space;
325  using access_mode = typename rest_type::access_mode;
326  };
327 
328  template<class First, class ... Rest>
329  struct LocalAccessTraits<First, Rest...> {
330  private:
331  using rest_type = LocalAccessTraits<Rest...>;
332  public:
333  using execution_space = typename std::conditional<
334  Kokkos::Impl::is_execution_space<First>::value,
335  First,
336  typename rest_type::execution_space>::type;
337  using memory_space = typename std::conditional<
338  Kokkos::Impl::is_memory_space<First>::value,
339  First,
340  typename rest_type::memory_space>::type;
341  using access_mode = typename std::conditional<
342  is_access_mode<First>::value,
343  First,
344  typename rest_type::access_mode>::type;
345  };
346 
347  template<class GlobalObjectType,
348  class Traits,
349  bool is_execution_space_specified = ! std::is_same<
350  typename Traits::execution_space,
351  unspecified_type>::value,
352  bool is_memory_space_specified = ! std::is_same<
353  typename Traits::memory_space,
354  unspecified_type>::value>
355  struct SpaceTypes {};
356 
357  template<class GlobalObjectType, class Traits>
358  struct SpaceTypes<GlobalObjectType, Traits, true, true> {
359  using execution_space = typename Traits::execution_space;
360  using memory_space = typename Traits::memory_space;
361  };
362 
363  // If only memory_space was specified, then get execution_space
364  // from memory_space's default execution space.
365  template<class GlobalObjectType, class Traits>
366  struct SpaceTypes<GlobalObjectType, Traits, false, true> {
367  using execution_space =
368  typename Traits::memory_space::execution_space;
369  using memory_space = typename Traits::memory_space;
370  };
371 
372  // If only execution_space was specified, then get memory_space
373  // from execution_space's default memory space.
374  template<class GlobalObjectType, class Traits>
375  struct SpaceTypes<GlobalObjectType, Traits, true, false> {
376  using execution_space = typename Traits::execution_space;
377  using memory_space =
378  typename Traits::execution_space::memory_space;
379  };
380 
381  // If neither execution_space nor memory_space were specified,
382  // then get them both from their defaults, that depend on
383  // GlobalObjectType.
384  template<class GlobalObjectType, class Traits>
385  struct SpaceTypes<GlobalObjectType, Traits, false, false> {
386  using execution_space =
387  typename DefaultExecutionSpace<GlobalObjectType>::type;
388  using memory_space =
389  typename DefaultMemorySpace<GlobalObjectType>::type;
390  };
391 
459  template<class GlobalObjectType, class ... Args>
460  class LocalAccess {
461  private:
462  using this_type = LocalAccess<GlobalObjectType, Args...>;
463  using traits = LocalAccessTraits<Args...>;
464  static constexpr bool is_execution_space_specified =
465  ! std::is_same<typename traits::execution_space,
466  unspecified_type>::value;
467  static constexpr bool is_memory_space_specified =
468  ! std::is_same<typename traits::memory_space,
469  unspecified_type>::value;
470  static constexpr bool is_access_mode_specified =
471  ! std::is_same<typename traits::access_mode,
472  unspecified_type>::value;
473  static_assert(is_access_mode_specified, "LocalAccess requires "
474  "that you specify the access mode. You may do this by "
475  "always starting construction of a LocalAccess instance "
476  "with readOnly, writeOnly, or readWrite.");
477 
478  public:
479  using global_object_type = GlobalObjectType;
480 
481  using execution_space =
482  typename SpaceTypes<global_object_type, traits>::execution_space;
483  static_assert(! is_execution_space_specified ||
484  Kokkos::Impl::is_execution_space<execution_space>::value,
485  "Specified execution space is not a Kokkos execution space.");
486  static_assert(is_execution_space_specified ||
487  Kokkos::Impl::is_execution_space<execution_space>::value,
488  "Default execution space is not a Kokkos execution space.");
489 
490  using memory_space =
491  typename SpaceTypes<global_object_type, traits>::memory_space;
492  static_assert(! is_memory_space_specified ||
493  Kokkos::Impl::is_memory_space<memory_space>::value,
494  "Specified memory space is not a Kokkos memory space.");
495  static_assert(is_memory_space_specified ||
496  Kokkos::Impl::is_memory_space<memory_space>::value,
497  "Default memory space is not a Kokkos memory space.");
498 
499  using access_mode = typename traits::access_mode;
500 
516  LocalAccess (global_object_type& G,
517  const execution_space& execSpace,
518  const memory_space& memSpace,
519  const bool viewIsValid = true) :
520  G_ (G),
521  execSpace_ (execSpace),
522  memSpace_ (memSpace),
523  valid_ (viewIsValid)
524  {}
525 
528  LocalAccess (global_object_type& G,
529  const bool viewIsValid = true) :
530  LocalAccess (G,
531  DefaultExecutionSpace<global_object_type>::space (G),
532  DefaultMemorySpace<global_object_type>::space (G),
533  viewIsValid)
534  {}
535 
539  LocalAccess (global_object_type& G,
540  const execution_space& execSpace,
541  const bool viewIsValid = true) :
542  LocalAccess (G,
543  execSpace,
544  DefaultMemorySpace<global_object_type>::space (G),
545  viewIsValid)
546  {}
547 
551  LocalAccess (global_object_type& G,
552  const memory_space& memSpace,
553  const bool viewIsValid = true) :
554  LocalAccess (G,
555  DefaultExecutionSpace<global_object_type>::space (G),
556  memSpace,
557  viewIsValid)
558  {}
559 
567  bool isValid () const { return valid_; }
568 
571  execution_space getExecutionSpace () const { return execSpace_; }
572 
575  memory_space getMemorySpace () const { return memSpace_; }
576 
588  this_type
589  valid (const bool thisIsValid) const {
590  return {G_, getExecutionSpace(), getMemorySpace(),
591  thisIsValid};
592  }
593 
596  template<class NewMemorySpace>
597  typename std::conditional<
598  is_execution_space_specified,
599  LocalAccess<global_object_type, execution_space,
600  NewMemorySpace, access_mode>,
602  >::type
603  on(const NewMemorySpace& memSpace) const {
604  using Kokkos::Impl::is_memory_space;
605  static_assert(is_memory_space<NewMemorySpace>::value,
606  "NewMemorySpace must be a Kokkos memory space.");
607 
608  // We can't use std::conditional here, because we need the
609  // "select" method.
610  using alt_execution_space =
611  typename LocalAccess<global_object_type, NewMemorySpace,
612  access_mode>::execution_space;
613  auto execSpace = Kokkos::Impl::if_c<
614  is_execution_space_specified,
615  execution_space,
616  alt_execution_space>::select(
618  alt_execution_space());
619  return {G_, execSpace, memSpace, isValid()};
620  }
621 
624  template<class NewExecutionSpace>
625  typename std::conditional<
626  is_memory_space_specified,
627  LocalAccess<global_object_type, NewExecutionSpace,
628  memory_space, access_mode>,
630  >::type
631  at(const NewExecutionSpace& execSpace) const {
632  using Kokkos::Impl::is_execution_space;
633  static_assert(is_execution_space<NewExecutionSpace>::value,
634  "NewExecutionSpace must be a Kokkos execution space.");
635 
636  // We can't use std::conditional here, because we need the
637  // "select" method.
638  using alt_memory_space =
639  typename LocalAccess<global_object_type, NewExecutionSpace,
640  access_mode>::memory_space;
641  auto memSpace = Kokkos::Impl::if_c<
642  is_memory_space_specified,
643  memory_space,
644  alt_memory_space>::select(
645  getMemorySpace(),
646  alt_memory_space());
647  return {G_, execSpace, memSpace, isValid()};
648  }
649 
650  public:
656  global_object_type& G_;
657 
663  execution_space execSpace_;
664 
670  memory_space memSpace_;
671 
673  bool valid_;
674  };
675  } // namespace Details
676 
678  // Implementations of readOnly, writeOnly, and readWrite
680 
681  template<class GlobalObjectType>
683  GlobalObjectType,
685  readOnly (GlobalObjectType& G)
686  {
687  return {G};
688  }
689 
690  template<class GlobalObjectType>
691  Details::LocalAccess<
692  GlobalObjectType,
693  Details::read_only>
694  readOnly (const GlobalObjectType& G)
695  {
696  GlobalObjectType& G_nc = const_cast<GlobalObjectType&> (G);
697  return {G_nc};
698  }
699 
700  template<class GlobalObjectType>
701  Details::LocalAccess<
702  GlobalObjectType,
703  Details::write_only>
704  writeOnly (GlobalObjectType& G)
705  {
706  return {G};
707  }
708 
709  template<class GlobalObjectType>
710  Details::LocalAccess<
711  GlobalObjectType,
712  Details::read_write>
713  readWrite (GlobalObjectType& G)
714  {
715  return {G};
716  }
717 
719  // Implementation of withLocalAccess
721 
722  namespace Details {
723 
725  // Use std::tuple as a compile-time list (Greenspun's 10th Rule)
727 
728  // cons<T, std::tuple<Args...>>::type is std::tuple<T, Args...>.
729  // This is the usual Lisp CONS function, but for std::tuple. I
730  // got the idea from
731  // https://stackoverflow.com/questions/18701798/building-and-accessing-a-list-of-types-at-compile-time
732  // but without "struct Void {};", and with head of new list
733  // ordered first instead of last (hence "cons").
734  template<class ...> struct cons;
735 
736  // (CONS SomeType NIL)
737  template<class T, template <class ...> class List>
738  struct cons<T, List<>> {
739  using type = List<T>;
740  };
741 
742  // (CONS SomeType ListOfTypes)
743  template <class T, template <class ...> class List, class ...Types>
744  struct cons<T, List<Types...>>
745  {
746  typedef List<T, Types...> type;
747  };
748 
750  // Map from std::tuple<Ts...> to std::function<void (Ts...)>.
752 
753  // I got inspiration from
754  // https://stackoverflow.com/questions/15418841/how-do-i-strip-a-tuple-back-into-a-variadic-template-list-of-types
755  //
756  // The only significant change was from "using type = T<Ts...>;",
757  // to "using type = std::function<void (Ts...)>;".
758  template<class T>
759  struct tuple_to_function_type { };
760 
761  template<typename... Ts>
762  struct tuple_to_function_type<std::tuple<Ts...> >
763  {
764  using type = std::function<void (Ts...)>;
765  };
766 
767  // Map from a list of zero or more LocalAccess types, to the
768  // corresponding list of arguments for the user function to give to
769  // withLocalAccess.
770  template<class ... Args>
771  struct ArgsToFunction {};
772 
773  template<>
774  struct ArgsToFunction<> {
775  using arg_list_type = std::tuple<>;
776 
777  // Implementers should only use this.
778  using type = std::function<void ()>;
779  };
780 
781  template<class FirstLocalAccessType, class ... Rest>
782  struct ArgsToFunction<FirstLocalAccessType, Rest...> {
783  using head_arg_type =
784  with_local_access_function_argument_type<FirstLocalAccessType>;
785  using tail_arg_list_type =
786  typename ArgsToFunction<Rest...>::arg_list_type;
787  using arg_list_type =
788  typename cons<head_arg_type, tail_arg_list_type>::type;
789 
790  // Implementers should only use this.
791  using type = typename tuple_to_function_type<arg_list_type>::type;
792  };
793 
798  template<class ... LocalAccessTypes>
800  using current_user_function_type =
801  typename ArgsToFunction<LocalAccessTypes...>::type;
802 
803  static void
804  withLocalAccess (LocalAccessTypes...,
805  typename ArgsToFunction<LocalAccessTypes...>::type);
806  };
807 
811  template<>
812  struct WithLocalAccess<> {
813  using current_user_function_type =
814  typename ArgsToFunction<>::type;
815 
816  static void
817  withLocalAccess (current_user_function_type userFunction)
818  {
819  userFunction ();
820  }
821  };
822 
829  template<class FirstLocalAccessType, class ... Rest>
830  struct WithLocalAccess<FirstLocalAccessType, Rest...> {
831  using current_user_function_type =
832  typename ArgsToFunction<FirstLocalAccessType, Rest...>::type;
833 
834  static void
835  withLocalAccess (current_user_function_type userFunction,
836  FirstLocalAccessType first,
837  Rest... rest)
838  {
839  // The "master" local object is the scope guard for local
840  // data. Its constructor may allocate temporary storage, copy
841  // data to the desired memory space, etc. Its destructor will
842  // put everything back. "Put everything back" could be a
843  // no-op, or it could copy data back so where they need to go
844  // and/or free temporary storage.
845  //
846  // Users define this function and the type it returns by
847  // specializing GetMasterLocalObject for LocalAccess
848  // specializations.
849  auto first_lcl_master = getMasterLocalObject (first);
850 
851  // The "nonowning" local object is a nonowning view of the
852  // "master" local object. This is the only local object that
853  // users see, and they see it as input to their function.
854  // Subsequent slices / subviews view this nonowning local
855  // object. All such nonowning views must have lifetime
856  // contained within the lifetime of the master local object.
857  //
858  // Users define this function and the type it returns by
859  // specializing GetNonowningLocalObject (see above).
860  //
861  // Constraining the nonowning views' lifetime to this scope
862  // means that master local object types may use low-cost
863  // ownership models, like that of std::unique_ptr. There
864  // should be no need for reference counting (in the manner of
865  // std::shared_ptr) or Herb Sutter's deferred_heap.
866  auto first_lcl_view = getNonowningLocalObject (first, first_lcl_master);
867 
868  // Curry the user's function by fixing the first argument.
869 
870  // The commented-out implementation requires C++14, because it
871  // uses a generic lambda (the special case where parameters
872  // are "auto"). We do have the types of the arguments,
873  // though, from ArgsToFunction, so we don't need this feature.
874 
875  // WithLocalAccess<Rest...>::withLocalAccess
876  // (rest...,
877  // [=] (auto ... args) {
878  // userFunction (first_lcl_view, args...);
879  // });
880 
883  userFunction (first_lcl_view, args...);
884  },
885  rest...);
886  }
887  };
888  } // namespace Details
889 
891  // withLocalAccess function declaration and definition
893 
926  template<class ... LocalAccessTypes>
927  void
929  (typename Details::ArgsToFunction<LocalAccessTypes...>::type userFunction,
930  LocalAccessTypes... localAccesses)
931  {
932  using impl_type = Details::WithLocalAccess<LocalAccessTypes...>;
933  impl_type::withLocalAccess (userFunction, localAccesses...);
934  }
935 
936 } // namespace Tpetra
937 
938 #endif // TPETRA_WITHLOCALACCESS_HPP
Mapping from &quot;master&quot; local object type to the nonowning &quot;local view&quot; type that users see (as argumen...
bool valid_
Will I actually need to access this object?
memory_space getMemorySpace() const
Memory space instance, on which the user will access local data.
Declaration of access intent for a global object.
LocalAccess(global_object_type &G, const bool viewIsValid=true)
Constructor that specifies the global object and (optionally) whether it is valid.
global_object_type & G_
Reference to the global object whose data the user will access.
this_type valid(const bool thisIsValid) const
Declare at run time whether you actually want to access the object.
std::conditional< is_memory_space_specified, LocalAccess< global_object_type, NewExecutionSpace, memory_space, access_mode >, LocalAccess< global_object_type, NewExecutionSpace, access_mode > >::type at(const NewExecutionSpace &execSpace) const
Declare intent to access this object&#39;s local data at a specific (Kokkos) execution space (instance)...
LocalAccess(global_object_type &G, const execution_space &execSpace, const memory_space &memSpace, const bool viewIsValid=true)
Constructor that specifies the global object, the execution memory space instance on which to view it...
GetNonowningLocalObject< LocalAccessType >::nonowning_local_object_type getNonowningLocalObject(LocalAccessType LA, const typename GetMasterLocalObject< LocalAccessType >::master_local_object_type &master)
Given a master local object, get an instance of a nonowning local object.
Details::LocalAccess< GlobalObjectType, Details::write_only > writeOnly(GlobalObjectType &)
Declare that you want to access the given global object&#39;s local data in write-only mode...
Implementation of withLocalAccess.
EAccess
Enum for declaring access intent.
Details::LocalAccess< GlobalObjectType, Details::read_write > readWrite(GlobalObjectType &)
Declare that you want to access the given global object&#39;s local data in read-and-write mode...
LocalAccess(global_object_type &G, const execution_space &execSpace, const bool viewIsValid=true)
Constructor that specifies the global object, the execution space instance at which to view its local...
execution_space execSpace_
Execution space instance, with which the user will access local data.
GetMasterLocalObject< LocalAccessType >::master_local_object_type getMasterLocalObject(LocalAccessType LA)
Given a LocalAccess instance (which has a reference to a global object), get an instance of its maste...
Deduce types from parameter pack of LocalAccess.
void withLocalAccess(typename Details::ArgsToFunction< LocalAccessTypes...>::type userFunction, LocalAccessTypes...localAccesses)
Get access to a Tpetra global object&#39;s local data.
Tag indicating an unspecified type in LocalAccessTraits.
LocalAccess(global_object_type &G, const memory_space &memSpace, const bool viewIsValid=true)
Constructor that specifies the global object, the memory space instance on which to view its local da...
memory_space memSpace_
Memory space instance, on which the user will access local data.
Given a global object, get its default execution space (both the type and the default instance thereo...
bool isValid() const
Is access supposed to be valid?
Tag class for declaring access intent.
std::conditional< is_execution_space_specified, LocalAccess< global_object_type, execution_space, NewMemorySpace, access_mode >, LocalAccess< global_object_type, NewMemorySpace, access_mode > >::type on(const NewMemorySpace &memSpace) const
Declare intent to access this object&#39;s local data on a specific (Kokkos) memory space (instance)...
Details::LocalAccess< GlobalObjectType, Details::read_only > readOnly(GlobalObjectType &)
Declare that you want to access the given global object&#39;s local data in read-only mode...
typename Details::GetNonowningLocalObject< LocalAccessType >::nonowning_local_object_type with_local_access_function_argument_type
Type of the local object, that is an argument to the function the user gives to withLocalAccess.
Given a global object, get its default memory space (both the type and the default instance thereof)...
Mapping from LocalAccess to the &quot;master&quot; local object type.
execution_space getExecutionSpace() const
Execution space instance, at which the user will access local data.