MPI IO Fileviews

Any MPI file, like all files, is a linear sequence of bytes. We could store any data in any order we wished by careful computation of offsets. However, this rapidly becomes complicated and error-prone for more complex data structures. Fortunately, MPI provides the fileview to help with this task. The fileview is the “window” into the file that each process sees. In this way, we can define the global structure without having to allocate space for it on any process.

One of the most common data structures for numerically-intensive programming is the multidimensional array. We have used them for many examples. To create a fileview, we assign a portion of the array to each process, generally by the usual topology computation.

Assignment of a portion of a global array to each process.

To accomplish this, we use a subarray. When we examined subarrays previously, we created subarrays as subsets of the local array on each process. For a fileview, we will create a subarray relative to the global array. We use the topology to compute the start and stop positions for row and column (Fortran programmers, remember we count from 0). The global dimensions make up the size and the local dimensions are the subsizes. Remember that the global array does not need to exist on the process; we are telling MPI how to subdivide it.

After defining the subarray we pass it to MPI_File_set_view, with the file handle of a previously-opened MPI file. We can then call MPI_File_write<_all> or MPI_file_read<_all> as we have done before, passing it the local data array.

MPI_File_set_view(fh, offset, elem_type, subtype, repr, info);
MPI_File_write_all(fh, loc_dat, count , elem_type, status);
call MPI_FILE_SET_VIEW(fh, offset, elem_type, subtype, repr, info)
call MPI_FILE_WRITE_ALL(fh,loc_data, count, elem_type, status)
```python
fh.Set_view(offset, elem_type, subtype, repr, info)
fh.Write_all(local_dat)

In this syntax, elem_type is the elementary type and subtype is the subarray we defined.

The repr is a string indicating the data representation format. It can take values:

«{ table >}}

Value Representation
’native' bytes are dumped directly from memory.
‘internal’ the representation used internally by the specific MPI implementation.
’external` The XDR portable data formats are used.

XDR (External Data Representation) is a standard intended to make data easily convertible from one representation to another. It was especially important before computer systems largely settled on one standard; however, data representation can still vary based on platform and XDR is still used in a variety of applications, including network transmissions.

MPI guarantees full interoperability within an environment, so native is often used for the data representation if the file will be written and read on the same platform. Otherwise the representation external32 for 32-bit XDR data is widely used.

A full example for each language is below:

C++

Contents of mpi_io_fileview.cxx

#include <cstring>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
#include <mpi.h>

using namespace std;

int main (int argc, char *argv[]) {

    // Declarations for MPI
    int rank, nprocs;
    int errcode;
    MPI_Status mpi_stat;
    MPI_Info info;
    MPI_Offset offset;
    MPI_File fh;
    int root=0, tag=0;
    int mpi_err;

    int nrows, ncols;

    // Check number of parameters and read in filename
    if (argc < 2) {
       printf ("USAGE:  %s output-file <nrows> <ncols>\n", argv[0]);
    exit(1);
    }
    const char *fname=argv[1];

    if (argc == 2) {
       nrows=4;
       ncols=4;
    }
    else if (argc == 3) {
       nrows=atoi(argv[2]);
       ncols=nrows;
    } 
    else if (argc == 4) {
       nrows=atoi(argv[2]);
       ncols=atoi(argv[3]);
    }

    //Initialize MPI
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);

    if (rows*cols != nprocs) {
        cout<<"Number of rows times columns does not equal nprocs\n";
        MPI_Finalize();
    return 1;
    }

    // Hard-code sizes so we can see what we're doing

    int nrl = 4;
    int ncl = 4;

    int N=nrl*nrows;
    int M=ncl*ncols;

    //Set up the topology
    int lrow=rank/ncols;
    int lcol=rank%ncols;

    int **loc_u=new int*[nrl];
    int *luptr=new int[(nrl)*(ncl)];

    for (int i=0;i<nrl;++i,luptr+=ncl) {
       loc_u[i] = luptr;
    }

    for ( int i = 0; i < nrl; i++ ) {
        for (int j = 0; j < ncl; j++ ) {
             loc_u[i][j] = rank+1;
         }
    }

    int lusize=nrl*ncl;

    //Write each segment to conventional file (the "old way")
    ofstream fp;
    string myfile=argv[1]+to_string(rank);
    fp.open(myfile,ios::out);
    for (int i = 0; i < nrl; i++) {
        for (int j = 0; j < ncl; j++) {
            fp<<loc_u[i][j]<<" ";
        }
        fp<<"\n";
    }
    fp.close();

    int gdims[]={N,M};
    int ldims[]={nrl,ncl};
    int starts[]={ncl*lrow,nrl*lcol};
    cout<<"Starts for rank "<<rank<<" are  "<<starts[0]<<" "<<starts[1]<<endl;

    MPI_Datatype locarray;
    MPI_Type_create_subarray(2,gdims,ldims,starts,MPI_ORDER_C,MPI_INT,&locarray);
    MPI_Type_commit(&locarray);

    int amode=MPI_MODE_CREATE | MPI_MODE_WRONLY;
    mpi_err=MPI_File_open(MPI_COMM_WORLD,fname,amode,MPI_INFO_NULL,&fh);

    if (mpi_err != MPI_SUCCESS) {
        cout<<"Unable to open file for writing\n";
        MPI_Finalize();
        exit(2);
    }

    offset=0;
    MPI_File_set_view(fh, offset, MPI_INT, locarray, "native", MPI_INFO_NULL);

    MPI_File_write_all(fh,&loc_u[0][0], lusize , MPI_INT, &mpi_stat);
    MPI_File_close(&fh);

    //All processes read file
    amode=MPI_MODE_RDONLY;
    mpi_err=MPI_File_open(MPI_COMM_WORLD,fname,amode,MPI_INFO_NULL,&fh);
        if ( mpi_err != MPI_SUCCESS) {
       MPI_Finalize();
       cout<<"Unable to open MPI file for reading\n";
       exit(2);
    }

    int **rbuf=new int*[nrl];
    int *rptr=new int[(nrl)*(ncl)];

    for (int i=0;i<nrl;++i,rptr+=ncl) {
       rbuf[i] = rptr;
    }

    for ( int i = 0; i < nrl; i++ ) {
        for (int j = 0; j < ncl; j++ ) {
             rbuf[i][j] = 0;
         }
    }

    MPI_File_set_view(fh, offset, MPI_INT, locarray, "native", MPI_INFO_NULL);
    MPI_File_read_all(fh, &rbuf[0][0], lusize, MPI_INT, &mpi_stat);
    MPI_File_close(&fh);

    //Usual trick to print one rank at a time
    int position;
    int *u =  new int[lusize];

    if ( rank == 0 ) {
        cout<<"Result for rank 0\n";
        for (int i=0;i<nrl;i++) {
            for (int j=0;j<ncl;j++) {
                cout<<rbuf[i][j]<<" ";
            }
            cout<<endl;
        }
        for (int n=1;n<nprocs;++n) {
           memset(u,0.,lusize);
           MPI_Recv(u,lusize,MPI_INT,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&mpi_stat);
           cout<<"Result for rank "<<mpi_stat.MPI_SOURCE<<endl;
           position=0;
           for (int i=0;i<nrl;i++) {
               for (int j=0;j<ncl;j++) {
                   cout<<u[position++]<<" ";
               }
               cout<<endl;
           }
       }
    }
    else {
        // Pack the 2D array into the buffer
        position=0;
        for (int i=0; i<nrl; i++)
            for (int j=0;j<ncl; j++)
                u[position++]=rbuf[i][j];

        MPI_Send(u,lusize, MPI_INT, 0, rank, MPI_COMM_WORLD);
    }

    MPI_Type_free(&locarray);

    MPI_Finalize();

}

Download mpi_io_fileview.cxx file

Fortran

Contents of mpi_io_fileview.f90

program mpiwrite
   use mpi_f08
   implicit none

   integer            :: i,j
   character(len=80)  :: arg
   integer            :: numargs

   integer            :: nrows, ncols
   integer            :: nrl, ncl, lrow, lcol
   integer            :: N, M

   integer            :: rank, nprocs
   integer            :: mpi_err
   integer, parameter :: root=0
   type(MPI_Status)   :: mpi_stat
   type(MPI_File)     :: fh
   type(MPI_Datatype) :: locarray
   integer            :: amode
   integer            :: lusize
   integer, dimension(2) :: gdims, ldims, starts
   integer(kind=MPI_OFFSET_KIND) :: offset=0
   integer, allocatable, dimension(:,:) :: loc_u, u, rbuf
   character(len=24)  :: fname
   character(len=80)  :: myfile

   ! check number of parameters and read in filename
   ! all ranks do this, avoids broadcast
   numargs=command_argument_count()
   if (numargs .lt. 1) then
      stop 'USAGE: output-file <nrows> <ncols>'
  else 
      call get_command_argument(1,fname)
      nrows=4
      ncols=4
   endif
   if (numargs .eq. 2) then
      call get_command_argument(2,arg)
      read(arg,'(i4)') nrows
      ncols=nrows
   else if (numargs .eq. 3) then
      call get_command_argument(2,arg)
      read(arg,'(i4)') nrows
      call get_command_argument(3,arg)
      read(arg,'(i4)') ncols
   endif

   !Initialize MPI
   call MPI_INIT()
   call MPI_COMM_SIZE(MPI_COMM_WORLD,nprocs)
   call MPI_COMM_RANK(MPI_COMM_WORLD,rank)

   if (nrows*ncols /= nprocs) then
       call MPI_Finalize()
       stop "Number of rows times columns does not equal nprocs"
   endif


   ! Hard-code sizes so we can see what we're doing

   nrl = 4
   ncl = 4

   N=nrl*nrows
   M=ncl*ncols

   !Set up the topology
   lrow=rank/ncols
   lcol=mod(rank,ncols)

   allocate(loc_u(nrl,ncl))
   loc_u=(rank+1)

   !Write each segment to conventional file (the "old way")
   write(myfile,'(a,i2.2)') fname(1:len_trim(fname)),rank

   open(unit=10,file=myfile,status='unknown')
   do i=1,nrl
      write (10,'(*(g0,1x))') loc_u(i,:)
   enddo
   close(10)

   gdims=[N,M]
   ldims=[nrl,ncl]
   starts=[ncl*lrow,nrl*lcol]
   write(*,*) rank,starts

   call MPI_TYPE_CREATE_SUBARRAY(2, gdims, ldims, starts, MPI_ORDER_FORTRAN,   &
                                                          MPI_INTEGER, locarray)

   call MPI_TYPE_COMMIT(locarray)

   amode=ior(MPI_MODE_CREATE, MPI_MODE_WRONLY)
   call MPI_FILE_OPEN(MPI_COMM_WORLD,trim(fname),amode,MPI_INFO_NULL,fh,mpi_err)

   if ( mpi_err /= MPI_SUCCESS) then
       call MPI_FINALIZE()
       stop "Unable to open MPI file for writing"
   endif

   offset=0
   call MPI_FILE_SET_VIEW(fh, offset, MPI_INTEGER, locarray,                   &
                                    'native', MPI_INFO_NULL)

   call MPI_FILE_WRITE_ALL(fh,loc_u, size(loc_u), MPI_INTEGER, mpi_stat)
   call MPI_FILE_CLOSE(fh)

   !All processes read file
   amode=MPI_MODE_RDONLY
   call MPI_FILE_OPEN(MPI_COMM_WORLD,trim(fname),amode,MPI_INFO_NULL,fh,mpi_err)
   if ( mpi_err /= MPI_SUCCESS) then
       call MPI_FINALIZE()
       stop "Unable to open MPI file for reading"
   endif

   allocate(u(nrl,ncl),rbuf(nrl,ncl))
   rbuf=0
   lusize=size(u)

   call MPI_FILE_SET_VIEW(fh, offset, MPI_INTEGER, locarray,                   &
                                    'native', MPI_INFO_NULL)

   call MPI_FILE_READ_ALL(fh, rbuf, lusize, MPI_INTEGER, mpi_stat)
   call MPI_FILE_CLOSE(fh)

  !Usual trick to print one rank at a time
   if (rank==0) then
       write(*,*) 'Result for rank 0'
       do j=1,nrl
          write(*,'(*(i10))') rbuf(j,:)
       enddo
       do i=1,nprocs-1
          u=0
          call MPI_Recv(u,lusize, MPI_INTEGER,MPI_ANY_SOURCE,                  &
                                 MPI_ANY_TAG, MPI_COMM_WORLD,mpi_stat)
          write(*,*) 'Result for rank',mpi_stat%MPI_SOURCE
          do j=1,nrl
             write(*,'(*(i10))') u(j,:)
          enddo
       enddo
   else
       call MPI_Send(rbuf,lusize,MPI_INTEGER,0,rank,MPI_COMM_WORLD)
   endif

   call MPI_TYPE_FREE(locarray)

   call MPI_Finalize()

end program

Download mpi_io_fileview.f90 file

Python

Contents of mpi_io_fileview.py

import sys
import numpy as np
from mpi4py import MPI

if len(sys.argv)<2:
    print("Usage: filename <opt> nrows <opt> ncols")
    exit()
else:
    filename=sys.argv[1]

if len(sys.argv)==2:
    nrows=4
    ncols=4
elif len(sys.argv)==3:
    nrows=int(float(sys.argv[2]))
    ncols=nrows
elif len(sys.argv)==4:
    nrows=int(float(sys.argv[2]))
    ncols=int(float(sys.argv[3]))

comm = MPI.COMM_WORLD
rank = comm.Get_rank()
nprocs = comm.Get_size()

if nrows*ncols != nprocs:
    print("Number of rows times columns does not equal nprocs")
    sys.exit()

# Hard-code local array size so we can see what we're doing
nrl = 4
ncl = 4

N=nrl*nrows
M=ncl*ncols

#Set up the topology
lrow=rank//ncols
lcol=rank%ncols

ndims=2
gdims=np.array([N,M],dtype='int')
ldims=np.array([nrl,ncl],dtype='int')
starts=np.array([ncl*lrow,nrl*lcol],dtype='int')
print(rank,starts)

#Generate arbitrary values.
loc_u=np.ones((nrl,ncl),dtype="int")*(rank+1)

#Write each segment to conventional file (the "old way")
myfile=f'{filename:}{rank:02}'
myfh=open(myfile,'w')
for i in range(nrl):
    print(loc_u[i,:],file=myfh)
myfh.close()

locarr=MPI.INT.Create_subarray(gdims,ldims, starts, order=MPI.ORDER_C)
locarr.Commit()

amode=MPI.MODE_CREATE | MPI.MODE_WRONLY
fh=MPI.File.Open(comm,filename,amode)

#No header
disp=0
fh.Set_view(disp,MPI.INT,locarr,"native",MPI.INFO_NULL)
fh.Write_all(loc_u)

fh.Close()

amode=MPI.MODE_RDONLY
fh=MPI.File.Open(comm,filename,amode)

buf=np.zeros_like(loc_u,dtype='int')
fh.Set_view(disp,MPI.INT,locarr,"native",MPI.INFO_NULL)
fh.Read_all([buf, MPI.INT])
fh.Close()

#Usual trick to show one rank at a time
status=MPI.Status()
if rank==0:
    u=np.zeros_like(buf)
    print("Read for rank 0")
    print(buf)
    for i in range(1,nprocs):
        comm.Recv([u,MPI.INT],source=MPI.ANY_SOURCE,tag=MPI.ANY_TAG,status=status)
        print("Read for rank ",status.Get_source())
        print(u)
else:
    comm.Send([buf,MPI.INT],dest=0,tag=rank)

comm.Barrier()


Download mpi_io_fileview.py file

Previous
Next
© 2026 The Rector and Visitors of the University of Virginia