Hi Stephen,

Let me try to answer your questions inline (I don't have extensive experience with the separate model and from my experience most implementations support the unified model, with some exceptions):

On 5/31/20 1:31 AM, Stephen Guzik via users wrote:
Hi,

I'm trying to get a better understanding of coordinating (non-overlapping) local stores with remote puts when using passive synchronization for RMA.  I understand that the window should be locked for a local store, but can it be a shared lock?

Yes. There is no reason why that cannot be a shared lock.

In my example, each process retrieves and increments an index (indexBuf and indexWin) from a target process and then stores it's rank into an array (dataBuf and dataWin) at that index on the target.  If the target is local, a local store is attempted:

/* indexWin on indexBuf, dataWin on dataBuf */
std::vector<int> myvals(numProc);
MPI_Win_lock_all(0, indexWin);
MPI_Win_lock_all(0, dataWin);
for (int tgtProc = 0; tgtProc != numProc; ++tgtProc)
   {
    MPI_Fetch_and_op(&one, &myvals[tgtProc], MPI_INT, tgtProc, 0, MPI_SUM,indexWin);
     MPI_Win_flush_local(tgtProc, indexWin);
     // Put our rank into the right location of the target
     if (tgtProc == procID)
       {
         dataBuf[myvals[procID]] = procID;
       }
     else
       {
        MPI_Put(&procID, 1, MPI_INT, tgtProc, myvals[tgtProc], 1, MPI_INT,dataWin);
       }
   }
MPI_Win_flush_all(dataWin);  /* Force completion and time synchronization */
MPI_Barrier(MPI_COMM_WORLD);
/* Proceed with local loads and unlock windows later */

I believe this is valid for a unified memory model but would probably fail for a separate model (unless a separate model very cleverly merges a private and public window?)  Is this understanding correct?  And if I instead use MPI_Put for the local write, then it should be valid for both memory models?

Yes, if you use RMA operations even on local memory it is valid for both memory models.

The MPI standard on page 455 (S3) states that "a store to process memory to a location in a window must not start once a put or accumulate update to that target window has started, until the put or accumulate update becomes visible in process memory." So there is no clever merging and it is up to the user to ensure that there are no puts and stores happening at the same time.


Another approach is specific locks.  I don't like this because it seems there are excessive synchronizations.  But if I really want to mix local stores and remote puts, is this the only way using locks?

/* indexWin on indexBuf, dataWin on dataBuf */
std::vector<int> myvals(numProc);
for (int tgtProc = 0; tgtProc != numProc; ++tgtProc)
   {
     MPI_Win_lock(MPI_LOCK_SHARED, tgtProc, 0, indexWin);
    MPI_Fetch_and_op(&one, &myvals[tgtProc], MPI_INT, tgtProc, 0, MPI_SUM,indexWin);
     MPI_Win_unlock(tgtProc, indexWin);
     // Put our rank into the right location of the target
     if (tgtProc == procID)
       {
         MPI_Win_lock(MPI_LOCK_EXCLUSIVE, tgtProc, 0, dataWin);
         dataBuf[myvals[procID]] = procID;
         MPI_Win_unlock(tgtProc, dataWin);  /*(A)*/
       }
     else
       {
         MPI_Win_lock(MPI_LOCK_SHARED, tgtProc, 0, dataWin);
        MPI_Put(&procID, 1, MPI_INT, tgtProc, myvals[tgtProc], 1, MPI_INT,dataWin);
         MPI_Win_unlock(tgtProc, dataWin);
       }
   }
/* Proceed with local loads */

I believe this is also valid for both memory models?  An unlock must have followed the last access to the local window, before the exclusive lock is gained.  That should have synchronized the windows and another synchronization should happen at (A).  Is that understanding correct?

That is correct for both memory models, yes. It is likely to be slower because locking and unlocking involves some effort. You are better off using put instead.

If you really want to use local stores you can check for the MPI_WIN_UNIFIED attribute and fall-back to using puts only for the separate model.

> If so, how does one ever get into a situation where MPI_Win_sync must be used?

You can think of a synchronization scheme where each process takes a shared lock on a window, stores data to a local location, calls MPI_Win_sync and signals to other processes that the data is now available, e.g., through a barrier or a send. In that case processes keep the lock and use some non-RMA synchronization instead.


Final question.  In the first example, let's say there is a lot of computation in the loop and I want the MPI_Puts to immediately make progress.  Would it be sensible to follow the MPI_Put with a MPI_Win_flush_local to get things moving?  Or is it best to avoid any unnecessary synchronizations?

That is highly implementation-specific. Some implementations may buffer the puts and delay the transfer to the flush, some may initiate it immediately, and some may treat a local flush similar to a regular flush. I would not make any assumptions about the underlying implementation and defer flushes as long as possible.

Cheers
Joseph


Thanks,
Stephen

Reply via email to