PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
SPAStar.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*----------------------------- File SPAStar.h -----------------------------*/
3/*--------------------------------------------------------------------------*/
62/*--------------------------------------------------------------------------*/
63/*------------------------------ DEFINITIONS -------------------------------*/
64/*--------------------------------------------------------------------------*/
65
66#ifndef __PBeFS_SPAStar
67 #define __PBeFS_SPAStar
68 /* self-identification: #endif at the end of the file */
69
70/*--------------------------------------------------------------------------*/
71/*-------------------------------- INCLUDES --------------------------------*/
72/*--------------------------------------------------------------------------*/
73
74#include <atomic>
75
76#include <memory>
77
78#include <mutex>
79
80#include <type_traits>
81
82#include <vector>
83
85
86#include "PBeFS/astar/HDAStar.h"
87
88/*--------------------------------------------------------------------------*/
89/*------------------------------- NAMESPACE --------------------------------*/
90/*--------------------------------------------------------------------------*/
91
93namespace PBeFS_di_unipi_it
94{
95
96/*--------------------------------------------------------------------------*/
97/*----------------------------- CLASS SPAStar ------------------------------*/
98/*--------------------------------------------------------------------------*/
100
119
120template< Domain D , ADS::WorkerExecutor Executor = ADS::ThreadPool ,
121 class Open = std::conditional_t<
122 std::is_integral_v< cost_t< D > > ,
125class SPAStar {
126
127/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
128
129 public:
130
131/*------------------------------ PUBLIC TYPES ------------------------------*/
132
137
138/*------------------------------ CONSTRUCTOR -------------------------------*/
139
141
142 explicit SPAStar( const D & domain , const ParallelOptions & options = {} )
143 : f_domain( domain ) , f_options( options ) {
144 f_options.normalize();
145 }
146
147/*------------------------- OTHER INITIALIZATIONS --------------------------*/
151
152 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
153
154/*-------------------------------- SOLVING ---------------------------------*/
156
157 [[nodiscard]] SolutionT solve( void ) const
158 {
159 const unsigned p = f_options.threads;
160 Stopwatch clock;
161 Shared sh( f_domain );
162 std::vector< std::unique_ptr< Worker > > w( p );
163 for( unsigned i = 0 ; i < p ; ++i )
164 w[ i ] = std::make_unique< Worker >( *this , sh );
165
166 { // the root, inserted by the first worker before the start
167 const State s0 = f_domain.initial_state();
168 NodeT * root = w[ 0 ]->update( s0 , Cost( 0 ) ,
169 Cost( f_domain.heuristic( s0 ) ) ,
170 nullptr , no_op );
171 sh.open.push( root );
172 }
173
174 run_workers( f_ex , p , [ & ]( unsigned i ) { w[ i ]->run(); } );
175
176 SolutionT sol;
177 SearchStats & st = sol.stats;
178 for( auto & x : w ) {
179 st.expanded += x->f_st.expanded;
180 st.reexpanded += x->f_st.reexpanded;
181 st.generated += x->f_st.generated;
182 st.duplicates += x->f_st.duplicates;
183 st.stored += x->f_local.stored();
184 st.expanded_by.push_back( x->f_st.expanded );
185 st.stored_by.push_back( x->f_local.stored() );
186 }
187 if( sh.limit_hit.load() )
188 sol.status = Status::node_limit;
189 else
190 if( sh.goal ) {
191 sol.status = Status::solved;
192 sol.cost = sh.goal->g;
193 sol.path = trace_path( sh.goal );
194 }
195 st.seconds = clock.seconds();
196 return( sol );
197 }
198
199/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
200
201 private:
202
203 using Lock = typename Executor::lock_type;
204
205 using Closed = ConcurrentClosed< D , Lock >;
206
207/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
208 // the data shared by the workers
209
210 struct Shared {
211 explicit Shared( const D & d ) : closed( d ) {}
212 alignas( cache_line ) Lock open_lock; // protects open and busy
213 Open open; // the OPEN list
214 unsigned busy = 0; // workers expanding
215 Closed closed; // the CLOSED list
216 alignas( cache_line ) std::atomic< Cost > incumbent{
218 std::atomic< bool > done{ false };
219 std::atomic< bool > limit_hit{ false };
220 std::mutex goal_mutex; // protects goal
221 NodeT * goal = nullptr; // the best goal found
222 };
223
224/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
225 // a worker: its pool of nodes and its counters
226
227 struct alignas( cache_line ) Worker {
228
229 Worker( const SPAStar & a , Shared & s ) : f_a( a ) , f_sh( s ) {}
230
231 // the main loop
232 void run( void ) {
233 ADS::Backoff backoff;
234 std::vector< OpenEntry< NodeT > > fresh;
235 while( ! f_sh.done.load( std::memory_order_acquire ) ) {
236 OpenEntry< NodeT > e{ nullptr , Cost( 0 ) };
237 {
238 std::lock_guard< Lock > guard( f_sh.open_lock );
239 if( ! f_sh.open.empty() && ( f_sh.open.top_f() <
240 f_sh.incumbent.load() ) ) {
241 e = f_sh.open.pop();
242 ++f_sh.busy;
243 }
244 else
245 if( f_sh.busy == 0 ) {
246 f_sh.done.store( true , std::memory_order_release );
247 break;
248 }
249 }
250 if( ! e.node ) {
251 backoff.pause();
252 continue;
253 }
254 backoff.reset();
255 fresh.clear();
256 expand( e , fresh );
257 std::lock_guard< Lock > guard( f_sh.open_lock );
258 for( const auto & x : fresh )
259 f_sh.open.push( x.node , x.g );
260 --f_sh.busy;
261 }
262 }
263
264 // expands the node of e unless the entry is stale; the nodes to put in
265 // OPEN, with the g they had under their lock, are appended to fresh
266 void expand( const OpenEntry< NodeT > & e ,
267 std::vector< OpenEntry< NodeT > > & fresh ) {
268 NodeT * n = e.node;
269 State s;
270 Cost g , h;
271 std::uint8_t op;
272 {
273 std::lock_guard< Lock > guard( f_sh.closed.lock( n ) );
274 if( e.stale() )
275 return;
276 n->closed = true;
277 if( n->expanded )
278 ++f_st.reexpanded;
279 n->expanded = true;
280 s = n->state;
281 g = n->g;
282 h = n->h;
283 op = n->op;
284 }
285 ++f_st.expanded;
286
287 if( f_a.f_domain.is_goal( s ) ) {
288 std::lock_guard< std::mutex > guard( f_sh.goal_mutex );
289 if( g < f_sh.incumbent.load() ) {
290 f_sh.incumbent.store( g );
291 f_sh.goal = n;
292 }
293 return;
294 }
295
296 // the successors first, but the parent [see OperatorDomain], with the
297 // places of CLOSED that their updates read prefetched, so that the
298 // misses of all of them overlap
299 f_succ.clear();
300 for_each_child( f_a.f_domain , s , h , op , [ & ]( const State & t ,
301 Cost c , Cost ht ,
302 std::uint8_t ot ) {
303 ++f_st.generated;
304 const Cost gt = g + c;
305 if( gt + ht >= f_sh.incumbent.load( std::memory_order_relaxed ) )
306 return;
307 const std::uint64_t hash = f_a.f_domain.hash( t );
308 f_sh.closed.prefetch( hash );
309 f_succ.push_back( { t , gt , ht , hash , ot } );
310 } );
311 for( const auto & v : f_succ )
312 if( NodeT * x = f_sh.closed.update( v.state , v.hash , v.g , v.h , n ,
313 v.op , f_local , f_st.duplicates ) )
314 fresh.push_back( OpenEntry< NodeT >{ x , v.g } );
315
316 if( f_a.f_options.node_limit &&
317 ( f_local.stored() >= f_a.f_options.node_limit ) ) {
318 f_sh.limit_hit.store( true );
319 f_sh.done.store( true , std::memory_order_release );
320 }
321 }
322
323 // records the path to t of cost g through parent and op; returns the
324 // node of t if it has to go into OPEN, nullptr if it is a duplicate
325 NodeT * update( const State & t , Cost g , Cost h , NodeT * parent ,
326 std::uint8_t op ) {
327 return( f_sh.closed.update( t , f_a.f_domain.hash( t ) , g , h ,
328 parent , op , f_local , f_st.duplicates ) );
329 }
330
331 const SPAStar & f_a; // the search
332 Shared & f_sh; // the shared data
333 typename Closed::Local f_local; // the nodes it allocated
334 std::vector< typename Closed::Successor > f_succ; // of the expansion
335 SearchStats f_st; // its counters
336 };
337
338/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
339
340 const D & f_domain;
341 ParallelOptions f_options;
342 Executor * f_ex = nullptr;
343
344 }; // end( class( SPAStar ) )
345
346/*--------------------------------------------------------------------------*/
347
348} // end( namespace PBeFS_di_unipi_it )
349
350/*--------------------------------------------------------------------------*/
351
352#endif /* __PBeFS_SPAStar */
353
354/*--------------------------------------------------------------------------*/
355/*--------------------------- End File SPAStar.h ---------------------------*/
356/*--------------------------------------------------------------------------*/
Header file for the class template ConcurrentClosed, the closed list that the workers of SPAStar and ...
Header file for the class template HDAStar, Hash Distributed A* on a shared-memory machine,...
the OPEN list on the BucketQueue of ADS, for integer costs
Definition OpenList.h:122
the closed list shared by the workers, a lock-free set of the nodes
Definition ConcurrentClosed.h:76
the OPEN list on the DaryHeap of ADS
Definition OpenList.h:52
SPAStar(const D &domain, const ParallelOptions &options={})
constructor, taking the Domain (which must outlive the SPAStar)
Definition SPAStar.h:142
state_t< D > State
the state type
Definition SPAStar.h:133
Solution< State, Cost > SolutionT
the solution type
Definition SPAStar.h:136
Node< State, Cost > NodeT
the node type
Definition SPAStar.h:135
cost_t< D > Cost
the cost type
Definition SPAStar.h:134
SolutionT solve(void) const
searches the Domain, returning the Solution
Definition SPAStar.h:157
void set_executor(Executor &ex) noexcept
sets the Executor that runs the workers, which must outlive the searches; without it,...
Definition SPAStar.h:152
a wall-clock stopwatch, started at construction
Definition SearchUtils.h:175
double seconds(void) const
the seconds elapsed since the construction
Definition SearchUtils.h:182
a search domain
Definition Domain.h:103
namespace for the Parallel Best-First Search library
Definition AStar.h:83
void for_each_child(const D &d, const state_t< D > &s, cost_t< D > h, std::uint8_t op, F &&f)
the successors of s but its parent, when D knows the operators
Definition Domain.h:276
typename D::cost_type cost_t
the cost type of a Domain
Definition Domain.h:245
constexpr std::size_t cache_line
the size of a cache line, used to keep the data of the workers apart
Definition SearchUtils.h:66
@ solved
an optimal path has been found
Definition Solution.h:47
@ node_limit
the limit on the stored nodes was reached first
Definition Solution.h:49
constexpr Cost infinite_cost(void) noexcept
the largest value of a cost type, used as "no incumbent yet"
Definition Node.h:100
typename D::state_type state_t
the state type of a Domain
Definition Domain.h:241
void run_workers(Executor *ex, unsigned p, F &&f)
runs the workers f( 0 ) , ... , f( p - 1 ) at once on an Executor
Definition SearchUtils.h:121
constexpr std::uint8_t no_op
the operator of the root, which no state was generated by
Definition Domain.h:205
auto trace_path(const NodeT *goal)
the states from the root to goal, following the parent pointers
Definition Node.h:126
the nodes that a worker allocates, and its spare node
Definition ConcurrentClosed.h:88
a node of the search graph
Definition Node.h:74
Cost g
the cost of the best path found from the root
Definition Node.h:77
the options of the parallel searches: those of the sequential search, and the parameters of the paral...
Definition HDAStar.h:114
void normalize(void) noexcept
sets to 1 any parameter that is 0
Definition SearchUtils.h:102
the counters of a search
Definition Solution.h:64
std::vector< std::uint64_t > expanded_by
expansions of each worker
Definition Solution.h:75
std::uint64_t generated
successors generated
Definition Solution.h:68
std::uint64_t reexpanded
expansions of a node already expanded
Definition Solution.h:67
std::uint64_t stored
distinct nodes stored at the end
Definition Solution.h:73
double seconds
wall-clock time of the search
Definition Solution.h:74
std::uint64_t duplicates
generated states already known with a not larger
Definition Solution.h:69
std::vector< std::uint64_t > stored_by
nodes stored by each worker
Definition Solution.h:76
std::uint64_t expanded
expansions, re-expansions included
Definition Solution.h:66
a path from the initial state to a goal, and its cost
Definition Solution.h:100