@@ -689,19 +689,38 @@ std::vector<std::array<std::int64_t, 3>> exchange_ghost_indexing(
689689// / @param[in] global_to_local Sorted array of (global, local) indices.
690690std::vector<std::int32_t > convert_to_local_indexing (
691691 std::span<const std::int64_t > g,
692- std::span<const std::pair<std::int64_t , std::int32_t >> global_to_local)
692+ std::span<const std::pair<std::int64_t , std::int32_t >> global_to_local,
693+ int num_threads)
693694{
695+ auto transform =
696+ [](std::span<std::int32_t > data, std::span<const std::int64_t > g,
697+ std::span<const std::pair<std::int64_t , std::int32_t >> global_to_local)
698+ {
699+ std::transform (g.begin (), g.end (), data.begin (),
700+ [&global_to_local](auto i)
701+ {
702+ auto it = std::ranges::lower_bound (
703+ global_to_local, i, std::ranges::less (),
704+ [](auto & e) { return e.first ; });
705+ assert (it != global_to_local.end ());
706+ assert (it->first == i);
707+ return it->second ;
708+ });
709+ };
710+
694711 std::vector<std::int32_t > data (g.size ());
695- std::transform (g.begin (), std::next (g.begin (), data.size ()), data.begin (),
696- [&global_to_local](auto i)
697- {
698- auto it = std::ranges::lower_bound (
699- global_to_local, i, std::ranges::less (),
700- [](auto & e) { return e.first ; });
701- assert (it != global_to_local.end ());
702- assert (it->first == i);
703- return it->second ;
704- });
712+ if (num_threads > 0 )
713+ {
714+ std::vector<std::jthread> threads (num_threads);
715+ for (int i = 0 ; i < num_threads; ++i)
716+ {
717+ auto [c0, c1] = dolfinx::MPI::local_range (i, g.size (), num_threads);
718+ threads[i] = std::jthread (transform, std::span (data.data () + c0, c1 - c0),
719+ g.subspan (c0, c1 - c0), global_to_local);
720+ }
721+ }
722+ else
723+ transform (data, g, global_to_local);
705724
706725 return data;
707726}
@@ -733,7 +752,6 @@ build_entity_types(const std::vector<CellType>& cell_types)
733752 }
734753 return entity_types;
735754}
736-
737755} // namespace
738756
739757// -----------------------------------------------------------------------------
@@ -834,9 +852,10 @@ std::shared_ptr<const common::IndexMap> Topology::index_map(int dim) const
834852 = this ->index_maps (dim);
835853 if (im.empty ())
836854 {
837- throw std::runtime_error (std::format (
838- " Missing IndexMap in Topology. Maybe you need to create_entities({})." ,
839- dim));
855+ throw std::runtime_error (
856+ std::format (" Missing IndexMap in Topology. Maybe you need to "
857+ " create_entities({})." ,
858+ dim));
840859 }
841860
842861 return im.at (0 );
@@ -1034,7 +1053,7 @@ Topology mesh::create_topology(
10341053 std::vector<std::span<const std::int64_t >> cells,
10351054 std::vector<std::span<const std::int64_t >> original_cell_index,
10361055 std::vector<std::span<const int >> ghost_owners,
1037- std::span<const std::int64_t > boundary_vertices)
1056+ std::span<const std::int64_t > boundary_vertices, int num_threads )
10381057{
10391058 common::Timer timer (" Topology: create" );
10401059
@@ -1044,6 +1063,7 @@ Topology mesh::create_topology(
10441063
10451064 // Check cell data consistency and compile spans of owned and ghost
10461065 // cells
1066+ common::Timer timer0 (" Topology: 0" );
10471067 spdlog::info (" Create topology (generalised)" );
10481068 std::vector<std::int32_t > num_local_cells (cell_types.size ());
10491069 std::vector<std::span<const std::int64_t >> owned_cells;
@@ -1064,6 +1084,11 @@ Topology mesh::create_topology(
10641084 ghost_cells.push_back (cells[i].last (ghost_owners[i].size () * num_vertices));
10651085 }
10661086
1087+ timer0.stop ();
1088+ timer0.flush ();
1089+
1090+ common::Timer timer1 (" Topology: 1" );
1091+
10671092 // Create sets of owned and unowned vertices from the cell ownership
10681093 // and the list of boundary vertices
10691094 auto [owned_vertices, unowned_vertices]
@@ -1075,9 +1100,14 @@ Topology mesh::create_topology(
10751100 const graph::AdjacencyList<int > global_vertex_to_ranks
10761101 = determine_sharing_ranks (comm, boundary_vertices);
10771102
1103+ timer1.stop ();
1104+ timer1.flush ();
1105+
10781106 // Iterate over vertices that have 'unknown' ownership, and if flagged
10791107 // as owned by determine_sharing_ranks update ownership status
10801108 {
1109+ common::Timer timer2 (" Topology: 2" );
1110+
10811111 const int mpi_rank = dolfinx::MPI::rank (comm);
10821112 std::vector<std::int64_t > owned_shared_vertices;
10831113 for (std::size_t i = 0 ; i < boundary_vertices.size (); ++i)
@@ -1102,13 +1132,15 @@ Topology mesh::create_topology(
11021132 dolfinx::radix_sort (owned_vertices);
11031133 }
11041134
1135+ // NOTE: This block is relatively expensive
11051136 // Number all owned vertices, iterating over vertices cell-wise
11061137 std::vector<std::int32_t > local_vertex_indices (owned_vertices.size (), -1 );
11071138 {
1139+ common::Timer timer3 (" Topology: 3 " );
11081140 std::int32_t v = 0 ;
1109- for (std::size_t i = 0 ; i < cell_types. size (); ++i )
1141+ for (std::span< const std:: int64_t > cells_t : cells )
11101142 {
1111- for (auto vtx : cells[i] )
1143+ for (auto vtx : cells_t )
11121144 {
11131145 if (auto it = std::ranges::lower_bound (owned_vertices, vtx);
11141146 it != owned_vertices.end () and *it == vtx)
@@ -1121,6 +1153,8 @@ Topology mesh::create_topology(
11211153 }
11221154 }
11231155
1156+ common::Timer timer4 (" Topology: 4" );
1157+
11241158 // Compute the global offset for owned (local) vertex indices
11251159 std::int64_t global_offset_v = 0 ;
11261160 {
@@ -1136,29 +1170,39 @@ Topology mesh::create_topology(
11361170 std::span cell_idx (original_cell_index[i]);
11371171 cell_ghost_indices.push_back (graph::build::compute_ghost_indices (
11381172 comm, cell_idx.first (num_local_cells[i]),
1139- cell_idx.last (ghost_owners[i].size ()), ghost_owners[i]));
1173+ cell_idx.last (ghost_owners[i].size ()), ghost_owners[i], num_threads ));
11401174
11411175 // Create index maps for each cell type
11421176 index_map_c.push_back (std::make_shared<common::IndexMap>(
11431177 comm, num_local_cells[i], cell_ghost_indices[i], ghost_owners[i],
11441178 static_cast <int >(dolfinx::MPI ::tag::consensus_nbx) + i));
11451179 }
11461180
1181+ timer4.stop ();
1182+ timer4.flush ();
1183+
11471184 // Send and receive ((input vertex index) -> (new global index, owner
11481185 // rank)) data with neighbours (for vertices on 'true domain
11491186 // boundary')
1187+ common::Timer timer5 (" Topology: 5" );
1188+
11501189 const std::vector<std::int64_t > unowned_vertex_data = exchange_indexing (
11511190 comm, boundary_vertices, global_vertex_to_ranks, global_offset_v,
11521191 owned_vertices, local_vertex_indices);
11531192 assert (unowned_vertex_data.size () % 3 == 0 );
11541193
1194+ timer5.stop ();
1195+ timer5.flush ();
1196+
11551197 // Unpack received data and build array of ghost vertices and owners
11561198 // of the ghost vertices
11571199 std::vector<std::int64_t > ghost_vertices;
11581200 std::vector<int > ghost_vertex_owners;
11591201 std::vector<std::int32_t > local_vertex_indices_unowned (
11601202 unowned_vertices.size (), -1 );
11611203 {
1204+ common::Timer timer6 (" Topology: 6" );
1205+
11621206 std::int32_t v = owned_vertices.size ();
11631207 for (std::size_t i = 0 ; i < unowned_vertex_data.size (); i += 3 )
11641208 {
@@ -1244,14 +1288,18 @@ Topology mesh::create_topology(
12441288 { return {idx0, idx1}; });
12451289 std::ranges::sort (global_to_local_vertices);
12461290
1291+ common::Timer timer7 (" Topology: 7" );
12471292 std::vector<std::vector<std::int32_t >> _cells_local_idx;
12481293 _cells_local_idx.reserve (cells.size ());
12491294 for (std::span<const std::int64_t > c : cells)
12501295 {
12511296 _cells_local_idx.push_back (
1252- convert_to_local_indexing (c, global_to_local_vertices));
1297+ convert_to_local_indexing (c, global_to_local_vertices, num_threads ));
12531298 }
12541299
1300+ timer7.stop ();
1301+ timer7.flush ();
1302+
12551303 // -- Create Topology object
12561304
12571305 // Determine which ranks ghost vertices that are owned by this rank.
@@ -1309,11 +1357,12 @@ Topology
13091357mesh::create_topology (MPI_Comm comm, std::span<const std::int64_t > cells,
13101358 std::span<const std::int64_t > original_cell_index,
13111359 std::span<const int > ghost_owners, CellType cell_type,
1312- std::span<const std::int64_t > boundary_vertices)
1360+ std::span<const std::int64_t > boundary_vertices,
1361+ int num_threads)
13131362{
13141363 spdlog::info (" Create topology (single cell type)" );
13151364 return create_topology (comm, {cell_type}, {cells}, {original_cell_index},
1316- {ghost_owners}, boundary_vertices);
1365+ {ghost_owners}, boundary_vertices, num_threads );
13171366}
13181367// -----------------------------------------------------------------------------
13191368std::tuple<Topology, std::vector<int32_t >, std::vector<int32_t >>
0 commit comments