Commit 0ababf9c authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Disable internal bounds checking in property maps

This includes a new vector property map type (fast_vector_property_map)
which has optional disabling of bounds checking, through its associate
map type (unchecked_fast_vector_property_map). This should improve
performance on algorithms which depend on tight loops which access
property maps.

Bounds checking is only disabled locally just before the algorithms run,
and proper care is taken for bounds checking _beforehand_. The property
maps exposed to python still have internal bounds checking.
parent 64018e6f
// Copyright (C) Vladimir Prus 2003.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/graph/vector_property_map.html for
// documentation.
//
#ifndef VECTOR_PROPERTY_MAP_HPP_VP_2003_03_04
#define VECTOR_PROPERTY_MAP_HPP_VP_2003_03_04
#include <boost/property_map.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>
namespace boost {
template<typename T, typename IndexMap = identity_property_map>
class vector_property_map
: public boost::put_get_helper<
typename std::iterator_traits<
typename std::vector<T>::iterator >::reference,
vector_property_map<T, IndexMap> >
{
public:
typedef typename property_traits<IndexMap>::key_type key_type;
typedef T value_type;
typedef typename std::iterator_traits<
typename std::vector<T>::iterator >::reference reference;
typedef boost::lvalue_property_map_tag category;
vector_property_map(const IndexMap& index = IndexMap())
: store(new std::vector<T>()), index(index)
{}
vector_property_map(unsigned initial_size,
const IndexMap& index = IndexMap())
: store(new std::vector<T>(initial_size)), index(index)
{}
typename std::vector<T>::iterator storage_begin()
{
return store->begin();
}
typename std::vector<T>::iterator storage_end()
{
return store->end();
}
typename std::vector<T>::const_iterator storage_begin() const
{
return store->begin();
}
typename std::vector<T>::const_iterator storage_end() const
{
return store->end();
}
void reserve(size_t size)
{
if (store->size() < size)
store->resize(size);
}
std::vector<T>& get_storage() { return (*store); }
public:
// Copy ctor absent, default semantics is OK.
// Assignment operator absent, default semantics is OK.
// CONSIDER: not sure that assignment to 'index' is correct.
reference operator[](const key_type& v) const {
typename property_traits<IndexMap>::value_type i = get(index, v);
if (static_cast<unsigned>(i) >= store->size()) {
store->resize(i + 1, T());
}
return (*store)[i];
}
private:
// Conceptually, we have a vector of infinite size. For practical
// purposes, we start with an empty vector and grow it as needed.
// Note that we cannot store pointer to vector here -- we cannot
// store pointer to data, because if copy of property map resizes
// the vector, the pointer to data will be invalidated.
// I wonder if class 'pmap_ref' is simply needed.
shared_ptr< std::vector<T> > store;
IndexMap index;
};
template<typename T, typename IndexMap>
vector_property_map<T, IndexMap>
make_vector_property_map(IndexMap index)
{
return vector_property_map<T, IndexMap>(index);
}
}
#endif
...@@ -46,6 +46,7 @@ libgraph_tool_core_la_include_HEADERS = \ ...@@ -46,6 +46,7 @@ libgraph_tool_core_la_include_HEADERS = \
histogram.hh \ histogram.hh \
mpl_nested_loop.hh \ mpl_nested_loop.hh \
shared_map.hh \ shared_map.hh \
fast_vector_property_map.hh \
numpy_bind.hh \ numpy_bind.hh \
../../config.h ../../config.h
......
...@@ -33,12 +33,13 @@ using namespace graph_tool; ...@@ -33,12 +33,13 @@ using namespace graph_tool;
template <class PropertySequence> template <class PropertySequence>
struct prop_vector struct prop_vector
{ {
boost::any operator()(const vector<boost::any>& props) const boost::any operator()(const vector<boost::any>& props, size_t size) const
{ {
boost::any prop_vec; boost::any prop_vec;
mpl::for_each<PropertySequence> mpl::for_each<PropertySequence>
(lambda::bind<void>(get_prop_vector(), lambda::_1, (lambda::bind<void>(get_prop_vector(), lambda::_1,
lambda::var(props), lambda::var(prop_vec))); lambda::var(props), lambda::var(prop_vec),
size));
return prop_vec; return prop_vec;
} }
...@@ -46,16 +47,17 @@ struct prop_vector ...@@ -46,16 +47,17 @@ struct prop_vector
{ {
template <class Property> template <class Property>
void operator()(Property, const vector<boost::any>& props, void operator()(Property, const vector<boost::any>& props,
boost::any& prop_vec) const boost::any& prop_vec, size_t size) const
{ {
if (typeid(Property) == props[0].type()) if (typeid(Property) == props[0].type())
{ {
try try
{ {
vector<Property> vec; vector<typename Property::unchecked_t> vec;
vec.resize(props.size()); vec.resize(props.size());
for (size_t i = 0; i < props.size(); ++i) for (size_t i = 0; i < props.size(); ++i)
vec[i] = any_cast<Property>(props[i]); vec[i] =
any_cast<Property>(props[i]).get_unchecked(size);
prop_vec = vec; prop_vec = vec;
} }
catch (bad_any_cast){} catch (bad_any_cast){}
...@@ -70,7 +72,7 @@ struct get_property_vector_type ...@@ -70,7 +72,7 @@ struct get_property_vector_type
template <class Property> template <class Property>
struct apply struct apply
{ {
typedef vector<Property> type; typedef vector<typename Property::unchecked_t> type;
}; };
}; };
...@@ -80,7 +82,9 @@ void extended_clustering(GraphInterface& g, python::list props) ...@@ -80,7 +82,9 @@ void extended_clustering(GraphInterface& g, python::list props)
for (size_t i = 0; i < cmaps.size(); ++i) for (size_t i = 0; i < cmaps.size(); ++i)
cmaps[i] = python::extract<boost::any>(props[i])(); cmaps[i] = python::extract<boost::any>(props[i])();
boost::any vprop = prop_vector<vertex_scalar_properties>()(cmaps); boost::any vprop =
prop_vector<writable_vertex_scalar_properties>()
(cmaps, num_vertices(g.GetGraph()));
if (vprop.empty()) if (vprop.empty())
throw GraphException("all vertex properties must be of the same" throw GraphException("all vertex properties must be of the same"
" floating point type"); " floating point type");
......
...@@ -38,12 +38,6 @@ void community_network(GraphInterface& gi, GraphInterface& cgi, ...@@ -38,12 +38,6 @@ void community_network(GraphInterface& gi, GraphInterface& cgi,
boost::any community_property, boost::any vertex_count, boost::any community_property, boost::any vertex_count,
boost::any edge_count, boost::any weight) boost::any edge_count, boost::any weight)
{ {
// using boost::lambda::bind;
// using boost::lambda::_1;
// using boost::lambda::_2;
// using boost::lambda::_3;
// using boost::lambda::_4;
typedef DynamicPropertyMapWrap<double,GraphInterface::edge_t> weight_map_t; typedef DynamicPropertyMapWrap<double,GraphInterface::edge_t> weight_map_t;
typedef ConstantPropertyMap<double,GraphInterface::edge_t> no_weight_map_t; typedef ConstantPropertyMap<double,GraphInterface::edge_t> no_weight_map_t;
typedef mpl::vector<weight_map_t,no_weight_map_t> weight_properties; typedef mpl::vector<weight_map_t,no_weight_map_t> weight_properties;
...@@ -53,7 +47,8 @@ void community_network(GraphInterface& gi, GraphInterface& cgi, ...@@ -53,7 +47,8 @@ void community_network(GraphInterface& gi, GraphInterface& cgi,
else else
weight = weight_map_t(weight, edge_scalar_properties()); weight = weight_map_t(weight, edge_scalar_properties());
typedef vector_property_map<int32_t,GraphInterface::vertex_index_map_t> typedef property_map_type::apply<int32_t,
GraphInterface::vertex_index_map_t>::type
vcount_t; vcount_t;
vcount_t vcount(gi.GetVertexIndex()); vcount_t vcount(gi.GetVertexIndex());
try try
......
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007 Tiago de Paula Peixoto <tiago@forked.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Copyright (C) Vladimir Prus 2003.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/graph/vector_property_map.html for
// documentation.
//
//
// This is a modification of boost's vector property map which optionally
// disables bound checking for better performance.
//
#ifndef FAST_VECTOR_PROPERTY_MAP_HH
#define FAST_VECTOR_PROPERTY_MAP_HH
#include <boost/property_map.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>
namespace boost {
template<typename T, typename IndexMap>
class unchecked_fast_vector_property_map;
template<typename T, typename IndexMap = identity_property_map>
class fast_vector_property_map
: public boost::put_get_helper<
typename std::iterator_traits<
typename std::vector<T>::iterator >::reference,
fast_vector_property_map<T, IndexMap> >
{
public:
typedef typename property_traits<IndexMap>::key_type key_type;
typedef T value_type;
typedef typename std::iterator_traits<
typename std::vector<T>::iterator >::reference reference;
typedef boost::lvalue_property_map_tag category;
template<typename Type, typename Index>
friend class unchecked_fast_vector_property_map;
typedef unchecked_fast_vector_property_map<T, IndexMap> unchecked_t;
typedef IndexMap index_map_t;
typedef fast_vector_property_map<T,IndexMap> self_t;
fast_vector_property_map(const IndexMap& index = IndexMap())
: store(new std::vector<T>()), index(index)
{}
fast_vector_property_map(unsigned initial_size,
const IndexMap& index = IndexMap())
: store(new std::vector<T>(initial_size)), index(index)
{}
typename std::vector<T>::iterator storage_begin()
{
return store->begin();
}
typename std::vector<T>::iterator storage_end()
{
return store->end();
}
typename std::vector<T>::const_iterator storage_begin() const
{
return store->begin();
}
typename std::vector<T>::const_iterator storage_end() const
{
return store->end();
}
void reserve(size_t size) const
{
if (store->size() < size)
store->resize(size);
}
std::vector<T>& get_storage() const { return (*store); }
unchecked_t get_unchecked(size_t size = 0) const
{
reserve(size);
return unchecked_t(*this, size);
}
public:
// Copy ctor absent, default semantics is OK.
// Assignment operator absent, default semantics is OK.
// CONSIDER: not sure that assignment to 'index' is correct.
reference operator[](const key_type& v) const {
typename property_traits<IndexMap>::value_type i = get(index, v);
if (static_cast<unsigned>(i) >= store->size()) {
store->resize(i + 1, T());
}
return (*store)[i];
}
protected:
// Conceptually, we have a vector of infinite size. For practical
// purposes, we start with an empty vector and grow it as needed.
// Note that we cannot store pointer to vector here -- we cannot
// store pointer to data, because if copy of property map resizes
// the vector, the pointer to data will be invalidated.
// I wonder if class 'pmap_ref' is simply needed.
shared_ptr< std::vector<T> > store;
IndexMap index;
};
template<typename T, typename IndexMap = identity_property_map>
class unchecked_fast_vector_property_map
: public boost::put_get_helper<
typename std::iterator_traits<
typename std::vector<T>::iterator >::reference,
unchecked_fast_vector_property_map<T, IndexMap> >
{
public:
typedef typename property_traits<IndexMap>::key_type key_type;
typedef T value_type;
typedef typename std::iterator_traits<
typename std::vector<T>::iterator >::reference reference;
typedef boost::lvalue_property_map_tag category;
typedef fast_vector_property_map<T, IndexMap> vmap_t;
unchecked_fast_vector_property_map(const vmap_t& vmap = vmap_t(),
size_t size = 0)
: _vmap(vmap)
{
if (size > 0 && _vmap.store->size() < size)
_vmap.store->resize(size);
}
void reserve(size_t size) const { _vmap.reserve(size); }
reference operator[](const key_type& v) const
{
typename property_traits<IndexMap>::value_type i =
get(_vmap.index, v);
return (*_vmap.store)[i];
}
private:
vmap_t _vmap;
};
template<typename T, typename IndexMap>
fast_vector_property_map<T, IndexMap>
make_fast_vector_property_map(IndexMap index)
{
return fast_vector_property_map<T, IndexMap>(index);
}
}
#endif
...@@ -77,7 +77,7 @@ size_t GraphInterface::GetNumberOfEdges() const ...@@ -77,7 +77,7 @@ size_t GraphInterface::GetNumberOfEdges() const
if (IsEdgeFilterActive() || IsVertexFilterActive()) if (IsEdgeFilterActive() || IsVertexFilterActive())
run_action<>()(*this, var(n)=bind<size_t>(HardNumEdges(),_1))(); run_action<>()(*this, var(n)=bind<size_t>(HardNumEdges(),_1))();
else else
run_action<>()(*this, var(n)=bind<size_t>(SoftNumEdges(),_1))(); n = _nedges;
return n; return n;
} }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#include <boost/graph/graph_traits.hpp> #include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp> #include <boost/graph/adjacency_list.hpp>
#include <boost/vector_property_map.hpp> #include "fast_vector_property_map.hh"
#include <boost/dynamic_property_map.hpp> #include <boost/dynamic_property_map.hpp>
#include <boost/lambda/lambda.hpp> #include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp> #include <boost/lambda/bind.hpp>
...@@ -178,6 +178,10 @@ private: ...@@ -178,6 +178,10 @@ private:
// this is the main graph // this is the main graph
multigraph_t _mg; multigraph_t _mg;
// keep track of the number of edges, since num_edges() is O(V) in
// adjacency_list... :-(
size_t _nedges;
// this will hold an instance of the graph views at run time // this will hold an instance of the graph views at run time
vector<boost::any> _graph_views; vector<boost::any> _graph_views;
...@@ -192,18 +196,20 @@ private: ...@@ -192,18 +196,20 @@ private:
edge_index_map_t _edge_index; edge_index_map_t _edge_index;
vector<size_t> _free_indexes; // indexes of deleted edges to be used up for vector<size_t> _free_indexes; // indexes of deleted edges to be used up for
// new edges to avoid needless fragmentation // new edges to avoid needless fragmentation
size_t _max_edge_index; // needed for property map bounds
// graph index map // graph index map
graph_index_map_t _graph_index; graph_index_map_t _graph_index;
// vertex filter // vertex filter
typedef vector_property_map<uint8_t,vertex_index_map_t> vertex_filter_t; typedef unchecked_fast_vector_property_map<uint8_t,vertex_index_map_t>
vertex_filter_t;
vertex_filter_t _vertex_filter_map; vertex_filter_t _vertex_filter_map;
bool _vertex_filter_invert; bool _vertex_filter_invert;
bool _vertex_filter_active; bool _vertex_filter_active;
// edge filter // edge filter
typedef vector_property_map<uint8_t,edge_index_map_t> edge_filter_t; typedef unchecked_fast_vector_property_map<uint8_t,edge_index_map_t> edge_filter_t;
edge_filter_t _edge_filter_map; edge_filter_t _edge_filter_map;
bool _edge_filter_invert; bool _edge_filter_invert;
bool _edge_filter_active; bool _edge_filter_active;
......
...@@ -125,12 +125,14 @@ boost::any check_directed(const Graph &g, bool reverse, bool directed, ...@@ -125,12 +125,14 @@ boost::any check_directed(const Graph &g, bool reverse, bool directed,
template <class Graph, class EdgeFilter, class VertexFilter> template <class Graph, class EdgeFilter, class VertexFilter>
boost::any boost::any
check_filtered(const Graph &g, const EdgeFilter& edge_filter, check_filtered(const Graph &g, const EdgeFilter& edge_filter,
const bool& e_invert, bool e_active, const bool& e_invert, bool e_active, size_t max_eindex,
const VertexFilter& vertex_filter, const bool& v_invert, const VertexFilter& vertex_filter, const bool& v_invert,
bool v_active, vector<boost::any>& graph_views, bool reverse, bool v_active, vector<boost::any>& graph_views, bool reverse,
bool directed) bool directed)
{ {
#ifndef NO_GRAPH_FILTERING #ifndef NO_GRAPH_FILTERING
edge_filter.reserve(max_eindex);
vertex_filter.reserve(num_vertices(g));
MaskFilter<EdgeFilter> e_filter(edge_filter, e_invert); MaskFilter<EdgeFilter> e_filter(edge_filter, e_invert);
MaskFilter<VertexFilter> v_filter(vertex_filter, v_invert); MaskFilter<VertexFilter> v_filter(vertex_filter, v_invert);
...@@ -184,7 +186,7 @@ boost::any GraphInterface::GetGraphView() const ...@@ -184,7 +186,7 @@ boost::any GraphInterface::GetGraphView() const
boost::any graph = boost::any graph =
check_filtered(_mg, _edge_filter_map, _edge_filter_invert, check_filtered(_mg, _edge_filter_map, _edge_filter_invert,
_edge_filter_active, _vertex_filter_map, _edge_filter_active, _max_edge_index, _vertex_filter_map,
_vertex_filter_invert, _vertex_filter_active, _vertex_filter_invert, _vertex_filter_active,
const_cast<vector<boost::any>&>(_graph_views), const_cast<vector<boost::any>&>(_graph_views),
_reversed, _directed); _reversed, _directed);
...@@ -270,7 +272,8 @@ void GraphInterface::SetVertexFilterProperty(boost::any property, bool invert) ...@@ -270,7 +272,8 @@ void GraphInterface::SetVertexFilterProperty(boost::any property, bool invert)
try try
{ {
_vertex_filter_map = any_cast<vertex_filter_t>(property); _vertex_filter_map =
any_cast<vertex_filter_t::vmap_t>(property).get_unchecked();
_vertex_filter_invert = invert; _vertex_filter_invert = invert;
_vertex_filter_active = true; _vertex_filter_active = true;
} }
...@@ -288,7 +291,8 @@ void GraphInterface::SetEdgeFilterProperty(boost::any property, bool invert) ...@@ -288,7 +291,8 @@ void GraphInterface::SetEdgeFilterProperty(boost::any property, bool invert)
try try
{ {
_edge_filter_map = any_cast<edge_filter_t>(property); _edge_filter_map =
any_cast<edge_filter_t::vmap_t>(property).get_unchecked();
_edge_filter_invert = invert; _edge_filter_invert = invert;
_edge_filter_active = true; _edge_filter_active = true;
} }
......
...@@ -307,7 +307,8 @@ struct get_all_pairs ...@@ -307,7 +307,8 @@ struct get_all_pairs
template <class Scalar, class IndexMap> template <class Scalar, class IndexMap>
struct get_property_map_type struct get_property_map_type
{ {
typedef typename property_map_type::apply<Scalar, IndexMap>::type type; typedef typename property_map_type::apply<Scalar, IndexMap>
::type::unchecked_t type;
}; };
template <class IndexMap> template <class IndexMap>
...@@ -492,7 +493,66 @@ BOOST_MPL_ASSERT_RELATION(n_views::value, == , mpl::int_<3>::value); ...@@ -492,7 +493,66 @@ BOOST_MPL_ASSERT_RELATION(n_views::value, == , mpl::int_<3>::value);
// run_action() implementation // run_action() implementation
// =========================== // ===========================
//
// wrap action to be called, to deal with property maps, i.e., return version
// with no bounds checking.
template <class Action>
struct action_wrap
{
action_wrap(Action a, size_t max_v, size_t max_e)
: _a(a), _max_v(max_v), _max_e(max_e) {}
template <class Type>
typename fast_vector_property_map
<Type,GraphInterface::vertex_index_map_t>::unchecked_t
uncheck(fast_vector_property_map
<Type,GraphInterface::vertex_index_map_t> a) const
{
return a.get_unchecked(_max_v);
}
template <class Type>
typename fast_vector_property_map
<Type,GraphInterface::edge_index_map_t>::unchecked_t
uncheck(fast_vector_property_map
<Type,GraphInterface::edge_index_map_t> a) const
{
return a.get_unchecked(_max_e);
}
template <class Type>
scalarS<typename Type::unchecked_t>
uncheck(scalarS<Type> a) const