I have an N-Body simulator based on the Barnes-Hut algorithm which I've multithreaded using OpenMP. Most of the program was made parallel by simply adding #pragma omp parallel for in a few key places. This provides a healthy speedup, which scales nicely with the number of cores when the number of gravitational bodies is under a couple thousand.
Because my program uses the Barnes-Hut algorithm, at its heart is a tree structure, in 2d this is a quadtree, and in my case an octree. I'm running into trouble multithreading the process of populating the tree. Making this step single threaded prevents the program from making full use of my processor. My CPU utilization actually goes down the more bodies I add, because more time is spent adding all the bodies to the octree using only one core.
Right now the method for adding a single body to the octree looks like this:
void octant::addBody(vec3 newPosition, float newMass) {
// Making room for new bodies by dividing if the node is a leaf
if (isLeaf) {
// Subdividing the octant
divide();
// Moving the body already contained
subdivisionEnclosing(this->position)->addBody(this->position, this->mass);
}
// Adding the body to the appropriate subdivision if the node is divided
if (divided) {
// Adding the new body to the appropriate octant
subdivisionEnclosing(newPosition)->addBody(newPosition, newMass);
return;
}
// If the node doesnt yet contain any bodies at all, add the new one
this->position = newPosition;
this->mass = newMass;
// This node only contains one body, so the center of mass is accurate
isLeaf = true;
calculatedCOM = true;
}
This works just fine when called in series, but naturally breaks down when I try to add multiple bodies to the same root node simultaneously. This code doesn't include any measures to make the octant object thread safe.
Ideally I'd like to be able to call the addBody method in parallel using something like this:
#pragma omp parallel for
for (int b = 0; b < bodies.size(); ++b) {
octree->addBody(bodies[b]->getPosition(), bodies[b]->getMass());
}
I've experimented with adding #pragma omp critical(name) to parts of the method where data is changed and #pragma omp single where the node is subdivided. Nothing I tried prevented an immediate segfault.
I also built a method which adds the bodies in batches. It took in a vector of body objects, sorted them into vectors based on what subdivision they fit into, and passed those vectors into their respective subdivisions. Each subdivision got its own thread, and the process was recursive. This functioned and used all my cores, but it was significantly slower. I think putting the bodies into vectors added a ton of overhead.
I'm pretty new to OpenMP and even newer to the concept of thread safety. What would the best way to solve this problem? I cant seem to find very many examples of thread-safe tree structures online, and none using OpenMP. What is the ideal approach to populating a tree using multiple threads? At the very least, what tools do you think would be useful in making this kind of thing work?
Edit: Does anyone know of any examples of a completely thread-safe tree structure? Even if it's not in OpenMP, I'm primarily interested in how a tree can be added to/generated/filled in a thread-safe way.