I've been working on an MPI project in which the slaves all send data back to the master. For some reason the master will only receive the data if I do 2 consecutive Sends in a row. This is very odd and I think it is causing some other weird problems I am getting. Any idea what would cause this? I think the first send is sending some kind of junk data or something. The sends are the exact same line of code though.
EDIT: Code below...
if (numProcs > 0)
MPI_Barrier( MPI_COMM_WORLD ) ; //only wait if there are other processes to wait for
if (rank != 0)
{
MPI_Send(handArray, 10, MPI_DOUBLE, 0, TAG_HAND, MPI_COMM_WORLD);
MPI_Send(handArray, 10, MPI_DOUBLE, 0, TAG_HAND, MPI_COMM_WORLD);
}
//8. After the main loop the master process receives and sums together the hand counts array
// from each slave process.
else
{
int activeProcs = numProcs - 1;
getHandsFromSlaves( activeProcs, handArray );
then the master proceeds to print some data...
Here is the getHands FromSlaves method. Please note I have also tried using blocking calls for this as well with the same problems.
void getHandsFromSlaves( int& activeCount, double handTotals[10] ){
static MPI_Request request;
static int msgBuff, recvFlag;
static double handBuff[10];
MPI_Status status;
while (activeCount > 0)
{
if( request )
{
// Already listening for a message
// Test to see if message has been received
MPI_Test( &request, &recvFlag, &status );
//cout << "TAG: " << status.MPI_TAG << " SOURCE: "<< status.MPI_SOURCE << " ERROR: " << status.MPI_ERROR << endl;
if( recvFlag )
{
// Message received
if( status.MPI_TAG == TAG_HAND )
{
cout << "Hand Received!" << endl;
for(int m = 0; m < 10; ++m)
{
handTotals[m] += handBuff[m];
}
activeCount--;
}
else
{
//error report... what happened?
cout << "TAG: " << status.MPI_TAG << " SOURCE: "<< status.MPI_SOURCE << " ERROR: " << status.MPI_ERROR << endl;
}
// Reset the request handle
request = 0;
}
}
if( !request && activeCount > 0 )
// Start listening again
MPI_Irecv(&handBuff, 10, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &request);
}
}
Well, you're probably trying to process one too many messages because your request variable is undefined on entering your getHandsFromSlaves() routine. Since on enter, request is almost certainly non-zero, you immediately try to MPI_Test for a message something even though you haven't posted an Irecv.
In fact, there's a lot of really strange things about the code excerpt posted here. Why are the local variables static? Why would you implement your own busywait on MPI_Test() instead of using MPI_Wait()? Why are you using non-blocking receives at all if you're not doing anything useful between receives? And indeed, if you're just summing up all of the arrays anyway, why are you doing individual point-to-point receives at all instead of doing an MPI_Reduce()?
The following much shorter code seeems to do what you're trying to do above:
#include <stdio.h>
#include <mpi.h>
int main (int argc, char **argv) {
int rank, numProcs;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
double handArray[10];
double handTotal[10];
for (int i=0; i<10; i++)
handArray[i] = rank + i;
if (rank == 0) // Since apparently rank 0 doesn't do anything
{
for (int i=0; i<10; i++)
handArray[i] = 0;
}
MPI_Reduce(handArray, handTotal, 10, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (rank == 0) {
printf("Hand Totals= \n");
for (int i=0; i<10; i++)
printf(" %lf ", handTotal[i]);
printf("\n");
}
MPI_Finalize();
}
Related
I am trying to write a C++ program by using MPI, in which each rank will send a matrix to rank 0. When the matrix size is relatively small, the code works perfectly. However, when the matrix size becomes big. The code starts to give strange error which will only happen when I use specific amount of CPUs.
If you feel the full code is too long, please directly go down to the minimum example below.
To avoid overlooking some part, I give the full source code here:
#include <iostream>
#include <mpi.h>
#include <cmath>
int world_size;
int world_rank;
MPI_Comm comm;
int m, m_small, m_small2;
int index(int row, int column)
{
return m * row + column;
}
int index3(int row, int column)
{
return m_small2 * row + column;
}
int main(int argc, char **argv) {
MPI_Init(&argc, &argv);
MPI_Status status;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
m = atoi(argv[1]); //Size
int ndims = 2;
int *dims = new int[ndims];
int *period = new int[ndims];
int *coords = new int[ndims];
for (int i=0; i<ndims; i++) dims[i] = 0;
for (int i=0; i<ndims; i++) period[i] = 0;
for (int i=0; i<ndims; i++) coords[i] = 0;
MPI_Dims_create(world_size, ndims, dims);
MPI_Cart_create(MPI_COMM_WORLD, ndims, dims, period, 0, &comm);
MPI_Cart_coords(comm, world_rank, ndims, coords);
double *a, *a_2;
if (0 == world_rank) {
a = new double [m*m];
for (int i=0; i<m; i++) {
for (int j=0; j<m; j++) {
a[index(i,j)] = 0;
}
}
}
/*m_small is along the vertical direction, m_small2 is along the horizental direction*/
//The upper cells will take the reminder of total lattice point along vertical direction divided by the cell number along that direction
if (0 == coords[0]){
m_small = int(m / dims[0]) + m % dims[0];
}
else m_small = int(m / dims[0]);
//The left cells will take the reminder of total lattice point along horizental direction divided by the cell number along that direction
if (0 == coords[1]) {
m_small2 = int(m / dims[1]) + m % dims[1];
}
else m_small2 = int(m / dims[1]);
double *a_small = new double [m_small * m_small2];
/*Initialization of matrix*/
for (int i=0; i<m_small; i++) {
for (int j=0; j<m_small2; j++) {
a_small[index3(i,j)] = 2.5 ;
}
}
if (0 == world_rank) {
a_2 = new double[m_small*m_small2];
for (int i=0; i<m_small; i++) {
for (int j=0; j<m_small2; j++) {
a_2[index3(i,j)] = 0;
}
}
}
int loc[2];
int m1_rec, m2_rec;
MPI_Request send_req;
MPI_Isend(coords, 2, MPI_INT, 0, 1, MPI_COMM_WORLD, &send_req);
//This Isend may have problem!
MPI_Isend(a_small, m_small*m_small2, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &send_req);
if (0 == world_rank) {
for (int i = 0; i < world_size; i++) {
MPI_Recv(loc, 2, MPI_INT, i, 1, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
/*Determine the size of matrix for receiving the information*/
if (0 == loc[0]) {
m1_rec = int(m / dims[0]) + m % dims[0];
} else {
m1_rec = int(m / dims[0]);
}
if (0 == loc[1]) {
m2_rec = int(m / dims[1]) + m % dims[1];
} else {
m2_rec = int(m / dims[1]);
}
//This receive may have problem!
MPI_Recv(a_2, m1_rec * m2_rec, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
}
}
delete[] a_small;
if (0 == world_rank) {
delete[] a;
delete[] a_2;
}
delete[] dims;
delete[] period;
delete[] coords;
MPI_Finalize();
return 0;
}
Basically, the code reads an input value m, and then construct a big matrix of m x m. MPI creates a 2D topology according to the number of CPUs, which divide the big matrix to sub-matrix. The size of the sub-matrix is m_small x m_small2. There should be no problem in these steps.
The problem happens when I send the sub-matrix in each rank to rank-0 using MPI_Isend(a_small, m_small*m_small2, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &send_req); and MPI_Recv(a_2, m1_rec * m2_rec, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);.
For example, when I run the code by this command: mpirun -np 2 ./a.out 183, I will get the error of
Read -1, expected 133224, errno = 14
*** Process received signal ***
Signal: Segmentation fault (11)
Signal code: Address not mapped (1)
Failing at address: 0x7fb23b485010
--------------------------------------------------------------------------
Primary job terminated normally, but 1 process returned
a non-zero exit code. Per user-direction, the job has been aborted.
--------------------------------------------------------------------------
--------------------------------------------------------------------------
mpirun noticed that process rank 1 with PID 0 on node dx1-500-24164 exited on signal 11 (Segmentation fault).
Strangely, If I modify the CPU number or decrease the value of input argument, the problem is not there anymore. Also, If I just comment out the MPI_Isend/Recv, there is no problem either.
So I am really wondering how to solve this problem?
Edit.1
The minimum example to reproduce the problem.
When the size of matrix is small, there is no problem. But problem comes when you increase the size of matrix (at least for me):
#include <iostream>
#include <mpi.h>
#include <cmath>
int world_size;
int world_rank;
MPI_Comm comm;
int m, m_small, m_small2;
int main(int argc, char **argv) {
MPI_Init(&argc, &argv);
MPI_Status status;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
m = atoi(argv[1]); //Size
double *a_2;
//Please increase the size of m_small and m_small2 and wait for the problem to happen
m_small = 100;
m_small2 = 200;
double *a_small = new double [m_small * m_small2];
if (0 == world_rank) {
a_2 = new double[m_small*m_small2];
}
MPI_Request send_req;
MPI_Isend(a_small, m_small*m_small2, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &send_req);
if (0 == world_rank) {
for (int i = 0; i < world_size; i++) {
MPI_Recv(a_2, m_small*m_small2, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
}
}
delete[] a_small;
if (0 == world_rank) {
delete[] a_2;
}
MPI_Finalize();
return 0;
}
Command to run mpirun -np 2 ./a.out 183 (The input argument is actually not used the by code this time)
The problem is in the line
MPI_Isend(a_small, m_small*m_small2, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &send_req);
MPI_Isend is non-blocking send (which you pair with blocking MPI_Recv), thus when it returns it still may use a_small until you wait for the send to complete (when you are free to use a_small again) using, e.g., MPI_Wait(&send_req, MPI_STATUS_IGNORE);. So, you then delete a_small while it may still be in use by non-blocking message sending code, which likely causes access of deleted memory, which can lead to segfault and crash. Try using blocked send like this:
MPI_Send(a_small, m_small*m_small2, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
This will return when a_small can be reused (including by deletion), though data may still not be recieved by recievers by that time, but rather held in an internal temporary buffer.
I'm attempting to solve a homework problem to debug the following unit test.
Basically the master process generates random integers and sends them to child processes to check for primality, the result is communicated back to the master process, and the algorithm ends.
I know that the loops should be replaced with collective communication, however that is a different part of the question. I want to understand why this code I have here is causing deadlock.
From reading other questions, I know that the number of sends/receives should match each other. However I don't see how this isn't the case in my code.
The current behaviour is that a prime is found, sent back to the master process, at which point the program just hangs - until it's canceled manually using ctrl-C.
I understand that this isn't the idiomatic way of solving this problem, but would really like to know exactly where the bug is in this approach.
thanks!
TEST_CASE("3a - Finding prime numbers", "[MPI]" )
{
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
// Random number generation
std::minstd_rand generator;
unsigned min(2342), max(12342340);
std::uniform_int_distribution<> distribution(min, max);
// candidates too big, one of the size values is the master node
std::vector<unsigned> candidates(size - 1);
// Main loop continues until a prime is found
int found_prime(0);
while (found_prime == 0) {
if (rank == 0) {
// Create some candidate numbers for each process to test
std::generate(candidates.begin(), candidates.end(),
[&]() { return distribution(generator); });
// Send one to each worker
for (int worker(1); worker < size; ++worker) {
int rc = MPI_Ssend(&candidates[worker - 1], 1, MPI_UNSIGNED,
worker, 0, MPI_COMM_WORLD);
REQUIRE(rc == MPI_SUCCESS);
}
// Receive whether it was prime
for (int worker(1); worker < size; ++worker) {
unsigned result;
int rc = MPI_Recv(&result, 1, MPI_UNSIGNED, worker, 0,
MPI_COMM_WORLD, MPI_STATUS_IGNORE);
REQUIRE(rc == MPI_SUCCESS);
if (result == 1) {
found_prime = candidates[worker - 1];
std::cout << "Worker " << worker << " found prime "
<< found_prime << std::endl;
}
}
} else {
// Receive the candidate to check
unsigned candidate;
int rc = MPI_Recv(&candidate, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
REQUIRE(rc == MPI_SUCCESS);
// Do the check
unsigned is_prime = mp::IsPrime(candidate) ? 1 : 0;
// Return the result
rc = MPI_Ssend(&is_prime, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD);
REQUIRE(rc == MPI_SUCCESS);
}
}
std::cout << "Finished" << rank << std::endl;
}
I know nothing about MPI, but in your code, if rank != 0, the while loop can never be exited because found_prime is never set in the else branch (and rank is never changed too).
Edit:
As #DanielLangr said, the slaves will need a way to find out that there's no more work to come and exit (the loop).
So let's say that I have created a main-worker program with the following steps:
1 - main sends tasks to all workers
2 - faster workers accomplish tasks and send the results back to the main
3 - main receives the results from the fastest workers and sends new tasks to everyone
4 - faster workers are ready to receive the task, but slower workers have to interrupt or cancel the old, slow, task that they were doing, in order to start the new task at the same time as the faster worker
I know how to do all the steps, except for step 4, where I would have to interrupt what the slower workers are doing in order to proceed to the next task.
Here is an example of an incomplete code that is missing that part:
#include <mpi.h>
#include <iostream>
#include <string>
#include <unistd.h>
using namespace std;
int main(int argc, char* argv[])
{
MPI_Init(&argc,&argv);
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
if (rank == 0) {
int value = 17;
string valuec = "Hello";
for(int i = 1; i < world_size; i++){
int result = MPI_Send(valuec.c_str(), valuec.length(), MPI_CHAR, i, 0, MPI_COMM_WORLD);
if (result == MPI_SUCCESS)
std::cout << "Rank 0 OK!" << std::endl;
}
int workersDone = 0;
MPI_Status status;
int flag = 0;
while(1){
flag=0;
MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &status);
if(flag==1){
workersDone++;
cout << "Workers done: " << workersDone << endl;
}
if(workersDone >= world_size/2){/* here the main moves on
before all workers are done
*/
cout << "Main breaking" << endl;
break;
}
}
/* interruption Here:
How do I make the main tell to the slow workers
interrupt or cancel what they were doing in order
to receive new tasks
*/
// New tasks should go here here
} else if (rank != 0) {
int receivedMessages = 0;
while(1){
MPI_Status status;
int flag = 0;
MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &status);
if(flag==1){
receivedMessages++;
int value;
char buffer[256];
int result = MPI_Recv(&buffer, 256, MPI_CHAR, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
cout << rank << " received " << buffer << endl;
sleep(rank); /* this hypothetical task will be slower
in some workers, faster in others. In the
final version of code of course this
will not be a sleep command, and the time
it takes will not be proportional to the
process rank.
*/
MPI_Send(buffer, sizeof(buffer), MPI_CHAR, 0, 0, MPI_COMM_WORLD);
cout << rank << " breaking" << endl;
break;
}
}
}
MPI_Finalize();
return 0;
}
I'm trying to scatter values among processes belonging to an hypercube group (quicksort project).
Depending on the amount of processes I either create a new communicator excluding excessive processes, or I duplicate MPI_COMM_WORLD if it fits exactly any hypercube (power of 2).
In both cases, processes other than 0 receive their data, but:
- On first scenario, process 0 throws a segmentation fault 11
- On second scenario, nothing faults, but process 0 received values are gibberish.
NOTE: If I try a regular MPI_Scatter everything works well.
//Input
vector<int> LoadFromFile();
int d; //dimension of hypercube
int p; //active processes
int idle; //idle processes
vector<int> values; //values loaded
int arraySize; //number of total values to distribute
int main(int argc, char* argv[])
{
int mpiWorldRank;
int mpiWorldSize;
int mpiRank;
int mpiSize;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &mpiWorldRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiWorldSize);
MPI_Comm MPI_COMM_HYPERCUBE;
d = log2(mpiWorldSize);
p = pow(2, d); //Number of processes belonging to the hypercube
idle = mpiWorldSize - p; //number of processes in excess
int toExclude[idle]; //array of idle processes to exclude from communicator
int sendCounts[p]; //array of values sizes to be sent to processes
//
int i = 0;
while (i < idle)
{
toExclude[i] = mpiWorldSize - 1 - i;
++i;
}
//CREATING HYPERCUBE GROUP: Group of size of power of 2 -----------------
MPI_Group world_group;
MPI_Comm_group(MPI_COMM_WORLD, &world_group);
// Remove excessive processors if any from communicator
if (idle > 0)
{
MPI_Group newGroup;
MPI_Group_excl(world_group, 1, toExclude, &newGroup);
MPI_Comm_create(MPI_COMM_WORLD, newGroup, &MPI_COMM_HYPERCUBE);
//Abort any processor not part of the hypercube.
if (mpiWorldRank > p)
{
cout << "aborting: " << mpiWorldRank <<endl;
MPI_Finalize();
return 0;
}
}
else
{
MPI_Comm_dup(MPI_COMM_WORLD, &MPI_COMM_HYPERCUBE);
}
MPI_Comm_rank(MPI_COMM_HYPERCUBE, &mpiRank);
MPI_Comm_size(MPI_COMM_HYPERCUBE, &mpiSize);
//END OF: CREATING HYPERCUBE GROUP --------------------------
if (mpiRank == 0)
{
//STEP1: Read input
values = LoadFromFile();
arraySize = values.size();
}
//Transforming input vector into an array
int valuesArray[values.size()];
if(mpiRank == 0)
{
copy(values.begin(), values.end(), valuesArray);
}
//Broadcast input size to all processes
MPI_Bcast(&arraySize, 1, MPI_INT, 0, MPI_COMM_HYPERCUBE);
//MPI_Scatterv: determining size of arrays to be received and displacement
int nmin = arraySize / p;
int remainingData = arraySize % p;
int displs[p];
int recvCount;
int k = 0;
for (i=0; i<p; i++)
{
sendCounts[i] = i < remainingData
? nmin+1
: nmin;
displs[i] = k;
k += sendCounts[i];
}
recvCount = sendCounts[mpiRank];
int recvValues[recvCount];
//Following MPI_Scatter works well:
// MPI_Scatter(&valuesArray, 13, MPI_INT, recvValues , 13, MPI_INT, 0, MPI_COMM_HYPERCUBE);
MPI_Scatterv(&valuesArray, sendCounts, displs, MPI_INT, recvValues , recvCount, MPI_INT, 0, MPI_COMM_HYPERCUBE);
int j = 0;
while (j < recvCount)
{
cout << "rank " << mpiRank << " received: " << recvValues[j] << endl;
++j;
}
MPI_Finalize();
return 0;
}
First of all, you are supplying wrong arguments to MPI_Group_excl:
MPI_Group_excl(world_group, 1, toExclude, &newGroup);
// ^
The second argument specifies the number of entries in the exclusion list and should therefore be equal to idle. Since you are excluding a single rank only, the resulting group has mpiWorldSize-1 ranks and hence MPI_Scatterv expects that both sendCounts[] and displs[] have that many elements. Of those only p elements are properly initialised and and the rest are random, therefore MPI_Scatterv crashes in the root.
Another error is the code that aborts the idle processes: it should read if (mpiWorldRank >= p).
I would recommend that the entire exclusion code is replaced by a single call to MPI_Comm_split instead:
MPI_Comm comm_hypercube;
int colour = mpiWorldRank >= p ? MPI_UNDEFINED : 0;
MPI_Comm_split(MPI_COMM_WORLD, colour, mpiWorldRank, &comm_hypercube);
if (comm_hypercube == MPI_COMM_NULL)
{
MPI_Finalize();
return 0;
}
When no process supplies MPI_UNDEFINED as its colour, the call is equivalent to MPI_Comm_dup.
Note that you should avoid using in your code names starting with MPI_ as those could clash with symbols from the MPI implementation.
Additional note: std::vector<T> uses contiguous storage, therefore you could do without copying the elements into a regular array and simply provide the address of the first element in the call to MPI_Scatter(v):
MPI_Scatterv(&values[0], ...);
I'm not sure that I am correctly understanding what MPI_Scatterv is supposed to do. I have 79 items to scatter amounts a variable amount of nodes. However, when I use the MPI_Scatterv command I get ridiculous numbers (as if the array elements of my receiving buffer are uninitialized). Here is the relevant code snippet:
MPI_Init(&argc, &argv);
int id, procs;
MPI_Comm_rank(MPI_COMM_WORLD, &id);
MPI_Comm_size(MPI_COMM_WORLD, &procs);
//Assign each file a number and figure out how many files should be
//assigned to each node
int file_numbers[files.size()];
int send_counts[nodes] = {0};
int displacements[nodes] = {0};
for (int i = 0; i < files.size(); i++)
{
file_numbers[i] = i;
send_counts[i%nodes]++;
}
//figure out the displacements
int sum = 0;
for (int i = 0; i < nodes; i++)
{
displacements[i] = sum;
sum += send_counts[i];
}
//Create a receiving buffer
int *rec_buf = new int[79];
if (id == 0)
{
MPI_Scatterv(&file_numbers, send_counts, displacements, MPI_INT, rec_buf, 79, MPI_INT, 0, MPI_COMM_WORLD);
}
cout << "got here " << id << " checkpoint 1" << endl;
cout << id << ": " << rec_buf[0] << endl;
cout << "got here " << id << " checkpoint 2" << endl;
MPI_Barrier(MPI_COMM_WORLD);
free(rec_buf);
MPI_Finalize();
When I run that code I receive this output:
got here 1 checkpoint 1
1: -1168572184
got here 1 checkpoint 2
got here 2 checkpoint 1
2: 804847848
got here 2 checkpoint 2
got here 3 checkpoint 1
3: 1364787432
got here 3 checkpoint 2
got here 4 checkpoint 1
4: 903413992
got here 4 checkpoint 2
got here 0 checkpoint 1
0: 0
got here 0 checkpoint 2
I read the documentation for OpenMPI and looked through some code examples, I'm not sure what I'm missing any help would be great!
One of the most common MPI mistakes strikes again:
if (id == 0) // <---- PROBLEM
{
MPI_Scatterv(&file_numbers, send_counts, displacements, MPI_INT,
rec_buf, 79, MPI_INT, 0, MPI_COMM_WORLD);
}
MPI_SCATTERV is a collective MPI operation. Collective operations must be executed by all processes in the specified communicator in order to complete successfully. You are executing it only in rank 0 and that's why only it gets the correct values.
Solution: remove the conditional if (...).
But there is another subtle mistake here. Since collective operations do not provide any status output, the MPI standard enforces strict matching of the number of elements sent to some rank and the number of elements the rank is willing to receive. In your case the receiver always specifies 79 elements which might not match the corresponding number in send_counts. You should instead use:
MPI_Scatterv(file_numbers, send_counts, displacements, MPI_INT,
rec_buf, send_counts[id], MPI_INT,
0, MPI_COMM_WORLD);
Also note the following discrepancy in your code that might as well be a typo while posting the question here:
MPI_Comm_size(MPI_COMM_WORLD, &procs);
^^^^^
int send_counts[nodes] = {0};
^^^^^
int displacements[nodes] = {0};
^^^^^
While you obtain the number of ranks in the procs variable, nodes is used in the rest of your code. I guess nodes should be replaced by procs.