if (world_.rank() == 0)
std::cout << "test the memory requirements of edges only using own graph" << std::endl;
world_.barrier();
// std::cout << getpid() << std::endl;
auto mems1 = MemoryMonitor::instance().get_all_proc_memory();
if (world_.rank() == 0) {
std::cout << "before create graph" << std::endl;
std::cout << mems1 << std::endl;
}
size_type vertex_size = 5000;
size_type per_batch_size = vertex_size / world_.size();
typedef boost::adjacency_list<boost::vecS, boost::distributedS<ProcessGroup, boost::vecS>, boost::bidirectionalS> Graph;
ProcessGroup pg;
Graph g(vertex_size, pg);
Timer t;
for (size_type i = world_.rank() * per_batch_size; i < (world_.rank() + 1) * per_batch_size; i++) {
for (size_type j = 0; j < vertex_size; j++) {
Graph::vertex_descriptor from = boost::vertex(i, g);
Graph::vertex_descriptor to = boost::vertex(j, g);
boost::add_edge(from, to, g);
}
}
synchronize(g);
t.stop();
t.print();
world_.barrier();
auto mems2 = MemoryMonitor::instance().get_all_proc_memory();
if (world_.rank() == 0) {
std::cout << "after create graph" << std::endl;
// std::cout << getpid() "has" << std::endl;
std::cout << mems2 << std::endl;
}
auto total_edge_size = vertex_size * vertex_size;
auto edge_size_per_proc = total_edge_size / world_.size();
std::cout << "edge_size_per_proc:" << edge_size_per_proc << std::endl;
if (world_.rank() == 0) {
for (auto i = 0; i < world_.size(); i++) {
std::cout << "mem increase total:" << mems2[i] - mems1[i];
std::cout << "\tper edge:"
<< (mems2[i] - mems1[i]) * 1024 * 1024 / edge_size_per_proc
<< std::endl;
}
}
returned below results
edge_size_per_proc:6250000
mem increase total:1020.52 per edge:171.215
mem increase total:1016.33 per edge:170.512
mem increase total:1029.33 per edge:172.693
mem increase total:1018.72 per edge:170.913
It says memory usage per edge is 170 bytes. While I test memory for vertex, it is only 4 bytes, so I wonder why it takes 170 bytes to store an edge? I have looked deeper into the source code of boost, and found that it keeps two vectors per vertex for in_edge and out_edge edges, with the definition below:
std::vector< boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, MyVertexDescriptor, MyEdgeDescriptor>>
But when I use sizeof printed its size, it is only 16 bytes, and after counting in both in and out edges, it should be 32 bytes. So where is the rest 140+ bytes gone?
The real answer is in the profiler and the source code.
That only makes sense when using
boost::bidirectionalSinstead ofboost::undirectedS.Regardless, obviously the size of your (very very poorly named) property types (
MyVertexDescriptor[sic] andMyEdgeDescriptor[sic]) is extremely important. If, e.g. the edge property is 170 bytes, then you'd expect even more memory consumed per edge.Diving In
Adjacency lists store iterators to out edges in the container of your choice (
vecSselector in your example) inside each stored vertex.I changed your sample to actually only use "own graph" (so removing the distributedS adaptor).
Live On Coliru
Prints e.g.
As you can see from the static assert
the storage for edges seems pretty reasonable: just enough to store your own data and the source/target descriptors.
Improving?
I see few ways to improve:
The last way to improve is to /not use
boost::adjacency_listbut instead model your graph concept using your own custom code. You can optimize this in any way you see fit. Of course, it is the most amount of work.BONUS Profiling
I forgot to include the Massif memory profiling graph of the sample program I included. Perhaps it is is of use to you: