SHMEMmer Wanna Be

My Journey To Understanding and Using SHMEM:

Lesson 2: shmem_put

Ok. So we now have a way to grab data from another pe. But, we need a way to put data onto other pes. And one question pops out in my mind: can we do so without shaking hands prior?

Similar to shmem_get, the put command allows a pe to put data onto other pes without receiving permission. BUT, with much freedom comes much responsibility ... We must be careful that we don't overwrite data that is needed - in other words, we need to avoid race conditions, and this is where you and I come in as programmers.


Today, we are only going to cover three of the basic put commands:
  1. copying only one element onto another pe
  2. copying an array of elements onto another pe
  3. copying strided data onto another pe

---Copying only one element onto another pe---

To do so, we use the following command:
        void shmem_<TYPENAME>_p(TYPE *dest, TYPE value, int pe);
If you remember from lesson 1, the get command is very similar except in this case nothing is returned. In addition, the types allowed are the same:
TYPE TYPENAME
short short
int int
long long
long long longlong
unsigned short ushort
unsigned int uint
unsigned long ulong
unsigned long long ulonglong
int32_t int32
int64_t int64
uint32_t uint32
uint64_t uint64
size_t size
ptrdiff_t ptrdiff


//put_data_ex0.c

#include <stdio.h>
#include <shmem.h>

int main(){
	int my_pe, num_pe;          //declare variables for both pe id of processor and the number of pes
	
	shmem_init();
	num_pe = shmem_n_pes();     //obtain the number of pes that can be used
	my_pe  = shmem_my_pe();     //obtain the pe id number

	int pe_source = 0;          //pe where the desired data is residing
	int pe_dest   = 1;
	
	double source = 1;
	static double dest;         //variable to be filled
	
	//if main node, put data onto destination node
	if (my_pe == pe_source){
		shmem_double_p(&dest, source, pe_dest);
	}
	shmem_barrier_all();
	if (my_pe == pe_dest){
		printf("%d: Data at dest is now %f\n", my_pe, dest);	
	}
	shmem_finalize();
	return 0;
}


To compile:
        oshcc put_data_ex0.c -o put_data_ex0
To run:
        oshrun -np 10 put_data_ex0

Lets first take a look at the code
	if (my_pe == pe_source){
		shmem_double_p(&dest, source, pe_dest);
	}	
Note that PE 1 skips this command and would just go on to the next. So, what would happen then if the next command was to print the data? Do you think the correct data would be there yet? Unless PE 1 is super lazy and moving ultra slow, the answer is no. The data it would print would be the data from of old. Haha. I just imagine an assembly line in a cartoon with Tom and Jerry. Tom (the cat) would just be redoing his work over and over again --- Jerry in this instance would probably be slow just to spite Tom.

So, how do we ensure that PE 1 (or Tom) has the correct piece? This is where a sync comes into play. In the above, I used the most basic: shmem_barrier_all(), which makes all the PEs stop until both the local and remote data are in their proper locations. But, you are probably already asking as I was, "wait. What about the scenario where you only want to wait for a certain PE's data to finish moving? Or what if you don't care if all of the data is in the correct location before moving on?" Well, thankfully, there are commands that cover these scenarios and more. The next lesson will go into more detail on these.

---Copying an array of elements onto another pe---

I don't know about you, but memorization is not my forte. And my short term memory is very similar to Dory's from Finding Nemo. But, this is where I am super thankful for understandable commands and similar commands!!!!!!

Think back to the last lesson where we learned shmem_g and shmem_get. Notice that for only one element, there is only one single letter "g"; whereas for the grabbing an array of elements we use the whole word "get". Well, thankfully, we are in a similar situation. To put an array of elements onto another pe, the whole word "put" is used and the arguments are identical to that of the "get" command.

To put an array of elements onto another pe, we can use one of the two following commands: where the former can only be used with your basic C types. Now, some of you may be fortunate (I currently am not) and can use the simple version
        shmem_put(TYPE *dest, const TYPE *source, size_t nelems, int pe)
To be able to use the above, you need a C11 compiler. In that case, you can use the shortened version of all of the commands (just eliminate the TYPENAME or SIZE).

//put_data_ex1.c

#include <stdio.h>
#include <shmem.h>

int main(){
	int my_pe, num_pe;          //declare variables for both pe id of processor 
                                //and the number of pes
	
	shmem_init();
	num_pe = shmem_n_pes();     //obtain the number of pes that can be used
	my_pe  = shmem_my_pe();     //obtain the pe id number

	int pe_source = 0;          //pe where the desired data is residing
	int pe_dest   = 1;
	
	double source[10] = {1,2,3,4,5,6,7,8,9,10};
	static double dest[10];     //array to be filled
	
	//if main node, put data onto destination node
	if (my_pe == pe_source){
		shmem_double_put(dest, source, 10, pe_dest);
	}
	shmem_barrier_all();
	if (my_pe == pe_dest){
		printf("%d: Data at dest is now ", my_pe);
		for (int i=0; i<10; i++)
			printf(" %d," dest[i]);	
		printf("\n");
	}
	shmem_finalize();
	return 0;
}


To compile:
        oshcc put_data_ex1.c -o put_data_ex1
To run:
        oshrun -np 10 put_data_ex1