2 Copyright (c) 2005-2019 Intel Corporation
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include "tbb/tick_count.h"
23 #include "tbb/task_group.h"
24 #include "tbb/concurrent_priority_queue.h"
25 #include "tbb/spin_mutex.h"
26 #include "tbb/parallel_for.h"
27 #include "tbb/blocked_range.h"
28 #include "tbb/global_control.h"
29 #include "../../common/utility/utility.h"
30 #include "../../common/utility/fast_random.h"
31 #include "../../common/utility/get_default_num_threads.h"
33 #if defined(_MSC_VER) && defined(_Wp64)
34 // Workaround for overzealous compiler warnings in /Wp64 mode
35 #pragma warning (disable: 4267)
36 #endif /* _MSC_VER && _Wp64 */
44 point(double _x, double _y) : x(_x), y(_y) {}
45 point(const point& p) : x(p.x), y(p.y) {}
48 double get_distance(const point& p1, const point& p2) {
49 double xdiff=p1.x-p2.x, ydiff=p1.y-p2.y;
50 return sqrt(xdiff*xdiff + ydiff*ydiff);
53 // generates random points on 2D plane within a box of maxsize width & height
54 point generate_random_point(utility::FastRandom& mr) {
55 const size_t maxsize=500;
56 double x = (double)(mr.get() % maxsize);
57 double y = (double)(mr.get() % maxsize);
61 // weighted toss makes closer nodes (in the point vector) heavily connected
62 bool die_toss(size_t a, size_t b, utility::FastRandom& mr) {
63 int node_diff = std::abs((int)(a-b));
65 if (node_diff < 16) return true;
67 if (node_diff < 64) return ((int)mr.get() % 8 == 0);
69 if (node_diff < 512) return ((int)mr.get() % 16 == 0);
73 typedef vector<point> point_set;
74 typedef size_t vertex_id;
75 typedef std::pair<vertex_id,double> vertex_rec;
76 typedef vector<vector<vertex_id> > edge_set;
78 bool verbose = false; // prints bin details and other diagnostics to screen
79 bool silent = false; // suppress all output except for time
80 size_t N = 1000; // number of vertices
81 size_t src = 0; // start of path
82 size_t dst = N-1; // end of path
83 double INF=100000.0; // infinity
84 size_t grainsize = 16; // number of vertices per task on average
85 size_t max_spawn; // max tasks to spawn
86 std::atomic<size_t> num_spawn; // number of active tasks
88 point_set vertices; // vertices
89 edge_set edges; // edges
90 vector<vertex_id> predecessor; // for recreating path from src to dst
92 vector<double> f_distance; // estimated distances at particular vertex
93 vector<double> g_distance; // current shortest distances from src vertex
94 spin_mutex *locks; // a lock for each vertex
95 task_group *sp_group; // task group for tasks executing sub-problems
99 bool operator()(const vertex_rec& u, const vertex_rec& v) const {
100 return u.second>v.second;
104 concurrent_priority_queue<vertex_rec, compare_f> open_set; // tentative vertices
106 void shortpath_helper();
108 #if !__TBB_CPP11_LAMBDAS_PRESENT
109 class shortpath_helper_functor {
111 shortpath_helper_functor() {};
112 void operator() () const { shortpath_helper(); }
117 sp_group = new task_group;
118 g_distance[src] = 0.0; // src's distance from src is zero
119 f_distance[src] = get_distance(vertices[src], vertices[dst]); // estimate distance from src to dst
120 open_set.push(make_pair(src,f_distance[src])); // push src into open_set
121 #if __TBB_CPP11_LAMBDAS_PRESENT
122 sp_group->run([](){ shortpath_helper(); });
124 sp_group->run( shortpath_helper_functor() );
130 void shortpath_helper() {
132 while (open_set.try_pop(u_rec)) {
133 vertex_id u = u_rec.first;
134 if (u==dst) continue;
135 double f = u_rec.second;
136 double old_g_u = 0.0;
138 spin_mutex::scoped_lock l(locks[u]);
139 if (f > f_distance[u]) continue; // prune search space
140 old_g_u = g_distance[u];
142 for (size_t i=0; i<edges[u].size(); ++i) {
143 vertex_id v = edges[u][i];
144 double new_g_v = old_g_u + get_distance(vertices[u], vertices[v]);
145 double new_f_v = 0.0;
146 // the push flag lets us move some work out of the critical section below
149 spin_mutex::scoped_lock l(locks[v]);
150 if (new_g_v < g_distance[v]) {
152 g_distance[v] = new_g_v;
153 new_f_v = f_distance[v] = g_distance[v] + get_distance(vertices[v], vertices[dst]);
158 open_set.push(make_pair(v,new_f_v));
159 size_t n_spawn = ++num_spawn;
160 if (n_spawn < max_spawn) {
161 #if __TBB_CPP11_LAMBDAS_PRESENT
162 sp_group->run([]{ shortpath_helper(); });
164 sp_group->run( shortpath_helper_functor() );
174 void make_path(vertex_id src, vertex_id dst, vector<vertex_id>& path) {
175 vertex_id at = predecessor[dst];
176 if (at == N) path.push_back(src);
177 else if (at == src) { path.push_back(src); path.push_back(dst); }
178 else { make_path(src, at, path); path.push_back(dst); }
182 vector<vertex_id> path;
183 double path_length=0.0;
184 make_path(src, dst, path);
185 if (verbose) printf("\n ");
186 for (size_t i=0; i<path.size(); ++i) {
187 if (path[i] != dst) {
188 double seg_length = get_distance(vertices[path[i]], vertices[path[i+1]]);
189 if (verbose) printf("%6.1f ", seg_length);
190 path_length += seg_length;
192 else if (verbose) printf("\n");
195 for (size_t i=0; i<path.size(); ++i) {
196 if (path[i] != dst) printf("(%4d)------>", (int)path[i]);
197 else printf("(%4d)\n", (int)path[i]);
200 if (verbose) printf("Total distance = %5.1f\n", path_length);
201 else if (!silent) printf(" %5.1f\n", path_length);
204 #if !__TBB_CPP11_LAMBDAS_PRESENT
208 void operator() (blocked_range<size_t>& r) const {
209 utility::FastRandom my_random((unsigned int)r.begin());
210 for (size_t i=r.begin(); i!=r.end(); ++i) {
211 vertices[i] = generate_random_point(my_random);
219 void operator() (blocked_range<size_t>& r) const {
220 utility::FastRandom my_random((unsigned int)r.begin());
221 for (size_t i=r.begin(); i!=r.end(); ++i) {
222 for (size_t j=0; j<i; ++j) {
223 if (die_toss(i, j, my_random))
224 edges[i].push_back(j);
230 class reset_vertices {
233 void operator() (blocked_range<size_t>& r) const {
234 for (size_t i=r.begin(); i!=r.end(); ++i) {
235 f_distance[i] = g_distance[i] = INF;
242 void InitializeGraph() {
243 global_control c(tbb::global_control::max_allowed_parallelism, utility::get_default_num_threads());
246 predecessor.resize(N);
247 g_distance.resize(N);
248 f_distance.resize(N);
249 locks = new spin_mutex[N];
250 if (verbose) printf("Generating vertices...\n");
251 #if __TBB_CPP11_LAMBDAS_PRESENT
252 parallel_for(blocked_range<size_t>(0,N,64),
253 [&](blocked_range<size_t>& r) {
254 utility::FastRandom my_random(r.begin());
255 for (size_t i=r.begin(); i!=r.end(); ++i) {
256 vertices[i] = generate_random_point(my_random);
258 }, simple_partitioner());
260 parallel_for(blocked_range<size_t>(0,N,64), gen_vertices(), simple_partitioner());
262 if (verbose) printf("Generating edges...\n");
263 #if __TBB_CPP11_LAMBDAS_PRESENT
264 parallel_for(blocked_range<size_t>(0,N,64),
265 [&](blocked_range<size_t>& r) {
266 utility::FastRandom my_random(r.begin());
267 for (size_t i=r.begin(); i!=r.end(); ++i) {
268 for (size_t j=0; j<i; ++j) {
269 if (die_toss(i, j, my_random))
270 edges[i].push_back(j);
273 }, simple_partitioner());
275 parallel_for(blocked_range<size_t>(0,N,64), gen_edges(), simple_partitioner());
277 for (size_t i=0; i<N; ++i) {
278 for (size_t j=0; j<edges[i].size(); ++j) {
279 vertex_id k = edges[i][j];
280 edges[k].push_back(i);
283 if (verbose) printf("Done.\n");
286 void ReleaseGraph() {
291 global_control c(tbb::global_control::max_allowed_parallelism, utility::get_default_num_threads());
292 #if __TBB_CPP11_LAMBDAS_PRESENT
293 parallel_for(blocked_range<size_t>(0,N),
294 [&](blocked_range<size_t>& r) {
295 for (size_t i=r.begin(); i!=r.end(); ++i) {
296 f_distance[i] = g_distance[i] = INF;
301 parallel_for(blocked_range<size_t>(0,N), reset_vertices());
305 int main(int argc, char *argv[]) {
307 utility::thread_number_range threads(utility::get_default_num_threads);
308 utility::parse_cli_arguments(argc, argv,
309 utility::cli_argument_pack()
310 //"-h" option for displaying help is present implicitly
311 .positional_arg(threads,"#threads",utility::thread_number_range_desc)
312 .arg(verbose,"verbose"," print diagnostic output to screen")
313 .arg(silent,"silent"," limits output to timing info; overrides verbose")
314 .arg(N,"N"," number of vertices")
315 .arg(src,"start"," start of path")
316 .arg(dst,"end"," end of path")
318 if (silent) verbose = false; // make silent override verbose
320 printf("shortpath will run with %d vertices to find shortest path between vertices"
321 " %d and %d using %d:%d threads.\n",
322 (int)N, (int)src, (int)dst, (int)threads.first, (int)threads.last);
326 printf("end value %d is invalid for %d vertices; correcting to %d\n", (int)dst, (int)N, (int)N-1);
331 max_spawn = N/grainsize;
334 for (int n_thr=threads.first; n_thr<=threads.last; n_thr=threads.step(n_thr)) {
336 global_control c(tbb::global_control::max_allowed_parallelism, n_thr);
337 t0 = tick_count::now();
339 t1 = tick_count::now();
341 if (predecessor[dst] != N) {
342 printf("%d threads: [%6.6f] The shortest path from vertex %d to vertex %d is:",
343 (int)n_thr, (t1-t0).seconds(), (int)src, (int)dst);
347 printf("%d threads: [%6.6f] There is no path from vertex %d to vertex %d\n",
348 (int)n_thr, (t1-t0).seconds(), (int)src, (int)dst);
351 utility::report_elapsed_time((t1-t0).seconds());
355 } catch(std::exception& e) {
356 cerr<<"error occurred. error text is :\"" <<e.what()<<"\"\n";