Skip to content

Commit

Permalink
Build a map of edge src/dest to index on the host
Browse files Browse the repository at this point in the history
This operates similarly to the vertex one.
  • Loading branch information
Robadob committed Oct 9, 2023
1 parent acd609e commit 4d0eab6
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,38 +105,18 @@ class HostEnvironmentDirectedGraph {
void setVertexProperty(const std::string& property_name, unsigned int vertex_index, flamegpu::size_type element_index, T property_value);
template<typename T, flamegpu::size_type N>
void setVertexProperty(const std::string& property_name, unsigned int vertex_index, const std::array<T, N>& property_value);
/**
* Sets the source vertex ID of the specified edge
* @param edge_index Index of the edge to update
* @param vertex_source_id The source vertex ID to assign to the edge
*
* @throw exception::InvalidArgument If vertex_source_id is not a valid vertex ID, or 0
* @throw exception::OutOfBoundsException If edge_index exceeds the number of edges
* @see setEdgeDestination()
*/
void setEdgeSource(unsigned int edge_index, id_t vertex_source_id);
/**
* Sets the destination vertex ID of the specified edge
* @param edge_index Index of the edge to update
* @param vertex_dest_id The destination vertex ID to assign to the edge
*
* @throw exception::InvalidArgument If vertex_dest_id is not a valid vertex ID, or 0
* @throw exception::OutOfBoundsException If edge_index exceeds the number of edges
* @see setEdgeSource()
*/
void setEdgeDestination(unsigned int edge_index, id_t vertex_dest_id);
/**
* Sets the source and destination vertex IDs of the specified edge
* @param edge_index Index of the edge to update
* @param vertex_source_id The source vertex ID to assign to the edge
* @param vertex_dest_id The destination vertex ID to assign to the edge
* @param source_vertex_id The source vertex ID to assign to the edge
* @param dest_vertex_id The destination vertex ID to assign to the edge
*
* @throw exception::InvalidArgument If vertex_source_id or vertex_dest_id is not a valid vertex ID, or 0
* @throw exception::OutOfBoundsException If edge_index exceeds the number of edges
* @see setEdgeSource()
* @see setEdgeDestination()
*/
void setEdgeSourceDestination(unsigned int edge_index, id_t vertex_source_id, id_t vertex_dest_id);
void setEdgeSourceDestination(unsigned int edge_index, id_t source_vertex_id, id_t dest_vertex_id);
/**
* Set the value of the specified property of the edge at the specific index
* @param property_name The name of the property to set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "flamegpu/defines.h"
#include "flamegpu/simulation/detail/CUDAErrorChecking.cuh"
#include "flamegpu/detail/type_decode.h"
#include "flamegpu/util/StringPair.h"

namespace flamegpu {
namespace detail {
Expand Down Expand Up @@ -85,7 +86,7 @@ class CUDAEnvironmentDirectedGraphBuffers {
void deallocateVertexBuffers();
void deallocateEdgeBuffers();
/*
* Reset the internal vertex ID range tracking variables asthough no vertices have been assigned IDs
* Reset the internal vertex ID range tracking variables as though no vertices have been assigned IDs
*/
void resetVertexIDBounds();
unsigned int vertex_id_min = std::numeric_limits<unsigned int>::max();
Expand All @@ -94,6 +95,7 @@ class CUDAEnvironmentDirectedGraphBuffers {
* Host ID map, this allows HostAPI methods to operate using vertex id
*/
std::map<id_t, unsigned int> h_vertex_index_map;
util::PairMap<id_t, id_t, unsigned int> h_edge_index_map;
/**
* Some kind of internal validation method to ensure edges are valid, all vertices have been assigned IDS (or all are 0?)
* When is it triggered?
Expand Down Expand Up @@ -235,6 +237,25 @@ class CUDAEnvironmentDirectedGraphBuffers {
* @throws exception::InvalidID If the ID is not in use
*/
unsigned int getVertexIndex(id_t vertex_id) const;
/**
* Updates the edge ID buffer
* Updates the internal host map of src:dest->index
*
* @param edge_index The index of the edge
* @param src_vertex_id The ID that has been assigned to the source vertex of the edge
* @param dest_vertex_id The ID that has been assigned to the destination vertex of the edge
*
* @throws exception::IDCollision If the ID is already assigned to a different vertex
*/
void setEdgeSourceDestination(unsigned int edge_index, id_t src_vertex_id, id_t dest_vertex_id);
/**
* Returns the index of the edge with the given source and destination vertices
* @param src_vertex_id The ID that has been assigned to the source vertex of the edge
* @param dest_vertex_id The ID that has been assigned to the destination vertex of the edge
*
* @throws exception::InvalidID If the ID is not in use
*/
unsigned int getEdgeIndex(id_t src_vertex_id, id_t dest_vertex_id) const;
};


Expand Down
24 changes: 16 additions & 8 deletions include/flamegpu/util/StringPair.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,42 @@ typedef std::pair<std::string, std::string> StringPair;
/**
* Compare function so that StringPair can be used in a map
*/
struct StringPairCompare {
bool operator() (const std::pair<std::string, std::string>& lhs, const std::pair<std::string, std::string>& rhs) const {
template<typename T1, typename T2>
struct PairCompare {
bool operator() (const std::pair<T1, T2>& lhs, const std::pair<T1, T2>& rhs) const {
if (lhs.first == rhs.first)
return lhs.second < rhs.second;
return lhs.first < rhs.first;
}
};
typedef PairCompare<std::string, std::string> StringPairCompare;
/**
* Hash function so that StringPair can be used as a key in an unordered map
*/
struct StringPairHash {
size_t operator()(const std::pair<std::string, std::string>& k) const {
return std::hash<std::string>()(k.first) ^
(std::hash<std::string>()(k.second) << 1);
template<typename T1, typename T2>
struct PairHash {
size_t operator()(const std::pair<T1, T2>& k) const {
return std::hash<T1>()(k.first) ^
(std::hash<T2>()(k.second) << 1);
}
};
typedef PairHash<std::string, std::string> StringPairHash;

/**
* Ordered map with StringPair as the key type
*/
template<typename A, typename B, typename T>
using PairMap = std::map<std::pair<A, B>, T, PairCompare<A, B>>;
template<typename T>
using StringPairMap = std::map<StringPair, T, StringPairCompare>;
using StringPairMap = PairMap<std::string, std::string, T>;

/**
* Unordered map with StringPair as the key type
*/
template<typename A, typename B, typename T>
using PairUnorderedMap = std::unordered_map<std::pair<A, B>, T, PairHash<A, B>>;
template<typename T>
using StringPairUnorderedMap = std::unordered_map<StringPair, T, StringPairHash>;
using StringPairUnorderedMap = PairUnorderedMap<std::string, std::string, T>;

} // namespace util
} // namespace flamegpu
Expand Down
21 changes: 17 additions & 4 deletions src/flamegpu/io/JSONGraphReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ class JSONAdjacencyGraphReader : public rapidjson::BaseReaderHandler<rapidjson::
* Tracks current position reading variable array
*/
unsigned int current_variable_array_index = 0;
/**
* Source and target are stored separately, but must be set within graph simultaneously.
* So these are used to cache the first value temporarily
*/
unsigned int last_source = ID_NOT_SET, last_target = ID_NOT_SET;

public:
JSONAdjacencyGraphReader(const std::string &_filename,
Expand Down Expand Up @@ -254,12 +259,20 @@ class JSONAdjacencyGraphReader : public rapidjson::BaseReaderHandler<rapidjson::
THROW exception::RapidJSONError("Edge refers to unrecognised Vertex ID '%s', unable to load input file '%s'.\n", str, filename.c_str());
}
if (lastKey == "source") {
size_type len = 2;
graph->getEdgePropertyBuffer<id_t>(GRAPH_SOURCE_DEST_VARIABLE_NAME, len, stream)[current_index * 2 + 1] = f->second;
if (last_target == ID_NOT_SET) {
last_source = f->second;
} else {
graph->setEdgeSourceDestination(current_index, f->second, last_target);
last_target = ID_NOT_SET;
}
return true;
} else if (lastKey == "target") {
size_type len = 2;
graph->getEdgePropertyBuffer<id_t>(GRAPH_SOURCE_DEST_VARIABLE_NAME, len, stream)[current_index * 2 + 0] = f->second;
if (last_source == ID_NOT_SET) {
last_target = f->second;
} else {
graph->setEdgeSourceDestination(current_index, last_source, f->second);
last_source = ID_NOT_SET;
}
return true;
} else {
if (current_index) {
Expand Down
29 changes: 3 additions & 26 deletions src/flamegpu/runtime/environment/HostEnvironmentDirectedGraph.cu
Original file line number Diff line number Diff line change
Expand Up @@ -77,35 +77,12 @@ flamegpu::size_type HostEnvironmentDirectedGraph::getEdgeCount() {
THROW exception::ExpiredWeakPtr("Graph nolonger exists, weak pointer could not be locked, in HostEnvironmentDirectedGraph::getEdgeCount()\n");
}
}
void HostEnvironmentDirectedGraph::setEdgeSource(unsigned int edge_index, const id_t vertex_source_id) {
if (vertex_source_id == ID_NOT_SET) {
THROW exception::IDOutOfBounds("%u is not a valid ID, this is reserved as the internal flag for unset IDs, in HostEnvironmentDirectedGraph::setEdgeSource()\n", ID_NOT_SET);
}
setEdgeProperty<id_t, 2>(GRAPH_SOURCE_DEST_VARIABLE_NAME, edge_index, 1, vertex_source_id);
if (const auto dg = directed_graph.lock()) {
dg->markForRebuild();
} else {
THROW exception::ExpiredWeakPtr("Graph nolonger exists, weak pointer could not be locked, in HostEnvironmentDirectedGraph::setEdgeSource()\n");
}
}
void HostEnvironmentDirectedGraph::setEdgeDestination(unsigned int edge_index, const id_t vertex_dest_id) {
if (vertex_dest_id == ID_NOT_SET) {
THROW exception::IDOutOfBounds("%u is not a valid ID, this is reserved as the internal flag for unset IDs, in HostEnvironmentDirectedGraph::setEdgeDestination()\n", ID_NOT_SET);
}
setEdgeProperty<id_t, 2>(GRAPH_SOURCE_DEST_VARIABLE_NAME, edge_index, 0, vertex_dest_id);
if (const auto dg = directed_graph.lock()) {
dg->markForRebuild();
} else {
THROW exception::ExpiredWeakPtr("Graph nolonger exists, weak pointer could not be locked, in HostEnvironmentDirectedGraph::setEdgeDestination()\n");
}
}
void HostEnvironmentDirectedGraph::setEdgeSourceDestination(unsigned int edge_index, const id_t vertex_source_id, const id_t vertex_dest_id) {
if (vertex_source_id == ID_NOT_SET || vertex_dest_id == ID_NOT_SET) {
void HostEnvironmentDirectedGraph::setEdgeSourceDestination(unsigned int edge_index, const id_t src_vertex_id, const id_t dest_vertex_id) {
if (src_vertex_id == ID_NOT_SET || dest_vertex_id == ID_NOT_SET) {
THROW exception::IDOutOfBounds("%u is not a valid ID, this is reserved as the internal flag for unset IDs, in HostEnvironmentDirectedGraph::setEdgeSourceDestination()\n", ID_NOT_SET);
}
setEdgeProperty<id_t, 2>(GRAPH_SOURCE_DEST_VARIABLE_NAME, edge_index, { vertex_dest_id, vertex_source_id });
if (const auto dg = directed_graph.lock()) {
dg->markForRebuild();
dg->setEdgeSourceDestination(edge_index, src_vertex_id, dest_vertex_id);
} else {
THROW exception::ExpiredWeakPtr("Graph nolonger exists, weak pointer could not be locked, in HostEnvironmentDirectedGraph::setEdgeSourceDestination()\n");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ void CUDAEnvironmentDirectedGraphBuffers::deallocateEdgeBuffers() {
d_ipbm_edges = nullptr;
}
edge_count = 0;
h_edge_index_map.clear();
}

void CUDAEnvironmentDirectedGraphBuffers::setVertexCount(const size_type count, const cudaStream_t stream) {
Expand Down Expand Up @@ -549,6 +550,11 @@ void CUDAEnvironmentDirectedGraphBuffers::syncDevice_async(detail::CUDAScatter&
const unsigned int BLOCK_CT = static_cast<unsigned int>(ceil(edge_count / static_cast<float>(BLOCK_SZ)));
translateSrcDest << <BLOCK_CT, BLOCK_SZ, 0, stream >> > (static_cast<id_t*>(e_srcdest_b.d_ptr), d_vertex_index_map, edge_count, d_pbm_swap, vertex_id_min, vertex_id_max);
gpuErrchkLaunch()
// Rebuild the edge index map
h_edge_index_map.clear();
for (unsigned int i = 0; i < edge_count; ++i) {
h_edge_index_map.emplace(std::pair{static_cast<id_t*>(e_srcdest_b.h_ptr)[i * 2 + 1], static_cast<id_t*>(e_srcdest_b.h_ptr)[i * 2 + 0]}, i);
}
}
requires_rebuild = false;
}
Expand All @@ -568,7 +574,7 @@ void CUDAEnvironmentDirectedGraphBuffers::resetVertexIDBounds() {
void CUDAEnvironmentDirectedGraphBuffers::setVertexID(unsigned int vertex_index, id_t vertex_id, cudaStream_t stream) {
if (vertex_index >= vertex_count) {
THROW exception::OutOfBoundsException("Vertex index exceeds bounds %u >= %u, "
"in CUDAEnvironmentDirectedGraphBuffers::setVertexID()\n", vertex_index, vertex_id);
"in CUDAEnvironmentDirectedGraphBuffers::setVertexID()\n", vertex_index, vertex_count);
}
// Purge old vertex ID from host map
auto& vb = vertex_buffers.at(ID_VARIABLE_NAME);
Expand All @@ -587,6 +593,7 @@ void CUDAEnvironmentDirectedGraphBuffers::setVertexID(unsigned int vertex_index,

// Update vertex's ID in buffer
static_cast<id_t*>(vb.h_ptr)[vertex_index] = vertex_id;
vb.ready = Buffer::Host;

// Update range calc
vertex_id_min = std::min(vertex_id_min, vertex_id);
Expand All @@ -599,5 +606,46 @@ unsigned int CUDAEnvironmentDirectedGraphBuffers::getVertexIndex(id_t vertex_id)
}
return find->second;
}
void CUDAEnvironmentDirectedGraphBuffers::setEdgeSourceDestination(unsigned int edge_index, id_t src_vertex_id, id_t dest_vertex_id) {
if (edge_index >= edge_count) {
THROW exception::OutOfBoundsException("Edge index exceeds bounds %u >= %u, "
"in CUDAEnvironmentDirectedGraphBuffers::setEdgeSourceDestination()\n", edge_index, edge_count);
}
// Purge old edge src/dest from host map
auto& eb = edge_buffers.at(GRAPH_SOURCE_DEST_VARIABLE_NAME);
// Don't need to update buffer, src_dest is not stored as ID on device
id_t& edge_dest = static_cast<id_t*>(eb.h_ptr)[edge_index * 2 + 0];
id_t& edge_src = static_cast<id_t*>(eb.h_ptr)[edge_index * 2 + 1];

if (edge_src == ID_NOT_SET && edge_dest == ID_NOT_SET) {
h_edge_index_map.erase({edge_src, edge_dest});
} else if (edge_src == ID_NOT_SET || edge_dest == ID_NOT_SET) {
THROW exception::UnknownInternalError("Edge found without both source and destination set, "
"in CUDAEnvironmentDirectedGraphBuffers::setEdgeSourceDestination()\n");
}

// Add new edge ID to host map (validate it's not already in use)
const auto find = h_edge_index_map.find({src_vertex_id, dest_vertex_id});
if (find != h_edge_index_map.end()) {
THROW exception::IDCollision("Edge collision, an edge has already been assigned source %u dest %u at index %u, "
"in CUDAEnvironmentDirectedGraphBuffers::setEdgeSourceDestination()\n", src_vertex_id, dest_vertex_id, find->second);
}
h_edge_index_map.emplace(std::pair{src_vertex_id, dest_vertex_id}, edge_index);

// Update edge's src dest in buffer
edge_dest = dest_vertex_id;
edge_src = src_vertex_id;
eb.ready = Buffer::Host;

// Require rebuild before use
markForRebuild();
}
unsigned int CUDAEnvironmentDirectedGraphBuffers::getEdgeIndex(id_t src_vertex_id, id_t dest_vertex_id) const {
const auto find = h_edge_index_map.find({src_vertex_id, dest_vertex_id});
if (find != h_edge_index_map.end()) {
THROW exception::InvalidID("No edge found with source %u, dest %u, in CUDAEnvironmentDirectedGraphBuffers::getEdgeIndex()\n", src_vertex_id, dest_vertex_id);
}
return find->second;
}
} // namespace detail
} // namespace flamegpu
Loading

0 comments on commit 4d0eab6

Please sign in to comment.