Memory allocation for struct (low performance) - c++

I do have a question related to slow performance of allocating memory for several structs.
I have a struct which is looking like: see below
typedef struct _node
{
// Pointer to leaves & neigbours
struct _node *children[nrChild], *neighb[nrNeigh];
// Pointer to parent Node
struct _node *parentNode;
struct _edgeCorner *edgePointID[nrOfEdge];
int indexID; // Value
double f[latDir]; // Lattice Velos
double rho; // Density
double Umag; // Mag. velocity
int depth; // Depth of octree element
} node
At the beginning of my code I do have to create a lot of them (100.000 – 1.000.000 ) by
Using :
tree = new node();
and initiating the elements after it.
Unfortunately, this is pretty slow, therefore do anyone of you have an idea to improve the performance?

Firstly, you'll want to fix it so that it's actually written in C++.
struct node
{
// Pointer to leaves & neigbours
std::array<std::unique_ptr<node>, nrChild> children;
std::array<node*, nrNeigh> neighb;
// Pointer to parent Node
node* parentNode;
std::array<_edgeCorner*, nrOfEdge> edgePointID;
int indexID; // Value
std::array<double, latDir> f; // Lattice Velos
double rho; // Density
double Umag; // Mag. velocity
int depth; // Depth of octree element
};
Secondly, in order to improve your performance, you will require a custom allocator. Boost.Pool would be a fine choice- it's a pre-existing solution that is explicitly designed for repeated allocations of the same size, in this case, sizeof(node). There are other schemes like memory arena that can be even faster, depending on your deallocation needs.

If you know how many nodes you will have, you could allocate them all in one go:
node* Nodes = new node[1000000];
You will need to set the values afterwards, just like you would do if you did it one by one. If it's a lot faster this way, you could try an architecture where you find out how many nodes you will need before allocating them, even if you don't have that number right now.

Related

C++ avoid dynamic memory allocation

Imagine I have some Node struct that contains pointers to the left and right children and some data:
struct Node {
int data;
Node *left;
Node *right;
};
Now I want to do some state space search, and naturally I want to construct the graph as I go. So I will have a kind of loop that will have to create Nodes and keep them around. Something like:
Node *curNode = ... ; // starting node
while (!done) {
// ...
curNode->left = new Node();
curNode->right = new Node();
// ..
// Go left (for example)
curNode = curNode->left;
}
The problem is that I have to dynamically allocate node on each iteration, which is slow. So the question is: how can I have pointers to some memory but not by allocating it one by one?
The first solution I thought of is to have a std::vector<Node> that will contain all the allocated nodes. The problem is that when we push_back elements, all references might be invalidated, so all my left/right pointers will be garbage.
The second solution is to allocate a big chunk of memory upfront, and then we just grab the next available pointer when we want to create a Node. To avoid references invalidation, we just have to create a linked list of big chunks of memory when we exceed the capacity of the current chunk so every given pointer stays valid. I think that std::deque behaves like this, but it's not explicitly created for this.
Another solution would be to store vector indices instead of pointers but this is not a solution because a Node doesn't want to be associated with any container, it wants the pointer directly.
So what is the good solution here, that would avoid having to allocated new nodes on each iteration?
You can use std::deque<Node> and it will do memory management for you creating elements by groups and no invalidating pointers if you do not delete elements in middle. Though if you want to have more precise control on how many elements in a group you can quite simply create something like that:
class NodePool {
constexpr size_t blockSize = 512;
using Block = std::array<Node,blockSize>;
using Pool = std::list<Block>;
size_t allocated = blockSize;
Pool pool;
public:
Node *allocate()
{
if( allocated == blockSize ) {
pool.emplace_back();
allocated = 0;
}
return &( pool.back()[ allocated++ ] );
}
};
I did not try to compile it, but it should be enough to exress the idea. Here changing blockSize you can fine tune performance of your program. Though you should be aware than Node objects will be fully constructed by groups (unlike hoiw std::deque would do it). As much as I am aware there is no way to create raw memory for Node objects which is standard comformant.

Private array of adjacent node addresses in C++

////EDIT #2: Deleted all the previous info and just post the working code now. Previous question became too lengthy:
#include <iostream>
#include <vector>
using namespace std;
template<class T>
class Node{
T data;
vector<Node<T>*> adjacent;
friend class Graph;
public:
int n;
Node(T initData) : data(initData), n(0){}
void addAdjacent(Node<T>& other){
adjacent.push_back(&other);
n++;
}
T getData(){
return data;
}
Node<T>* getEdge(int edgeNum){
return adjacent[edgeNum];
}
};
template<class T>
class GraphCl{
int n;
vector<Node<T>*> nodes;
T input;
public:
GraphCl(int size): n(size){
for (int i=0;i<n;i++){
cout << "Enter data for node " << i << ": ";
cin >> input;
nodes.push_back(new Node<T>(input)) ;
}
}
void addEdge(int baseNode, int edgeNode){
nodes[baseNode]->addAdjacent(*nodes[edgeNode]);
}
void printGraph(){
for (int i=0;i<n;i++){
Node<T> *base = nodes[i];
cout << "Data of node " << i <<": "<< base->getData() <<endl;
for (int j=0;j<base->n;j++){
cout << "Edge #"<< j+1 << " of node " << i << ": " << base->getEdge(j) <<endl;
}
}
}
};
int main(){
GraphCl<int> *myGraph = new GraphCl<int>(5);
myGraph->addEdge(0,1);
myGraph->addEdge(0,2);
myGraph->addEdge(0,3);
myGraph->addEdge(0,4);
myGraph->addEdge(3,1);
myGraph->addEdge(3,0);
myGraph->printGraph();
return 0;
}
Output:
Enter data for node 0: -34
Enter data for node 1: 12
Enter data for node 2: 56
Enter data for node 3: 3
Enter data for node 4: 23
Data of node 0: -34
Edge #1 of node 0: 0x7fbeebd00040
Edge #2 of node 0: 0x7fbeebd00080
Edge #3 of node 0: 0x7fbeebe00000
Edge #4 of node 0: 0x7fbeebd000d0
Data of node 1: 12
Data of node 2: 56
Data of node 3: 3
Edge #1 of node 3: 0x7fbeebd00040
Edge #2 of node 3: 0x7fbeebd00000
Data of node 4: 23
As you can see this simple implementation is working. I decided to just cut out all the complicated stuff and keep it simple with dynamically changing vectors. Obviously less efficient but I can work from here on. Since I am new with C++ the previous implementation just got my head spinning 360 degrees thinking about where all the pointers to pointers went, without even thinking about memory allocation. The above code basically is a directed graph that is very sensitive to input errors, so I got to work on it still.
Thanks for all the help!
Accessibility
Regarding the accessibility of the array to the Graph, the closest thing to the current implementation is to declare declare Graph as a friend of Node. Simply add:
friend Graph;
To the end of the Node class declaration.
That said, making a class as a friend is sometimes a sign that the API you defined isn't exactly right if classes need to know too much about each others' implementation details. You can alternatively provide an interface for Node such as:
void AddAdjacent(Node* other);
Managing Adjacent Nodes
If you want your adjacent pointer array to be growable, then you are basically re-creating std::vector, so I would suggest using std::vector<Node*>. Initializing a vector with the default (empty) constructor would take care of it, and a nodes[baseNode]->adjacent.push_back(...) would be all you need in addEdges.
If memory is not a consideration and you have a maximal number of nodes in the graph, you can instantiate a constant-sized array.
If you really don't want to use std::vector, but you actually want a growable array of pointers, then you'll have to manage your own malloc and free calls. I'll write something up to that effect, but my advice is to just go ahead with vector.
In case you are curious, the array approach would look something like:
template<class T>
class Node : public Graph{
Node **adjacent; //pointer to array of POINTERS TO adjacent Nodes
int n;
size_t capacity;
T data;
friend Graph;
public:
Node(T initData) : data(initData), capacity(8) {
n = 0;
adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
}
~Node() {
free(adjacent);
}
void Grow() {
size_t new_cap = base.capacity * 2;
Node<int> **copy = reinterpret_cast<Node<int>**>(malloc(new_cap * sizeof(Node**)));
memcpy(copy, base.adjacent, base.capacity); // copy and adjacent are non-overlapping, we can use memcpy
free(base.adjacent);
base.adjacent = copy;
base.capacity = new_cap;
}
};
And the insertion:
Node<T>& base = nodes[baseNode];
Node<T>* edge = &(nodes[edgeNode]);
if (base.capacity == base.n) base.Grow();
base.adjacent[base.n++] = edge;
Answering the updated question
There are a few issues with putting Nodes directly in a std::vector in your case.
Using a std::vector is great for many things, but if you are doing that, you should make sure not to take pointers to vectors. Remember, pointers refer to exact addresses in memory of where an object is stored. A vector is a growable container of elements. To store elements contiguously, the vector allocates a bunch of memory, puts objects there, and if it has to grow, it will allocate more memory and move the objects around. It is essentially doing something similar to what you are doing in your Node and grow (except, in its case, its explicitly destroying the objects before freeing the old memory).
Notice that your Grow function allocates new memory and copies the pointers. Simlarly, vectors can allocate new memory and copy the data over. This means that holding pointers to data in a vector is bad. The only guarantee a vector gives you is that its data will continue to be accessible using array-style indexing, find, iteration, etc., not that the data will exist in the same memory location forever.
Explaining the exact bug you are seeing
The vector is invoking a copy constructor. The default copy constructor copies every field one-by-one. This is not what you want in the case of Node, because then you have two vectors that think they own the Node** adjacent memory location. When the first node (the old copy) is being destroyed, it will free its adjacent nodes (which is the same as the copy's adjacent node). When the new copy is being destroyed, it will attempt to free that same memory location, but it is already freed. You also have the problem here that, if you attempted to access the memory after it has been destroyed in the first node, you'll be in trouble.
Why was this bug showing up when you were only adding nodes?
When a vector grows to a certain amount, it needs to resize. In most implementation, the process is roughly:
Allocate a bunch more memory (usually twice the old capacity)
Invoke the copy constructor to copy elements from the old location to the new location
Destroy the elements in the old location (say, by explicitly calling the destructor)
Insert the new element in the new location
Your bug is showing up because of steps 2 and 3, basically.
Fixing this particular bug
For your case, the default copy constructor is no good because copying a node should meet a deep copy of all of the data. A regular copy in C++ will copy all of the data on the class or struct itself. If the data is a pointer, then the pointer is copied, not the thing its pointing to.
Override the copy constructor and assignment operator:
Node(const Node<T>& other) : data(other.data), capacity(other.capacity), n(other.n) {
adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
memcpy(adjacent, other.adjacent, capacity * sizeof(Node**));
}
Node<T>& operator= (const Node<T>& other) {
data = other.data;
capacity = other.capacity;
n = other.n;
adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
memcpy(adjacent, other.adjacent, capacity * sizeof(Node**));
}
A Bigger Problem
A bigger problem with your code is that the use of an std::vector and pointers to its elements. Choose one of:
Use a fixed-sized array (which is stable in memory), and point to these objects
Forget about pointers altogether, and make your adjacent list a list of indices into the vector (its less performant as you need to go through the vector each time, but that likely won't be your bottleneck for now)

Constructing a binary tree using queue in C++

I am trying to write some data structure HW in C++. When I was trying to construct a binary tree using a queue, I was somehow confused by the pointer issues.
class Tree{
private:
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};
public:
TreeNode* root = NULL;
Tree();
Tree(queue<int>& val);
~Tree();
string toString_bf();
};
Tree(queue<int> &vals){
if (vals.empty())
return;
root = new TreeNode(vals.front());
vals.pop();
queue<TreeNode**> pts; // what is the meaning of this? Why should use pointers to pointer?
pts.push(&(root->left)); // also have doubts here, about the reference used in the parameter
pts.push(&(root->right));
while (!vals.empty()){
TreeNode* t = new TreeNode(vals.front());
*(pts.front()) = t; // and here
pts.pop();
vals.pop();
pts.push(&(t->left));
pts.push(&(t->right));
}
}
According to my understanding, left and right are both pointers, why could not just pass values to them?
The variable pts is a queue of pointers to where subtrees need to be placed later. Those locations are themselves pointers to TreeNode (each being either a left or right data member, of type TreeNode *) so it makes sense that you need a TreeNode ** to point at them.
In other words, each time *(pts.front()) = t; is executed, a left or right member of a previously contructed TreeNode is being set to point to the most recently constructed node.
You could make a typedef TreeNode *TreePtr; and perhaps it would look cleaner using that.
To further clarify what's happening:
Each time the 'while' loop starts:
root is the root of a tree under construction, containing n+1 nodes, each holding one value taken from the input queue vals (where n is the number of times the while loop has already run)
vals contains all the remaining values yet to be put into the tree (if any)
pts contains a set of pointers to pointers within the nodes in the tree; it contains pointers to all of the pointers which have not yet had subtrees assigned to them; there are n+2 of these.
So each loop makes one new node, makes one of these unassigned pointers point at the new node, and then adds the new node's unassigned pointers to the queue.
Note that when the operation ends, there are always n+2 pointers in pts, which are discarded. By knowing how many nodes are needed ( = vals.size()) it's possible to stop doing the pts.push operations once enough have been done, which reduces the amount of temporary space needed by about 50%:
vals.pop();
if( pts.size() < vals.size()) {
pts.push(&(t->left));
pts.push(&(t->right))
} // otherwise we have enough for all remaining vals.

C++ using array or vector for many items

The following code is an extract from a source code of a project about graph theory for the university (representation for the directed weighted graph):
struct Edge
{
int neighboor : 20;
int weight : 12;
} e;
struct Node
{
double x;
double y;
vector<Edge> neighboors;
};
...
vector<Node> list;
list.resize(countNode);
Is there a way to save even more memory by replacing vector< Edge > with for example an array?
I'm thinking that there are thousands of vector< Edge > being created, and that takes a lot of memory, doesn't it?
Sorry for my English. Thanks in advance.
You may share the vector of Edge for all nodes:
Something like:
struct Edge
{
int32_t neighboor : 20;
int32_t weight : 12;
} e;
struct Node
{
double x;
double y;
int32_t indexInEdges : 28;
int32_t neighboorCount : 4; // You may adjust these numbers
};
std::vector<Node> nodes;
std::vector<Edge> edges; // So edges contains the edges of nodes[0]
// then those of nodes[1] and so on.
You may also reduce the size of Node by using float instead of double
An array is an ordered collection with static length, and a vector is an ordered collection with dynamic length. Whether an array or a vector is the most memory-efficient data type for you depends on the number of elements that you intend to store. If all elements in the graph have the same number of edges, an array is more memory-efficient, because you get rid of the overhead for variable-length storage.
If the number of edges per element varies strongly, vector is definitely the better choice, because you don't have to statically over-allocate memory on every node for the edges that might be there, but aren't.
If you have small variation in the number of edges (say, 100 to 103 edges per element), an array might still be the better choice, because you can trade static overallocation for the overhead of both a dynamic allocation and the bookkeeping for the vector's size and capacity. Just how large this overhead is depends strongly on your vector implementation, and an experiment is the best way to decide whether it's worth it.
If an experiment seems too much hassle or if you don't have enough data for an experiment, don't overoptimize, and use the vector.
If you use C++98 then you can save significant amount of memory by using arrays instead of vector<Edge> and vector<Node>. vector's consume more memory than they actually needed because they reserve some additional memory for reducing number of memory relocation when a vector is growing.
If you use C++11 you can use vector::shrink_to_fit() to free the unused memory.

C++ Vector of Object , Memory Usage on Empty Object Creation

C++ Vector - part of it to point to same address
Hi , my subject might be confusing.
Here it goes.
I got a vector
struct node{
int nodeid;
vector<string> data;
vector<fTable> fdata;
}
struct fTable{
int index;
int key;
}
vector<node> myNode;
as at some function...
void chord::someFunc(int nodeid)
{
node myTempNode;
vector<string> data1;
vector<fTable> fdata1;
myTempNode.nodeid = nodeid
myTempNode.data = data1;
myTempNode.fTable = ftable1;
myNode.push_back(myTempNode);
myTempNode.clear();
}
I will be creating 10000 objects, at this point of time, i only got the value for nodeid.
But for data and fTable, i am setting to some empty string vector and empty fTable vector but i wonder if i create 10000 objects and doing the same thing.
am i creating 10000 empty string and fTable vector
Is there a way i can set all this object point to same string vector (null value) and fTable vector ( empty value) so i can save some memories. considering i will or might create 10000 nodes or so. and memory consumption is a concern to me.
Thanks for all help.
No, since the vectors are empty, they don't consume much space and no string or fTable objects are created.
Give your limited c++ knowledge I would stay clear of pointers and stick to values.
You don't need to do any of the (immediately) following, the constructor of node takes care of that. This simply overwrites empty vectors with empty vectors.
node myTempNode;
vector<string> data1;
vector<fTable> fdata1;
myTempNode.data = data1;
myTempNode.fTable = ftable1;
If you give your node a constructor like this:
struct node{
int node(int id) : nodeid(id) {}
int nodeid;
vector<string> data;
vector<fTable> fdata;
}
then you only need to write:
myNode.push_back( node(nodeid) );
Creating a vector does not always create its data : the data of a vector is allocated when needed, so vectors with no data will be likely to take sizeof(std:vector<...>) bytes (if reserved size is 0), and vectors with data will in real take sizeof(vector<...>) + n * sizeof(data), where n is the number of reserved items in the vector. The size of a vector is 28 bytes on my implementation.
1st method: vector as fields. The advantage of having vector fields is they're not dynamically allocated, saving you from a bunch of new/delete manual calls : it is more safe.
2nd method: you can also use pointers as you said:
struct node
{
int nodeid;
vector<string>* data; // pointer
vector<fTable>* fdata; // pointer
};
You can set them to 0 (null), saving the size of a vector minus the size of pointer, per node. When you need a node to have a vector, simply new a vector, and set the appropriated pointer. However, this method will eventually take more space than the previous, because it will also take the size of the pointers. And you will have to manage the delete (it can be done with the node destructor, but may be less efficient that deallocating vectors before node destruction).
Conclusion: I suggest you estimate the total size occupied by your data (ex: 10000 * ...), and see if you have to use a specific model (ie, measure first). Personnally, I advise you to take the first (no pointers).
I also recommend that you use a constructor (or two) for node, for a better code.
Yes, use a vector of pointers then, i.e.
struct node {
node(int nid) : nodeid(nid), data(0), fdata(0) { }
int nodeid;
vector<string *> data;
vector<fTable *> fdata;
}
But beware of memory management: now when a node is deleted, the string and the fTable pointed by data and fdata are not deleted. If these data should be owned by a node once assigned, add a destructor:
struct node {
node(int nid) : nodeid(nid), data(0), fdata(0) { }
~node() {
for (auto i = data.begin(); i != data.end(); ++i)
delete *i;
for (auto i = fdata.begin(); i != fdata.end(); ++i)
delete *i;
}
int nodeid;
vector<string *> data;
vector<fTable *> fdata;
}