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 = \
histogram.hh \
mpl_nested_loop.hh \
shared_map.hh \
fast_vector_property_map.hh \
numpy_bind.hh \
../../config.h
......
......@@ -33,12 +33,13 @@ using namespace graph_tool;
template <class PropertySequence>
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;
mpl::for_each<PropertySequence>
(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;
}
......@@ -46,16 +47,17 @@ struct prop_vector
{
template <class Property>
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())
{
try
{
vector<Property> vec;
vector<typename Property::unchecked_t> vec;
vec.resize(props.size());
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;
}
catch (bad_any_cast){}
......@@ -70,7 +72,7 @@ struct get_property_vector_type
template <class Property>
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)
for (size_t i = 0; i < cmaps.size(); ++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())
throw GraphException("all vertex properties must be of the same"
" floating point type");
......
......@@ -38,12 +38,6 @@ void community_network(GraphInterface& gi, GraphInterface& cgi,
boost::any community_property, boost::any vertex_count,
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 ConstantPropertyMap<double,GraphInterface::edge_t> no_weight_map_t;
typedef mpl::vector<weight_map_t,no_weight_map_t> weight_properties;
......@@ -53,7 +47,8 @@ void community_network(GraphInterface& gi, GraphInterface& cgi,
else
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(gi.GetVertexIndex());
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
if (IsEdgeFilterActive() || IsVertexFilterActive())
run_action<>()(*this, var(n)=bind<size_t>(HardNumEdges(),_1))();
else
run_action<>()(*this, var(n)=bind<size_t>(SoftNumEdges(),_1))();
n = _nedges;
return n;
}
......
......@@ -21,7 +21,7 @@
#include <boost/graph/graph_traits.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/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
......@@ -178,6 +178,10 @@ private:
// this is the main graph
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
vector<boost::any> _graph_views;
......@@ -192,18 +196,20 @@ private:
edge_index_map_t _edge_index;
vector<size_t> _free_indexes; // indexes of deleted edges to be used up for
// new edges to avoid needless fragmentation
size_t _max_edge_index; // needed for property map bounds
// graph index map
graph_index_map_t _graph_index;
// 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;
bool _vertex_filter_invert;
bool _vertex_filter_active;
// 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;
bool _edge_filter_invert;
bool _edge_filter_active;
......
......@@ -125,12 +125,14 @@ boost::any check_directed(const Graph &g, bool reverse, bool directed,
template <class Graph, class EdgeFilter, class VertexFilter>
boost::any
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,
bool v_active, vector<boost::any>& graph_views, bool reverse,
bool directed)
{
#ifndef NO_GRAPH_FILTERING
edge_filter.reserve(max_eindex);
vertex_filter.reserve(num_vertices(g));
MaskFilter<EdgeFilter> e_filter(edge_filter, e_invert);
MaskFilter<VertexFilter> v_filter(vertex_filter, v_invert);
......@@ -184,7 +186,7 @@ boost::any GraphInterface::GetGraphView() const
boost::any graph =
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,
const_cast<vector<boost::any>&>(_graph_views),
_reversed, _directed);
......@@ -270,7 +272,8 @@ void GraphInterface::SetVertexFilterProperty(boost::any property, bool invert)
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_active = true;
}
......@@ -288,7 +291,8 @@ void GraphInterface::SetEdgeFilterProperty(boost::any property, bool invert)
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_active = true;
}
......
......@@ -307,7 +307,8 @@ struct get_all_pairs
template <class Scalar, class IndexMap>
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>
......@@ -492,7 +493,66 @@ BOOST_MPL_ASSERT_RELATION(n_views::value, == , mpl::int_<3>::value);
// 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
{
return scalarS<typename Type::unchecked_t>(uncheck(a._pmap));
}
//no op
template <class Type>
Type uncheck(Type a) const { return a; }
void operator()() const {};
template <class T1> void operator()(const T1& a1) const { _a(uncheck(a1)); }
template <class T1, class T2>
void operator()(const T1& a1, const T2& a2) const
{ _a(a1, uncheck(a2)); }
template <class T1, class T2, class T3>
void operator()(const T1& a1, const T2& a2, const T3& a3) const
{ _a(a1, uncheck(a2), uncheck(a3));}
template <class T1, class T2, class T3, class T4>
void operator()(const T1& a1, const T2& a2, const T3& a3, const T4& a4)
const
{ _a(a1, uncheck(a2), uncheck(a3), uncheck(a4)); }
template <class T1, class T2, class T3, class T4, class T5>
void operator()(const T1& a1, const T2& a2, const T3& a3, const T4& a4,
const T5& a5) const
{ _a(a1, uncheck(a2), uncheck(a3), uncheck(a4), uncheck(a5)); }
Action _a;
size_t _max_v, _max_e;
};
// this functor encapsulates another functor Action, which takes a pointer to a
// graph view as first argument
template <class Action, class GraphViews, class TR1, class TR2, class TR3,
......@@ -502,7 +562,8 @@ struct graph_action
struct graph_view_pointers:
mpl::transform<GraphViews, mpl::quote1<add_pointer> >::type {};
graph_action(const GraphInterface& g, Action a): _g(g), _a(a) {}
graph_action(const GraphInterface& g, Action a)
: _g(g), _a(a, num_vertices(g._mg), g._max_edge_index + 1) {}
void operator()() const
{
......@@ -581,10 +642,11 @@ struct graph_action
}
const GraphInterface &_g;
Action _a;
action_wrap<Action> _a;
};
} // details namespace
// all definitions of run_action with different arity
template <class GraphViews = detail::all_graph_views>
struct run_action
......
......@@ -241,7 +241,8 @@ struct get_python_property
void operator()(ValueType, IndexMap, dynamic_property_map* map,
python::object& pmap) const
{
typedef vector_property_map<ValueType, IndexMap> map_t;
typedef typename property_map_type::apply<ValueType, IndexMap>::type
map_t;
try
{
pmap = python::object
......@@ -280,10 +281,12 @@ struct check_value_type
{
try
{
vector_property_map<ValueType, IndexMap> vector_map(_index_map);
typedef typename property_map_type::apply<ValueType, IndexMap>::type
map_t;
map_t vector_map(_index_map);
vector_map[_key] = any_cast<ValueType>(_value);
_map = new boost::detail::dynamic_property_map_adaptor
<vector_property_map<ValueType, IndexMap> >(vector_map);
_map = new boost::detail::dynamic_property_map_adaptor<map_t>
(vector_map);
}
catch (bad_any_cast) {}
}
......@@ -469,7 +472,7 @@ void build_stream
python::tuple GraphInterface::ReadFromFile(string file, python::object pfile,
string format)
string format)
{
bool graphviz = false;
if (format == "dot")
......@@ -515,6 +518,7 @@ python::tuple GraphInterface::ReadFromFile(string file, python::object pfile,
else
read_graphml(stream, ug, dp);
}
_nedges = num_edges(_mg);
python::dict vprops, eprops, gprops;
for(