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