Shadowrun: Awakened 29 September 2011 - Build 871
convex_hull_bench.cpp
Go to the documentation of this file.
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.

GNU Lesser General Public License 3 Sourceforge.net