Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
Merge pull request #45 from gunrock/docs
Browse files Browse the repository at this point in the history
Code Documentation... and minor fixes.
  • Loading branch information
neoblizz authored Apr 14, 2021
2 parents 114b37f + 21f8619 commit 3cc5c63
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 98 deletions.
3 changes: 2 additions & 1 deletion include/gunrock/applications/sssp.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ struct problem_t : gunrock::problem_t<graph_t> {
auto g = this->get_graph();
auto n_vertices = g.get_number_of_vertices();

auto policy = this->context->get_context(0)->execution_policy();
auto context = this->get_single_context();
auto policy = context->execution_policy();

auto d_distances = thrust::device_pointer_cast(this->result.distances);
thrust::fill(policy, d_distances + 0, d_distances + n_vertices,
Expand Down
153 changes: 126 additions & 27 deletions include/gunrock/framework/enactor.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,53 @@
namespace gunrock {

/**
* @brief
* @brief A simple struct to store enactor properties.
*
* @par Overview
* Modify this per-algorithm if the defaults need to be changed. A
* gunrock-specific parameter can also control the frontier sizing factor used
* to resize and scale the frontier larger than the needed size.
*/
struct enactor_properties_t {
/*!
* Resizes the frontier by this factor * the size required.
* @code frontier.resize(frontier_sizing_factor * new_size);
*/
float frontier_sizing_factor{2};

/*!
* Number of frontier buffers to manage.
* @note Not used in the implementation yet.
*/
std::size_t number_of_frontier_buffers{2};

/**
* @brief Construct a new enactor properties t object with default values.
*/
enactor_properties_t() = default;
};

/**
* @brief Building block of the algorithm within gunrock. An enactor structure
* defines how, what and wehre the problem is going to be executed. Note that
* the enactor enact() function can be extended to support multi-gpu contexts
* (using execution policy like model). Enactor also has two pure virtual
* functions, which MUST be implemented within the algorithm the user is trying
* to write the enactor for. These functions prepare the initial frontier of the
* algorithm (which could be one node, the entire graph or any variation), and
* finally defines the main loop of iteration that iterates until the algorithm
* converges. Default convergence condition is when the frontier is empty, the
* algorithm has finished, but this convergence function can ALSO be extended to
* support any custom convergence condition.
* @brief Building block of the algorithm within gunrock. The methods within the
* enactor structure are overriden by an application writer (methods such as
* loop) to extend the enactor to implement a graph algorithm.
*
* @par Overview
* Note that the enactor's @see `enact()` member can be extended to support
* multi-gpu contexts (using execution policies like model). Enactor also has
* pure virtual functions, which **must** be implemented within the graph
* algorithm by the developers. These functions prepare the initial frontier of
* the algorithm (which could be one node, the entire graph or any subset),
* and defines the main loop of iteration that iterates until the algorithm
* converges.
* @see prepare_frontier()
* @see loop()
* @see is_converged()
*
* @tparam algorithm_problem_t algorithm specific problem type.
* @tparam frontier_kind @see `gunrock::frontier_kind_t` enum (vertex or
* edge-based frontier). Currently, only vertex frontier is supported.
*
* @tparam algorithm_problem_t algorithm specific problem type
*/
template <typename algorithm_problem_t,
frontier_kind_t frontier_kind = frontier_kind_t::vertex_frontier>
Expand All @@ -56,21 +80,77 @@ struct enactor_t {
vertex_t,
edge_t>>;

/*!
* Enactor properties (frontier resizing factor, buffers, etc.)
*/
enactor_properties_t properties;

/*!
* A shared_ptr to a multi-gpu context @see `gunrock::cuda::multi_context_t`.
*/
std::shared_ptr<cuda::multi_context_t> context;

/*!
* Algorithm's problem structure.
*/
algorithm_problem_t* problem;

/*!
* Vector of frontier buffers (default number of buffers = 2).
*/
thrust::host_vector<frontier_type> frontiers;

/*!
* Extra space to store scan of the work domain (for advance).
* @note This is not used for all advance algorithms.
* @todo Find a way to only allocate this if the advance algorithm that
* actually needs it is being run. Otherwise, it maybe a waste of memory space
* to allocate this.
*/
thrust::device_vector<vertex_t> scanned_work_domain;

/*!
* Active frontier buffer, this pointer can be obtained by
* `get_input_frontier()` method. This buffer is used as an input frontier.
*/
frontier_type* active_frontier;

/*!
* Inactive frontier buffer, this pointer can be obtained by
* `get_output_frontier()` method. This buffer is often but not always used as
* an output frontier.
*/
frontier_type* inactive_frontier;

/*!
* An internal selector used to swap frontier buffers.
*/
int buffer_selector;

/*!
* Number of iterations for the `loop` method. Increments per loop-iteration.
*/
int iteration;

// Disable copy ctor and assignment operator.
// We don't want to let the user copy only a slice.
/*!
* Disable copy ctor and assignment operator. We don't want to let the user
* copy only a slice.
* @todo Look at enabling copy constructors.
*/
enactor_t(const enactor_t& rhs) = delete;
enactor_t& operator=(const enactor_t& rhs) = delete;

/**
* @brief Construct a new enactor t object.
*
* @param _problem algorithm's problem data structure.
* @param _context shared pointer to the cuda::multi_context_t context that
* stores information about multiple GPUs (such as streams, device ids,
* events, etc.)
* @param _properties @see `gunrock::enactor_properties_t`, includes
* properties such as frontier resizing factor (default = 2) and number of
* frontier buffers to create for the enactor.
*/
enactor_t(algorithm_problem_t* _problem,
std::shared_ptr<cuda::multi_context_t> _context,
enactor_properties_t _properties = enactor_properties_t())
Expand Down Expand Up @@ -114,18 +194,27 @@ struct enactor_t {
*/
frontier_type* get_output_frontier() { return inactive_frontier; }

/**
* @brief Swap the inactive and active frontier buffers such that the inactive
* buffer becomes the input buffer to the next operator and vice-versa.
*/
void swap_frontier_buffers() {
buffer_selector ^= 1;
active_frontier = &frontiers[buffer_selector];
inactive_frontier = &frontiers[buffer_selector ^ 1];
}

/**
* @brief Get the pointer to the enactor object.
* @return enactor_t*
*/
enactor_t* get_enactor() { return this; }

/**
* @brief Run the enactor with the given problem and the loop.
* @note We can work on evolving this into a multi-gpu implementation.
* @return float time took for enactor to complete.
* @todo We can work on evolving this into a multi-gpu implementation.
* @return float time took for enactor to complete (this is often used as
* **the** time for performance measurements).
*/
float enact() {
auto context0 = context->get_context(0);
Expand All @@ -140,14 +229,18 @@ struct enactor_t {
}

/**
* @brief This is the core of the implementation for any algorithm. loops
* till the convergence condition is met (see: is_converged()). Note that this
* function is on the host and is timed, so make sure you are writing the most
* efficient implementation possible. Avoid performing copies in this function
* or running API calls that are incredibly slow (such as printfs), unless
* they are part of your algorithms' implementation.
* @brief This is the core of the implementation for any algorithm. Graph
* algorithm developers should override this virtual function to implement
* their own `loop` function, which loops till a convergence condition is
* met @see `is_converged()`.
*
* @param context
* @par Overview
* This function is on the host and is timed, so make sure you are writing the
* most efficient implementation possible. Avoid performing copies in this
* function if they are not part of the algorithm's core, or running API calls
* that are incredibly slow (such as `printfs` or debug statements).
*
* @param context @see `gunrock::cuda::multi_context_t`.
*/
virtual void loop(cuda::multi_context_t& context) = 0;

Expand All @@ -162,10 +255,16 @@ struct enactor_t {
/**
* @brief Algorithm is converged if true is returned, keep on iterating if
* false is returned. This function is checked at the end of every iteration
* of the enact().
* of the `enact()`. Graph algorithm developers may override this function
* with their own custom convergence condition.
*
* @par Overview
* Default behavior of `is_converged()` method is to claim that the algorithm
* is converged if the frontier size is set to be empty (`frontier->is_empty()
* == true`).
*
* @return true
* @return false
* @return true converged!
* @return false not converged, keep looping!
*/
virtual bool is_converged(cuda::multi_context_t& context) {
return active_frontier->is_empty();
Expand Down
Loading

0 comments on commit 3cc5c63

Please sign in to comment.