Skip to content

Commit

Permalink
Optimize SemistaticGraph using shallow copy semantics to share data w…
Browse files Browse the repository at this point in the history
…ith the NormalizedComponent. Decreases injection time by ~15%.
  • Loading branch information
poletti-marco committed Nov 9, 2014
1 parent 5c1bf6d commit 050d600
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 134 deletions.
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()

add_definitions("-std=c++11 -W -Wall")
add_definitions(-std=c++11 -W -Wall)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
add_definitions("-g -O2 -Werror -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -ftemplate-backtrace-limit=0")
add_definitions(-g -O2 -Werror -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -ftemplate-backtrace-limit=0)
endif()

include_directories(${CMAKE_SOURCE_DIR}/include)

#set(CMAKE_CXX_COMPILER "/usr/bin/clang++")

# Unsafe, only for debugging/benchmarking.
#add_definitions("-DFRUIT_NO_LOOP_CHECK")
#add_definitions(-DFRUIT_NO_LOOP_CHECK)


set(INSTALL_LIBRARY_DIR lib CACHE PATH "Installation directory for libraries")
Expand Down
70 changes: 22 additions & 48 deletions include/fruit/impl/data_structures/semistatic_graph.defn.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,20 @@ inline SemistaticGraph<NodeId, Node>::node_iterator::node_iterator(typename std:

template <typename NodeId, typename Node>
inline Node& SemistaticGraph<NodeId, Node>::node_iterator::getNode() {
assert(itr->edgesBeginOffset != invalidEdgesBeginOffset);
assert(itr->edgesBegin != 1);
return itr->node;
}

template <typename NodeId, typename Node>
inline bool SemistaticGraph<NodeId, Node>::node_iterator::isTerminal() {
assert(itr->edgesBeginOffset != invalidEdgesBeginOffset);
return itr->edgesBeginOffset == 0;
assert(itr->edgesBegin != 1);
return itr->edgesBegin == 0;
}

template <typename NodeId, typename Node>
inline void SemistaticGraph<NodeId, Node>::node_iterator::setTerminal() {
assert(itr->edgesBeginOffset != invalidEdgesBeginOffset);
itr->edgesBeginOffset = 0;
assert(itr->edgesBegin != 1);
itr->edgesBegin = 0;
}

template <typename NodeId, typename Node>
Expand All @@ -60,19 +60,19 @@ inline SemistaticGraph<NodeId, Node>::const_node_iterator::const_node_iterator(

template <typename NodeId, typename Node>
inline const Node& SemistaticGraph<NodeId, Node>::const_node_iterator::getNode() {
assert(itr->edgesBeginOffset != invalidEdgesBeginOffset);
assert(itr->edgesBegin != 1);
return itr->node;
}

template <typename NodeId, typename Node>
inline bool SemistaticGraph<NodeId, Node>::const_node_iterator::isTerminal() {
assert(itr->edgesBeginOffset != invalidEdgesBeginOffset);
assert(itr->edgesBegin != 1);
return itr->edgesBeginOffset == 0;
}

template <typename NodeId, typename Node>
inline void SemistaticGraph<NodeId, Node>::const_node_iterator::setTerminal() {
assert(itr->edgesBeginOffset != invalidEdgesBeginOffset);
assert(itr->edgesBegin != 1);
itr->edgesBeginOffset = 0;
}

Expand All @@ -83,9 +83,10 @@ inline bool SemistaticGraph<NodeId, Node>::const_node_iterator::operator==(const


template <typename NodeId, typename Node>
inline typename SemistaticGraph<NodeId, Node>::edge_iterator SemistaticGraph<NodeId, Node>::node_iterator::neighborsBegin(
SemistaticGraph<NodeId, Node>& graph) {
return edge_iterator{graph.edgesStorage.data() + itr->edgesBeginOffset};
inline typename SemistaticGraph<NodeId, Node>::edge_iterator SemistaticGraph<NodeId, Node>::node_iterator::neighborsBegin() {
assert(itr->edgesBegin != 0);
assert(itr->edgesBegin != 1);
return edge_iterator{reinterpret_cast<InternalNodeId*>(itr->edgesBegin)};
}

template <typename NodeId, typename Node>
Expand Down Expand Up @@ -133,7 +134,7 @@ inline typename SemistaticGraph<NodeId, Node>::const_node_iterator SemistaticGra
return const_node_iterator{nodes.end()};
} else {
auto itr = nodes.begin() + internalNodeIdPtr->id;
if (itr->edgesBeginOffset == invalidEdgesBeginOffset) {
if (itr->edgesBegin == 1) {
return const_node_iterator{nodes.end()};
}
return const_node_iterator{itr};
Expand All @@ -147,7 +148,7 @@ inline typename SemistaticGraph<NodeId, Node>::node_iterator SemistaticGraph<Nod
return node_iterator{nodes.end()};
} else {
auto itr = nodes.begin() + internalNodeIdPtr->id;
if (itr->edgesBeginOffset == invalidEdgesBeginOffset) {
if (itr->edgesBegin == 1) {
return node_iterator{nodes.end()};
}
return node_iterator{itr};
Expand All @@ -156,6 +157,7 @@ inline typename SemistaticGraph<NodeId, Node>::node_iterator SemistaticGraph<Nod

template <typename NodeId, typename Node>
inline SemistaticGraphInternalNodeId SemistaticGraph<NodeId, Node>::getOrAllocateInternalNodeId(NodeId nodeId) {
// TODO: Optimize this, do a single lookup.
InternalNodeId* internalNodeIdPtr = nodeIndexMap.find(nodeId);
if (internalNodeIdPtr != nullptr) {
return *internalNodeIdPtr;
Expand All @@ -164,49 +166,21 @@ inline SemistaticGraphInternalNodeId SemistaticGraph<NodeId, Node>::getOrAllocat
std::size_t nodeIndex = nodes.size();
nodeIndexMap.insert(nodeId, InternalNodeId{nodeIndex}, [](InternalNodeId x, InternalNodeId){ return x;});
nodes.push_back(NodeData{
#ifndef NDEBUG
#ifdef FRUIT_EXTRA_DEBUG
nodeId,
#endif
Node(), ~std::size_t(0)});
Node(), 1});
return InternalNodeId{nodeIndex};
}
}

template <typename NodeId, typename Node>
template <typename EdgeIter, typename Combine>
void SemistaticGraph<NodeId, Node>::setNode(NodeId nodeId, Node node, EdgeIter edgesBegin, EdgeIter edgesEnd, Combine combine) {
InternalNodeId internalNodeId = getOrAllocateInternalNodeId(nodeId);
NodeData& nodeData = nodes[internalNodeId.id];
#ifndef NDEBUG
nodeData.key = nodeId;
#endif
if (nodeData.edgesBeginOffset == ~std::size_t(0)) {
// The node did not exist.
nodeData.node = node;
} else {
nodeData.node = combine(nodeData.node, node);
}
nodeData.edgesBeginOffset = edgesStorage.size();
for (EdgeIter i = edgesBegin; i != edgesEnd; ++i) {
edgesStorage.push_back(getOrAllocateInternalNodeId(*i));
}
}

template <typename NodeId, typename Node>
template <typename Combine>
void SemistaticGraph<NodeId, Node>::setTerminalNode(NodeId nodeId, Node node, Combine combine) {
InternalNodeId internalNodeId = getOrAllocateInternalNodeId(nodeId);
void SemistaticGraph<NodeId, Node>::changeNodeToTerminal(NodeId nodeId) {
assert(nodeIndexMap.find(nodeId) != nullptr);
InternalNodeId internalNodeId = nodeIndexMap.at(nodeId);
NodeData& nodeData = nodes[internalNodeId.id];
#ifndef NDEBUG
nodeData.key = nodeId;
#endif
if (nodeData.edgesBeginOffset == ~std::size_t(0)) {
// The node did not exist.
nodeData.node = node;
} else {
nodeData.node = combine(nodeData.node, node);
}
nodeData.edgesBeginOffset = 0;
assert(nodeData.edgesBegin != 1);
nodeData.edgesBegin = 0;
}

} // namespace impl
Expand Down
43 changes: 25 additions & 18 deletions include/fruit/impl/data_structures/semistatic_graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

#include "semistatic_map.h"

#ifdef FRUIT_EXTRA_DEBUG
#include <iostream>
#endif

namespace fruit {
namespace impl {

Expand Down Expand Up @@ -50,20 +54,20 @@ class SemistaticGraph {
// nodeIndexMap contains all known NodeIds, including ones known only due to an outgoing edge ending there from another node.
SemistaticMap<NodeId, InternalNodeId> nodeIndexMap;

static constexpr std::size_t invalidEdgesBeginOffset = ~std::size_t(0);

struct NodeData {
#ifndef NDEBUG
#ifdef FRUIT_EXTRA_DEBUG
NodeId key;
#endif

Node node;
// (edgesStorage.begin() + edgesBeginOffset) is the beginning of the edges range.
// If edgesBeginOffset==0, this is a terminal node.
// If edgesBeginOffset==invalidEdgesBeginOffset, this node doesn't exist, it's just referenced by another node.
std::size_t edgesBeginOffset;
// If edgesBeginOffset==1, this node doesn't exist, it's just referenced by another node.
// Otherwise, reinterpret_cast<InternalNodeId*>(edgesBegin) is the beginning of the edges range.
std::uintptr_t edgesBegin;
};

std::size_t firstUnusedIndex;

std::vector<NodeData> nodes;

// Stores vectors of dependencies as contiguous chunks of elements.
Expand All @@ -73,6 +77,9 @@ class SemistaticGraph {

InternalNodeId getOrAllocateInternalNodeId(NodeId nodeId);

// TODO: Remove.
void printEdgesBegin(std::ostream& os, std::uintptr_t edgesBegin);

public:

class edge_iterator;
Expand All @@ -95,7 +102,7 @@ class SemistaticGraph {

// Assumes !isTerminal().
// neighborsEnd() is NOT provided/stored for efficiency, the client code is expected to know the number of neighbors.
edge_iterator neighborsBegin(SemistaticGraph<NodeId, Node>& graph);
edge_iterator neighborsBegin();

bool operator==(const node_iterator&) const;
};
Expand Down Expand Up @@ -159,6 +166,14 @@ class SemistaticGraph {
SemistaticGraph(SemistaticGraph&&) = default;
SemistaticGraph(const SemistaticGraph&) = default;

// Creates a copy of x with the additional nodes in [first, last). The requirements on NodeIter as the same as for the 2-arg
// constructor.
// The nodes in [first, last) must NOT be already in x, but can be neighbors of nodes in x.
// The new graph will share data with `x', so must be destroyed before `x' is destroyed.
// Also, after this is called, `x' must not be modified until this object has been destroyed.
template <typename NodeIter>
SemistaticGraph(const SemistaticGraph& x, NodeIter first, NodeIter last);

SemistaticGraph& operator=(const SemistaticGraph&) = default;
SemistaticGraph& operator=(SemistaticGraph&&) = default;

Expand All @@ -174,18 +189,10 @@ class SemistaticGraph {
node_iterator find(NodeId nodeId);
const_node_iterator find(NodeId nodeId) const;

// Sets nodeId as a non-terminal node with outgoing edges [edgesBegin, edgesEnd).
// If the node already exists, combine(oldNode, newNode) is called and the result (also of type Node) is used as the node value.
template <typename EdgeIter, typename Combine>
void setNode(NodeId nodeId, Node node, EdgeIter edgesBegin, EdgeIter edgesEnd, Combine combine);

// Sets nodeId as a terminal node.
// If the node already exists, combine(oldNode, newNode) is called and the result (also of type Node) is used as the node value.
// For cases where the node is known to exist, use the setTerminal() method on an iterator, it's faster.
template <typename Combine>
void setTerminalNode(NodeId nodeId, Node node, Combine combine);
// Changes the node with ID nodeId (that must exist) to a terminal node.
void changeNodeToTerminal(NodeId nodeId);

#ifndef NDEBUG
#ifdef FRUIT_EXTRA_DEBUG
// Emits a runtime error if some node was not created but there is an edge pointing to it.
void checkFullyConstructed();
#endif
Expand Down
Loading

0 comments on commit 050d600

Please sign in to comment.