[Rename diff] Change the terminology to match the rest.
Reviewers: sasa.stanko Reviewed By: sasa.stanko Subscribers: buda, lion Differential Revision: https://phabricator.memgraph.io/D672
This commit is contained in:
parent
061b8933a7
commit
58453794a6
@ -1,7 +1,18 @@
|
||||
|
||||
# distributed memgraph
|
||||
|
||||
This subdirectory structure implements distributed infrastructure of Memgraph.
|
||||
|
||||
## terminology
|
||||
|
||||
* Memgraph Node Id (mnid): a machine (processs) that runs a (distributed) Memgraph program.
|
||||
* Node: a computer that performs (distributed) work.
|
||||
* Vertex: an abstract graph concept.
|
||||
* Reactor: a unit of concurrent execution, lives on its own thread.
|
||||
* Connector: a communication abstraction between Reactors. The reactors can be on the same machine or on different processes.
|
||||
* EventStream: read-end of a connector, is owned by exactly one Reactor/thread.
|
||||
* Channel: write-end of a connector, can be owned (wrote into) by multiple threads.
|
||||
|
||||
## conventions
|
||||
|
||||
1. Locked: A method having a Locked... prefix indicates that you
|
||||
|
@ -8,155 +8,157 @@
|
||||
|
||||
enum class EdgeType { OUTGOING, INCOMING };
|
||||
|
||||
/** A node in the graph. Has incoming and outgoing edges which
|
||||
* are defined as global addresses of other nodes */
|
||||
class Node {
|
||||
/** A vertex in the graph. Has incoming and outgoing edges which
|
||||
* are defined as global addresses of other vertices */
|
||||
class Vertex {
|
||||
public:
|
||||
Node(const GlobalId &id) : id_(id) {}
|
||||
Vertex(const UniqueVid &id) : id_(id) {}
|
||||
|
||||
const auto &id() const { return id_; };
|
||||
const auto &edges_out() const { return edges_out_; }
|
||||
const auto &edges_in() const { return edges_in_; }
|
||||
|
||||
void AddConnection(EdgeType edge_type, const GlobalAddress &gad) {
|
||||
void AddConnection(EdgeType edge_type, const GlobalVertAddress &gad) {
|
||||
(edge_type == EdgeType::INCOMING ? edges_in_ : edges_out_)
|
||||
.emplace_back(gad);
|
||||
}
|
||||
|
||||
/** Changes all old_address edges to have the new_worker */
|
||||
void RedirectEdges(const GlobalAddress old_address, size_t new_worker) {
|
||||
/** Changes all old_address edges to have the new Memgraph node id */
|
||||
void RedirectEdges(const GlobalVertAddress& old_address, int64_t new_mnid) {
|
||||
for (auto &address : edges_in_)
|
||||
if (address == old_address) address.worker_id_ = new_worker;
|
||||
if (address == old_address) address.cur_mnid_ = new_mnid;
|
||||
for (auto &address : edges_out_)
|
||||
if (address == old_address) address.worker_id_ = new_worker;
|
||||
if (address == old_address) address.cur_mnid_ = new_mnid;
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO remove id_ from Node if not necessary
|
||||
GlobalId id_;
|
||||
UniqueVid id_;
|
||||
|
||||
// global addresses of nodes this node is connected to
|
||||
std::vector<GlobalAddress> edges_out_;
|
||||
std::vector<GlobalAddress> edges_in_;
|
||||
// global addresses of vertices this vertex is connected to
|
||||
std::vector<GlobalVertAddress> edges_out_;
|
||||
std::vector<GlobalVertAddress> edges_in_;
|
||||
};
|
||||
|
||||
/** A worker / shard in the distributed system */
|
||||
class Worker {
|
||||
/**
|
||||
* A storage that doesn't assume everything is in-memory.
|
||||
*/
|
||||
class ShardedStorage {
|
||||
public:
|
||||
// unique worker ID. uniqueness is ensured by the worker
|
||||
// owner (the Distributed class)
|
||||
const int64_t id_;
|
||||
// Unique Memgraph node ID. Uniqueness is ensured by the (distributed) system.
|
||||
const int64_t mnid_;
|
||||
|
||||
Worker(int64_t id) : id_(id) {}
|
||||
ShardedStorage(int64_t mnid) : mnid_(mnid) {}
|
||||
|
||||
int64_t NodeCount() const { return nodes_.size(); }
|
||||
int64_t VertexCount() const { return vertices_.size(); }
|
||||
|
||||
/** Gets a node. */
|
||||
Node &GetNode(const GlobalId &gid) {
|
||||
auto found = nodes_.find(gid);
|
||||
assert(found != nodes_.end());
|
||||
/** Gets a vertex. */
|
||||
Vertex &GetVertex(const UniqueVid &gid) {
|
||||
auto found = vertices_.find(gid);
|
||||
assert(found != vertices_.end());
|
||||
return found->second;
|
||||
}
|
||||
|
||||
/** Returns the number of edges that cross from this
|
||||
* graph / worker into another one */
|
||||
/**
|
||||
* Returns the number of edges that cross from this
|
||||
* node into another one
|
||||
*/
|
||||
int64_t BoundaryEdgeCount() const {
|
||||
int64_t count = 0;
|
||||
auto count_f = [this, &count](const auto &edges) {
|
||||
for (const GlobalAddress &address : edges)
|
||||
if (address.worker_id_ != id_) count++;
|
||||
for (const GlobalVertAddress &address : edges)
|
||||
if (address.cur_mnid_ != mnid_) count++;
|
||||
};
|
||||
for (const auto &node : nodes_) {
|
||||
count_f(node.second.edges_out());
|
||||
count_f(node.second.edges_in());
|
||||
for (const auto &vertex : vertices_) {
|
||||
count_f(vertex.second.edges_out());
|
||||
count_f(vertex.second.edges_in());
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Creates a new node on this worker. Returns it's global id */
|
||||
const GlobalId &MakeNode() {
|
||||
GlobalId new_id(id_, next_node_sequence_++);
|
||||
auto new_node = nodes_.emplace(std::make_pair(new_id, Node(new_id)));
|
||||
return new_node.first->first;
|
||||
/** Creates a new vertex on this node. Returns its global id */
|
||||
const UniqueVid &MakeVertex() {
|
||||
UniqueVid new_id(mnid_, next_vertex_sequence_++);
|
||||
auto new_vertex = vertices_.emplace(std::make_pair(new_id, Vertex(new_id)));
|
||||
return new_vertex.first->first;
|
||||
};
|
||||
|
||||
/** Places the existing node on this worker */
|
||||
void PlaceNode(const GlobalId &gid, const Node &node) {
|
||||
nodes_.emplace(gid, node);
|
||||
/** Places the existing vertex on this node */
|
||||
void PlaceVertex(const UniqueVid &gid, const Vertex &vertex) {
|
||||
vertices_.emplace(gid, vertex);
|
||||
}
|
||||
|
||||
/** Removes the node with the given ID from this worker */
|
||||
void RemoveNode(const GlobalId &gid) { nodes_.erase(gid); }
|
||||
/** Removes the vertex with the given ID from this node */
|
||||
void RemoveVertex(const UniqueVid &gid) { vertices_.erase(gid); }
|
||||
|
||||
auto begin() const { return nodes_.begin(); }
|
||||
auto begin() const { return vertices_.begin(); }
|
||||
|
||||
auto end() const { return nodes_.end(); }
|
||||
auto end() const { return vertices_.end(); }
|
||||
|
||||
private:
|
||||
// counter of sequences numbers of nodes created on this worker
|
||||
int64_t next_node_sequence_{0};
|
||||
// counter of sequences numbers of vertices created on this node
|
||||
int64_t next_vertex_sequence_{0};
|
||||
|
||||
// node storage of this worker
|
||||
std::unordered_map<GlobalId, Node> nodes_;
|
||||
// vertex storage of this node
|
||||
std::unordered_map<UniqueVid, Vertex> vertices_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A distributed system consisting of mulitple workers.
|
||||
* A distributed system consisting of mulitple nodes.
|
||||
* For the time being it's not modelling a distributed
|
||||
* system correctly in terms of message passing (as opposed
|
||||
* to operating on workers and their data directly).
|
||||
* to operating on nodes and their data directly).
|
||||
*/
|
||||
class Distributed {
|
||||
public:
|
||||
/** Creates a distributed with the given number of workers */
|
||||
Distributed(int initial_worker_count = 0) {
|
||||
for (int worker_id = 0; worker_id < initial_worker_count; worker_id++)
|
||||
AddWorker();
|
||||
/** Creates a distributed with the given number of nodes */
|
||||
Distributed(int initial_mnode_count = 0) {
|
||||
for (int mnode_id = 0; mnode_id < initial_mnode_count; mnode_id++)
|
||||
AddMnode();
|
||||
}
|
||||
|
||||
int64_t AddWorker() {
|
||||
int64_t new_worker_id = workers_.size();
|
||||
workers_.emplace_back(new_worker_id);
|
||||
return new_worker_id;
|
||||
int64_t AddMnode() {
|
||||
int64_t new_mnode_id = mnodes_.size();
|
||||
mnodes_.emplace_back(new_mnode_id);
|
||||
return new_mnode_id;
|
||||
}
|
||||
|
||||
int WorkerCount() const { return workers_.size(); }
|
||||
int MnodeCount() const { return mnodes_.size(); }
|
||||
|
||||
auto &GetWorker(int64_t worker_id) { return workers_[worker_id]; }
|
||||
auto &GetMnode(int64_t mnode_id) { return mnodes_[mnode_id]; }
|
||||
|
||||
GlobalAddress MakeNode(int64_t worker_id) {
|
||||
return {worker_id, workers_[worker_id].MakeNode()};
|
||||
GlobalVertAddress MakeVertex(int64_t mnid) {
|
||||
return {mnid, mnodes_[mnid].MakeVertex()};
|
||||
}
|
||||
|
||||
Node &GetNode(const GlobalAddress &address) {
|
||||
return workers_[address.worker_id_].GetNode(address.id_);
|
||||
Vertex &GetVertex(const GlobalVertAddress &address) {
|
||||
return mnodes_[address.cur_mnid_].GetVertex(address.uvid_);
|
||||
}
|
||||
|
||||
/** Moves a node with the given global id to the given worker */
|
||||
void MoveNode(const GlobalAddress &gid, int64_t destination) {
|
||||
const Node &node = GetNode(gid);
|
||||
/** Moves a vertex with the given global id to the given mnode */
|
||||
void MoveVertex(const GlobalVertAddress &gad, int64_t destination) {
|
||||
const Vertex &vertex = GetVertex(gad);
|
||||
|
||||
// make sure that all edges to and from the node are updated
|
||||
for (auto &edge : node.edges_in())
|
||||
GetNode(edge).RedirectEdges(gid, destination);
|
||||
for (auto &edge : node.edges_out())
|
||||
GetNode(edge).RedirectEdges(gid, destination);
|
||||
// make sure that all edges to and from the vertex are updated
|
||||
for (auto &edge : vertex.edges_in())
|
||||
GetVertex(edge).RedirectEdges(gad, destination);
|
||||
for (auto &edge : vertex.edges_out())
|
||||
GetVertex(edge).RedirectEdges(gad, destination);
|
||||
|
||||
// change node destination
|
||||
workers_[destination].PlaceNode(gid.id_, node);
|
||||
workers_[gid.worker_id_].RemoveNode(gid.id_);
|
||||
// change vertex destination
|
||||
mnodes_[destination].PlaceVertex(gad.uvid_, vertex);
|
||||
mnodes_[gad.cur_mnid_].RemoveVertex(gad.uvid_);
|
||||
}
|
||||
|
||||
void MakeEdge(const GlobalAddress &from, const GlobalAddress &to) {
|
||||
GetNode(from).AddConnection(EdgeType::OUTGOING, to);
|
||||
GetNode(to).AddConnection(EdgeType::INCOMING, from);
|
||||
void MakeEdge(const GlobalVertAddress &from, const GlobalVertAddress &to) {
|
||||
GetVertex(from).AddConnection(EdgeType::OUTGOING, to);
|
||||
GetVertex(to).AddConnection(EdgeType::INCOMING, from);
|
||||
}
|
||||
|
||||
auto begin() const { return workers_.begin(); }
|
||||
auto begin() const { return mnodes_.begin(); }
|
||||
|
||||
auto end() const { return workers_.end(); }
|
||||
auto end() const { return mnodes_.end(); }
|
||||
|
||||
private:
|
||||
std::vector<Worker> workers_;
|
||||
std::vector<ShardedStorage> mnodes_;
|
||||
};
|
||||
|
@ -36,76 +36,76 @@ auto MaxRandom(const std::vector<double> &scores) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the best (highest scored) worker
|
||||
* for the given node. If there are multiple workers with
|
||||
* the best score, node prefers to remain on the same worker
|
||||
* Returns the index of the best (highest scored) mnode
|
||||
* for the given vertex. If there are multiple mnodes with
|
||||
* the best score, vertex prefers to remain on the same mnode
|
||||
* (if among the best), or one is chosen at random.
|
||||
*
|
||||
* @param distributed - the distributed system.
|
||||
* @param node - the node which is being evaluated.
|
||||
* @param penalties - a vector of penalties (per worker).
|
||||
* @param current_worker - the worker on which the given
|
||||
* node is currently residing.
|
||||
* @param vertex - the vertex which is being evaluated.
|
||||
* @param penalties - a vector of penalties (per mnode).
|
||||
* @param current_mnode - the mnode on which the given
|
||||
* vertex is currently residing.
|
||||
* @return - std::pair<int, std::vector<double>> which is a
|
||||
* pair of (best worker, score_per_worker).
|
||||
* pair of (best mnode, score_per_mnode).
|
||||
*/
|
||||
auto BestWorker(const Distributed &distributed, const Node &node,
|
||||
const std::vector<double> &penalties, int current_worker) {
|
||||
// scores per worker
|
||||
std::vector<double> scores(distributed.WorkerCount(), 0.0);
|
||||
auto BestMnode(const Distributed &distributed, const Vertex &vertex,
|
||||
const std::vector<double> &penalties, int current_mnode) {
|
||||
// scores per mnode
|
||||
std::vector<double> scores(distributed.MnodeCount(), 0.0);
|
||||
|
||||
for (auto &edge : node.edges_in()) scores[edge.worker_id_] += 1.0;
|
||||
for (auto &edge : node.edges_out()) scores[edge.worker_id_] += 1.0;
|
||||
for (auto &edge : vertex.edges_in()) scores[edge.cur_mnid_] += 1.0;
|
||||
for (auto &edge : vertex.edges_out()) scores[edge.cur_mnid_] += 1.0;
|
||||
|
||||
for (int worker = 0; worker < distributed.WorkerCount(); ++worker) {
|
||||
// normalize contribution of worker over neighbourhood size
|
||||
scores[worker] /= node.edges_out().size() + node.edges_in().size();
|
||||
for (int mnode = 0; mnode < distributed.MnodeCount(); ++mnode) {
|
||||
// normalize contribution of mnode over neighbourhood size
|
||||
scores[mnode] /= vertex.edges_out().size() + vertex.edges_in().size();
|
||||
// add balancing penalty
|
||||
scores[worker] -= penalties[worker];
|
||||
scores[mnode] -= penalties[mnode];
|
||||
}
|
||||
|
||||
// pick the best destination, but prefer to stay if you can
|
||||
size_t destination = MaxRandom(scores);
|
||||
if (scores[current_worker] == scores[destination])
|
||||
destination = current_worker;
|
||||
if (scores[current_mnode] == scores[destination])
|
||||
destination = current_mnode;
|
||||
|
||||
return std::make_pair(destination, scores);
|
||||
}
|
||||
|
||||
/** Indication if Spinner worker penality is calculated based on
|
||||
* vertex or edge worker cardinalities */
|
||||
/** Indication if Spinner mnode penality is calculated based on
|
||||
* vertex or edge mnode cardinalities */
|
||||
enum class PenaltyType { Vertex, Edge };
|
||||
|
||||
/** Calcualtes Spinner penalties for workers in the given
|
||||
/** Calcualtes Spinner penalties for mnodes in the given
|
||||
* distributed system. */
|
||||
auto Penalties(const Distributed &distributed,
|
||||
PenaltyType penalty_type = PenaltyType::Edge) {
|
||||
std::vector<double> penalties;
|
||||
int64_t total_count{0};
|
||||
|
||||
for (const auto &worker : distributed) {
|
||||
int64_t worker_count{0};
|
||||
for (const auto &mnode : distributed) {
|
||||
int64_t mnode_count{0};
|
||||
switch (penalty_type) {
|
||||
case PenaltyType::Vertex:
|
||||
worker_count += worker.NodeCount();
|
||||
mnode_count += mnode.VertexCount();
|
||||
break;
|
||||
case PenaltyType::Edge:
|
||||
for (const auto &node_kv : worker) {
|
||||
// Spinner counts the edges on a worker as the sum
|
||||
// of degrees of nodes on that worker. In that sense
|
||||
for (const auto &vertex_kv : mnode) {
|
||||
// Spinner counts the edges on a mnode as the sum
|
||||
// of degrees of vertices on that mnode. In that sense
|
||||
// both incoming and outgoing edges are individually
|
||||
// added...
|
||||
worker_count += node_kv.second.edges_out().size();
|
||||
worker_count += node_kv.second.edges_in().size();
|
||||
mnode_count += vertex_kv.second.edges_out().size();
|
||||
mnode_count += vertex_kv.second.edges_in().size();
|
||||
}
|
||||
break;
|
||||
}
|
||||
total_count += worker_count;
|
||||
penalties.emplace_back(worker_count);
|
||||
total_count += mnode_count;
|
||||
penalties.emplace_back(mnode_count);
|
||||
}
|
||||
|
||||
for (auto &penalty : penalties)
|
||||
penalty /= c * total_count / distributed.WorkerCount();
|
||||
penalty /= c * total_count / distributed.MnodeCount();
|
||||
|
||||
return penalties;
|
||||
}
|
||||
@ -117,31 +117,31 @@ void PerformSpinnerStep(Distributed &distributed) {
|
||||
// here a strategy can be injected for limiting
|
||||
// the number of movements performed in one step.
|
||||
// limiting could be based on (for example):
|
||||
// - limiting the number of movements per worker
|
||||
// - limiting the number of movements per mnode
|
||||
// - limiting only to movements that are above
|
||||
// a treshold (score improvement or something)
|
||||
// - not executing on all the workers (also prevents
|
||||
// - not executing on all the mnodes (also prevents
|
||||
// oscilations)
|
||||
//
|
||||
// in the first implementation just accumulate all
|
||||
// the movements and execute together.
|
||||
|
||||
// relocation info: contains the address of the Node
|
||||
// that needs to relocate and it's destination worker
|
||||
std::vector<std::pair<GlobalAddress, int>> movements;
|
||||
// relocation info: contains the address of the Vertex
|
||||
// that needs to relocate and it's destination mnode
|
||||
std::vector<std::pair<GlobalVertAddress, int>> movements;
|
||||
|
||||
for (const Worker &worker : distributed)
|
||||
for (const auto &gid_node_pair : worker) {
|
||||
// (best destination, scores) pair for node
|
||||
for (const ShardedStorage &mnode : distributed)
|
||||
for (const auto &gid_vertex_pair : mnode) {
|
||||
// (best destination, scores) pair for vertex
|
||||
std::pair<int, std::vector<double>> destination_scores =
|
||||
BestWorker(distributed, gid_node_pair.second, penalties, worker.id_);
|
||||
if (destination_scores.first != worker.id_)
|
||||
movements.emplace_back(GlobalAddress(worker.id_, gid_node_pair.first),
|
||||
BestMnode(distributed, gid_vertex_pair.second, penalties, mnode.mnid_);
|
||||
if (destination_scores.first != mnode.mnid_)
|
||||
movements.emplace_back(GlobalVertAddress(mnode.mnid_, gid_vertex_pair.first),
|
||||
destination_scores.first);
|
||||
}
|
||||
|
||||
// execute movements. it is likely that in the real system
|
||||
// this will need to happen as a single db transaction
|
||||
for (const auto &m : movements) distributed.MoveNode(m.first, m.second);
|
||||
for (const auto &m : movements) distributed.MoveVertex(m.first, m.second);
|
||||
}
|
||||
} // namespace spinner
|
||||
|
@ -1,62 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/hashing/fnv.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
/** A globally defined identifier. Defines a worker
|
||||
* and the sequence number on that worker */
|
||||
class GlobalId {
|
||||
/**
|
||||
* Globally unique id (in the entire distributed system) of a vertex.
|
||||
*
|
||||
* It is identified by a pair of a (original memgraph node, local vertex id)
|
||||
*/
|
||||
class UniqueVid {
|
||||
public:
|
||||
GlobalId(int64_t worker_id, int64_t sequence_number)
|
||||
: worker_id_(worker_id), sequence_number_(sequence_number) {}
|
||||
// TODO perhaps make members const and replace instead of changing
|
||||
// when migrating nodes
|
||||
int64_t worker_id_;
|
||||
int64_t sequence_number_;
|
||||
UniqueVid(int64_t orig_mnid, int64_t vid)
|
||||
: orig_mnid_(orig_mnid), vid_(vid) {}
|
||||
/** Original Memgraph node the vertex was created **/
|
||||
int64_t orig_mnid_;
|
||||
|
||||
bool operator==(const GlobalId &other) const {
|
||||
return worker_id_ == other.worker_id_ &&
|
||||
sequence_number_ == other.sequence_number_;
|
||||
/** Original vertex id it was assigned on creation. **/
|
||||
int64_t vid_;
|
||||
|
||||
bool operator==(const UniqueVid &other) const {
|
||||
return orig_mnid_ == other.orig_mnid_ &&
|
||||
vid_ == other.vid_;
|
||||
}
|
||||
|
||||
bool operator!=(const GlobalId &other) const { return !(*this == other); }
|
||||
bool operator!=(const UniqueVid &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
/** Defines a location in the system where something can be found.
|
||||
* Something can be found on some worker, for some Id */
|
||||
class GlobalAddress {
|
||||
/**
|
||||
* Specifies where a vertex is in the distributed system.
|
||||
*/
|
||||
class GlobalVertAddress {
|
||||
public:
|
||||
GlobalAddress(int64_t worker_id, GlobalId id)
|
||||
: worker_id_(worker_id), id_(id) {}
|
||||
// TODO perhaps make members const and replace instead of changing
|
||||
// when migrating nodes
|
||||
int64_t worker_id_;
|
||||
GlobalId id_;
|
||||
GlobalVertAddress(int64_t cur_mnid, const UniqueVid &uvid)
|
||||
: cur_mnid_(cur_mnid), uvid_(uvid) {}
|
||||
|
||||
bool operator==(const GlobalAddress &other) const {
|
||||
return worker_id_ == other.worker_id_ && id_ == other.id_;
|
||||
/** The current Memgraph node where the vertex is **/
|
||||
int64_t cur_mnid_;
|
||||
UniqueVid uvid_;
|
||||
|
||||
bool operator==(const GlobalVertAddress &other) const {
|
||||
return cur_mnid_ == other.cur_mnid_ && uvid_ == other.uvid_;
|
||||
}
|
||||
|
||||
bool operator!=(const GlobalAddress &other) const {
|
||||
bool operator!=(const GlobalVertAddress &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<GlobalId> {
|
||||
size_t operator()(const GlobalId &id) const {
|
||||
return id.sequence_number_ << 4 ^ id.worker_id_;
|
||||
struct hash<UniqueVid> {
|
||||
size_t operator()(const UniqueVid &uid) const {
|
||||
return HashCombine<decltype(uid.orig_mnid_), decltype(uid.vid_)>()(uid.orig_mnid_, uid.vid_);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<GlobalAddress> {
|
||||
size_t operator()(const GlobalAddress &ga) const {
|
||||
return gid_hash(ga.id_) << 4 ^ ga.worker_id_;
|
||||
struct hash<GlobalVertAddress> {
|
||||
size_t operator()(const GlobalVertAddress &ga) const {
|
||||
return HashCombine<decltype(ga.cur_mnid_), decltype(ga.uvid_)>()(ga.cur_mnid_, ga.uvid_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::hash<GlobalId> gid_hash{};
|
||||
};
|
||||
}
|
||||
|
@ -5,63 +5,63 @@
|
||||
#include "graph.hpp"
|
||||
|
||||
void test_global_id() {
|
||||
GlobalId a(1, 1);
|
||||
assert(a == GlobalId(1, 1));
|
||||
assert(a != GlobalId(1, 2));
|
||||
assert(a != GlobalId(2, 1));
|
||||
UniqueVid a(1, 1);
|
||||
assert(a == UniqueVid(1, 1));
|
||||
assert(a != UniqueVid(1, 2));
|
||||
assert(a != UniqueVid(2, 1));
|
||||
}
|
||||
|
||||
void test_global_address() {
|
||||
GlobalAddress a(1, {1, 1});
|
||||
assert(a == GlobalAddress(1, {1, 1}));
|
||||
assert(a != GlobalAddress(2, {1, 1}));
|
||||
assert(a != GlobalAddress(1, {2, 1}));
|
||||
GlobalVertAddress a(1, {1, 1});
|
||||
assert(a == GlobalVertAddress(1, {1, 1}));
|
||||
assert(a != GlobalVertAddress(2, {1, 1}));
|
||||
assert(a != GlobalVertAddress(1, {2, 1}));
|
||||
}
|
||||
|
||||
void test_worker() {
|
||||
Worker worker0{0};
|
||||
assert(worker0.NodeCount() == 0);
|
||||
GlobalId n0 = worker0.MakeNode();
|
||||
assert(worker0.NodeCount() == 1);
|
||||
void test_mnode() {
|
||||
ShardedStorage mnode0{0};
|
||||
assert(mnode0.VertexCount() == 0);
|
||||
UniqueVid n0 = mnode0.MakeVertex();
|
||||
assert(mnode0.VertexCount() == 1);
|
||||
|
||||
Worker worker1{1};
|
||||
worker1.PlaceNode(n0, worker0.GetNode(n0));
|
||||
worker0.RemoveNode(n0);
|
||||
assert(worker0.NodeCount() == 0);
|
||||
assert(worker1.NodeCount() == 1);
|
||||
ShardedStorage mnode1{1};
|
||||
mnode1.PlaceVertex(n0, mnode0.GetVertex(n0));
|
||||
mnode0.RemoveVertex(n0);
|
||||
assert(mnode0.VertexCount() == 0);
|
||||
assert(mnode1.VertexCount() == 1);
|
||||
|
||||
worker1.MakeNode();
|
||||
assert(worker1.NodeCount() == 2);
|
||||
assert(std::distance(worker1.begin(), worker1.end()) == 2);
|
||||
mnode1.MakeVertex();
|
||||
assert(mnode1.VertexCount() == 2);
|
||||
assert(std::distance(mnode1.begin(), mnode1.end()) == 2);
|
||||
}
|
||||
|
||||
void test_distributed() {
|
||||
Distributed d;
|
||||
assert(d.WorkerCount() == 0);
|
||||
auto w0 = d.AddWorker();
|
||||
assert(d.WorkerCount() == 1);
|
||||
auto w1 = d.AddWorker();
|
||||
assert(d.WorkerCount() == 2);
|
||||
assert(d.MnodeCount() == 0);
|
||||
auto w0 = d.AddMnode();
|
||||
assert(d.MnodeCount() == 1);
|
||||
auto w1 = d.AddMnode();
|
||||
assert(d.MnodeCount() == 2);
|
||||
|
||||
GlobalAddress n0 = d.MakeNode(w0);
|
||||
assert(d.GetWorker(w0).NodeCount() == 1);
|
||||
GlobalAddress n1 = d.MakeNode(w1);
|
||||
GlobalVertAddress n0 = d.MakeVertex(w0);
|
||||
assert(d.GetMnode(w0).VertexCount() == 1);
|
||||
GlobalVertAddress n1 = d.MakeVertex(w1);
|
||||
|
||||
assert(d.GetNode(n0).edges_out().size() == 0);
|
||||
assert(d.GetNode(n0).edges_in().size() == 0);
|
||||
assert(d.GetNode(n1).edges_out().size() == 0);
|
||||
assert(d.GetNode(n1).edges_in().size() == 0);
|
||||
assert(d.GetVertex(n0).edges_out().size() == 0);
|
||||
assert(d.GetVertex(n0).edges_in().size() == 0);
|
||||
assert(d.GetVertex(n1).edges_out().size() == 0);
|
||||
assert(d.GetVertex(n1).edges_in().size() == 0);
|
||||
d.MakeEdge(n0, n1);
|
||||
assert(d.GetNode(n0).edges_out().size() == 1);
|
||||
assert(d.GetNode(n0).edges_in().size() == 0);
|
||||
assert(d.GetNode(n1).edges_out().size() == 0);
|
||||
assert(d.GetNode(n1).edges_in().size() == 1);
|
||||
assert(d.GetVertex(n0).edges_out().size() == 1);
|
||||
assert(d.GetVertex(n0).edges_in().size() == 0);
|
||||
assert(d.GetVertex(n1).edges_out().size() == 0);
|
||||
assert(d.GetVertex(n1).edges_in().size() == 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_global_id();
|
||||
test_global_address();
|
||||
test_worker();
|
||||
test_mnode();
|
||||
test_distributed();
|
||||
std::cout << "All tests passed" << std::endl;
|
||||
}
|
||||
|
@ -12,16 +12,16 @@
|
||||
void PrintStatistics(const Distributed &distributed) {
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
for (const Worker &worker : distributed) {
|
||||
cout << " Worker " << worker.id_ << ":";
|
||||
cout << " #nodes = " << worker.NodeCount();
|
||||
for (const ShardedStorage &mnode : distributed) {
|
||||
cout << " ShardedStorage " << mnode.mnid_ << ":";
|
||||
cout << " #vertices = " << mnode.VertexCount();
|
||||
int64_t edge_count{0};
|
||||
for (const auto &gid_node_pair : worker) {
|
||||
edge_count += gid_node_pair.second.edges_in().size();
|
||||
edge_count += gid_node_pair.second.edges_out().size();
|
||||
for (const auto &gid_vertex_pair : mnode) {
|
||||
edge_count += gid_vertex_pair.second.edges_in().size();
|
||||
edge_count += gid_vertex_pair.second.edges_out().size();
|
||||
}
|
||||
cout << ", #edges = " << edge_count;
|
||||
cout << ", #cuts = " << worker.BoundaryEdgeCount() << endl;
|
||||
cout << ", #cuts = " << mnode.BoundaryEdgeCount() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,8 +33,8 @@ void PrintStatistics(const Distributed &distributed) {
|
||||
* https://snap.stanford.edu/data/facebook_combined.txt.gz
|
||||
* add number of vertices and edges in the first line of that file
|
||||
*/
|
||||
Distributed ReadGraph(std::string filename, int worker_count) {
|
||||
Distributed distributed(worker_count);
|
||||
Distributed ReadGraph(std::string filename, int mnode_count) {
|
||||
Distributed distributed(mnode_count);
|
||||
|
||||
std::fstream fs;
|
||||
fs.open(filename, std::fstream::in);
|
||||
@ -43,17 +43,17 @@ Distributed ReadGraph(std::string filename, int worker_count) {
|
||||
int vertex_count, edge_count;
|
||||
fs >> vertex_count >> edge_count;
|
||||
|
||||
// assign vertices to random workers
|
||||
std::vector<GlobalAddress> nodes;
|
||||
// assign vertices to random mnodes
|
||||
std::vector<GlobalVertAddress> vertices;
|
||||
for (int i = 0; i < vertex_count; ++i)
|
||||
nodes.emplace_back(distributed.MakeNode(rand() % worker_count));
|
||||
vertices.emplace_back(distributed.MakeVertex(rand() % mnode_count));
|
||||
|
||||
// add edges
|
||||
for (int i = 0; i < edge_count; ++i) {
|
||||
size_t u, v;
|
||||
fs >> u >> v;
|
||||
assert(u < nodes.size() && v < nodes.size());
|
||||
distributed.MakeEdge(nodes[u], nodes[v]);
|
||||
assert(u < vertices.size() && v < vertices.size());
|
||||
distributed.MakeEdge(vertices[u], vertices[v]);
|
||||
}
|
||||
fs.close();
|
||||
return distributed;
|
||||
|
@ -65,3 +65,16 @@ struct FnvCollection {
|
||||
private:
|
||||
static const uint64_t fnv_prime = 1099511628211u;
|
||||
};
|
||||
|
||||
template<typename TA, typename TB>
|
||||
struct HashCombine {
|
||||
size_t operator()(const TA& a, const TB& b) const {
|
||||
constexpr size_t fnv_prime = 1099511628211UL;
|
||||
constexpr size_t fnv_offset = 14695981039346656037UL;
|
||||
size_t ret = fnv_offset;
|
||||
ret ^= std::hash<TA>()(a);
|
||||
ret *= fnv_prime;
|
||||
ret ^= std::hash<TB>()(b);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user