PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
HDAStar.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*----------------------------- File HDAStar.h -----------------------------*/
3/*--------------------------------------------------------------------------*/
63/*--------------------------------------------------------------------------*/
64/*------------------------------ DEFINITIONS -------------------------------*/
65/*--------------------------------------------------------------------------*/
66
67#ifndef __PBeFS_HDAStar
68 #define __PBeFS_HDAStar
69 /* self-identification: #endif at the end of the file */
70
71/*--------------------------------------------------------------------------*/
72/*-------------------------------- INCLUDES --------------------------------*/
73/*--------------------------------------------------------------------------*/
74
75#include <atomic>
76
77#include <memory>
78
79#include <mutex>
80
81#include <optional>
82
83#include <string>
84
85#include <type_traits>
86
87#include <vector>
88
89#include "PBeFS/astar/AStar.h"
90
92
94
95#include "ADS/parallel/Channels.h"
96
97#include "ADS/parallel/Mailbox.h"
98
99/*--------------------------------------------------------------------------*/
100/*------------------------------- NAMESPACE --------------------------------*/
101/*--------------------------------------------------------------------------*/
102
104namespace PBeFS_di_unipi_it
105{
106
107/*--------------------------------------------------------------------------*/
108/*------------------------- CLASS ParallelOptions --------------------------*/
109/*--------------------------------------------------------------------------*/
113
115 }; // end( struct( ParallelOptions ) )
116
117/*--------------------------------------------------------------------------*/
118/*----------------------------- CLASS HDAStar ------------------------------*/
119/*--------------------------------------------------------------------------*/
121
137
138template< Domain D , class Dist = StateHashDistribution< D > ,
139 ADS::WorkerExecutor Executor = ADS::ThreadPool ,
140 class Open = std::conditional_t<
141 std::is_integral_v< cost_t< D > > ,
142 BucketOpenList< Node< state_t< D > , cost_t< D > > > ,
143 HeapOpenList< Node< state_t< D > , cost_t< D > > > > >
144class HDAStar {
145
146/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
147
148 public:
149
150/*------------------------------ PUBLIC TYPES ------------------------------*/
151
156
158 struct Msg {
163 [[no_unique_address]] OperatorOf< D > o; // the operator of s
164 std::uint32_t key; // the key of s
165 };
166
167/*------------------------------ CONSTRUCTOR -------------------------------*/
168
171
172 HDAStar( const D & domain , const Dist & dist ,
173 const ParallelOptions & options = {} )
174 : f_domain( domain ) , f_dist( dist ) , f_options( options ) {
175 f_options.normalize();
176 }
177
178/*------------------------- OTHER INITIALIZATIONS --------------------------*/
182
183 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
184
185/*-------------------------------- SOLVING ---------------------------------*/
187
188 [[nodiscard]] SolutionT solve( void ) const
189 {
190 const unsigned p = f_options.threads;
191 Stopwatch clock;
192 Shared sh( p , f_options.batch );
193
194 // the root, which its owner inserts before the first expansion
195 const State s0 = f_domain.initial_state();
196 sh.root.emplace( Msg{ s0 , Cost( 0 ) , Cost( f_domain.heuristic( s0 ) ) ,
197 nullptr , no_op ,
198 distribution_key< D >( f_dist , s0 ) } );
199 sh.root_owner = f_dist.owner( s0 , p );
200
201 std::vector< std::unique_ptr< Worker > > w( p );
202 for( unsigned i = 0 ; i < p ; ++i )
203 w[ i ] = std::make_unique< Worker >( *this , sh , i );
204
205 run_workers( f_ex , p , [ & ]( unsigned i ) { w[ i ]->run(); } );
206
207 SolutionT sol;
208 SearchStats & st = sol.stats;
209 for( auto & x : w ) {
210 st.expanded += x->f_st.expanded;
211 st.reexpanded += x->f_st.reexpanded;
212 st.generated += x->f_st.generated;
213 st.duplicates += x->f_st.duplicates;
214 st.sent += x->f_st.sent;
215 st.messages += sh.mail.batches( x->f_id );
216 st.stored += x->f_pool.size();
217 st.expanded_by.push_back( x->f_st.expanded );
218 st.stored_by.push_back( x->f_pool.size() );
219 }
220 if( sh.limit_hit.load() )
221 sol.status = Status::node_limit;
222 else
223 if( sh.goal ) {
224 sol.status = Status::solved;
225 sol.cost = sh.goal->g;
226 sol.path = trace_path( sh.goal );
227 }
228 st.seconds = clock.seconds();
229 return( sol );
230 }
231
232/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
233
234 private:
235
236 using Mailbox = ADS::Mailbox< Msg , ADS::DefaultChannels< Msg , Executor > >;
237
238/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
239 // the data shared by the workers
240
241 struct Shared {
242
243 Shared( unsigned np , std::size_t batch ) : p( np ) , mail( np , batch )
244 {}
245
246 unsigned p; // the number of workers
247 Mailbox mail; // the messages
248 std::optional< Msg > root; // the root
249 unsigned root_owner = 0; // its owner
250 alignas( cache_line ) std::atomic< Cost > incumbent{
252 std::atomic< bool > limit_hit{ false };
253 std::mutex goal_mutex; // protects goal
254 NodeT * goal = nullptr; // the best goal found
255 };
256
257/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
258 // a worker: its OPEN and CLOSED lists, its outboxes and its counters
259
260 struct alignas( cache_line ) Worker {
261
262 Worker( const HDAStar & a , Shared & s , unsigned i )
263 : f_a( a ) , f_sh( s ) , f_id( i ) , f_dist( a.f_dist ) {}
264
265 // the main loop
266 void run( void ) {
267 if( f_id == f_sh.root_owner )
268 insert( *f_sh.root , f_a.f_domain.hash( f_sh.root->s ) );
269 unsigned since_flush = 0;
270 ADS::Backoff backoff;
271 while( ! f_sh.mail.stopped() ) {
272 v_in.clear();
273 f_sh.mail.poll( f_id , [ & ]( Msg && m ) {
274 v_in.push_back( std::move( m ) );
275 } );
276 if( const std::size_t n = v_in.size() ) {
277 // the hashes and the prefetches of the whole batch first, so that
278 // the cache misses of the lookups overlap
279 v_hash.resize( n );
280 for( std::size_t k = 0 ; k < n ; ++k ) {
281 v_hash[ k ] = f_a.f_domain.hash( v_in[ k ].s );
282 f_table.prefetch( v_hash[ k ] );
283 }
284 for( std::size_t k = 0 ; k < n ; ++k )
285 insert( v_in[ k ] , v_hash[ k ] );
286 }
287
288 if( NodeT * n = next() ) {
289 backoff.reset();
290 expand( n );
291 if( ++since_flush >= f_a.f_options.flush_period ) {
292 since_flush = 0;
293 f_sh.mail.flush( f_id );
294 }
295 if( f_a.f_options.node_limit &&
296 ( f_pool.size() >= f_a.f_options.node_limit ) ) {
297 f_sh.limit_hit.store( true );
298 f_sh.mail.stop();
299 }
300 continue;
301 }
302
303 // nothing to expand: send everything, and end if no worker has work
304 // and no message is in transit
305 if( f_sh.mail.quiescent( f_id ) )
306 return;
307 backoff.pause();
308 }
309 }
310
311 // the best node of OPEN with f < incumbent, removed; nullptr if none
312 NodeT * next( void ) {
313 while( ! f_open.empty() ) {
314 const OpenEntry< NodeT > e = f_open.top();
315 if( e.stale() ) {
316 f_open.pop();
317 continue;
318 }
319 if( e.node->g + e.node->h >= f_sh.incumbent.load(
320 std::memory_order_relaxed ) )
321 return( nullptr );
322 f_open.pop();
323 return( e.node );
324 }
325 return( nullptr );
326 }
327
328 // expands n, or records it as the incumbent if it is a goal
329 void expand( NodeT * n ) {
330 n->closed = true;
331 ++f_st.expanded;
332 if( n->expanded )
333 ++f_st.reexpanded;
334 n->expanded = true;
335
336 if( f_a.f_domain.is_goal( n->state ) ) {
337 std::lock_guard< std::mutex > lock( f_sh.goal_mutex );
338 if( n->g < f_sh.incumbent.load() ) {
339 f_sh.incumbent.store( n->g );
340 f_sh.goal = n;
341 }
342 return;
343 }
344
345 const auto owner = successor_owners< D >( f_dist , n->state , n->key ,
346 f_sh.p );
347 for_each_child( f_a.f_domain , n->state , n->h , n->op ,
348 [ & ]( const State & t , Cost c , Cost ht ,
349 std::uint8_t ot ) {
350 ++f_st.generated;
351 const Cost g = n->g + c;
352 if( g + ht >= f_sh.incumbent.load( std::memory_order_relaxed ) )
353 return;
354 const auto [ dst , key ] = owner( t );
355 if( dst == f_id ) {
356 insert( Msg{ t , g , ht , n , ot , key } , f_a.f_domain.hash( t ) );
357 return;
358 }
359 f_sh.mail.send( f_id , dst , Msg{ t , g , ht , n , ot , key } );
360 ++f_st.sent;
361 } );
362 }
363
364 // adds the node of m, whose state has hash h, to OPEN, unless it is a
365 // duplicate
366 void insert( const Msg & m , std::uint64_t h ) {
367 if( m.g + m.h >= f_sh.incumbent.load( std::memory_order_relaxed ) )
368 return;
369 auto [ x , fresh ] = f_table.find_or_insert( m.s , h , [ & ] {
370 NodeT * y = f_pool.allocate();
371 *y = NodeT{ m.s , m.g , m.h , m.parent , false , false , m.o.op ,
372 m.key };
373 return( y );
374 } );
375 if( fresh )
376 f_open.push( x );
377 else
378 if( m.g < x->g ) {
379 x->g = m.g;
380 x->parent = m.parent;
381 x->op = m.o.op;
382 x->closed = false;
383 f_open.push( x );
384 }
385 else
386 ++f_st.duplicates;
387 }
388
389 const HDAStar & f_a; // the search
390 Shared & f_sh; // the shared data
391 unsigned f_id; // the index of the worker
392 Dist f_dist; // its distribution
393 NodePool< NodeT > f_pool; // its nodes
394 NodeTable< NodeT > f_table; // its CLOSED list
395 Open f_open; // its OPEN list
396 std::vector< Msg > v_in; // the last inbox received
397 std::vector< std::uint64_t > v_hash; // the hashes of v_in
398 SearchStats f_st; // its counters
399 };
400
401/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
402
403 const D & f_domain;
404 const Dist & f_dist;
405 ParallelOptions f_options;
406 Executor * f_ex = nullptr;
407
408 }; // end( class( HDAStar ) )
409
410/*--------------------------------------------------------------------------*/
411
412} // end( namespace PBeFS_di_unipi_it )
413
414/*--------------------------------------------------------------------------*/
415
416#endif /* __PBeFS_HDAStar */
417
418/*--------------------------------------------------------------------------*/
419/*--------------------------- End File HDAStar.h ---------------------------*/
420/*--------------------------------------------------------------------------*/
Header file for the class template AStar, the sequential A* search of Hart, Nilsson and Raphael (1968...
Header file for the work distribution functions of hash-based parallel A*: each one maps a state to i...
Header file for the small helpers shared by the searches: a stopwatch, the placement of the workers o...
Hash Distributed A* on a shared-memory machine.
Definition HDAStar.h:144
state_t< D > State
the state type
Definition HDAStar.h:152
Solution< State, Cost > SolutionT
the solution type
Definition HDAStar.h:155
Node< State, Cost > NodeT
the node type
Definition HDAStar.h:154
cost_t< D > Cost
the cost type
Definition HDAStar.h:153
HDAStar(const D &domain, const Dist &dist, const ParallelOptions &options={})
constructor, taking the Domain and the distribution (which must outlive the HDAStar)
Definition HDAStar.h:172
SolutionT solve(void) const
searches the Domain, returning the Solution
Definition HDAStar.h:188
void set_executor(Executor &ex) noexcept
sets the Executor that runs the workers, which must outlive the searches; without it,...
Definition HDAStar.h:183
the arena of the nodes, on the Arena of ADS, on huge pages
Definition NodePool.h:48
the closed list, on the PointerTable of ADS, on huge pages
Definition NodeTable.h:49
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
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
std::uint32_t distribution_key(const X &x, const state_t< D > &s)
the key of s for x, 0 if X is not an IncrementalDistribution
Definition Distribution.h:399
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
auto successor_owners(const X &x, const state_t< D > &s, std::uint32_t k, unsigned p)
the owner among p workers and the key of each successor of s
Definition Distribution.h:414
a node travelling to its owner
Definition HDAStar.h:158
State s
Definition HDAStar.h:159
Cost g
Definition HDAStar.h:160
NodeT * parent
Definition HDAStar.h:162
Cost h
Definition HDAStar.h:161
OperatorOf< D > o
Definition HDAStar.h:163
std::uint32_t key
Definition HDAStar.h:164
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
Definition Domain.h:260
the options of the parallel searches: those of the sequential search, and the parameters of the paral...
Definition HDAStar.h:114
the parameters shared by the parallel searches
Definition SearchUtils.h:94
void normalize(void) noexcept
sets to 1 any parameter that is 0
Definition SearchUtils.h:102
the options shared by all the searches
Definition Solution.h:119
the counters of a search
Definition Solution.h:64
std::uint64_t messages
batches of nodes sent
Definition Solution.h:72
std::vector< std::uint64_t > expanded_by
expansions of each worker
Definition Solution.h:75
std::uint64_t sent
nodes sent to another worker
Definition Solution.h:71
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