Shadowrun: Awakened 29 September 2011 - Build 871
convex_hull_sample.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 the TBB-based implementation of convex hull algortihm.
00031     It corresponds to the following settings in convex_hull_bench.cpp:
00032     - USETBB defined to 1
00033     - USECONCVEC defined to 1
00034     - INIT_ONCE defined to 0
00035     - only buffered version is used
00036 */
00037 #include "convex_hull.h"
00038 
00039 #include "tbb/task_scheduler_init.h"
00040 #include "tbb/parallel_for.h"
00041 #include "tbb/parallel_reduce.h"
00042 #include "tbb/blocked_range.h"
00043 #include "tbb/tick_count.h"
00044 #include "tbb/concurrent_vector.h"
00045 
00046 typedef util::point<double>               point_t;
00047 typedef tbb::concurrent_vector< point_t > pointVec_t;
00048 typedef tbb::blocked_range<size_t>        range_t;
00049 
00050 void appendVector(const point_t* src, size_t srcSize, pointVec_t& dest) {
00051     std::copy(src, src + srcSize, dest.grow_by(srcSize));
00052 }
00053 
00054 void appendVector(const pointVec_t& src, pointVec_t& dest) {
00055     std::copy(src.begin(), src.end(), dest.grow_by(src.size()));
00056 }
00057 
00058 class FillRNDPointsVector_buf {
00059     pointVec_t          &points;
00060     mutable unsigned int rseed;
00061 public:
00062     static const size_t  grainSize = cfg::GENERATE_GS;
00063 
00064     FillRNDPointsVector_buf(pointVec_t& _points)
00065         : points(_points), rseed(1) {}
00066 
00067     FillRNDPointsVector_buf(const FillRNDPointsVector_buf& other)
00068         : points(other.points), rseed(other.rseed+1) {}
00069 
00070     void operator()(const range_t& range) const {
00071         const size_t i_end = range.end();
00072         size_t count = 0, j = 0;
00073         point_t tmp_vec[grainSize];
00074         for(size_t i=range.begin(); i!=i_end; ++i) {
00075             tmp_vec[j++] = util::GenerateRNDPoint<double>(count, rseed);
00076         }
00077         appendVector(tmp_vec, j, points);
00078     }
00079 };
00080 
00081 void initialize(pointVec_t &points) {
00082     points.clear();
00083 
00084     // In the buffered version, a temporary storage for as much as grainSize elements 
00085     // is allocated inside the body. Since auto_partitioner may increase effective
00086     // range size which would cause a crash, simple partitioner has to be used.
00087     tbb::parallel_for(range_t(0, cfg::MAXPOINTS, FillRNDPointsVector_buf::grainSize),
00088                       FillRNDPointsVector_buf(points), tbb::simple_partitioner());
00089 }
00090 
00091 class FindXExtremum {
00092 public:
00093     typedef enum {
00094         minX, maxX
00095     } extremumType;
00096 
00097     static const size_t  grainSize = cfg::FINDEXT_GS;
00098 
00099     FindXExtremum(const pointVec_t& points_, extremumType exType_)
00100         : points(points_), exType(exType_), extrXPoint(points[0]) {}
00101 
00102     FindXExtremum(const FindXExtremum& fxex, tbb::split)
00103         // Can run in parallel with fxex.operator()() or fxex.join().
00104         // The data race reported by tools is harmless.
00105         : points(fxex.points), exType(fxex.exType), extrXPoint(fxex.extrXPoint) {}
00106 
00107     void operator()(const range_t& range) {
00108         const size_t i_end = range.end();
00109         if(!range.empty()) {
00110             for(size_t i = range.begin(); i != i_end; ++i) {
00111                 if(closerToExtremum(points[i])) {
00112                     extrXPoint = points[i];
00113                 }
00114             }
00115         }
00116     }
00117 
00118     void join(const FindXExtremum &rhs) {
00119         if(closerToExtremum(rhs.extrXPoint)) {
00120             extrXPoint = rhs.extrXPoint;
00121         }
00122     }
00123 
00124     point_t extremeXPoint() {
00125         return extrXPoint;
00126     }
00127 
00128 private:
00129     const pointVec_t    &points;
00130     const extremumType   exType;
00131     point_t              extrXPoint;
00132     bool closerToExtremum(const point_t &p) const {
00133         switch(exType) {
00134         case minX:
00135             return p.x<extrXPoint.x; break;
00136         case maxX:
00137             return p.x>extrXPoint.x; break;
00138         }
00139         return false; // avoid warning
00140     }
00141 };
00142 
00143 template <FindXExtremum::extremumType type>
00144 point_t extremum(const pointVec_t &P) {
00145     FindXExtremum fxBody(P, type);
00146     tbb::parallel_reduce(range_t(0, P.size(), FindXExtremum::grainSize), fxBody);
00147     return fxBody.extremeXPoint();
00148 }
00149 
00150 class SplitByCP_buf {
00151     const pointVec_t    &initialSet;
00152     pointVec_t          &reducedSet;
00153     point_t              p1, p2;
00154     point_t              farPoint;
00155     double               howFar;
00156 public:
00157     static const size_t  grainSize = cfg::DIVIDE_GS;
00158 
00159     SplitByCP_buf( point_t _p1, point_t _p2,
00160         const pointVec_t &_initialSet, pointVec_t &_reducedSet)
00161         : p1(_p1), p2(_p2),
00162         initialSet(_initialSet), reducedSet(_reducedSet),
00163         howFar(0), farPoint(p1) {}
00164 
00165     SplitByCP_buf(SplitByCP_buf& sbcp, tbb::split)
00166         : p1(sbcp.p1), p2(sbcp.p2),
00167         initialSet(sbcp.initialSet), reducedSet(sbcp.reducedSet),
00168         howFar(0), farPoint(p1) {}
00169 
00170     void operator()(const range_t& range) {
00171         const size_t i_end = range.end();
00172         size_t j = 0;
00173         double cp;
00174         point_t tmp_vec[grainSize];
00175         for(size_t i = range.begin(); i != i_end; ++i) {
00176             if( (initialSet[i] != p1) && (initialSet[i] != p2) ) {            
00177                 cp = util::cross_product(p1, p2, initialSet[i]);
00178                 if(cp>0) {
00179                     tmp_vec[j++] = initialSet[i];
00180                     if(cp>howFar) {
00181                         farPoint = initialSet[i];
00182                         howFar   = cp;
00183                     }
00184                 }
00185             }
00186         }
00187 
00188         appendVector(tmp_vec, j, reducedSet);
00189     }
00190 
00191     void join(const SplitByCP_buf& rhs) {
00192         if(rhs.howFar>howFar) {
00193             howFar   = rhs.howFar;
00194             farPoint = rhs.farPoint;
00195         }
00196     }
00197 
00198     point_t farthestPoint() const {
00199         return farPoint;
00200     }
00201 };
00202 
00203 point_t divide(const pointVec_t &P, pointVec_t &P_reduced, 
00204                    const point_t &p1, const point_t &p2) {
00205     SplitByCP_buf sbcpb(p1, p2, P, P_reduced);
00206     // Must use simple_partitioner (see the comment in initialize() above)
00207     tbb::parallel_reduce(range_t(0, P.size(), SplitByCP_buf::grainSize),
00208                          sbcpb, tbb::simple_partitioner());
00209 
00210     if(util::VERBOSE) {
00211         std::stringstream ss;
00212         ss << P.size() << " nodes in bucket"<< ", "
00213             << "dividing by: [ " << p1 << ", " << p2 << " ], "
00214             << "farthest node: " << sbcpb.farthestPoint();
00215         util::OUTPUT.push_back(ss.str());
00216     }
00217 
00218     return sbcpb.farthestPoint();
00219 }
00220 
00221 void divide_and_conquer(const pointVec_t &P, pointVec_t &H,
00222                             point_t p1, point_t p2) {
00223     if (P.size()<2) {
00224         H.push_back(p1);
00225         appendVector(P, H);
00226     }
00227     else {
00228         pointVec_t P_reduced;
00229         pointVec_t H1, H2;
00230 
00231         point_t p_far = divide(P, P_reduced, p1, p2);
00232 
00233         divide_and_conquer(P_reduced, H1, p1, p_far);
00234         divide_and_conquer(P_reduced, H2, p_far, p2);
00235 
00236         appendVector(H1, H);
00237         appendVector(H2, H);
00238     }
00239 }
00240 
00241 void quickhull(const pointVec_t &points, pointVec_t &hull) {
00242     hull.clear();
00243 
00244     point_t p_maxx = extremum<FindXExtremum::maxX>(points);
00245     point_t p_minx = extremum<FindXExtremum::minX>(points);
00246 
00247     pointVec_t H;
00248 
00249     divide_and_conquer(points, hull, p_maxx, p_minx);
00250     divide_and_conquer(points, H, p_minx, p_maxx);
00251 
00252     appendVector(H, hull);
00253 }
00254 
00255 int main(int argc, char* argv[]) {
00256     util::ParseInputArgs(argc, argv);
00257 
00258     pointVec_t      points;
00259     pointVec_t      hull;
00260     int             nthreads;
00261     util::my_time_t tm_init, tm_start, tm_end;
00262 
00263     std::cout << " Starting TBB-bufferred version of QUICK HULL algorithm" << std::endl;
00264 
00265     for(nthreads=cfg::NUM_THREADS_START; nthreads<=cfg::NUM_THREADS_END;
00266         ++nthreads) {
00267         tbb::task_scheduler_init init(nthreads);
00268         tm_init = util::gettime();
00269         initialize(points);
00270         tm_start = util::gettime();
00271         quickhull(points, hull);
00272         tm_end = util::gettime();
00273 
00274         util::WriteResults(nthreads, util::time_diff(tm_init, tm_start),
00275             util::time_diff(tm_start, tm_end));
00276     }
00277 
00278     return 0;
00279 }

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