1 /*
2 Copyright (c) 2005-2021 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 /*
18 This file contains a few implementations, so it may look overly complicated.
19 The most efficient implementation is also separated into convex_hull_sample.cpp
20 */
21 #include "convex_hull.hpp"
22
23 typedef util::point<double> point_t;
24
25 #ifndef USETBB
26 #define USETBB 1
27 #endif
28 #ifndef USECONCVEC
29 #define USECONCVEC 1
30 #endif
31
32 #if !USETBB // Serial implementation of Quick Hull algorithm
33
34 typedef std::vector<point_t> pointVec_t;
35
36 void serial_initialize(pointVec_t &points);
37
38 class FindXExtremum : public std::unary_function<const point_t &, void> {
39 public:
40 typedef enum { minX, maxX } extremumType;
41
FindXExtremum(const point_t & frstPoint,extremumType exType_)42 FindXExtremum(const point_t &frstPoint, extremumType exType_)
43 : extrXPoint(frstPoint),
44 exType(exType_) {}
45
operator ()(const point_t & p)46 void operator()(const point_t &p) {
47 if (closerToExtremum(p))
48 extrXPoint = p;
49 }
50
operator point_t()51 operator point_t() {
52 return extrXPoint;
53 }
54
55 private:
56 const extremumType exType;
57 point_t extrXPoint;
58
closerToExtremum(const point_t & p) const59 bool closerToExtremum(const point_t &p) const {
60 switch (exType) {
61 case minX: return p.x < extrXPoint.x; break;
62 case maxX: return p.x > extrXPoint.x; break;
63 }
64 return false; // avoid warning
65 }
66 };
67
68 template <FindXExtremum::extremumType type>
extremum(const pointVec_t & points)69 point_t extremum(const pointVec_t &points) {
70 assert(!points.empty());
71 return std::for_each(points.begin(), points.end(), FindXExtremum(points[0], type));
72 }
73
74 class SplitByCP : public std::unary_function<const point_t &, void> {
75 pointVec_t &reducedSet;
76 point_t p1, p2;
77 point_t farPoint;
78 double howFar;
79
80 public:
SplitByCP(point_t _p1,point_t _p2,pointVec_t & _reducedSet)81 SplitByCP(point_t _p1, point_t _p2, pointVec_t &_reducedSet)
82 : p1(_p1),
83 p2(_p2),
84 reducedSet(_reducedSet),
85 howFar(0),
86 farPoint(p1) {}
87
operator ()(const point_t & p)88 void operator()(const point_t &p) {
89 double cp;
90 if ((p != p1) && (p != p2)) {
91 cp = util::cross_product(p1, p2, p);
92 if (cp > 0) {
93 reducedSet.push_back(p);
94 if (cp > howFar) {
95 farPoint = p;
96 howFar = cp;
97 }
98 }
99 }
100 }
101
operator point_t()102 operator point_t() {
103 return farPoint;
104 }
105 };
106
divide(const pointVec_t & P,pointVec_t & P_reduced,const point_t & p1,const point_t & p2)107 point_t divide(const pointVec_t &P, pointVec_t &P_reduced, const point_t &p1, const point_t &p2) {
108 SplitByCP splitByCP(p1, p2, P_reduced);
109 point_t farPoint = std::for_each(P.begin(), P.end(), splitByCP);
110
111 if (util::verbose) {
112 std::stringstream ss;
113 ss << P.size() << " nodes in bucket"
114 << ", "
115 << "dividing by: [ " << p1 << ", " << p2 << " ], "
116 << "farthest node: " << farPoint;
117 util::OUTPUT.push_back(ss.str());
118 }
119
120 return farPoint;
121 }
122
divide_and_conquer(const pointVec_t & P,pointVec_t & H,point_t p1,point_t p2)123 void divide_and_conquer(const pointVec_t &P, pointVec_t &H, point_t p1, point_t p2) {
124 assert(P.size() >= 2);
125 pointVec_t P_reduced;
126 pointVec_t H1, H2;
127 point_t p_far = divide(P, P_reduced, p1, p2);
128 if (P_reduced.size() < 2) {
129 H.push_back(p1);
130 H.insert(H.end(), P_reduced.begin(), P_reduced.end());
131 }
132 else {
133 divide_and_conquer(P_reduced, H1, p1, p_far);
134 divide_and_conquer(P_reduced, H2, p_far, p2);
135
136 H.insert(H.end(), H1.begin(), H1.end());
137 H.insert(H.end(), H2.begin(), H2.end());
138 }
139 }
140
quickhull(const pointVec_t & points,pointVec_t & hull)141 void quickhull(const pointVec_t &points, pointVec_t &hull) {
142 if (points.size() < 2) {
143 hull.insert(hull.end(), points.begin(), points.end());
144 return;
145 }
146 point_t p_maxx = extremum<FindXExtremum::maxX>(points);
147 point_t p_minx = extremum<FindXExtremum::minX>(points);
148
149 pointVec_t H;
150
151 divide_and_conquer(points, hull, p_maxx, p_minx);
152 divide_and_conquer(points, H, p_minx, p_maxx);
153 hull.insert(hull.end(), H.begin(), H.end());
154 }
155
main(int argc,char * argv[])156 int main(int argc, char *argv[]) {
157 utility::thread_number_range single_thread([] {
158 return -1;
159 });
160 util::ParseInputArgs(argc, argv, single_thread);
161
162 pointVec_t points;
163 pointVec_t hull;
164 util::my_time_t tm_init, tm_start, tm_end;
165
166 std::cout << "Starting serial version of QUICK HULL algorithm"
167 << "\n";
168
169 tm_init = util::gettime();
170 serial_initialize(points);
171 tm_start = util::gettime();
172 std::cout << "Init time: " << util::time_diff(tm_init, tm_start)
173 << " Points in input: " << points.size() << "\n";
174 tm_start = util::gettime();
175 quickhull(points, hull);
176 tm_end = util::gettime();
177 std::cout << "Serial time: " << util::time_diff(tm_start, tm_end)
178 << " Points in hull: " << hull.size() << "\n";
179
180 return 0;
181 }
182
183 #else // USETBB - parallel version of Quick Hull algorithm
184
185 #include "oneapi/tbb/parallel_for.h"
186 #include "oneapi/tbb/parallel_reduce.h"
187 #include "oneapi/tbb/blocked_range.h"
188
189 typedef oneapi::tbb::blocked_range<std::size_t> range_t;
190
191 #if USECONCVEC
192 #include "oneapi/tbb/concurrent_vector.h"
193
194 typedef oneapi::tbb::concurrent_vector<point_t> pointVec_t;
195
appendVector(const point_t * src,std::size_t srcSize,pointVec_t & dest)196 void appendVector(const point_t *src, std::size_t srcSize, pointVec_t &dest) {
197 std::copy(src, src + srcSize, dest.grow_by(srcSize));
198 }
199
appendVector(const pointVec_t & src,pointVec_t & dest)200 void appendVector(const pointVec_t &src, pointVec_t &dest) {
201 std::copy(src.begin(), src.end(), dest.grow_by(src.size()));
202 }
203
grow_vector_to_at_least(pointVec_t & vect,std::size_t size)204 void grow_vector_to_at_least(pointVec_t &vect, std::size_t size) {
205 vect.grow_to_at_least(size);
206 }
207 #else // USE STD::VECTOR - include spin_mutex.h and lock vector operations
208 #include "oneapi/tbb/spin_mutex.h"
209
210 typedef oneapi::tbb::spin_mutex mutex_t;
211 typedef std::vector<point_t> pointVec_t;
212
appendVector(mutex_t & insertMutex,const pointVec_t & src,pointVec_t & dest)213 void appendVector(mutex_t &insertMutex, const pointVec_t &src, pointVec_t &dest) {
214 mutex_t::scoped_lock lock(insertMutex);
215 dest.insert(dest.end(), src.begin(), src.end());
216 }
217
appendVector(mutex_t & insertMutex,const point_t * src,std::size_t srcSize,pointVec_t & dest)218 void appendVector(mutex_t &insertMutex, const point_t *src, std::size_t srcSize, pointVec_t &dest) {
219 mutex_t::scoped_lock lock(insertMutex);
220 dest.insert(dest.end(), src, src + srcSize);
221 }
222
grow_vector_to_at_least(mutex_t & mutex,pointVec_t & vect,std::size_t size)223 void grow_vector_to_at_least(mutex_t &mutex, pointVec_t &vect, std::size_t size) {
224 mutex_t::scoped_lock lock(mutex);
225 if (vect.size() < size) {
226 vect.resize(size);
227 }
228 }
229 #endif // USECONCVEC
230
231 class FillRNDPointsVector {
232 pointVec_t &points;
233
234 public:
235 static const std::size_t grainSize = cfg::generateGrainSize;
236 #if !USECONCVEC
237 static mutex_t pushBackMutex;
238 #endif // USECONCVEC
239
FillRNDPointsVector(pointVec_t & _points)240 explicit FillRNDPointsVector(pointVec_t &_points) : points(_points) {}
241
operator ()(const range_t & range) const242 void operator()(const range_t &range) const {
243 util::rng the_rng(range.begin());
244 const std::size_t i_end = range.end();
245 std::size_t count = 0;
246 #if USECONCVEC
247 points.grow_to_at_least(i_end);
248 #else // Locked enlarge to a not thread-safe STD::VECTOR
249 grow_vector_to_at_least(pushBackMutex, points, i_end);
250 #endif // USECONCVEC
251
252 for (std::size_t i = range.begin(); i != i_end; ++i) {
253 points[i] = util::GenerateRNDPoint<double>(count, the_rng, util::rng::max_rand);
254 }
255 }
256 };
257
258 class FillRNDPointsVector_buf {
259 pointVec_t &points;
260
261 public:
262 static const std::size_t grainSize = cfg::generateGrainSize;
263 #if !USECONCVEC
264 static mutex_t insertMutex;
265 #endif // USECONCVEC
266
FillRNDPointsVector_buf(pointVec_t & _points)267 explicit FillRNDPointsVector_buf(pointVec_t &_points) : points(_points) {}
268
operator ()(const range_t & range) const269 void operator()(const range_t &range) const {
270 util::rng the_rng(range.begin());
271 const std::size_t i_end = range.end();
272 std::size_t count = 0, j = 0;
273 point_t tmp_vec[grainSize];
274
275 for (std::size_t i = range.begin(); i != i_end; ++i) {
276 tmp_vec[j++] = util::GenerateRNDPoint<double>(count, the_rng, util::rng::max_rand);
277 }
278 #if USECONCVEC
279 grow_vector_to_at_least(points, range.end());
280 #else // USE STD::VECTOR
281 grow_vector_to_at_least(insertMutex, points, range.end());
282 #endif // USECONCVEC
283 std::copy(tmp_vec, tmp_vec + j, points.begin() + range.begin());
284 }
285 };
286
287 #if !USECONCVEC
288 mutex_t FillRNDPointsVector::pushBackMutex{};
289 mutex_t FillRNDPointsVector_buf::insertMutex{};
290 #endif
291
292 template <typename BodyType>
initialize(pointVec_t & points)293 void initialize(pointVec_t &points) {
294 //This function generate the same series of point on every call.
295 //Reproducibility is needed for benchmarking to produce reliable results.
296 //It is achieved through the following points:
297 // - FillRNDPointsVector_buf instance has its own local instance
298 // of random number generator, which in turn does not use any global data
299 // - oneapi::tbb::simple_partitioner produce the same set of ranges on every call to
300 // oneapi::tbb::parallel_for
301 // - local RNG instances are seeded by the starting indexes of corresponding ranges
302 // - grow_to_at_least() enables putting points into the resulting vector in deterministic order
303 // (unlike concurrent push_back or grow_by).
304
305 // In the buffered version, a temporary storage for as much as grainSize elements
306 // is allocated inside the body. Since auto_partitioner may increase effective
307 // range size which would cause a crash, simple partitioner has to be used.
308
309 oneapi::tbb::parallel_for(range_t(0, cfg::numberOfPoints, BodyType::grainSize),
310 BodyType(points),
311 oneapi::tbb::simple_partitioner());
312 }
313
314 class FindXExtremum {
315 public:
316 typedef enum { minX, maxX } extremumType;
317
318 static const std::size_t grainSize = cfg::findExtremumGrainSize;
319
FindXExtremum(const pointVec_t & points_,extremumType exType_)320 FindXExtremum(const pointVec_t &points_, extremumType exType_)
321 : points(points_),
322 exType(exType_),
323 extrXPoint(points[0]) {}
324
FindXExtremum(const FindXExtremum & fxex,oneapi::tbb::split)325 FindXExtremum(const FindXExtremum &fxex, oneapi::tbb::split)
326 : points(fxex.points),
327 exType(fxex.exType),
328 extrXPoint(fxex.extrXPoint) {}
329
operator ()(const range_t & range)330 void operator()(const range_t &range) {
331 const std::size_t i_end = range.end();
332 if (!range.empty()) {
333 for (std::size_t i = range.begin(); i != i_end; ++i) {
334 if (closerToExtremum(points[i])) {
335 extrXPoint = points[i];
336 }
337 }
338 }
339 }
340
join(const FindXExtremum & rhs)341 void join(const FindXExtremum &rhs) {
342 if (closerToExtremum(rhs.extrXPoint)) {
343 extrXPoint = rhs.extrXPoint;
344 }
345 }
346
extremeXPoint()347 point_t extremeXPoint() {
348 return extrXPoint;
349 }
350
351 private:
352 const pointVec_t &points;
353 const extremumType exType;
354 point_t extrXPoint;
closerToExtremum(const point_t & p) const355 bool closerToExtremum(const point_t &p) const {
356 switch (exType) {
357 case minX: return p.x < extrXPoint.x; break;
358 case maxX: return p.x > extrXPoint.x; break;
359 }
360 return false; // avoid warning
361 }
362 };
363
364 template <FindXExtremum::extremumType type>
extremum(const pointVec_t & P)365 point_t extremum(const pointVec_t &P) {
366 FindXExtremum fxBody(P, type);
367 oneapi::tbb::parallel_reduce(range_t(0, P.size(), FindXExtremum::grainSize), fxBody);
368 return fxBody.extremeXPoint();
369 }
370
371 class SplitByCP {
372 const pointVec_t &initialSet;
373 pointVec_t &reducedSet;
374 point_t p1, p2;
375 point_t farPoint;
376 double howFar;
377
378 public:
379 static const std::size_t grainSize = cfg::divideGrainSize;
380 #if !USECONCVEC
381 static mutex_t pushBackMutex;
382 #endif // USECONCVEC
383
SplitByCP(point_t _p1,point_t _p2,const pointVec_t & _initialSet,pointVec_t & _reducedSet)384 SplitByCP(point_t _p1, point_t _p2, const pointVec_t &_initialSet, pointVec_t &_reducedSet)
385 : p1(_p1),
386 p2(_p2),
387 initialSet(_initialSet),
388 reducedSet(_reducedSet),
389 howFar(0),
390 farPoint(p1) {}
391
SplitByCP(SplitByCP & sbcp,oneapi::tbb::split)392 SplitByCP(SplitByCP &sbcp, oneapi::tbb::split)
393 : p1(sbcp.p1),
394 p2(sbcp.p2),
395 initialSet(sbcp.initialSet),
396 reducedSet(sbcp.reducedSet),
397 howFar(0),
398 farPoint(p1) {}
399
operator ()(const range_t & range)400 void operator()(const range_t &range) {
401 const std::size_t i_end = range.end();
402 double cp;
403 for (std::size_t i = range.begin(); i != i_end; ++i) {
404 if ((initialSet[i] != p1) && (initialSet[i] != p2)) {
405 cp = util::cross_product(p1, p2, initialSet[i]);
406 if (cp > 0) {
407 #if USECONCVEC
408 reducedSet.push_back(initialSet[i]);
409 #else // Locked push_back to a not thread-safe STD::VECTOR
410 {
411 mutex_t::scoped_lock lock(pushBackMutex);
412 reducedSet.push_back(initialSet[i]);
413 }
414 #endif // USECONCVEC
415 if (cp > howFar) {
416 farPoint = initialSet[i];
417 howFar = cp;
418 }
419 }
420 }
421 }
422 }
423
join(const SplitByCP & rhs)424 void join(const SplitByCP &rhs) {
425 if (rhs.howFar > howFar) {
426 howFar = rhs.howFar;
427 farPoint = rhs.farPoint;
428 }
429 }
430
farthestPoint() const431 point_t farthestPoint() const {
432 return farPoint;
433 }
434 };
435
436 class SplitByCP_buf {
437 const pointVec_t &initialSet;
438 pointVec_t &reducedSet;
439 point_t p1, p2;
440 point_t farPoint;
441 double howFar;
442
443 public:
444 static const std::size_t grainSize = cfg::divideGrainSize;
445 #if !USECONCVEC
446 static mutex_t insertMutex;
447 #endif // USECONCVEC
448
SplitByCP_buf(point_t _p1,point_t _p2,const pointVec_t & _initialSet,pointVec_t & _reducedSet)449 SplitByCP_buf(point_t _p1, point_t _p2, const pointVec_t &_initialSet, pointVec_t &_reducedSet)
450 : p1(_p1),
451 p2(_p2),
452 initialSet(_initialSet),
453 reducedSet(_reducedSet),
454 howFar(0),
455 farPoint(p1) {}
456
SplitByCP_buf(SplitByCP_buf & sbcp,oneapi::tbb::split)457 SplitByCP_buf(SplitByCP_buf &sbcp, oneapi::tbb::split)
458 : p1(sbcp.p1),
459 p2(sbcp.p2),
460 initialSet(sbcp.initialSet),
461 reducedSet(sbcp.reducedSet),
462 howFar(0),
463 farPoint(p1) {}
464
operator ()(const range_t & range)465 void operator()(const range_t &range) {
466 const std::size_t i_end = range.end();
467 std::size_t j = 0;
468 double cp;
469 point_t tmp_vec[grainSize];
470 for (std::size_t i = range.begin(); i != i_end; ++i) {
471 if ((initialSet[i] != p1) && (initialSet[i] != p2)) {
472 cp = util::cross_product(p1, p2, initialSet[i]);
473 if (cp > 0) {
474 tmp_vec[j++] = initialSet[i];
475 if (cp > howFar) {
476 farPoint = initialSet[i];
477 howFar = cp;
478 }
479 }
480 }
481 }
482
483 #if USECONCVEC
484 appendVector(tmp_vec, j, reducedSet);
485 #else // USE STD::VECTOR
486 appendVector(insertMutex, tmp_vec, j, reducedSet);
487 #endif // USECONCVEC
488 }
489
join(const SplitByCP_buf & rhs)490 void join(const SplitByCP_buf &rhs) {
491 if (rhs.howFar > howFar) {
492 howFar = rhs.howFar;
493 farPoint = rhs.farPoint;
494 }
495 }
496
farthestPoint() const497 point_t farthestPoint() const {
498 return farPoint;
499 }
500 };
501
502 #if !USECONCVEC
503 mutex_t SplitByCP::pushBackMutex{};
504 mutex_t SplitByCP_buf::insertMutex{};
505 #endif
506
507 template <typename BodyType>
divide(const pointVec_t & P,pointVec_t & P_reduced,const point_t & p1,const point_t & p2)508 point_t divide(const pointVec_t &P, pointVec_t &P_reduced, const point_t &p1, const point_t &p2) {
509 BodyType body(p1, p2, P, P_reduced);
510 // Must use simple_partitioner (see the comment in initialize() above)
511 oneapi::tbb::parallel_reduce(
512 range_t(0, P.size(), BodyType::grainSize), body, oneapi::tbb::simple_partitioner());
513
514 if (util::verbose) {
515 std::stringstream ss;
516 ss << P.size() << " nodes in bucket"
517 << ", "
518 << "dividing by: [ " << p1 << ", " << p2 << " ], "
519 << "farthest node: " << body.farthestPoint();
520 util::OUTPUT.push_back(ss.str());
521 }
522
523 return body.farthestPoint();
524 }
525
divide_and_conquer(const pointVec_t & P,pointVec_t & H,point_t p1,point_t p2,bool buffered)526 void divide_and_conquer(const pointVec_t &P, pointVec_t &H, point_t p1, point_t p2, bool buffered) {
527 assert(P.size() >= 2);
528 pointVec_t P_reduced;
529 pointVec_t H1, H2;
530 point_t p_far;
531
532 if (buffered) {
533 p_far = divide<SplitByCP_buf>(P, P_reduced, p1, p2);
534 }
535 else {
536 p_far = divide<SplitByCP>(P, P_reduced, p1, p2);
537 }
538
539 if (P_reduced.size() < 2) {
540 H.push_back(p1);
541 #if USECONCVEC
542 appendVector(P_reduced, H);
543 #else // insert into STD::VECTOR
544 H.insert(H.end(), P_reduced.begin(), P_reduced.end());
545 #endif
546 }
547 else {
548 divide_and_conquer(P_reduced, H1, p1, p_far, buffered);
549 divide_and_conquer(P_reduced, H2, p_far, p2, buffered);
550
551 #if USECONCVEC
552 appendVector(H1, H);
553 appendVector(H2, H);
554 #else // insert into STD::VECTOR
555 H.insert(H.end(), H1.begin(), H1.end());
556 H.insert(H.end(), H2.begin(), H2.end());
557 #endif
558 }
559 }
560
quickhull(const pointVec_t & points,pointVec_t & hull,bool buffered)561 void quickhull(const pointVec_t &points, pointVec_t &hull, bool buffered) {
562 if (points.size() < 2) {
563 #if USECONCVEC
564 appendVector(points, hull);
565 #else // STD::VECTOR
566 hull.insert(hull.end(), points.begin(), points.end());
567 #endif // USECONCVEC
568 return;
569 }
570
571 point_t p_maxx = extremum<FindXExtremum::maxX>(points);
572 point_t p_minx = extremum<FindXExtremum::minX>(points);
573
574 pointVec_t H;
575
576 divide_and_conquer(points, hull, p_maxx, p_minx, buffered);
577 divide_and_conquer(points, H, p_minx, p_maxx, buffered);
578 #if USECONCVEC
579 appendVector(H, hull);
580 #else // STD::VECTOR
581 hull.insert(hull.end(), H.begin(), H.end());
582 #endif // USECONCVEC
583 }
584
main(int argc,char * argv[])585 int main(int argc, char *argv[]) {
586 utility::thread_number_range threads(utility::get_default_num_threads);
587 util::ParseInputArgs(argc, argv, threads);
588
589 int nthreads;
590 util::my_time_t tm_init, tm_start, tm_end;
591
592 #if USECONCVEC
593 std::cout << "Starting TBB unbuffered push_back version of QUICK HULL algorithm"
594 << "\n";
595 #else
596 std::cout << "Starting STL locked unbuffered push_back version of QUICK HULL algorithm"
597 << "\n";
598 #endif // USECONCVEC
599
600 for (nthreads = threads.first; nthreads <= threads.last; nthreads = threads.step(nthreads)) {
601 pointVec_t points;
602 pointVec_t hull;
603
604 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
605 nthreads);
606 tm_init = util::gettime();
607 initialize<FillRNDPointsVector>(points);
608 tm_start = util::gettime();
609 std::cout << "Parallel init time on " << nthreads
610 << " threads: " << util::time_diff(tm_init, tm_start)
611 << " Points in input: " << points.size() << "\n";
612
613 tm_start = util::gettime();
614 quickhull(points, hull, false);
615 tm_end = util::gettime();
616 std::cout << "Time on " << nthreads << " threads: " << util::time_diff(tm_start, tm_end)
617 << " Points in hull: " << hull.size() << "\n";
618 }
619
620 #if USECONCVEC
621 std::cout << "Starting TBB buffered version of QUICK HULL algorithm"
622 << "\n";
623 #else
624 std::cout << "Starting STL locked buffered version of QUICK HULL algorithm"
625 << "\n";
626 #endif
627
628 for (nthreads = threads.first; nthreads <= threads.last; nthreads = threads.step(nthreads)) {
629 pointVec_t points;
630 pointVec_t hull;
631
632 oneapi::tbb::global_control c(oneapi::tbb::global_control::max_allowed_parallelism,
633 nthreads);
634
635 tm_init = util::gettime();
636 initialize<FillRNDPointsVector_buf>(points);
637 tm_start = util::gettime();
638 std::cout << "Init time on " << nthreads
639 << " threads: " << util::time_diff(tm_init, tm_start)
640 << " Points in input: " << points.size() << "\n";
641
642 tm_start = util::gettime();
643 quickhull(points, hull, true);
644 tm_end = util::gettime();
645 std::cout << "Time on " << nthreads << " threads: " << util::time_diff(tm_start, tm_end)
646 << " Points in hull: " << hull.size() << "\n";
647 }
648
649 return 0;
650 }
651
652 #endif // USETBB
653
serial_initialize(pointVec_t & points)654 void serial_initialize(pointVec_t &points) {
655 points.reserve(cfg::numberOfPoints);
656
657 unsigned int rseed = 1;
658 for (std::size_t i = 0, count = 0; long(i) < cfg::numberOfPoints; ++i) {
659 points.push_back(util::GenerateRNDPoint<double>(count, &std::rand, RAND_MAX));
660 }
661 }
662