![]() |
Shadowrun: Awakened 29 September 2011 - Build 871
|
00001 /* 00002 Copyright 2005-2010 Intel Corporation. All Rights Reserved. 00003 00004 This file is part of Threading Building Blocks. 00005 00006 Threading Building Blocks is free software; you can redistribute it 00007 and/or modify it under the terms of the GNU General Public License 00008 version 2 as published by the Free Software Foundation. 00009 00010 Threading Building Blocks is distributed in the hope that it will be 00011 useful, but WITHOUT ANY WARRANTY; without even the implied warranty 00012 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with Threading Building Blocks; if not, write to the Free Software 00017 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00018 00019 As a special exception, you may use this file as part of a free software 00020 library without restriction. Specifically, if other files instantiate 00021 templates or use macros or inline functions from this file, or you compile 00022 this file and link it with other files to produce an executable, this 00023 file does not by itself cause the resulting executable to be covered by 00024 the GNU General Public License. This exception does not however 00025 invalidate any other reasons why the executable file might be covered by 00026 the GNU General Public License. 00027 */ 00028 00029 /* 00030 This file contains a few implementations, so it may look overly complicated. 00031 The most efficient implementation is also separated into convex_hull_sample.cpp 00032 */ 00033 #include "convex_hull.h" 00034 00035 typedef util::point<double> point_t; 00036 00037 #define USETBB 1 00038 #define USECONCVEC 1 00039 #define INIT_ONCE 1 00040 00041 #if !USETBB // Serial implementation of Quick Hull algorithm 00042 00043 typedef std::vector< point_t > pointVec_t; 00044 00045 // C++ style serial code 00046 00047 class FillRNDPointsVector : public std::unary_function<point_t&, void> { 00048 unsigned int rseed; 00049 size_t count; 00050 public: 00051 FillRNDPointsVector() : rseed(1), count(0) {} 00052 00053 void operator()(point_t& p) { 00054 p = util::GenerateRNDPoint<double>(count, rseed); 00055 } 00056 }; 00057 00058 void initialize(pointVec_t &points) { 00059 points.clear(); 00060 points.resize(cfg::MAXPOINTS); 00061 00062 std::for_each(points.begin(), points.end(), FillRNDPointsVector()); 00063 } 00064 00065 class FindXExtremum : public std::unary_function<const point_t&, void> { 00066 public: 00067 typedef enum { 00068 minX, maxX 00069 } extremumType; 00070 00071 FindXExtremum(const point_t& frstPoint, extremumType exType_) 00072 : extrXPoint(frstPoint), exType(exType_) {} 00073 00074 void operator()(const point_t& p) { 00075 if(closerToExtremum(p)) 00076 extrXPoint = p; 00077 } 00078 00079 operator point_t () { 00080 return extrXPoint; 00081 } 00082 00083 private: 00084 const extremumType exType; 00085 point_t extrXPoint; 00086 00087 bool closerToExtremum(const point_t &p) const { 00088 switch(exType) { 00089 case minX: 00090 return p.x<extrXPoint.x; break; 00091 case maxX: 00092 return p.x>extrXPoint.x; break; 00093 } 00094 } 00095 }; 00096 00097 template <FindXExtremum::extremumType type> 00098 point_t extremum(const pointVec_t &points) { 00099 assert(!points.empty()); 00100 return std::for_each(points.begin(), points.end(), FindXExtremum(points[0], type)); 00101 } 00102 00103 class SplitByCP : public std::unary_function<const point_t&, void> { 00104 pointVec_t &reducedSet; 00105 point_t p1, p2; 00106 point_t farPoint; 00107 double howFar; 00108 public: 00109 00110 SplitByCP( point_t _p1, point_t _p2, pointVec_t &_reducedSet) 00111 : p1(_p1), p2(_p2), reducedSet(_reducedSet), howFar(0), farPoint(p1) {} 00112 00113 void operator()(const point_t& p) { 00114 double cp; 00115 if( (p != p1) && (p != p2) ) { 00116 cp = util::cross_product(p1, p2, p); 00117 if(cp>0) { 00118 reducedSet.push_back(p); 00119 if(cp>howFar) { 00120 farPoint = p; 00121 howFar = cp; 00122 } 00123 } 00124 } 00125 } 00126 00127 operator point_t (){ 00128 return farPoint; 00129 } 00130 }; 00131 00132 point_t divide(const pointVec_t &P, pointVec_t &P_reduced, const point_t &p1, const point_t &p2) { 00133 SplitByCP splitByCP(p1, p2, P_reduced); 00134 point_t farPoint = std::for_each(P.begin(), P.end(), splitByCP); 00135 00136 if(util::VERBOSE) { 00137 std::stringstream ss; 00138 ss << P.size() << " nodes in bucket"<< ", " 00139 << "dividing by: [ " << p1 << ", " << p2 << " ], " 00140 << "farthest node: " << farPoint; 00141 util::OUTPUT.push_back(ss.str()); 00142 } 00143 00144 return farPoint; 00145 } 00146 00147 void divide_and_conquer(const pointVec_t &P, pointVec_t &H, point_t p1, point_t p2) { 00148 if (P.size()<2) { 00149 H.push_back(p1); 00150 H.insert(H.end(), P.begin(), P.end()); 00151 } 00152 else { 00153 pointVec_t P_reduced; 00154 pointVec_t H1, H2; 00155 point_t p_far; 00156 00157 p_far = divide(P, P_reduced, p1, p2); 00158 00159 divide_and_conquer(P_reduced, H1, p1, p_far); 00160 divide_and_conquer(P_reduced, H2, p_far, p2); 00161 00162 H.insert(H.end(), H1.begin(), H1.end()); 00163 H.insert(H.end(), H2.begin(), H2.end()); 00164 } 00165 } 00166 00167 void quickhull(const pointVec_t &points, pointVec_t &hull) { 00168 hull.clear(); 00169 00170 point_t p_maxx = extremum<FindXExtremum::maxX>(points); 00171 point_t p_minx = extremum<FindXExtremum::minX>(points); 00172 00173 pointVec_t H; 00174 00175 divide_and_conquer(points, hull, p_maxx, p_minx); 00176 divide_and_conquer(points, H, p_minx, p_maxx); 00177 hull.insert(hull.end(), H.begin(), H.end()); 00178 } 00179 00180 00181 int main(int argc, char* argv[]) { 00182 util::ParseInputArgs(argc, argv); 00183 00184 pointVec_t points; 00185 pointVec_t hull; 00186 util::my_time_t tm_init, tm_start, tm_end; 00187 00188 std::cout << "Starting serial version of QUICK HULL algorithm" << std::endl; 00189 00190 tm_init = util::gettime(); 00191 initialize(points); 00192 tm_start = util::gettime(); 00193 quickhull(points, hull); 00194 tm_end = util::gettime(); 00195 00196 util::WriteResults(1, util::time_diff(tm_init, tm_start), 00197 util::time_diff(tm_start, tm_end)); 00198 } 00199 00200 #else // USETBB - parallel version of Quick Hull algorithm 00201 00202 #include "tbb/task_scheduler_init.h" 00203 #include "tbb/parallel_for.h" 00204 #include "tbb/parallel_reduce.h" 00205 #include "tbb/blocked_range.h" 00206 00207 typedef tbb::blocked_range<size_t> range_t; 00208 00209 #if USECONCVEC 00210 #include "tbb/concurrent_vector.h" 00211 00212 typedef tbb::concurrent_vector<point_t> pointVec_t; 00213 00214 void appendVector(const point_t* src, size_t srcSize, pointVec_t& dest) { 00215 std::copy(src, src + srcSize, dest.grow_by(srcSize)); 00216 } 00217 00218 void appendVector(const pointVec_t& src, pointVec_t& dest) { 00219 std::copy(src.begin(), src.end(), dest.grow_by(src.size())); 00220 } 00221 00222 #else // USE STD::VECTOR - include spin_mutex.h and lock vector operations 00223 #include "tbb/spin_mutex.h" 00224 00225 typedef tbb::spin_mutex mutex_t; 00226 typedef std::vector<point_t> pointVec_t; 00227 00228 void appendVector(mutex_t& insertMutex, const pointVec_t& src, pointVec_t& dest) { 00229 mutex_t::scoped_lock lock(insertMutex); 00230 dest.insert(dest.end(), src.begin(), src.end()); 00231 } 00232 00233 void appendVector(mutex_t& insertMutex, const point_t* src, size_t srcSize, 00234 pointVec_t& dest) { 00235 mutex_t::scoped_lock lock(insertMutex); 00236 dest.insert(dest.end(), src, src + srcSize); 00237 } 00238 00239 #endif // USECONCVEC 00240 00241 class FillRNDPointsVector { 00242 pointVec_t &points; 00243 mutable unsigned int rseed; 00244 public: 00245 static const size_t grainSize = cfg::GENERATE_GS; 00246 #if !USECONCVEC 00247 static mutex_t pushBackMutex; 00248 #endif // USECONCVEC 00249 FillRNDPointsVector(pointVec_t& _points) 00250 : points(_points), rseed(1) {} 00251 00252 FillRNDPointsVector(const FillRNDPointsVector& other) 00253 : points(other.points), rseed(other.rseed+1) {} 00254 00255 void operator()(const range_t& range) const { 00256 const size_t i_end = range.end(); 00257 size_t count = 0; 00258 for(size_t i = range.begin(); i != i_end; ++i) { 00259 #if USECONCVEC 00260 points.push_back(util::GenerateRNDPoint<double>(count, rseed)); 00261 #else // Locked push_back to a not thread-safe STD::VECTOR 00262 { 00263 mutex_t::scoped_lock lock(pushBackMutex); 00264 points.push_back(util::GenerateRNDPoint<double>(count, rseed)); 00265 } 00266 #endif // USECONCVEC 00267 } 00268 } 00269 }; 00270 00271 class FillRNDPointsVector_buf { 00272 pointVec_t &points; 00273 mutable unsigned int rseed; 00274 public: 00275 static const size_t grainSize = cfg::GENERATE_GS; 00276 #if !USECONCVEC 00277 static mutex_t insertMutex; 00278 #endif // USECONCVEC 00279 00280 FillRNDPointsVector_buf(pointVec_t& _points) 00281 : points(_points), rseed(1) {} 00282 00283 FillRNDPointsVector_buf(const FillRNDPointsVector_buf& other) 00284 : points(other.points), rseed(other.rseed+1) {} 00285 00286 void operator()(const range_t& range) const { 00287 const size_t i_end = range.end(); 00288 size_t count = 0, j = 0; 00289 point_t tmp_vec[grainSize]; 00290 for(size_t i=range.begin(); i!=i_end; ++i) { 00291 tmp_vec[j++] = util::GenerateRNDPoint<double>(count, rseed); 00292 } 00293 #if USECONCVEC 00294 appendVector(tmp_vec, j, points); 00295 #else // USE STD::VECTOR 00296 appendVector(insertMutex, tmp_vec, j, points); 00297 #endif // USECONCVEC 00298 } 00299 }; 00300 00301 #if !USECONCVEC 00302 mutex_t FillRNDPointsVector::pushBackMutex = mutex_t(); 00303 mutex_t FillRNDPointsVector_buf::insertMutex = mutex_t(); 00304 #endif 00305 00306 template<typename BodyType> 00307 void initialize(pointVec_t &points) { 00308 points.clear(); 00309 00310 // In the buffered version, a temporary storage for as much as grainSize elements 00311 // is allocated inside the body. Since auto_partitioner may increase effective 00312 // range size which would cause a crash, simple partitioner has to be used. 00313 tbb::parallel_for(range_t(0, cfg::MAXPOINTS, BodyType::grainSize), 00314 BodyType(points), tbb::simple_partitioner()); 00315 } 00316 00317 class FindXExtremum { 00318 public: 00319 typedef enum { 00320 minX, maxX 00321 } extremumType; 00322 00323 static const size_t grainSize = cfg::FINDEXT_GS; 00324 00325 FindXExtremum(const pointVec_t& points_, extremumType exType_) 00326 : points(points_), exType(exType_), extrXPoint(points[0]) {} 00327 00328 FindXExtremum(const FindXExtremum& fxex, tbb::split) 00329 : points(fxex.points), exType(fxex.exType), extrXPoint(fxex.extrXPoint) {} 00330 00331 void operator()(const range_t& range) { 00332 const size_t i_end = range.end(); 00333 if(!range.empty()) { 00334 for(size_t i = range.begin(); i != i_end; ++i) { 00335 if(closerToExtremum(points[i])) { 00336 extrXPoint = points[i]; 00337 } 00338 } 00339 } 00340 } 00341 00342 void join(const FindXExtremum &rhs) { 00343 if(closerToExtremum(rhs.extrXPoint)) { 00344 extrXPoint = rhs.extrXPoint; 00345 } 00346 } 00347 00348 point_t extremeXPoint() { 00349 return extrXPoint; 00350 } 00351 00352 private: 00353 const pointVec_t &points; 00354 const extremumType exType; 00355 point_t extrXPoint; 00356 bool closerToExtremum(const point_t &p) const { 00357 switch(exType) { 00358 case minX: 00359 return p.x<extrXPoint.x; break; 00360 case maxX: 00361 return p.x>extrXPoint.x; break; 00362 } 00363 return false; // avoid warning 00364 } 00365 }; 00366 00367 template <FindXExtremum::extremumType type> 00368 point_t extremum(const pointVec_t &P) { 00369 FindXExtremum fxBody(P, type); 00370 tbb::parallel_reduce(range_t(0, P.size(), FindXExtremum::grainSize), fxBody); 00371 return fxBody.extremeXPoint(); 00372 } 00373 00374 class SplitByCP { 00375 const pointVec_t &initialSet; 00376 pointVec_t &reducedSet; 00377 point_t p1, p2; 00378 point_t farPoint; 00379 double howFar; 00380 public: 00381 static const size_t grainSize = cfg::DIVIDE_GS; 00382 #if !USECONCVEC 00383 static mutex_t pushBackMutex; 00384 #endif // USECONCVEC 00385 00386 SplitByCP( point_t _p1, point_t _p2, 00387 const pointVec_t &_initialSet, pointVec_t &_reducedSet) 00388 : p1(_p1), p2(_p2), 00389 initialSet(_initialSet), reducedSet(_reducedSet), 00390 howFar(0), farPoint(p1) { 00391 } 00392 00393 SplitByCP( SplitByCP& sbcp, tbb::split ) 00394 : p1(sbcp.p1), p2(sbcp.p2), 00395 initialSet(sbcp.initialSet), reducedSet(sbcp.reducedSet), 00396 howFar(0), farPoint(p1) {} 00397 00398 void operator()( const range_t& range ) { 00399 const size_t i_end = range.end(); 00400 double cp; 00401 for(size_t i=range.begin(); i!=i_end; ++i) { 00402 if( (initialSet[i] != p1) && (initialSet[i] != p2) ) { 00403 cp = util::cross_product(p1, p2, initialSet[i]); 00404 if(cp>0) { 00405 #if USECONCVEC 00406 reducedSet.push_back(initialSet[i]); 00407 #else // Locked push_back to a not thread-safe STD::VECTOR 00408 { 00409 mutex_t::scoped_lock lock(pushBackMutex); 00410 reducedSet.push_back(initialSet[i]); 00411 } 00412 #endif // USECONCVEC 00413 if(cp>howFar) { 00414 farPoint = initialSet[i]; 00415 howFar = cp; 00416 } 00417 } 00418 } 00419 } 00420 } 00421 00422 void join(const SplitByCP& rhs) { 00423 if(rhs.howFar>howFar) { 00424 howFar = rhs.howFar; 00425 farPoint = rhs.farPoint; 00426 } 00427 } 00428 00429 point_t farthestPoint() const { 00430 return farPoint; 00431 } 00432 }; 00433 00434 class SplitByCP_buf { 00435 const pointVec_t &initialSet; 00436 pointVec_t &reducedSet; 00437 point_t p1, p2; 00438 point_t farPoint; 00439 double howFar; 00440 public: 00441 static const size_t grainSize = cfg::DIVIDE_GS; 00442 #if !USECONCVEC 00443 static mutex_t insertMutex; 00444 #endif // USECONCVEC 00445 00446 SplitByCP_buf( point_t _p1, point_t _p2, 00447 const pointVec_t &_initialSet, pointVec_t &_reducedSet) 00448 : p1(_p1), p2(_p2), 00449 initialSet(_initialSet), reducedSet(_reducedSet), 00450 howFar(0), farPoint(p1) {} 00451 00452 SplitByCP_buf(SplitByCP_buf& sbcp, tbb::split) 00453 : p1(sbcp.p1), p2(sbcp.p2), 00454 initialSet(sbcp.initialSet), reducedSet(sbcp.reducedSet), 00455 howFar(0), farPoint(p1) {} 00456 00457 void operator()(const range_t& range) { 00458 const size_t i_end = range.end(); 00459 size_t j = 0; 00460 double cp; 00461 point_t tmp_vec[grainSize]; 00462 for(size_t i = range.begin(); i != i_end; ++i) { 00463 if( (initialSet[i] != p1) && (initialSet[i] != p2) ) { 00464 cp = util::cross_product(p1, p2, initialSet[i]); 00465 if(cp>0) { 00466 tmp_vec[j++] = initialSet[i]; 00467 if(cp>howFar) { 00468 farPoint = initialSet[i]; 00469 howFar = cp; 00470 } 00471 } 00472 } 00473 } 00474 00475 #if USECONCVEC 00476 appendVector(tmp_vec, j, reducedSet); 00477 #else // USE STD::VECTOR 00478 appendVector(insertMutex, tmp_vec, j, reducedSet); 00479 #endif // USECONCVEC 00480 } 00481 00482 void join(const SplitByCP_buf& rhs) { 00483 if(rhs.howFar>howFar) { 00484 howFar = rhs.howFar; 00485 farPoint = rhs.farPoint; 00486 } 00487 } 00488 00489 point_t farthestPoint() const { 00490 return farPoint; 00491 } 00492 }; 00493 00494 #if !USECONCVEC 00495 mutex_t SplitByCP::pushBackMutex = mutex_t(); 00496 mutex_t SplitByCP_buf::insertMutex = mutex_t(); 00497 #endif 00498 00499 template <typename BodyType> 00500 point_t divide(const pointVec_t &P, pointVec_t &P_reduced, 00501 const point_t &p1, const point_t &p2) { 00502 BodyType body(p1, p2, P, P_reduced); 00503 // Must use simple_partitioner (see the comment in initialize() above) 00504 tbb::parallel_reduce(range_t(0, P.size(), BodyType::grainSize), 00505 body, tbb::simple_partitioner() ); 00506 00507 if(util::VERBOSE) { 00508 std::stringstream ss; 00509 ss << P.size() << " nodes in bucket"<< ", " 00510 << "dividing by: [ " << p1 << ", " << p2 << " ], " 00511 << "farthest node: " << body.farthestPoint(); 00512 util::OUTPUT.push_back(ss.str()); 00513 } 00514 00515 return body.farthestPoint(); 00516 } 00517 00518 void divide_and_conquer(const pointVec_t &P, pointVec_t &H, 00519 point_t p1, point_t p2, bool buffered) { 00520 if (P.size()<2) { 00521 H.push_back(p1); 00522 #if USECONCVEC 00523 appendVector(P, H); 00524 #else // insert into STD::VECTOR 00525 H.insert(H.end(), P.begin(), P.end()); 00526 #endif 00527 } 00528 else { 00529 pointVec_t P_reduced; 00530 pointVec_t H1, H2; 00531 point_t p_far; 00532 00533 if(buffered) { 00534 p_far = divide<SplitByCP_buf>(P, P_reduced, p1, p2); 00535 } else { 00536 p_far = divide<SplitByCP>(P, P_reduced, p1, p2); 00537 } 00538 00539 divide_and_conquer(P_reduced, H1, p1, p_far, buffered); 00540 divide_and_conquer(P_reduced, H2, p_far, p2, buffered); 00541 00542 #if USECONCVEC 00543 appendVector(H1, H); 00544 appendVector(H2, H); 00545 #else // insert into STD::VECTOR 00546 H.insert(H.end(), H1.begin(), H1.end()); 00547 H.insert(H.end(), H2.begin(), H2.end()); 00548 #endif 00549 } 00550 } 00551 00552 void quickhull(const pointVec_t &points, pointVec_t &hull, bool buffered) { 00553 hull.clear(); 00554 00555 point_t p_maxx = extremum<FindXExtremum::maxX>(points); 00556 point_t p_minx = extremum<FindXExtremum::minX>(points); 00557 00558 pointVec_t H; 00559 00560 divide_and_conquer(points, hull, p_maxx, p_minx, buffered); 00561 divide_and_conquer(points, H, p_minx, p_maxx, buffered); 00562 #if USECONCVEC 00563 appendVector(H, hull); 00564 #else // STD::VECTOR 00565 hull.insert(hull.end(), H.begin(), H.end()); 00566 #endif // USECONCVEC 00567 } 00568 00569 int main(int argc, char* argv[]) { 00570 util::ParseInputArgs(argc, argv); 00571 00572 pointVec_t points; 00573 pointVec_t hull; 00574 int nthreads; 00575 util::my_time_t tm_init, tm_start, tm_end; 00576 pointVec_t tmp_points; 00577 00578 #if USECONCVEC 00579 std::cout << "Starting TBB unbufferred push_back version of QUICK HULL algorithm" << std::endl; 00580 #else 00581 std::cout << "Starting STL locked unbufferred push_back version of QUICK HULL algorithm" << std::endl; 00582 #endif // USECONCVEC 00583 00584 for(nthreads=cfg::NUM_THREADS_START; nthreads<=cfg::NUM_THREADS_END; 00585 ++nthreads) { 00586 tbb::task_scheduler_init init(nthreads); 00587 #if INIT_ONCE 00588 if(nthreads==cfg::NUM_THREADS_START) { 00589 tm_init = util::gettime(); 00590 initialize<FillRNDPointsVector>(points); 00591 } 00592 else /* timing generation for stats, but use original data set */ { 00593 tm_init = util::gettime(); 00594 initialize<FillRNDPointsVector>(tmp_points); 00595 } 00596 #else 00597 tm_init = util::gettime(); 00598 initialize<FillRNDPointsVector>(points); 00599 #endif // INIT_ONCE 00600 tm_start = util::gettime(); 00601 quickhull(points, hull, false); 00602 tm_end = util::gettime(); 00603 00604 util::WriteResults(nthreads, util::time_diff(tm_init, tm_start), 00605 util::time_diff(tm_start, tm_end)); 00606 } 00607 00608 #if USECONCVEC 00609 std::cout << "Starting TBB bufferred version of QUICK HULL algorithm" << std::endl; 00610 #else 00611 std::cout << "Starting STL locked bufferred version of QUICK HULL algorithm" << std::endl; 00612 #endif 00613 00614 for(nthreads=cfg::NUM_THREADS_START; nthreads<=cfg::NUM_THREADS_END; 00615 ++nthreads) { 00616 tbb::task_scheduler_init init(nthreads); 00617 #if INIT_ONCE 00618 if(nthreads==cfg::NUM_THREADS_START) { 00619 tm_init = util::gettime(); 00620 initialize<FillRNDPointsVector_buf>(points); 00621 } 00622 else /* timing generation for stats, but use original data set */ { 00623 tm_init = util::gettime(); 00624 initialize<FillRNDPointsVector_buf>(tmp_points); 00625 } 00626 #else 00627 tm_init = util::gettime(); 00628 initialize<FillRNDPointsVector_buf>(points); 00629 #endif // INIT_ONCE 00630 tm_start = util::gettime(); 00631 quickhull(points, hull, true); 00632 tm_end = util::gettime(); 00633 00634 util::WriteResults(nthreads, util::time_diff(tm_init, tm_start), 00635 util::time_diff(tm_start, tm_end)); 00636 } 00637 00638 return 0; 00639 } 00640 00641 #endif // USETBB
Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.