From ba19c7c9450ab6ef00706e5f018f9956e8c0c871 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= <klinkjak@fjfi.cvut.cz>
Date: Tue, 12 Dec 2017 17:31:53 +0100
Subject: [PATCH] Optimizing mesh initializer, replaced EntitySeedKey with
 EntitySeedHash

---
 src/TNL/Containers/CMakeLists.txt             |   2 +
 src/TNL/Containers/UnorderedIndexedSet.h      |  62 +++++++++
 src/TNL/Containers/UnorderedIndexedSet_impl.h | 130 ++++++++++++++++++
 .../MeshDetails/initializer/CMakeLists.txt    |   1 -
 .../MeshDetails/initializer/EntitySeed.h      |  57 +++++++-
 .../MeshDetails/initializer/EntitySeedKey.h   |  67 ---------
 .../MeshDetails/initializer/Initializer.h     |  22 +--
 .../SuperentityStorageInitializer.h           |   4 +-
 .../MeshDetails/traits/MeshEntityTraits.h     |   7 +-
 9 files changed, 267 insertions(+), 85 deletions(-)
 create mode 100644 src/TNL/Containers/UnorderedIndexedSet.h
 create mode 100644 src/TNL/Containers/UnorderedIndexedSet_impl.h
 delete mode 100644 src/TNL/Meshes/MeshDetails/initializer/EntitySeedKey.h

diff --git a/src/TNL/Containers/CMakeLists.txt b/src/TNL/Containers/CMakeLists.txt
index 51120b929e..5e5422f128 100644
--- a/src/TNL/Containers/CMakeLists.txt
+++ b/src/TNL/Containers/CMakeLists.txt
@@ -7,6 +7,8 @@ set( headers Array.h
              IndexedMap_impl.h
              IndexedSet.h
              IndexedSet_impl.h
+             UnorderedIndexedSet.h
+             UnorderedIndexedSet_impl.h
              List.h
              List_impl.h
              MultiArray.h
diff --git a/src/TNL/Containers/UnorderedIndexedSet.h b/src/TNL/Containers/UnorderedIndexedSet.h
new file mode 100644
index 0000000000..38a1e53937
--- /dev/null
+++ b/src/TNL/Containers/UnorderedIndexedSet.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+                          UnorderedIndexedSet.h  -  description
+                             -------------------
+    begin                : Dec 12, 2017
+    copyright            : (C) 2017 by Tomas Oberhuber et al.
+    email                : tomas.oberhuber@fjfi.cvut.cz
+ ***************************************************************************/
+
+/* See Copyright Notice in tnl/Copyright */
+
+#pragma once
+
+#include <unordered_map>
+#include <ostream>
+
+namespace TNL {
+namespace Containers {
+
+template< class Key,
+          class Index,
+          class Hash = std::hash< Key >,
+          class KeyEqual = std::equal_to< Key >,
+          class Allocator = std::allocator< std::pair<const Key, Index> > >
+class UnorderedIndexedSet
+{
+protected:
+   using map_type = std::unordered_map< Key, Index, Hash, KeyEqual, Allocator >;
+   map_type map;
+
+public:
+   using key_type = Key;
+   using index_type = Index;
+   using value_type = typename map_type::value_type;
+   using size_type = typename map_type::size_type;
+   using hasher = Hash;
+   using key_equal = KeyEqual;
+
+   void clear();
+
+   size_type size() const;
+
+   Index insert( const Key& key );
+
+   std::pair< Index, bool > try_insert( const Key& key );
+
+   bool find( const Key& key, Index& index ) const;
+
+   size_type count( const Key& key ) const;
+
+   size_type erase( const Key& key );
+
+   void print( std::ostream& str ) const;
+};
+
+template< typename Element,
+          typename Index >
+std::ostream& operator <<( std::ostream& str, UnorderedIndexedSet< Element, Index >& set );
+
+} // namespace Containers
+} // namespace TNL
+
+#include <TNL/Containers/UnorderedIndexedSet_impl.h>
diff --git a/src/TNL/Containers/UnorderedIndexedSet_impl.h b/src/TNL/Containers/UnorderedIndexedSet_impl.h
new file mode 100644
index 0000000000..4402ab55cc
--- /dev/null
+++ b/src/TNL/Containers/UnorderedIndexedSet_impl.h
@@ -0,0 +1,130 @@
+/***************************************************************************
+                          UnorderedIndexedSet_impl.h  -  description
+                             -------------------
+    begin                : Feb 15, 2014
+    copyright            : (C) 2014 by Tomas Oberhuber et al.
+    email                : tomas.oberhuber@fjfi.cvut.cz
+ ***************************************************************************/
+
+/* See Copyright Notice in tnl/Copyright */
+
+#pragma once
+
+#include <TNL/Containers/UnorderedIndexedSet.h>
+
+namespace TNL {
+namespace Containers {
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+void
+UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::clear()
+{
+   map.clear();
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+typename UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::size_type
+UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::size() const
+{
+   return map.size();
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+Index
+UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::insert( const Key& key )
+{
+   auto iter = map.insert( value_type( key, size() ) ).first;
+   return iter->second;
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+std::pair< Index, bool >
+UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::try_insert( const Key& key )
+{
+   auto pair = map.insert( value_type( key, size() ) );
+   return std::pair< Index, bool >{ pair.first->second, pair.second };
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+bool
+UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::find( const Key& key, Index& index ) const
+{
+   auto iter = map.find( Key( key ) );
+   if( iter == map.end() )
+      return false;
+   index = iter->second.index;
+   return true;
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+typename UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::size_type
+UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::count( const Key& key ) const
+{
+   return map.count( key );
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+typename UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::size_type
+UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::erase( const Key& key )
+{
+   return map.erase( key );
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+void UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >::print( std::ostream& str ) const
+{
+   auto iter = map.begin();
+   str << iter->second.data;
+   iter++;
+   while( iter != map.end() )
+   {
+      str << ", " << iter->second.data;
+      iter++;
+   }
+}
+
+template< class Key,
+          class Index,
+          class Hash,
+          class KeyEqual,
+          class Allocator >
+std::ostream& operator<<( std::ostream& str, UnorderedIndexedSet< Key, Index, Hash, KeyEqual, Allocator >& set )
+{
+   set.print( str );
+   return str;
+}
+
+} // namespace Containers
+} // namespace TNL
diff --git a/src/TNL/Meshes/MeshDetails/initializer/CMakeLists.txt b/src/TNL/Meshes/MeshDetails/initializer/CMakeLists.txt
index 9364a2817b..f452c0c022 100644
--- a/src/TNL/Meshes/MeshDetails/initializer/CMakeLists.txt
+++ b/src/TNL/Meshes/MeshDetails/initializer/CMakeLists.txt
@@ -1,6 +1,5 @@
 SET( headers EntityInitializer.h
              EntitySeed.h 
-             EntitySeedKey.h
              Initializer.h
              SubentitySeedsCreator.h
              SuperentityStorageInitializer.h
diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntitySeed.h b/src/TNL/Meshes/MeshDetails/initializer/EntitySeed.h
index e9b14ff177..b9d34d0701 100644
--- a/src/TNL/Meshes/MeshDetails/initializer/EntitySeed.h
+++ b/src/TNL/Meshes/MeshDetails/initializer/EntitySeed.h
@@ -17,11 +17,15 @@
 #pragma once
 
 #include <TNL/Meshes/MeshDetails/traits/MeshTraits.h>
-#include <TNL/Meshes/MeshDetails/initializer/EntitySeedKey.h>
 
 namespace TNL {
 namespace Meshes {
 
+template< typename EntitySeed >
+struct EntitySeedHash;
+template< typename EntitySeed >
+struct EntitySeedEq;
+
 template< typename MeshConfig,
           typename EntityTopology >
 class EntitySeed
@@ -33,7 +37,8 @@ class EntitySeed
       using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType;
       using LocalIndexType  = typename MeshTraits< MeshConfig >::LocalIndexType;
       using IdArrayType     = Containers::StaticArray< SubvertexTraits::count, GlobalIndexType >;
-      using KeyType         = EntitySeedKey< MeshConfig, EntityTopology >;
+      using HashType        = EntitySeedHash< EntitySeed >;
+      using KeyEqual        = EntitySeedEq< EntitySeed >;
 
       static String getType() { return String( "EntitySeed<>" ); }
 
@@ -74,7 +79,8 @@ class EntitySeed< MeshConfig, Topologies::Vertex >
       using GlobalIndexType = typename MeshTraits< MeshConfig >::GlobalIndexType;
       using LocalIndexType  = typename MeshTraits< MeshConfig >::LocalIndexType;
       using IdArrayType     = Containers::StaticArray< 1, GlobalIndexType >;
-      using KeyType         = EntitySeedKey< MeshConfig, Topologies::Vertex >;
+      using HashType        = EntitySeedHash< EntitySeed >;
+      using KeyEqual        = EntitySeedEq< EntitySeed >;
 
       static String getType() { return String( "EntitySeed<>" ); }
 
@@ -112,6 +118,49 @@ std::ostream& operator<<( std::ostream& str, const EntitySeed< MeshConfig, Entit
    return str;
 };
 
+template< typename EntitySeed >
+struct EntitySeedHash
+{
+   std::size_t operator()( const EntitySeed& seed ) const
+   {
+      using LocalIndexType = typename EntitySeed::LocalIndexType;
+      using GlobalIndexType = typename EntitySeed::GlobalIndexType;
+
+      // Note that we must use an associative function to combine the hashes,
+      // because we *want* to ignore the order of the corner IDs.
+      std::size_t hash = 0;
+      for( LocalIndexType i = 0; i < EntitySeed::getCornersCount(); i++ )
+//         hash ^= std::hash< GlobalIndexType >{}( seed.getCornerIds()[ i ] );
+         hash += std::hash< GlobalIndexType >{}( seed.getCornerIds()[ i ] );
+      return hash;
+   }
+};
+
+template< typename EntitySeed >
+struct EntitySeedEq
+{
+   bool operator()( const EntitySeed& left, const EntitySeed& right ) const
+   {
+      using IdArrayType = typename EntitySeed::IdArrayType;
+
+      IdArrayType sortedLeft( left.getCornerIds() );
+      IdArrayType sortedRight( right.getCornerIds() );
+      sortedLeft.sort();
+      sortedRight.sort();
+      return sortedLeft == sortedRight;
+   }
+};
+
+template< typename MeshConfig >
+struct EntitySeedEq< EntitySeed< MeshConfig, Topologies::Vertex > >
+{
+   using Seed = EntitySeed< MeshConfig, Topologies::Vertex >;
+
+   bool operator()( const Seed& left, const Seed& right ) const
+   {
+      return left.getCornerIds() == right.getCornerIds();
+   }
+};
+
 } // namespace Meshes
 } // namespace TNL
-
diff --git a/src/TNL/Meshes/MeshDetails/initializer/EntitySeedKey.h b/src/TNL/Meshes/MeshDetails/initializer/EntitySeedKey.h
deleted file mode 100644
index 8da9cb6e92..0000000000
--- a/src/TNL/Meshes/MeshDetails/initializer/EntitySeedKey.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/***************************************************************************
-                          EntitySeedKey.h  -  description
-                             -------------------
-    begin                : Feb 13, 2014
-    copyright            : (C) 2014 by Tomas Oberhuber et al.
-    email                : tomas.oberhuber@fjfi.cvut.cz
- ***************************************************************************/
-
-/* See Copyright Notice in tnl/Copyright */
-
-/***
- * Authors:
- * Oberhuber Tomas, tomas.oberhuber@fjfi.cvut.cz
- * Zabka Vitezslav, zabkav@gmail.com
- */
-
-#pragma once
-
-namespace TNL {
-namespace Meshes {
-
-template< typename MeshConfig,
-          typename EntityTopology >
-class EntitySeed;
-
-/****
- * Unique identification of a mesh entity by its vertices.
- * Uniqueness is preserved for entities of the same type only.
- */
-template< typename MeshConfig,
-          typename EntityTopology >
-class EntitySeedKey
-{
-   using EntitySeedType = EntitySeed< MeshConfig, EntityTopology >;
-   using IdArrayType = typename EntitySeedType::IdArrayType;
-
-public:
-   EntitySeedKey( const EntitySeedType& entitySeed )
-   {
-      for( typename IdArrayType::IndexType i = 0;
-           i < entitySeed.getCornersCount();
-           i++ )
-         this->sortedCorners[ i ] = entitySeed.getCornerIds()[ i ];
-      sortedCorners.sort( );
-   }
-
-   bool operator<( const EntitySeedKey& other ) const
-   {
-      for( typename IdArrayType::IndexType i = 0;
-           i < IdArrayType::size;
-           i++)
-      {
-         if( sortedCorners[ i ] < other.sortedCorners[ i ] )
-            return true;
-         else
-            if( sortedCorners[ i ] > other.sortedCorners[ i ] )
-               return false;
-      }
-      return false;
-   }
-
-private:
-   IdArrayType sortedCorners;
-};
-
-} // namespace Meshes
-} // namespace TNL
diff --git a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h
index 22f3077cb3..fe97e530ea 100644
--- a/src/TNL/Meshes/MeshDetails/initializer/Initializer.h
+++ b/src/TNL/Meshes/MeshDetails/initializer/Initializer.h
@@ -19,7 +19,6 @@
 #include <TNL/Meshes/DimensionTag.h>
 #include <TNL/Meshes/MeshDetails/initializer/EntityInitializer.h>
 #include <TNL/Meshes/MeshDetails/initializer/SubentitySeedsCreator.h>
-#include <TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h>
 #include <TNL/Meshes/MeshDetails/MeshEntityReferenceOrientation.h>
 #include <TNL/Meshes/MeshDetails/initializer/EntitySeed.h>
 
@@ -246,13 +245,14 @@ class InitializerLayer< MeshConfig,
    using PointArrayType        = typename MeshTraitsType::PointArrayType;
    using SeedType              = EntitySeed< MeshConfig, EntityTopology >;
    using SeedIndexedSet        = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedIndexedSetType;
+   using SeedSet               = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedSetType;
 
    public:
 
       GlobalIndexType getEntitiesCount( InitializerType& initializer, MeshType& mesh )
       {
          using SubentitySeedsCreator = SubentitySeedsCreator< MeshConfig, Meshes::DimensionTag< MeshType::getMeshDimension() >, DimensionTag >;
-         std::set< typename SeedIndexedSet::key_type > seedSet;
+         SeedSet seedSet;
 
          for( GlobalIndexType i = 0; i < mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); i++ )
          {
@@ -283,8 +283,10 @@ class InitializerLayer< MeshConfig,
             for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ )
             {
                auto& seed = subentitySeeds[ j ];
-               if( this->seedsIndexedSet.count( seed ) == 0 ) {
-                  const GlobalIndexType entityIndex = this->seedsIndexedSet.insert( seed );
+               const auto pair = this->seedsIndexedSet.try_insert( seed );
+               const GlobalIndexType& entityIndex = pair.first;
+               if( pair.second ) {
+                  // insertion took place, initialize the entity
                   EntityInitializerType::initEntity( mesh.template getEntity< DimensionTag::value >( entityIndex ), entityIndex, seed, initializer );
                }
             }
@@ -331,6 +333,8 @@ class InitializerLayer< MeshConfig,
    using LocalIndexType                = typename MeshTraitsType::LocalIndexType;
    using PointArrayType                = typename MeshTraitsType::PointArrayType;
    using SeedType                      = EntitySeed< MeshConfig, EntityTopology >;
+   using SeedIndexedSet                = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedIndexedSetType;
+   using SeedSet                       = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedSetType;
    using ReferenceOrientationType      = typename EntityTraitsType::ReferenceOrientationType;
    using ReferenceOrientationArrayType = typename EntityTraitsType::ReferenceOrientationArrayType;
 
@@ -339,7 +343,7 @@ class InitializerLayer< MeshConfig,
       GlobalIndexType getEntitiesCount( InitializerType& initializer, MeshType& mesh )
       {
          using SubentitySeedsCreator = SubentitySeedsCreator< MeshConfig, Meshes::DimensionTag< MeshType::getMeshDimension() >, DimensionTag >;
-         std::set< typename SeedIndexedSet::key_type > seedSet;
+         SeedSet seedSet;
 
          for( GlobalIndexType i = 0; i < mesh.template getEntitiesCount< MeshType::getMeshDimension() >(); i++ )
          {
@@ -371,8 +375,10 @@ class InitializerLayer< MeshConfig,
             for( LocalIndexType j = 0; j < subentitySeeds.getSize(); j++ )
             {
                auto& seed = subentitySeeds[ j ];
-               if( this->seedsIndexedSet.count( seed ) == 0 ) {
-                  const GlobalIndexType entityIndex = this->seedsIndexedSet.insert( seed );
+               const auto pair = this->seedsIndexedSet.try_insert( seed );
+               const GlobalIndexType& entityIndex = pair.first;
+               if( pair.second ) {
+                  // insertion took place, initialize the entity
                   EntityInitializerType::initEntity( mesh.template getEntity< DimensionTag::value >( entityIndex ), entityIndex, seed, initializer );
                   this->referenceOrientations[ entityIndex ] = ReferenceOrientationType( seed );
                }
@@ -393,8 +399,6 @@ class InitializerLayer< MeshConfig,
       }
 
    private:
-
-      using SeedIndexedSet = typename MeshTraits< MeshConfig >::template EntityTraits< DimensionTag::value >::SeedIndexedSetType;
       SeedIndexedSet seedsIndexedSet;
       ReferenceOrientationArrayType referenceOrientations;
 };
diff --git a/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h b/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h
index e166f600d9..c0327785d6 100644
--- a/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h
+++ b/src/TNL/Meshes/MeshDetails/initializer/SuperentityStorageInitializer.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <set>
-#include <map>
+#include <unordered_map>
 
 #include <TNL/Meshes/DimensionTag.h>
 #include <TNL/Meshes/MeshDetails/traits/MeshSuperentityTraits.h>
@@ -86,7 +86,7 @@ public:
 
 private:
    using DynamicIndexSet = std::set< GlobalIndexType >;
-   std::map< GlobalIndexType, DynamicIndexSet > dynamicStorageNetwork;
+   std::unordered_map< GlobalIndexType, DynamicIndexSet > dynamicStorageNetwork;
 
    GlobalIndexType getMaxSuperentityIndex()
    {
diff --git a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h
index 31660471db..6b21bcde96 100644
--- a/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h
+++ b/src/TNL/Meshes/MeshDetails/traits/MeshEntityTraits.h
@@ -17,11 +17,13 @@
 #pragma once
 
 #include <TNL/Containers/Array.h>
-#include <TNL/Containers/IndexedSet.h>
+#include <TNL/Containers/UnorderedIndexedSet.h>
 #include <TNL/Meshes/Topologies/SubentityVertexMap.h>
 #include <TNL/Meshes/MeshDetails/traits/MeshTraits.h>
 #include <TNL/Meshes/MeshDetails/initializer/EntitySeed.h>
 
+#include <unordered_set>
+
 namespace TNL {
 namespace Meshes {
 
@@ -81,7 +83,8 @@ public:
    using ReferenceOrientationType      = MeshEntityReferenceOrientation< MeshConfig, EntityTopology >;
 
    using StorageArrayType              = Containers::Array< EntityType, Device, GlobalIndexType >;
-   using SeedIndexedSetType            = Containers::IndexedSet< typename SeedType::KeyType, GlobalIndexType >;
+   using SeedIndexedSetType            = Containers::UnorderedIndexedSet< SeedType, GlobalIndexType, typename SeedType::HashType, typename SeedType::KeyEqual >;
+   using SeedSetType                   = std::unordered_set< typename SeedIndexedSetType::key_type, typename SeedIndexedSetType::hasher, typename SeedIndexedSetType::key_equal >;
    using ReferenceOrientationArrayType = Containers::Array< ReferenceOrientationType, Device, GlobalIndexType >;
 
    static constexpr bool storageEnabled = MeshConfig::entityStorage( Dimension );
-- 
GitLab