LCOV - code coverage report
Current view: top level - include/kokkos/base - KokkosMap.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 6f668f Lines: 54 57 94.7 %
Date: 2025-09-22 20:01:15 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://www.mooseframework.org
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include "KokkosArray.h"
      13             : 
      14             : #include <memory>
      15             : #include <map>
      16             : 
      17             : namespace Moose
      18             : {
      19             : namespace Kokkos
      20             : {
      21             : 
      22             : #ifdef MOOSE_KOKKOS_SCOPE
      23             : constexpr uint32_t FNV_PRIME = 0x01000193;        // 16777619
      24             : constexpr uint32_t FNV_OFFSET_BASIS = 0x811C9DC5; // 2166136261
      25             : 
      26             : template <typename T>
      27             : KOKKOS_FUNCTION uint32_t
      28       89648 : fnv1aHash(const T & key, uint32_t hash)
      29             : {
      30       89648 :   auto bytes = reinterpret_cast<const uint8_t *>(&key);
      31             : 
      32      448240 :   for (size_t i = 0; i < sizeof(T); ++i)
      33             :   {
      34      358592 :     hash ^= bytes[i];
      35      358592 :     hash *= FNV_PRIME;
      36             :   }
      37             : 
      38       89648 :   return hash;
      39             : }
      40             : 
      41             : template <typename T>
      42             : KOKKOS_FUNCTION uint32_t
      43       89648 : fnv1aHash(const T & key)
      44             : {
      45       89648 :   return fnv1aHash(key, FNV_OFFSET_BASIS);
      46             : }
      47             : 
      48             : template <typename T1, typename T2>
      49             : struct Pair;
      50             : 
      51             : template <typename T1, typename T2>
      52             : KOKKOS_FUNCTION uint32_t
      53             : fnv1aHash(const Pair<T1, T2> & key)
      54             : {
      55             :   return fnv1aHash(key.second, fnv1aHash(key.first));
      56             : }
      57             : #endif
      58             : 
      59             : template <typename T1, typename T2>
      60             : class Map;
      61             : 
      62             : template <typename T1, typename T2>
      63             : void dataStore(std::ostream & stream, Map<T1, T2> & map, void * context);
      64             : template <typename T1, typename T2>
      65             : void dataLoad(std::istream & stream, Map<T1, T2> & map, void * context);
      66             : 
      67             : /**
      68             :  * The Kokkos wrapper class for standard map.
      69             :  * The map can only be populated on host.
      70             :  * Make sure to call copyToDevice() or copyToDeviceNested() after populating the map on host.
      71             :  */
      72             : template <typename T1, typename T2>
      73             : class Map
      74             : {
      75             : public:
      76             :   /**
      77             :    * Get the beginning writeable iterator of the host map
      78             :    * @returns The beginning iterator
      79             :    */
      80             :   auto begin() { return get().begin(); }
      81             :   /**
      82             :    * Get the beginning const iterator of the host map
      83             :    * @returns The beginning iterator
      84             :    */
      85             :   auto begin() const { return get().cbegin(); }
      86             :   /**
      87             :    * Get the end writeable iterator of the host map
      88             :    * @returns The end iterator
      89             :    */
      90             :   auto end() { return get().end(); }
      91             :   /**
      92             :    * Get the end const iterator of the host map
      93             :    * @returns The end iterator
      94             :    */
      95             :   auto end() const { return get().cend(); }
      96             :   /**
      97             :    * Get the underlying writeable host map
      98             :    * @returns The writeable host map
      99             :    */
     100          48 :   auto & get()
     101             :   {
     102          48 :     if (!_map_host)
     103          12 :       _map_host = std::make_shared<std::map<T1, T2>>();
     104             : 
     105          48 :     return *_map_host;
     106             :   }
     107             :   /**
     108             :    * Get the underlying const host map
     109             :    * @returns The const host map
     110             :    */
     111          96 :   const auto & get() const
     112             :   {
     113          96 :     if (!_map_host)
     114           0 :       _map_host = std::make_shared<std::map<T1, T2>>();
     115             : 
     116          96 :     return *_map_host;
     117             :   }
     118             :   /**
     119             :    * Call host map's operator[]
     120             :    * @param key The key
     121             :    * @returns The writeable reference of the value
     122             :    */
     123          24 :   T2 & operator[](const T1 & key) { return get()[key]; }
     124             : 
     125             : #ifdef MOOSE_KOKKOS_SCOPE
     126             :   /**
     127             :    * Copy host map to device
     128             :    */
     129             :   void copyToDevice();
     130             :   /**
     131             :    * Copy host map to device, perform nested copy for Kokkos arrays
     132             :    */
     133             :   void copyToDeviceNested();
     134             :   /**
     135             :    * Swap with another Kokkos map
     136             :    * @param map The Kokkos map to be swapped
     137             :    */
     138             :   void swap(Map<T1, T2> & map);
     139             : 
     140             :   /**
     141             :    * Get the size of map
     142             :    * @returns The size of map
     143             :    */
     144      179296 :   KOKKOS_FUNCTION dof_id_type size() const
     145             :   {
     146      179296 :     KOKKOS_IF_ON_HOST(return get().size();)
     147             : 
     148      179200 :     return _keys.size();
     149             :   }
     150             :   /**
     151             :    * Find the index of a key
     152             :    * @param key The key
     153             :    * @returns The index of the key, invalid_id if the key does not exist
     154             :    */
     155             :   KOKKOS_FUNCTION dof_id_type find(const T1 & key) const;
     156             :   /**
     157             :    * Get whether a key exists
     158             :    * @param key The key
     159             :    * @returns Whether the key exists
     160             :    */
     161       44800 :   KOKKOS_FUNCTION bool exists(const T1 & key) const { return find(key) != invalid_id; }
     162             :   /**
     163             :    * Get the key of an index
     164             :    * @param idx The index returned by find()
     165             :    * @returns The const reference of the key
     166             :    */
     167             :   KOKKOS_FUNCTION const T1 & key(dof_id_type idx) const
     168             :   {
     169             :     KOKKOS_ASSERT(idx != invalid_id);
     170             : 
     171             :     return _keys[idx];
     172             :   }
     173             :   /**
     174             :    * Get the value of an index
     175             :    * @param idx The index returned by find()
     176             :    * @returns The const reference of the value
     177             :    */
     178             :   KOKKOS_FUNCTION const T2 & value(dof_id_type idx) const
     179             :   {
     180             :     KOKKOS_ASSERT(idx != invalid_id);
     181             : 
     182             :     return _values[idx];
     183             :   }
     184             :   /**
     185             :    * Get the value corresponding to a key
     186             :    * @param key The key
     187             :    * @returns The const reference of the value
     188             :    */
     189             :   ///@{
     190       44800 :   KOKKOS_FUNCTION const T2 & operator[](const T1 & key) const
     191             :   {
     192       44800 :     KOKKOS_IF_ON_HOST(return get().at(key);)
     193             : 
     194       44800 :     auto idx = find(key);
     195             : 
     196             :     KOKKOS_ASSERT(idx != invalid_id);
     197             : 
     198       44800 :     return _values[idx];
     199             :   }
     200             :   // Due to a stupid NVCC compiler bug, one cannot do var[i][j] for a variable whose type is
     201             :   // Array<Map<...>> (the second operator[] seems to be interpreted as if it is the operator of
     202             :   // Array), while var[i](j) works. Until we figure out what is going on, one can use the following
     203             :   // operator as a workaround.
     204             :   KOKKOS_FUNCTION const T2 & operator()(const T1 & key) const { return operator[](key); }
     205             :   ///@}
     206             : #endif
     207             : 
     208             :   static const dof_id_type invalid_id = libMesh::DofObject::invalid_id;
     209             : 
     210             : private:
     211             : #ifdef MOOSE_KOKKOS_SCOPE
     212             :   /**
     213             :    * Internal method to copy host map to device
     214             :    */
     215             :   void copy();
     216             : #endif
     217             : 
     218             :   /**
     219             :    * Standard map on host
     220             :    * Stored as a shared pointer to avoid deep copy
     221             :    */
     222             :   mutable std::shared_ptr<std::map<T1, T2>> _map_host;
     223             :   /**
     224             :    * Keys on device
     225             :    */
     226             :   Array<T1> _keys;
     227             :   /**
     228             :    * Values on device
     229             :    */
     230             :   Array<T2> _values;
     231             :   /**
     232             :    * Beginning offset into device arrays of each bucket
     233             :    */
     234             :   Array<dof_id_type> _offset;
     235             : 
     236             :   friend void dataStore<T1, T2>(std::ostream &, Map<T1, T2> &, void *);
     237             :   friend void dataLoad<T1, T2>(std::istream &, Map<T1, T2> &, void *);
     238             : };
     239             : 
     240             : #ifdef MOOSE_KOKKOS_SCOPE
     241             : template <typename T1, typename T2>
     242             : void
     243          12 : Map<T1, T2>::copy()
     244             : {
     245          12 :   _keys.create(size());
     246          12 :   _values.create(size());
     247          12 :   _offset.create(size() + 1);
     248          12 :   _offset = 0;
     249             : 
     250          36 :   for (const auto & [key, value] : get())
     251             :   {
     252          24 :     auto bucket = fnv1aHash(key) % size();
     253             : 
     254          24 :     _offset[bucket]++;
     255             :   }
     256             : 
     257          12 :   std::exclusive_scan(_offset.begin(), _offset.end(), _offset.begin(), 0);
     258             : 
     259          12 :   _offset.copyToDevice();
     260             : 
     261          12 :   std::vector<dof_id_type> idx(size(), 0);
     262             : 
     263          36 :   for (const auto & [key, value] : get())
     264             :   {
     265          24 :     auto bucket = fnv1aHash(key) % size();
     266             : 
     267          24 :     _keys[_offset[bucket] + idx[bucket]] = key;
     268          24 :     _values[_offset[bucket] + idx[bucket]] = value;
     269          24 :     idx[bucket]++;
     270             :   }
     271          12 : }
     272             : 
     273             : template <typename T1, typename T2>
     274             : void
     275          12 : Map<T1, T2>::copyToDevice()
     276             : {
     277          12 :   copy();
     278             : 
     279          12 :   _keys.copyToDevice();
     280          12 :   _values.copyToDevice();
     281          12 : }
     282             : 
     283             : template <typename T1, typename T2>
     284             : void
     285             : Map<T1, T2>::copyToDeviceNested()
     286             : {
     287             :   copy();
     288             : 
     289             :   _keys.copyToDeviceNested();
     290             :   _values.copyToDeviceNested();
     291             : }
     292             : 
     293             : template <typename T1, typename T2>
     294             : void
     295             : Map<T1, T2>::swap(Map<T1, T2> & map)
     296             : {
     297             :   get().swap(map.get());
     298             :   _keys.swap(map._keys);
     299             :   _values.swap(map._values);
     300             :   _offset.swap(map._offset);
     301             : }
     302             : 
     303             : template <typename T1, typename T2>
     304             : KOKKOS_FUNCTION dof_id_type
     305       89600 : Map<T1, T2>::find(const T1 & key) const
     306             : {
     307       89600 :   if (!size())
     308           0 :     return invalid_id;
     309             : 
     310       89600 :   auto bucket = fnv1aHash(key) % size();
     311       89600 :   auto begin = _offset[bucket];
     312       89600 :   auto end = _offset[bucket + 1];
     313             : 
     314       89600 :   for (dof_id_type i = begin; i < end; ++i)
     315       89600 :     if (_keys[i] == key)
     316       89600 :       return i;
     317             : 
     318           0 :   return invalid_id;
     319             : }
     320             : 
     321             : template <typename T1, typename T2>
     322             : void
     323             : dataStore(std::ostream & stream, Map<T1, T2> & map, void * context)
     324             : {
     325             :   using ::dataStore;
     326             : 
     327             :   dataStore(stream, map.get(), context);
     328             :   dataStore(stream, map._keys, context);
     329             :   dataStore(stream, map._values, context);
     330             :   dataStore(stream, map._offset, context);
     331             : }
     332             : 
     333             : template <typename T1, typename T2>
     334             : void
     335             : dataLoad(std::istream & stream, Map<T1, T2> & map, void * context)
     336             : {
     337             :   using ::dataLoad;
     338             : 
     339             :   dataLoad(stream, map.get(), context);
     340             :   dataLoad(stream, map._keys, context);
     341             :   dataLoad(stream, map._values, context);
     342             :   dataLoad(stream, map._offset, context);
     343             : }
     344             : #endif
     345             : 
     346             : } // namespace Kokkos
     347             : } // namespace Moose

Generated by: LCOV version 1.14