Skip to content

Commit 4c8f09c

Browse files
committed
Use unordered flat containers
1 parent 09939b5 commit 4c8f09c

16 files changed

+71
-76
lines changed

CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ set(CMAKE_CXX_EXTENSIONS OFF)
1616
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
1717
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
1818

19-
find_package(Boost CONFIG REQUIRED COMPONENTS container container_hash endian iterator program_options range)
19+
find_package(Boost CONFIG REQUIRED COMPONENTS container container_hash endian iterator program_options range unordered)
2020
find_package(CGAL CONFIG REQUIRED)
2121
find_package(FastFloat CONFIG REQUIRED)
2222
find_package(GTest CONFIG REQUIRED)
@@ -36,6 +36,7 @@ target_link_libraries(${TARGET} INTERFACE
3636
Boost::endian
3737
Boost::iterator
3838
Boost::range
39+
Boost::unordered
3940
CGAL::CGAL
4041
FastFloat::fast_float
4142
)

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ Here are the timings (in seconds) for computing the Boolean intersection between
2323

2424
| Test case | [coref.][coref] (seq.) | [geogram][geogram] (par.) | kigumi (seq.)¹ | kigumi (par.) | [libigl][libigl] (par.)² | [manif.][manif] (seq.)³ | manif. (par.) | [MCUT][mcut] (par.) |
2525
|-------------------|-----------------------:|--------------------------:|---------------:|--------------:|-------------------------:|------------------------:|--------------:|--------------------:|
26-
| **Open** | 4.6 | FAILED | 2.3 | 1.2 | FAILED | FAILED | FAILED | FAILED |
27-
| **Open & closed** | FAILED | 70.5 | 1.6 | 0.9 | FAILED | FAILED | FAILED | FAILED |
28-
| **Closed** | 57.4 | FAILED | 5.1 | 2.5 | 61.0 | 8.9 | 1.7 | 24.5 |
26+
| **Open** | 4.6 | FAILED | 2.0 | 1.1 | FAILED | FAILED | FAILED | FAILED |
27+
| **Open & closed** | FAILED | 70.5 | 1.3 | 0.7 | FAILED | FAILED | FAILED | FAILED |
28+
| **Closed** | 57.4 | FAILED | 4.7 | 2.2 | 61.0 | 8.9 | 1.7 | 24.5 |
2929
| **Non-manifold** | FAILED | FAILED | 0.5 | 0.2 | FAILED | FAILED | FAILED | FAILED |
3030

3131
¹ Ran with `KIGUMI_NUM_THREADS=1`. ² `igl::copyleft::cgal::mesh_boolean` with `CGAL::Lazy_exact_nt<mpq_class>` as the

include/kigumi/Classify_faces_globally.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
#include <queue>
1616
#include <stdexcept>
17-
#include <unordered_set>
1817
#include <vector>
1918

2019
namespace kigumi {
@@ -27,7 +26,7 @@ class Classify_faces_globally {
2726
using Triangle_soup = Triangle_soup<K, FaceData>;
2827

2928
public:
30-
Warnings operator()(Mixed_triangle_mesh& m, const std::unordered_set<Edge>& border_edges,
29+
Warnings operator()(Mixed_triangle_mesh& m, const Edge_set& border_edges,
3130
const Triangle_soup& left, const Triangle_soup& right) const {
3231
auto representative_faces = find_unclassified_connected_components(m, border_edges);
3332
Warnings warnings{};
@@ -56,7 +55,7 @@ class Classify_faces_globally {
5655

5756
private:
5857
static std::vector<Face_index> find_unclassified_connected_components(
59-
const Mixed_triangle_mesh& m, const std::unordered_set<Edge>& border_edges) {
58+
const Mixed_triangle_mesh& m, const Edge_set& border_edges) {
6059
std::vector<Face_index> representative_faces;
6160
std::vector<bool> visited(m.num_faces(), false);
6261
std::queue<Face_index> queue;

include/kigumi/Classify_faces_locally.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
#include <algorithm>
1414
#include <stdexcept>
15-
#include <unordered_set>
1615
#include <vector>
1716

1817
namespace kigumi {
@@ -25,7 +24,7 @@ class Classify_faces_locally {
2524

2625
public:
2726
Warnings operator()(Mixed_triangle_mesh<K, FaceData>& m, const Edge& edge,
28-
const std::unordered_set<Edge>& border_edges) const {
27+
const Edge_set& border_edges) const {
2928
bool found_untagged_face{};
3029
for (auto fi : m.faces_around_edge(edge)) {
3130
if (m.data(fi).tag == Face_tag::UNKNOWN) {

include/kigumi/Face_face_intersection.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include <array>
1010
#include <boost/container/static_vector.hpp>
1111
#include <boost/container_hash/hash.hpp>
12-
#include <unordered_map>
12+
#include <boost/unordered/unordered_flat_map.hpp>
1313
#include <utility>
1414
#include <vector>
1515

@@ -20,7 +20,7 @@ class Face_face_intersection {
2020
using Orientation_3_key = std::array<std::size_t, 4>;
2121
using Orientation_3_key_hash = boost::hash<Orientation_3_key>;
2222
using Orientation_3_map =
23-
std::unordered_map<Orientation_3_key, CGAL::Orientation, Orientation_3_key_hash>;
23+
boost::unordered_flat_map<Orientation_3_key, CGAL::Orientation, Orientation_3_key_hash>;
2424
using Point_list = Point_list<K>;
2525

2626
public:

include/kigumi/Find_coplanar_faces.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <algorithm>
99
#include <array>
1010
#include <boost/container_hash/hash.hpp>
11-
#include <unordered_map>
11+
#include <boost/unordered/unordered_flat_map.hpp>
1212
#include <utility>
1313
#include <vector>
1414

@@ -36,7 +36,7 @@ class Find_coplanar_faces {
3636
auto& a_face_tags = left_is_a ? left_face_tags : right_face_tags;
3737
auto& b_face_tags = left_is_a ? right_face_tags : left_face_tags;
3838

39-
std::unordered_map<Triangle, Face_index, Triangle_hash> triangle_to_fi;
39+
boost::unordered_flat_map<Triangle, Face_index, Triangle_hash> triangle_to_fi;
4040

4141
triangle_to_fi.reserve(a.num_faces());
4242
for (auto fi : a.faces()) {

include/kigumi/Find_defects.h

+33-35
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
#include <algorithm>
1313
#include <array>
14+
#include <boost/unordered/concurrent_flat_map.hpp>
15+
#include <boost/unordered/concurrent_flat_set.hpp>
1416
#include <boost/variant/get.hpp>
17+
#include <functional>
1518
#include <iterator>
16-
#include <unordered_map>
17-
#include <unordered_set>
1819
#include <utility>
1920
#include <vector>
2021

@@ -27,6 +28,17 @@ class Find_defects {
2728
using Triangle_soup = Triangle_soup<K, FaceData>;
2829
using Leaf = typename Triangle_soup::Leaf;
2930

31+
struct Halfedge_hash {
32+
std::size_t operator()(const Halfedge& he) const noexcept {
33+
std::size_t seed{};
34+
boost::hash_combine(seed, he[0].idx());
35+
boost::hash_combine(seed, he[1].idx());
36+
return seed;
37+
}
38+
};
39+
40+
using Halfedge_count = boost::unordered_flat_map<Halfedge, std::size_t, Halfedge_hash>;
41+
3042
public:
3143
explicit Find_defects(const Triangle_soup& m)
3244
: m_{merge_duplicate_vertices(m)},
@@ -153,7 +165,8 @@ class Find_defects {
153165
continue;
154166
}
155167

156-
std::unordered_map<Vertex_index, std::size_t> local_vis;
168+
boost::unordered_flat_map<Vertex_index, std::size_t, std::hash<kigumi::Vertex_index>>
169+
local_vis;
157170
for (auto v_fi : v_fis) {
158171
local_vis.emplace(next_vertex(v_fi, vi), local_vis.size());
159172
local_vis.emplace(prev_vertex(v_fi, vi), local_vis.size());
@@ -180,73 +193,58 @@ class Find_defects {
180193

181194
static Halfedge opposite(const Halfedge& he) { return {he[1], he[0]}; }
182195

183-
static std::unordered_multiset<Halfedge> collect_halfedges(const Triangle_soup& m) {
184-
std::unordered_multiset<Halfedge> hes;
196+
static Halfedge_count collect_halfedges(const Triangle_soup& m) {
197+
Halfedge_count hes;
185198
for (auto fi : m.faces()) {
186199
const auto& f = m.face(fi);
187200
if (f[0] == f[1] || f[1] == f[2] || f[2] == f[0]) {
188201
continue;
189202
}
190203
for (std::size_t i = 0; i < 3; ++i) {
191204
auto he = Halfedge{f.at(i), f.at((i + 1) % 3)};
192-
hes.insert(he);
205+
++hes[he];
193206
}
194207
}
195208
return hes;
196209
}
197210

198-
static std::vector<Edge> boundary_edges(const std::unordered_multiset<Halfedge>& hes) {
211+
static std::vector<Edge> boundary_edges(const Halfedge_count& hes) {
199212
std::vector<Edge> edges;
200213

201-
Halfedge last_he;
202-
for (const auto& he : hes) {
203-
if (he == last_he) {
204-
continue;
205-
}
206-
last_he = he;
207-
208-
if (hes.count(he) == 1 && !hes.contains(opposite(he))) {
214+
for (const auto& [he, count] : hes) {
215+
if (count == 1 && !hes.contains(opposite(he))) {
209216
edges.push_back(to_edge(he));
210217
}
211218
}
212219

213220
return edges;
214221
}
215222

216-
static std::vector<Edge> inconsistent_edges(const std::unordered_multiset<Halfedge>& hes) {
223+
static std::vector<Edge> inconsistent_edges(const Halfedge_count& hes) {
217224
std::vector<Edge> edges;
218225

219-
Halfedge last_he;
220-
for (const auto& he : hes) {
221-
if (he == last_he) {
222-
continue;
223-
}
224-
last_he = he;
225-
226-
if (hes.count(he) == 2 && !hes.contains(opposite(he))) {
226+
for (const auto& [he, count] : hes) {
227+
if (count == 2 && !hes.contains(opposite(he))) {
227228
edges.push_back(to_edge(he));
228229
}
229230
}
230231

231232
return edges;
232233
}
233234

234-
static std::vector<Edge> non_manifold_edges(const std::unordered_multiset<Halfedge>& hes) {
235+
static std::vector<Edge> non_manifold_edges(const Halfedge_count& hes) {
235236
std::vector<Edge> edges;
236237

237-
Halfedge last_he;
238-
for (const auto& he : hes) {
239-
if (he == last_he) {
240-
continue;
241-
}
242-
last_he = he;
238+
for (const auto& [he, count] : hes) {
239+
auto opp_it = hes.find(opposite(he));
240+
auto opp_count = opp_it == hes.end() ? 0 : opp_it->second;
243241

244-
if (he[0] > he[1] && hes.contains(opposite(he))) {
242+
if (he[0] > he[1] && opp_count > 0) {
245243
// Prevent double counting.
246244
continue;
247245
}
248246

249-
if (hes.count(he) + hes.count(opposite(he)) > 2) {
247+
if (count + opp_count > 2) {
250248
edges.push_back(to_edge(he));
251249
}
252250
}
@@ -298,7 +296,7 @@ class Find_defects {
298296
const std::vector<Face_index>& non_trivial_degenerate_faces) {
299297
std::vector<Face_index> fis;
300298

301-
std::unordered_set<Face_index> degenerate_faces;
299+
boost::unordered_flat_set<Face_index, std::hash<Face_index>> degenerate_faces;
302300
degenerate_faces.reserve(trivial_degenerate_faces.size() + non_trivial_degenerate_faces.size());
303301
degenerate_faces.insert(trivial_degenerate_faces.begin(), trivial_degenerate_faces.end());
304302
degenerate_faces.insert(non_trivial_degenerate_faces.begin(),
@@ -378,7 +376,7 @@ class Find_defects {
378376
Triangle_soup m_;
379377
std::vector<Vertex_index> isolated_vertices_;
380378
std::vector<Vertex_index> non_manifold_vertices_;
381-
std::unordered_multiset<Halfedge> halfedges_;
379+
Halfedge_count halfedges_;
382380
std::vector<Edge> boundary_edges_;
383381
std::vector<Edge> inconsistent_edges_;
384382
std::vector<Edge> non_manifold_edges_;

include/kigumi/Intersection_point_inserter.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
#include <array>
88
#include <boost/container_hash/hash.hpp>
9+
#include <boost/unordered/unordered_flat_map.hpp>
910
#include <limits>
1011
#include <tuple>
11-
#include <unordered_map>
1212
#include <utility>
1313

1414
namespace kigumi {
@@ -131,11 +131,11 @@ class Intersection_point_inserter {
131131
}
132132

133133
Point_list& points_;
134-
std::unordered_map<Line_line_intersection_key, std::size_t,
135-
boost::hash<Line_line_intersection_key>>
134+
boost::unordered_flat_map<Line_line_intersection_key, std::size_t,
135+
boost::hash<Line_line_intersection_key>>
136136
line_line_intersection_cache_;
137-
std::unordered_map<Plane_line_intersection_key, std::size_t,
138-
boost::hash<Plane_line_intersection_key>>
137+
boost::unordered_flat_map<Plane_line_intersection_key, std::size_t,
138+
boost::hash<Plane_line_intersection_key>>
139139
plane_line_intersection_cache_;
140140
};
141141

include/kigumi/Mesh_entities.h

+6-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <array>
66
#include <boost/container_hash/hash.hpp>
7+
#include <boost/unordered/unordered_flat_set.hpp>
78
#include <functional>
89
#include <utility>
910

@@ -20,14 +21,15 @@ inline Edge make_edge(Vertex_index first, Vertex_index second) {
2021
return {first, second};
2122
}
2223

23-
} // namespace kigumi
24-
25-
template <>
26-
struct std::hash<kigumi::Edge> {
24+
struct Edge_hash {
2725
std::size_t operator()(const kigumi::Edge& edge) const noexcept {
2826
std::size_t seed{};
2927
boost::hash_combine(seed, edge[0].idx());
3028
boost::hash_combine(seed, edge[1].idx());
3129
return seed;
3230
}
3331
};
32+
33+
using Edge_set = boost::unordered_flat_set<Edge, Edge_hash>;
34+
35+
} // namespace kigumi

include/kigumi/Mix.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class Mix {
6363
std::cout << "Local classification..." << std::endl;
6464

6565
auto intersecting_edges = corefine.get_intersecting_edges();
66-
std::unordered_set<Edge> border_edges(intersecting_edges.begin(), intersecting_edges.end());
66+
Edge_set border_edges(intersecting_edges.begin(), intersecting_edges.end());
6767
Warnings warnings{};
6868

6969
parallel_do(

include/kigumi/Point_list.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <boost/container_hash/hash.hpp>
4-
#include <unordered_map>
4+
#include <boost/unordered/unordered_flat_map.hpp>
55
#include <utility>
66
#include <vector>
77

@@ -70,7 +70,7 @@ class Point_list {
7070
};
7171

7272
std::vector<Point> points_;
73-
std::unordered_map<Point, std::size_t, Point_hash> point_to_index_;
73+
boost::unordered_flat_map<Point, std::size_t, Point_hash> point_to_index_;
7474
bool check_uniqueness_{};
7575
};
7676

include/kigumi/Propagate_face_tags.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#include <queue>
1010
#include <stdexcept>
11-
#include <unordered_set>
1211

1312
namespace kigumi {
1413

@@ -17,8 +16,7 @@ class Propagate_face_tags {
1716
using Mixed_triangle_mesh = Mixed_triangle_mesh<K, FaceData>;
1817

1918
public:
20-
Warnings operator()(Mixed_triangle_mesh& m, const std::unordered_set<Edge>& border_edges,
21-
Face_index seed) const {
19+
Warnings operator()(Mixed_triangle_mesh& m, const Edge_set& border_edges, Face_index seed) const {
2220
const auto& data = m.data(seed);
2321
auto from_left = data.from_left;
2422
auto tag = data.tag;

include/kigumi/Triangle_mesh.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
#include <kigumi/parallel_sort.h>
1010

1111
#include <boost/range/iterator_range.hpp>
12-
#include <memory>
13-
#include <unordered_set>
1412
#include <utility>
1513
#include <vector>
1614

@@ -159,7 +157,7 @@ class Triangle_mesh {
159157
Face_around_edge_iterator(i_end, i_end, j_end, j_end));
160158
}
161159

162-
auto faces_around_face(Face_index fi, const std::unordered_set<Edge>& border_edges) const {
160+
auto faces_around_face(Face_index fi, const Edge_set& border_edges) const {
163161
const auto& f = face(fi);
164162
auto e1 = make_edge(f[0], f[1]);
165163
auto e2 = make_edge(f[1], f[2]);

include/kigumi/Triangulation.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
#include <kigumi/Triangle_region.h>
88

99
#include <array>
10+
#include <boost/unordered/unordered_flat_map.hpp>
1011
#include <stdexcept>
11-
#include <unordered_map>
1212

1313
namespace kigumi {
1414

@@ -131,7 +131,7 @@ class Triangulation {
131131
Triangle_region f_{};
132132
CDT cdt_;
133133
std::array<Vertex_handle, 3> vhs_;
134-
std::unordered_map<std::size_t, Vertex_handle> id_to_vh_;
134+
boost::unordered_flat_map<std::size_t, Vertex_handle> id_to_vh_;
135135
};
136136

137137
} // namespace kigumi

0 commit comments

Comments
 (0)