diff options
Diffstat (limited to 'perform')
-rw-r--r-- | perform/Makefile.in | 55 | ||||
-rw-r--r-- | perform/chunk.c | 521 | ||||
-rw-r--r-- | perform/iopipe.c | 546 | ||||
-rw-r--r-- | perform/mpi-perf.c | 357 | ||||
-rw-r--r-- | perform/overhead.c | 403 | ||||
-rw-r--r-- | perform/perf.c | 453 |
6 files changed, 2335 insertions, 0 deletions
diff --git a/perform/Makefile.in b/perform/Makefile.in new file mode 100644 index 0000000..71bc0d8 --- /dev/null +++ b/perform/Makefile.in @@ -0,0 +1,55 @@ +## HDF5 Library Performance Makefile(.in) +## +## Copyright (C) 2001 National Center for Supercomputing Applications. +## All rights reserved. +## +top_srcdir=@top_srcdir@ +top_builddir=.. +srcdir=@srcdir@ +@COMMENCE@ + +## Add include directory to the C preprocessor flags and the hdf5 library +## to the library list. +CPPFLAGS=-I. -I$(srcdir) -I../src -I$(top_srcdir)/src @CPPFLAGS@ +LIBHDF5=../src/libhdf5.la +LIBH5TEST=../test/libh5test.la + +## These are the programs that `make all' or `make tests' will build and which +## `make check' will run. List them in the order they should be run. +TEST_PROGS_PARA=mpi-perf perf +TEST_PROGS=iopipe chunk overhead \ + $(TEST_PROGS_PARA) + +## These are the files that `make clean' (and derivatives) will remove from +## this directory. +CLEAN=*.h5 + +## List all source files here. The list of object files will be +## created by replacing the `.c' with a `.o'. This list is necessary +## for building automatic dependencies. +TEST_SRC_PARA=mpi-perf.c perf.c +TEST_SRC=iopipe.c chunk.c overhead.c \ + $(TEST_SRC_PARA) +TEST_OBJ=$(TEST_SRC:.c=.lo) + +## How to build the programs... they all depend on the hdf5 library +$(TEST_PROGS): $(LIBHDF5) + +perf: perf.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ perf.lo $(LIBH5TEST) $(LIBHDF5) \ + $(LDFLAGS) $(LIBS) + +mpi-perf: mpi-perf.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ mpi-perf.lo $(LIBH5TEST) $(LIBHDF5) \ + $(LDFLAGS) $(LIBS) + +iopipe: iopipe.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ iopipe.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +chunk: chunk.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ chunk.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +overhead: overhead.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ overhead.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +@CONCLUDE@ diff --git a/perform/chunk.c b/perform/chunk.c new file mode 100644 index 0000000..47cd4db --- /dev/null +++ b/perform/chunk.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 1998 NCSA + * All rights reserved. + * + * Programmer: Robb Matzke <robb@arborea.spizella.com> + * Thursday, May 14, 1998 + * + * Purpose: Checks the effect of various I/O request sizes and raw data + * cache sizes. Performance depends on the amount of data read + * from disk and we use a filter to get that number. + */ + +/* See H5private.h for how to include headers */ +#undef NDEBUG +#include "hdf5.h" + +#ifdef H5_STDC_HEADERS +# include <assert.h> +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +#endif + + +#ifndef H5_HAVE_ATTRIBUTE +# undef __attribute__ +# define __attribute__(X) /*void*/ +# define UNUSED /*void*/ +#else +# define UNUSED __attribute__((unused)) +#endif + +#define FILE_NAME "chunk.h5" +#define LINESPOINTS "lines" +#define CH_SIZE 100 /*squared in terms of bytes */ +#define DS_SIZE 20 /*squared in terms of chunks */ +#define FILTER_COUNTER 305 +#define READ 0 +#define WRITE 1 +#define MIN(X,Y) ((X)<(Y)?(X):(Y)) +#define MAX(X,Y) ((X)>(Y)?(X):(Y)) +#define SQUARE(X) ((X)*(X)) + +/* The row-major test */ +#define RM_CACHE_STRT 25 +#define RM_CACHE_END 25 +#define RM_CACHE_DELT 5 +#define RM_START 0.50 +#define RM_END 5.00 +#define RM_DELTA 0.50 +#define RM_W0 0.0 +#define RM_NRDCC 521 + +/* Diagonal test */ +#define DIAG_CACHE_STRT 25 +#define DIAG_CACHE_END 25 +#define DIAG_CACHE_DELT 5 +#define DIAG_START 0.50 +#define DIAG_END 5.00 +#define DIAG_DELTA 0.50 +/* #define DIAG_W0 0.65 */ +/* #define DIAG_NRDCC 521 */ + +static size_t nio_g; +static hid_t fapl_g = -1; + + +/*------------------------------------------------------------------------- + * Function: counter + * + * Purpose: Count number of bytes but don't do anything. + * + * Return: Success: src_nbytes-1 + * + * Failure: never fails + * + * Programmer: Robb Matzke + * Thursday, May 14, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static size_t +counter (unsigned UNUSED flags, size_t UNUSED cd_nelmts, + const unsigned UNUSED *cd_values, size_t nbytes, + size_t UNUSED *buf_size, void UNUSED **buf) +{ + nio_g += nbytes; + return nbytes; +} + + +/*------------------------------------------------------------------------- + * Function: create_dataset + * + * Purpose: Creates a square dataset with square chunks, registers a + * stupid compress/uncompress pair for counting I/O, and + * initializes the dataset. The chunk size is in bytes, the + * dataset size is in terms of chunks. + * + * Return: void + * + * Programmer: Robb Matzke + * Thursday, May 14, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +create_dataset (void) +{ + hid_t file, space, dcpl, dset; + hsize_t size[2]; + signed char *buf; + + /* The file */ + file = H5Fcreate (FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_g); + + /* The data space */ + size[0] = size[1] = DS_SIZE * CH_SIZE; + space = H5Screate_simple (2, size, size); + + /* The storage layout and compression */ + dcpl = H5Pcreate (H5P_DATASET_CREATE); + size[0] = size[1] = CH_SIZE; + H5Pset_chunk (dcpl, 2, size); + H5Zregister (FILTER_COUNTER, "counter", counter); + H5Pset_filter (dcpl, FILTER_COUNTER, 0, 0, NULL); + + /* The dataset */ + dset = H5Dcreate (file, "dset", H5T_NATIVE_SCHAR, space, dcpl); + assert (dset>=0); + + /* The data */ + buf = calloc (1, SQUARE (DS_SIZE*CH_SIZE)); + H5Dwrite (dset, H5T_NATIVE_SCHAR, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf); + free (buf); + + /* Close */ + H5Dclose (dset); + H5Sclose (space); + H5Pclose (dcpl); + H5Fclose (file); +} + + +/*------------------------------------------------------------------------- + * Function: test_rowmaj + * + * Purpose: Reads the entire dataset using the specified size-squared + * I/O requests in row major order. + * + * Return: Efficiency: data requested divided by data actually read. + * + * Programmer: Robb Matzke + * Thursday, May 14, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static double +test_rowmaj (int op, hsize_t cache_size, hsize_t io_size) +{ + hid_t file, dset, mem_space, file_space; + signed char *buf = calloc (1, SQUARE(io_size)); + hsize_t i, j, hs_size[2]; + hssize_t hs_offset[2]; + int mdc_nelmts, rdcc_nelmts; + double w0; + + H5Pget_cache (fapl_g, &mdc_nelmts, &rdcc_nelmts, NULL, &w0); +#ifdef RM_W0 + w0 = RM_W0; +#endif +#ifdef RM_NRDCC + rdcc_nelmts = RM_NRDCC; +#endif + H5Pset_cache (fapl_g, mdc_nelmts, rdcc_nelmts, + cache_size*SQUARE (CH_SIZE), w0); + file = H5Fopen (FILE_NAME, H5F_ACC_RDWR, fapl_g); + dset = H5Dopen (file, "dset"); + file_space = H5Dget_space (dset); + nio_g = 0; + + for (i=0; i<CH_SIZE*DS_SIZE; i+=io_size) { +#if 0 + fprintf (stderr, "%5d\b\b\b\b\b", (int)i); + fflush (stderr); +#endif + for (j=0; j<CH_SIZE*DS_SIZE; j+=io_size) { + hs_offset[0] = i; + hs_size[0] = MIN (io_size, CH_SIZE*DS_SIZE-i); + hs_offset[1] = j; + hs_size[1] = MIN (io_size, CH_SIZE*DS_SIZE-j); + mem_space = H5Screate_simple (2, hs_size, hs_size); + H5Sselect_hyperslab (file_space, H5S_SELECT_SET, hs_offset, + NULL, hs_size, NULL); + + if (READ==op) { + H5Dread (dset, H5T_NATIVE_SCHAR, mem_space, file_space, + H5P_DEFAULT, buf); + } else { + H5Dwrite (dset, H5T_NATIVE_SCHAR, mem_space, file_space, + H5P_DEFAULT, buf); + } + H5Sclose (mem_space); + } + } + + free (buf); + H5Sclose (file_space); + H5Dclose (dset); + H5Fclose (file); + + return (double)SQUARE(CH_SIZE*DS_SIZE)/(double)nio_g; +} + + +/*------------------------------------------------------------------------- + * Function: test_diag + * + * Purpose: Reads windows diagonally across the dataset. Each window is + * offset from the previous window by OFFSET in the x and y + * directions. The reading ends after the (k,k) value is read + * where k is the maximum index in the dataset. + * + * Return: Efficiency. + * + * Programmer: Robb Matzke + * Friday, May 15, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static double +test_diag (int op, hsize_t cache_size, hsize_t io_size, hsize_t offset) +{ + hid_t file, dset, mem_space, file_space; + hsize_t i, hs_size[2]; + hsize_t nio = 0; + hssize_t hs_offset[2]; + signed char *buf = calloc (1, SQUARE (io_size)); + int mdc_nelmts, rdcc_nelmts; + double w0; + + H5Pget_cache (fapl_g, &mdc_nelmts, &rdcc_nelmts, NULL, &w0); +#ifdef DIAG_W0 + w0 = DIAG_W0; +#endif +#ifdef DIAG_NRDCC + rdcc_nelmts = DIAG_NRDCC; +#endif + H5Pset_cache (fapl_g, mdc_nelmts, rdcc_nelmts, + cache_size*SQUARE (CH_SIZE), w0); + file = H5Fopen (FILE_NAME, H5F_ACC_RDWR, fapl_g); + dset = H5Dopen (file, "dset"); + file_space = H5Dget_space (dset); + nio_g = 0; + + for (i=0, hs_size[0]=io_size; hs_size[0]==io_size; i+=offset) { + hs_offset[0] = hs_offset[1] = i; + hs_size[0] = hs_size[1] = MIN (io_size, CH_SIZE*DS_SIZE-i); + mem_space = H5Screate_simple (2, hs_size, hs_size); + H5Sselect_hyperslab (file_space, H5S_SELECT_SET, hs_offset, NULL, + hs_size, NULL); + if (READ==op) { + H5Dread (dset, H5T_NATIVE_SCHAR, mem_space, file_space, + H5P_DEFAULT, buf); + } else { + H5Dwrite (dset, H5T_NATIVE_SCHAR, mem_space, file_space, + H5P_DEFAULT, buf); + } + H5Sclose (mem_space); + nio += hs_size[0]*hs_size[1]; + if (i>0) nio -= SQUARE (io_size-offset); + } + + free (buf); + H5Sclose (file_space); + H5Dclose (dset); + H5Fclose (file); + + /* + * The extra cast in the following statement is a bug workaround for the + * Win32 version 5.0 compiler. + * 1998-11-06 ptl + */ + return (double)(hssize_t)nio/(hssize_t)nio_g; +} + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: See file prologue. + * + * Return: Success: + * + * Failure: + * + * Programmer: Robb Matzke + * Thursday, May 14, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main (void) +{ + hsize_t io_size; + double effic, io_percent; + FILE *f, *d; + int cache_size; + double w0; + + /* + * Create a global file access property list. + */ + fapl_g = H5Pcreate (H5P_FILE_ACCESS); + H5Pget_cache (fapl_g, NULL, NULL, NULL, &w0); + + /* Create the file */ + create_dataset (); + f = fopen ("x-gnuplot", "w"); + + printf("Test %8s %8s %8s\n", "CacheSz", "ChunkSz", "Effic"); + printf("--------- -------- -------- --------\n"); + +#if 1 + /* + * Test row-major reading of the dataset with various sizes of request + * windows. + */ + if (RM_CACHE_STRT==RM_CACHE_END) { + fprintf (f, "set yrange [0:1.2]\n"); + fprintf (f, "set ytics 0, 0.1, 1\n"); + fprintf (f, "set xlabel \"%s\"\n", + "Request size as a fraction of chunk size"); + fprintf (f, "set ylabel \"Efficiency\"\n"); + fprintf (f, "set title \"Cache %d chunks, w0=%g, " + "Size=(total=%d, chunk=%d)\"\n", + RM_CACHE_STRT, w0, DS_SIZE*CH_SIZE, CH_SIZE); + } else { + fprintf (f, "set autoscale\n"); + fprintf (f, "set hidden3d\n"); + } + + fprintf (f, "set terminal postscript\nset output \"x-rowmaj-rd.ps\"\n"); + fprintf (f, "%s \"x-rowmaj-rd.dat\" title \"RowMaj-Read\" with %s\n", + RM_CACHE_STRT==RM_CACHE_END?"plot":"splot", + LINESPOINTS); + fprintf (f, "set terminal x11\nreplot\n"); + d = fopen ("x-rowmaj-rd.dat", "w"); + for (cache_size=RM_CACHE_STRT; + cache_size<=RM_CACHE_END; + cache_size+=RM_CACHE_DELT) { + for (io_percent=RM_START; io_percent<=RM_END; io_percent+=RM_DELTA) { + io_size = MAX (1, (int)(CH_SIZE*io_percent)); + printf ("Rowmaj-rd %8d %8.2f", cache_size, io_percent); + fflush (stdout); + effic = test_rowmaj (READ, cache_size, io_size); + printf (" %8.2f\n", effic); + if (RM_CACHE_STRT==RM_CACHE_END) { + fprintf (d, "%g %g\n", io_percent, effic); + } else { + fprintf (d, "%g\n", effic); + } + } + fprintf (d, "\n"); + } + fclose (d); + fprintf (f, "pause -1\n"); +#endif + +#if 1 + /* + * Test row-major writing of the dataset with various sizes of request + * windows. + */ + if (RM_CACHE_STRT==RM_CACHE_END) { + fprintf (f, "set yrange [0:1.2]\n"); + fprintf (f, "set ytics 0, 0.1, 1\n"); + fprintf (f, "set xlabel \"%s\"\n", + "Request size as a fraction of chunk size"); + fprintf (f, "set ylabel \"Efficiency\"\n"); + fprintf (f, "set title \"Cache %d chunks,w0=%g, " + "Size=(total=%d, chunk=%d)\"\n", + RM_CACHE_STRT, w0, DS_SIZE*CH_SIZE, CH_SIZE); + } else { + fprintf (f, "set autoscale\n"); + fprintf (f, "set hidden3d\n"); + } + + fprintf (f, "set terminal postscript\nset output \"x-rowmaj-wr.ps\"\n"); + fprintf (f, "%s \"x-rowmaj-wr.dat\" title \"RowMaj-Write\" with %s\n", + RM_CACHE_STRT==RM_CACHE_END?"plot":"splot", + LINESPOINTS); + fprintf (f, "set terminal x11\nreplot\n"); + d = fopen ("x-rowmaj-wr.dat", "w"); + for (cache_size=RM_CACHE_STRT; + cache_size<=RM_CACHE_END; + cache_size+=RM_CACHE_DELT) { + for (io_percent=RM_START; io_percent<=RM_END; io_percent+=RM_DELTA) { + io_size = MAX (1, (int)(CH_SIZE*io_percent)); + printf ("Rowmaj-wr %8d %8.2f", cache_size, io_percent); + fflush (stdout); + effic = test_rowmaj (WRITE, cache_size, io_size); + printf (" %8.2f\n", effic); + if (RM_CACHE_STRT==RM_CACHE_END) { + fprintf (d, "%g %g\n", io_percent, effic); + } else { + fprintf (d, "%g\n", effic); + } + } + fprintf (d, "\n"); + } + fclose (d); + fprintf (f, "pause -1\n"); +#endif + +#if 1 + /* + * Test diagonal read + */ + if (DIAG_CACHE_STRT==DIAG_CACHE_END) { + fprintf (f, "set yrange [0:1.2]\n"); + fprintf (f, "set ytics 0, 0.1, 1\n"); + fprintf (f, "set xlabel \"%s\"\n", + "Request size as a fraction of chunk size"); + fprintf (f, "set ylabel \"Efficiency\"\n"); + fprintf (f, "set title \"Cache %d chunks,w0=%g, " + "Size=(total=%d, chunk=%d)\"\n", + DIAG_CACHE_STRT, w0, DS_SIZE*CH_SIZE, CH_SIZE); + } else { + fprintf (f, "set autoscale\n"); + fprintf (f, "set hidden3d\n"); + } + fprintf (f, "set terminal postscript\nset output \"x-diag-rd.ps\"\n"); + fprintf (f, "%s \"x-diag-rd.dat\" title \"Diag-Read\" with %s\n", + DIAG_CACHE_STRT==DIAG_CACHE_END?"plot":"splot", LINESPOINTS); + fprintf (f, "set terminal x11\nreplot\n"); + d = fopen ("x-diag-rd.dat", "w"); + for (cache_size=DIAG_CACHE_STRT; + cache_size<=DIAG_CACHE_END; + cache_size+=DIAG_CACHE_DELT) { + for (io_percent=DIAG_START; + io_percent<=DIAG_END; + io_percent+=DIAG_DELTA) { + io_size = MAX (1, (int)(CH_SIZE*io_percent)); + printf ("Diag-rd %8d %8.2f", cache_size, io_percent); + fflush (stdout); + effic = test_diag (READ, cache_size, io_size, MAX (1, io_size/2)); + printf (" %8.2f\n", effic); + if (DIAG_CACHE_STRT==DIAG_CACHE_END) { + fprintf (d, "%g %g\n", io_percent, effic); + } else { + fprintf (d, "%g\n", effic); + } + } + fprintf (d, "\n"); + } + fclose (d); + fprintf (f, "pause -1\n"); +#endif + +#if 1 + /* + * Test diagonal write + */ + if (DIAG_CACHE_STRT==DIAG_CACHE_END) { + fprintf (f, "set yrange [0:1.2]\n"); + fprintf (f, "set ytics 0, 0.1, 1\n"); + fprintf (f, "set xlabel \"%s\"\n", + "Request size as a fraction of chunk size"); + fprintf (f, "set ylabel \"Efficiency\"\n"); + fprintf (f, "set title \"Cache %d chunks, w0=%g, " + "Size=(total=%d, chunk=%d)\"\n", + DIAG_CACHE_STRT, w0, DS_SIZE*CH_SIZE, CH_SIZE); + } else { + fprintf (f, "set autoscale\n"); + fprintf (f, "set hidden3d\n"); + } + fprintf (f, "set terminal postscript\nset output \"x-diag-wr.ps\"\n"); + fprintf (f, "%s \"x-diag-wr.dat\" title \"Diag-Write\" with %s\n", + DIAG_CACHE_STRT==DIAG_CACHE_END?"plot":"splot", LINESPOINTS); + fprintf (f, "set terminal x11\nreplot\n"); + d = fopen ("x-diag-wr.dat", "w"); + for (cache_size=DIAG_CACHE_STRT; + cache_size<=DIAG_CACHE_END; + cache_size+=DIAG_CACHE_DELT) { + for (io_percent=DIAG_START; + io_percent<=DIAG_END; + io_percent+=DIAG_DELTA) { + io_size = MAX (1, (int)(CH_SIZE*io_percent)); + printf ("Diag-wr %8d %8.2f", cache_size, io_percent); + fflush (stdout); + effic = test_diag (WRITE, cache_size, io_size, MAX (1, io_size/2)); + printf (" %8.2f\n", effic); + if (DIAG_CACHE_STRT==DIAG_CACHE_END) { + fprintf (d, "%g %g\n", io_percent, effic); + } else { + fprintf (d, "%g\n", effic); + } + } + fprintf (d, "\n"); + } + fclose (d); + fprintf (f, "pause -1\n"); +#endif + + + H5Pclose (fapl_g); + fclose (f); + return 0; +} + diff --git a/perform/iopipe.c b/perform/iopipe.c new file mode 100644 index 0000000..aa76369 --- /dev/null +++ b/perform/iopipe.c @@ -0,0 +1,546 @@ +/* + * Copyright (C) 1998 NCSA + * All rights reserved. + * + * Programmer: Robb Matzke <matzke@llnl.gov> + * Thursday, March 12, 1998 + */ + +/* See H5private.h for how to include headers */ +#undef NDEBUG +#include "hdf5.h" + +#ifdef H5_HAVE_WINSOCK_H +#include <Winsock.h> +#endif + +/*Winsock.h includes windows.h, due to the different value of +WINVER, windows.h should be put before H5private.h. Kent yang 6/21/2001*/ + +#include "H5private.h" + +#ifdef H5_STDC_HEADERS +# include <assert.h> +# include <fcntl.h> +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +#endif + +#ifdef H5_HAVE_UNISTD_H +# include <sys/types.h> +# include <unistd.h> +#endif + +#if defined(H5_TIME_WITH_SYS_TIME) +# include <sys/time.h> +# include <time.h> +#elif defined(H5_HAVE_SYS_TIME_H) +# include <sys/time.h> +#else +# include <time.h> +#endif + +#ifdef H5_HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif + + + +#if defined (__MWERKS__) +#ifdef H5_HAVE_SYS_TIMEB +#undef H5_HAVE_SYS_TIMEB +#endif +#ifdef H5_HAVE_SYSTEM +#undef H5_HAVE_SYSTEM +#endif +#endif /* __MWERKS__*/ + + +#ifdef H5_HAVE_SYS_TIMEB +#include <sys/timeb.h> +#endif + + +#define RAW_FILE_NAME "iopipe.raw" +#define HDF5_FILE_NAME "iopipe.h5" +#define HEADING "%-16s" +#define PROGRESS '=' + +#if 0 +/* Normal testing */ +#define REQUEST_SIZE_X 4579 +#define REQUEST_SIZE_Y 4579 +#define NREAD_REQUESTS 45 +#define NWRITE_REQUESTS 45 +#else +/* Speedy testing */ +#define REQUEST_SIZE_X 1000 +#define REQUEST_SIZE_Y 1000 +#define NREAD_REQUESTS 45 +#define NWRITE_REQUESTS 45 +#endif + + +/*------------------------------------------------------------------------- + * Function: print_stats + * + * Purpose: Prints statistics + * + * Return: void + * + * Programmer: Robb Matzke + * Thursday, March 12, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +#ifdef H5_HAVE_GETRUSAGE +static void +print_stats (const char *prefix, + struct rusage *r_start, struct rusage *r_stop, + struct timeval *t_start, struct timeval *t_stop, + size_t nbytes) +#else /* H5_HAVE_GETRUSAGE */ +static void +print_stats (const char *prefix, + struct timeval *r_start, struct timeval *r_stop, + struct timeval *t_start, struct timeval *t_stop, + size_t nbytes) +#endif /* H5_HAVE_GETRUSAGE */ +{ + double e_time, bw; +#ifdef H5_HAVE_GETRUSAGE + double u_time, s_time; + + u_time = ((double)(r_stop->ru_utime.tv_sec)+ + (double)(r_stop->ru_utime.tv_usec)/1000000.0) - + ((double)(r_start->ru_utime.tv_sec)+ + (double)(r_start->ru_utime.tv_usec)/1000000.0); + + s_time = ((double)(r_stop->ru_stime.tv_sec)+ + (double)(r_stop->ru_stime.tv_usec)/1000000.0) - + ((double)(r_start->ru_stime.tv_sec)+ + (double)(r_start->ru_stime.tv_usec)/1000000.0); +#endif +#ifndef H5_HAVE_SYS_TIMEB + e_time = ((double)(t_stop->tv_sec)+ + (double)(t_stop->tv_usec)/1000000.0) - + ((double)(t_start->tv_sec)+ + (double)(t_start->tv_usec)/1000000.0); +#else + e_time = ((double)(t_stop->tv_sec)+ + (double)(t_stop->tv_usec)/1000.0) - + ((double)(t_start->tv_sec)+ + (double)(t_start->tv_usec)/1000.0); +#endif + bw = (double)nbytes / e_time; + +#ifdef H5_HAVE_GETRUSAGE + printf (HEADING "%1.2fuser %1.2fsystem %1.2felapsed %1.2fMB/s\n", + prefix, u_time, s_time, e_time, bw/(1024*1024)); +#else + printf (HEADING "%1.2felapsed %1.2fMB/s\n", + prefix, e_time, bw/(1024*1024)); +#endif + +} + + +/*------------------------------------------------------------------------- + * Function: synchronize + * + * Purpose: + * + * Return: void + * + * Programmer: Robb Matzke + * Thursday, March 12, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +synchronize (void) +{ +#ifdef H5_HAVE_SYSTEM +#ifdef WIN32 +#ifdef __WATCOMC__ + flushall(); +#else /* __WATCOMC__ */ + _flushall(); +#endif /* __WATCOMC__ */ +#else + system ("sync"); + system ("df >/dev/null"); +#endif +#if 0 + /* + * This works well on Linux to get rid of all cached disk buffers. The + * number should be approximately the amount of RAM in MB. Do not + * include swap space in that amount or the command will fail. + */ + system ("/sbin/swapout 128"); +#endif +#endif +} + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: + * + * Return: Success: + * + * Failure: + * + * Programmer: Robb Matzke + * Thursday, March 12, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main (void) +{ + static hsize_t size[2] = {REQUEST_SIZE_X, REQUEST_SIZE_Y}; + static int nread=NREAD_REQUESTS, nwrite=NWRITE_REQUESTS; + + unsigned char *the_data = NULL; + hid_t file, dset, file_space=-1; + herr_t status; +#ifdef H5_HAVE_GETRUSAGE + struct rusage r_start, r_stop; +#else + struct timeval r_start, r_stop; +#endif + struct timeval t_start, t_stop; + int i, fd; + hssize_t n; + off_t offset; + hssize_t start[2]; + hsize_t count[2]; + + +#ifdef H5_HAVE_SYS_TIMEB + struct _timeb *tbstart = malloc(sizeof(struct _timeb)); + struct _timeb *tbstop = malloc(sizeof(struct _timeb)); +#endif + /* + * The extra cast in the following statement is a bug workaround for the + * Win32 version 5.0 compiler. + * 1998-11-06 ptl + */ + printf ("I/O request size is %1.1fMB\n", + (double)(hssize_t)(size[0]*size[1])/1024.0*1024); + + /* Open the files */ + file = H5Fcreate (HDF5_FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + assert (file>=0); + fd = HDopen (RAW_FILE_NAME, O_RDWR|O_CREAT|O_TRUNC, 0666); + assert (fd>=0); + + /* Create the dataset */ + file_space = H5Screate_simple (2, size, size); + assert (file_space>=0); + dset = H5Dcreate (file, "dset", H5T_NATIVE_UCHAR, file_space, H5P_DEFAULT); + assert (dset>=0); + the_data = malloc ((size_t)(size[0]*size[1])); + /*initial fill for lazy malloc*/ + memset (the_data, 0xAA, (size_t)(size[0]*size[1])); + + /* Fill raw */ + synchronize (); +#ifdef H5_HAVE_GETRUSAGE +printf("Before getrusage() call\n"); + getrusage (RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "fill raw"); + for (i=0; i<nwrite; i++) { + putc (PROGRESS, stderr); + fflush (stderr); + memset (the_data, 0xAA, (size_t)(size[0]*size[1])); + } +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_stop, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstop); + t_start.tv_sec = tbstart->time; + t_start.tv_usec = tbstart->millitm; + t_stop.tv_sec = tbstop->time; + t_stop.tv_usec = tbstop->millitm; +#endif +#endif + putc ('\n', stderr); + print_stats ("fill raw", + &r_start, &r_stop, &t_start, &t_stop, + (size_t)(nread*size[0]*size[1])); + + + /* Fill hdf5 */ + synchronize (); +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "fill hdf5"); + for (i=0; i<nread; i++) { + putc (PROGRESS, stderr); + fflush (stderr); + status = H5Dread (dset, H5T_NATIVE_UCHAR, file_space, file_space, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_stop, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstop); + t_start.tv_sec = tbstart->time; + t_start.tv_usec = tbstart->millitm; + t_stop.tv_sec = tbstop->time; + t_stop.tv_usec = tbstop->millitm; +#endif +#endif + putc ('\n', stderr); + print_stats ("fill hdf5", + &r_start, &r_stop, &t_start, &t_stop, + (size_t)(nread*size[0]*size[1])); + + /* Write the raw dataset */ + synchronize (); +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "out raw"); + for (i=0; i<nwrite; i++) { + putc (PROGRESS, stderr); + fflush (stderr); + offset = lseek (fd, 0, SEEK_SET); + assert (0==offset); + n = write (fd, the_data, (size_t)(size[0]*size[1])); + assert (n>=0 && (size_t)n==size[0]*size[1]); + } +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_stop, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstop); + t_start.tv_sec = tbstart->time; + t_start.tv_usec = tbstart->millitm; + t_stop.tv_sec = tbstop->time; + t_stop.tv_usec = tbstop->millitm; +#endif +#endif + putc ('\n', stderr); + print_stats ("out raw", + &r_start, &r_stop, &t_start, &t_stop, + (size_t)(nread*size[0]*size[1])); + + /* Write the hdf5 dataset */ + synchronize (); +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "out hdf5"); + for (i=0; i<nwrite; i++) { + putc (PROGRESS, stderr); + fflush (stderr); + status = H5Dwrite (dset, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_stop, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstop); + t_start.tv_sec = tbstart->time; + t_start.tv_usec = tbstart->millitm; + t_stop.tv_sec = tbstop->time; + t_stop.tv_usec = tbstop->millitm; +#endif +#endif + putc ('\n', stderr); + print_stats ("out hdf5", + &r_start, &r_stop, &t_start, &t_stop, + (size_t)(nread*size[0]*size[1])); + + /* Read the raw dataset */ + synchronize (); +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "in raw"); + for (i=0; i<nread; i++) { + putc (PROGRESS, stderr); + fflush (stderr); + offset = lseek (fd, 0, SEEK_SET); + assert (0==offset); + n = read (fd, the_data, (size_t)(size[0]*size[1])); + assert (n>=0 && (size_t)n==size[0]*size[1]); + } +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_stop, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstop); + t_start.tv_sec = tbstart->time; + t_start.tv_usec = tbstart->millitm; + t_stop.tv_sec = tbstop->time; + t_stop.tv_usec = tbstop->millitm; +#endif +#endif + putc ('\n', stderr); + print_stats ("in raw", + &r_start, &r_stop, &t_start, &t_stop, + (size_t)(nread*size[0]*size[1])); + + + /* Read the hdf5 dataset */ + synchronize (); +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "in hdf5"); + for (i=0; i<nread; i++) { + putc (PROGRESS, stderr); + fflush (stderr); + status = H5Dread (dset, H5T_NATIVE_UCHAR, file_space, file_space, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_stop, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstop); + t_start.tv_sec = tbstart->time; + t_start.tv_usec = tbstart->millitm; + t_stop.tv_sec = tbstop->time; + t_stop.tv_usec = tbstop->millitm; +#endif +#endif + putc ('\n', stderr); + print_stats ("in hdf5", + &r_start, &r_stop, &t_start, &t_stop, + (size_t)(nread*size[0]*size[1])); + + /* Read hyperslab */ + assert (size[0]>20 && size[1]>20); + start[0] = start[1] = 10; + count[0] = count[1] = size[0]-20; + status = H5Sselect_hyperslab (file_space, H5S_SELECT_SET, start, NULL, count, NULL); + assert (status>=0); + synchronize (); +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "in hdf5 partial"); + for (i=0; i<nread; i++) { + putc (PROGRESS, stderr); + fflush (stderr); + status = H5Dread (dset, H5T_NATIVE_UCHAR, file_space, file_space, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + getrusage (RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + gettimeofday (&t_stop, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstop); + t_start.tv_sec = tbstart->time; + t_start.tv_usec = tbstart->millitm; + t_stop.tv_sec = tbstop->time; + t_stop.tv_usec = tbstop->millitm; +#endif +#endif + putc ('\n', stderr); + print_stats ("in hdf5 partial", + &r_start, &r_stop, &t_start, &t_stop, + (size_t)(nread*count[0]*count[1])); + + + + /* Close everything */ + HDclose (fd); + H5Dclose (dset); + H5Sclose (file_space); + H5Fclose (file); + + return 0; +} diff --git a/perform/mpi-perf.c b/perform/mpi-perf.c new file mode 100644 index 0000000..09a134b --- /dev/null +++ b/perform/mpi-perf.c @@ -0,0 +1,357 @@ +/* + * (C) 1995-2001 Clemson University and Argonne National Laboratory. + * + * See COPYING in top-level directory. + * + * This is contributed by Robert Ross to the HDF5 software. + * and was called mpi-io-test.c + */ + +#ifdef H5_HAVE_PARALLEL +/* mpi-perf.c + * + * This is derived from code given to me by Rajeev Thakur. Dunno where + * it originated. + * + * It's purpose is to produce aggregate bandwidth numbers for varying + * block sizes, number of processors, an number of iterations. + * + * This is strictly an mpi program - it is used to test the MPI I/O + * functionality implemented by Romio. + * + * Compiling is usually easiest with something like: + * mpicc -Wall -Wstrict-prototypes mpi-io-test.c -o mpi-io-test + * + * NOTE: This code assumes that all command line arguments make it out to all + * the processes that make up the parallel job, which isn't always the case. + * So if it doesn't work on some platform, that might be why. + */ +/* Modifications: + * Albert Cheng, Apr 30, 20001 + * Changed MPI_File_open to use MPI_COMM_WORLD (was MPI_COMM_SELF). + * Albert Cheng, May 5, 20001 + * Changed MPI_File_seek then MPI_File_write or MPI_File_read to just + * MPI_File_write_at and MPI_File_read_at. Some compiler, e.g., IBM + * mpcc_r does not support MPI_File_seek and MPI_File_read or MPI_File_write. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/time.h> +#include <mpi.h> +#ifndef MPI_FILE_NULL /*MPIO may be defined in mpi.h already */ +# include <mpio.h> +#endif + + + +/* DEFAULT VALUES FOR OPTIONS */ +int64_t opt_block = 1048576*16; +int opt_iter = 1; +int opt_stripe = -1; +int opt_correct = 0; +int amode = O_RDWR | O_CREAT; +char opt_file[256] = "/foo/test.out\0"; +char opt_pvfstab[256] = "notset\0"; +int opt_pvfstab_set = 0; + +/* function prototypes */ +int parse_args(int argc, char **argv); +double Wtime(void); + +extern int errno; +extern int debug_on; + +/* globals needed for getopt */ +extern char *optarg; +extern int optind, opterr; + +int main(int argc, char **argv) +{ + char *buf, *tmp, *buf2, *tmp2, *check; + int i, j, mynod=0, nprocs=1, err, my_correct = 1, correct, myerrno; + double stim, etim; + double write_tim = 0; + double read_tim = 0; + double read_bw, write_bw; + double max_read_tim, max_write_tim; + double min_read_tim, min_write_tim; + double ave_read_tim, ave_write_tim; + int64_t iter_jump = 0; + int64_t seek_position = 0; + MPI_File fh; + MPI_Status status; + int nchars; + + /* startup MPI and determine the rank of this process */ + MPI_Init(&argc,&argv); + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &mynod); + + /* parse the command line arguments */ + parse_args(argc, argv); + + if (mynod == 0) printf("# Using mpi-io calls.\n"); + + + /* kindof a weird hack- if the location of the pvfstab file was + * specified on the command line, then spit out this location into + * the appropriate environment variable: */ + +#if H5_HAVE_SETENV +/* no setenv or unsetenv */ + if (opt_pvfstab_set) { + if((setenv("PVFSTAB_FILE", opt_pvfstab, 1)) < 0){ + perror("setenv"); + goto die_jar_jar_die; + } + } +#endif + + /* this is how much of the file data is covered on each iteration of + * the test. used to help determine the seek offset on each + * iteration */ + iter_jump = nprocs * opt_block; + + /* setup a buffer of data to write */ + if (!(tmp = (char *) malloc(opt_block + 256))) { + perror("malloc"); + goto die_jar_jar_die; + } + buf = tmp + 128 - (((long)tmp) % 128); /* align buffer */ + + if (opt_correct) { + /* do the same buffer setup for verifiable data */ + if (!(tmp2 = (char *) malloc(opt_block + 256))) { + perror("malloc2"); + goto die_jar_jar_die; + } + buf2 = tmp + 128 - (((long)tmp) % 128); + } + + /* open the file for writing */ + err = MPI_File_open(MPI_COMM_WORLD, opt_file, + MPI_MODE_CREATE | MPI_MODE_RDWR, MPI_INFO_NULL, &fh); + if (err < 0) { + fprintf(stderr, "node %d, open error: %s\n", mynod, strerror(errno)); + goto die_jar_jar_die; + } + + /* now repeat the write operations the number of times + * specified on the command line */ + for (j=0; j < opt_iter; j++) { + + /* calculate the appropriate position depending on the iteration + * and rank of the current process */ + seek_position = (j*iter_jump)+(mynod*opt_block); + + if (opt_correct) /* fill in buffer for iteration */ { + for (i=mynod+j, check=buf; i<opt_block; i++,check++) *check=(char)i; + } + + /* discover the starting time of the operation */ + MPI_Barrier(MPI_COMM_WORLD); + stim = MPI_Wtime(); + + /* write out the data */ + nchars = opt_block/sizeof(char); + err = MPI_File_write_at(fh, seek_position, buf, nchars, MPI_CHAR, &status); + if(err){ + fprintf(stderr, "node %d, write error: %s\n", mynod, + strerror(errno)); + } + + /* discover the ending time of the operation */ + etim = MPI_Wtime(); + + write_tim += (etim - stim); + + /* we are done with this "write" iteration */ + } + + err = MPI_File_close(&fh); + if(err){ + fprintf(stderr, "node %d, close error after write\n", mynod); + } + + /* wait for everyone to synchronize at this point */ + MPI_Barrier(MPI_COMM_WORLD); + + /* reopen the file to read the data back out */ + err = MPI_File_open(MPI_COMM_WORLD, opt_file, + MPI_MODE_CREATE | MPI_MODE_RDWR, MPI_INFO_NULL, &fh); + if (err < 0) { + fprintf(stderr, "node %d, open error: %s\n", mynod, strerror(errno)); + goto die_jar_jar_die; + } + + + /* we are going to repeat the read operation the number of iterations + * specified */ + for (j=0; j < opt_iter; j++) { + /* calculate the appropriate spot give the current iteration and + * rank within the MPI processes */ + seek_position = (j*iter_jump)+(mynod*opt_block); + + /* discover the start time */ + MPI_Barrier(MPI_COMM_WORLD); + stim = MPI_Wtime(); + + /* read in the file data */ + if (!opt_correct){ + err = MPI_File_read_at(fh, seek_position, buf, nchars, MPI_CHAR, &status); + } + else{ + err = MPI_File_read_at(fh, seek_position, buf2, nchars, MPI_CHAR, &status); + } + myerrno = errno; + + /* discover the end time */ + etim = MPI_Wtime(); + read_tim += (etim - stim); + + if (err < 0) fprintf(stderr, "node %d, read error, loc = %Ld: %s\n", + mynod, mynod*opt_block, strerror(myerrno)); + + /* if the user wanted to check correctness, compare the write + * buffer to the read buffer */ + if (opt_correct && memcmp(buf, buf2, opt_block)) { + fprintf(stderr, "node %d, correctness test failed\n", mynod); + my_correct = 0; + MPI_Allreduce(&my_correct, &correct, 1, MPI_INT, MPI_MIN, + MPI_COMM_WORLD); + } + + /* we are done with this read iteration */ + } + + /* close the file */ + err = MPI_File_close(&fh); + if(err){ + fprintf(stderr, "node %d, close error after write\n", mynod); + } + + /* compute the read and write times */ + MPI_Allreduce(&read_tim, &max_read_tim, 1, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); + MPI_Allreduce(&read_tim, &min_read_tim, 1, MPI_DOUBLE, MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(&read_tim, &ave_read_tim, 1, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + + /* calculate the average from the sum */ + ave_read_tim = ave_read_tim / nprocs; + + MPI_Allreduce(&write_tim, &max_write_tim, 1, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); + MPI_Allreduce(&write_tim, &min_write_tim, 1, MPI_DOUBLE, MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(&write_tim, &ave_write_tim, 1, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + + /* calculate the average from the sum */ + ave_write_tim = ave_write_tim / nprocs; + + /* print out the results on one node */ + if (mynod == 0) { + read_bw = ((int64_t)(opt_block*nprocs*opt_iter))/(max_read_tim*1000000.0); + write_bw = ((int64_t)(opt_block*nprocs*opt_iter))/(max_write_tim*1000000.0); + + printf("nr_procs = %d, nr_iter = %d, blk_sz = %ld\n", nprocs, + opt_iter, (long)opt_block); + + printf("# total_size = %ld\n", (long)(opt_block*nprocs*opt_iter)); + + printf("# Write: min_time = %f, max_time = %f, mean_time = %f\n", + min_write_tim, max_write_tim, ave_write_tim); + printf("# Read: min_time = %f, max_time = %f, mean_time = %f\n", + min_read_tim, max_read_tim, ave_read_tim); + + printf("Write bandwidth = %f Mbytes/sec\n", write_bw); + printf("Read bandwidth = %f Mbytes/sec\n", read_bw); + + if (opt_correct) { + printf("Correctness test %s.\n", correct ? "passed" : "failed"); + } + } + + +die_jar_jar_die: + +#if H5_HAVE_SETENV +/* no setenv or unsetenv */ + /* clear the environment variable if it was set earlier */ + if (opt_pvfstab_set){ + unsetenv("PVFSTAB_FILE"); + } +#endif + + free(tmp); + if (opt_correct) free(tmp2); + MPI_Finalize(); + return(0); +} + +int parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, "s:b:i:f:p:c")) != EOF) { + switch (c) { + case 's': /* stripe */ + opt_stripe = atoi(optarg); + break; + case 'b': /* block size */ + opt_block = atoi(optarg); + break; + case 'i': /* iterations */ + opt_iter = atoi(optarg); + break; + case 'f': /* filename */ + strncpy(opt_file, optarg, 255); + break; + case 'p': /* pvfstab file */ + strncpy(opt_pvfstab, optarg, 255); + opt_pvfstab_set = 1; + break; + case 'c': /* correctness */ + opt_correct = 1; + break; + case '?': /* unknown */ + default: + break; + } + } + return(0); +} + +/* Wtime() - returns current time in sec., in a double */ +double Wtime() +{ + struct timeval t; + + gettimeofday(&t, NULL); + return((double)t.tv_sec + (double)t.tv_usec / 1000000); +} + +/* + * Local variables: + * c-indent-level: 3 + * c-basic-offset: 3 + * tab-width: 3 + * End: + */ + +#else /* H5_HAVE_PARALLEL */ +/* dummy program since H5_HAVE_PARALLE is not configured in */ +int +main() +{ +printf("No parallel performance because parallel is not configured in\n"); +return(0); +} +#endif /* H5_HAVE_PARALLEL */ diff --git a/perform/overhead.c b/perform/overhead.c new file mode 100644 index 0000000..60a5e90 --- /dev/null +++ b/perform/overhead.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 1998 NCSA + * All rights reserved. + * + * Programmer: Robb Matzke <matzke@llnl.gov> + * Monday, September 28, 1998 + * + * Purpose: Creates a chunked dataset and measures the storage overhead. + */ + +/* See H5private.h for how to include headers */ +#undef NDEBUG +#include "hdf5.h" + +#ifdef H5_STDC_HEADERS +# include <ctype.h> +# include <fcntl.h> +# include <stdlib.h> +# include <sys/stat.h> +# include <string.h> +#endif + +#ifdef H5_HAVE_IO_H +# include <io.h> +#endif + +#ifdef H5_HAVE_UNISTD_H +# include <sys/types.h> +# include <unistd.h> +#endif + +#ifdef H5_HAVE_IO_H +# include <io.h> +#endif + +#ifndef H5_HAVE_ATTRIBUTE +# undef __attribute__ +# define __attribute__(X) /*void*/ +# define UNUSED /*void*/ +#else +# define UNUSED __attribute__((unused)) +#endif + +#define FILE_NAME_1 "overhead.h5" +#define FALSE 0 +#define TRUE 1 + +typedef enum fill_t { + FILL_ALL, + FILL_FORWARD, + FILL_REVERSE, + FILL_INWARD, + FILL_OUTWARD, + FILL_RANDOM +} fill_t; + + +/*------------------------------------------------------------------------- + * Function: usage + * + * Purpose: Prints a usage message and exits. + * + * Return: never returns + * + * Programmer: Robb Matzke + * Wednesday, September 30, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +usage(const char *prog) +{ + fprintf(stderr, "usage: %s [STYLE|cache] [LEFT [MIDDLE [RIGHT]]]\n", + prog); + fprintf(stderr, "\ + STYLE is the order that the dataset is filled and should be one of:\n\ + forward -- Fill the dataset from lowest address to highest\n\ + address. This style tests the right split ratio.\n\ + reverse -- Fill the dataset from highest address to lowest\n\ + address. This is the reverse order of `forward' and\n\ + tests the left split ratio.\n\ + inward -- Fill beginning at both the lowest and highest\n\ + addresses and work in toward the center of the\n\ + dataset. This tests the middle split ratio.\n\ + outward -- Start at the center of the dataset and work outward\n\ + toward the lowest and highest addresses. This tests\n\ + both left and right split ratios.\n\ + random -- Write the chunks of the dataset in random order. This\n\ + tests all split ratios.\n\ + If no fill style is specified then all fill styles are tried and a\n\ + single value is printed for each one.\n\ +\n\ + If the word `cache' is used instead of a fill style then the raw data\n\ + cache is enabled. It is not possible to enable the raw data cache when\n\ + a specific fill style is used because H5Fflush() is called after each\n\ + chunk is written in order to calculate overhead during the test. If\n\ + the cache is enabled then chunks are written to disk in different orders\n\ + than the actual H5Dwrite() calls in the test due to collisions and the\n\ + resulting B-tree will be split differently.\n\ +\n\ + LEFT, MIDDLE, and RIGHT are the ratios to use for splitting and should\n\ + be values between zero and one, inclusive.\n"); + exit(1); +} + + +/*------------------------------------------------------------------------- + * Function: cleanup + * + * Purpose: Removes test files + * + * Return: void + * + * Programmer: Robb Matzke + * Thursday, June 4, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +cleanup (void) +{ + if (!getenv ("HDF5_NOCLEANUP")) { + remove (FILE_NAME_1); + } +} + + +/*------------------------------------------------------------------------- + * Function: display_error_cb + * + * Purpose: Displays the error stack after printing "*FAILED*". + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Robb Matzke + * Wednesday, March 4, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +display_error_cb (void UNUSED *client_data) +{ + puts ("*FAILED*"); + H5Eprint (stdout); + return 0; +} + + +/*------------------------------------------------------------------------- + * Function: test + * + * Purpose: The guts of the test + * + * Return: Success: 0 + * + * Failure: number of errors + * + * Programmer: Robb Matzke + * Wednesday, September 30, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static int +test(fill_t fill_style, const double splits[], + hbool_t verbose, hbool_t use_rdcc) +{ + hid_t file, fapl, dcpl, xfer, mspace, fspace, dset; + hsize_t ch_size[1] = {1}; /*chunk size */ + hsize_t cur_size[1] = {1000}; /*current dataset size */ + hsize_t max_size[1] = {H5S_UNLIMITED}; /*maximum dataset size */ + hssize_t hs_start[1]; /*hyperslab start offset*/ + hsize_t hs_count[1] = {1}; /*hyperslab nelmts */ + int fd; /*h5 file direct */ + static int *had = NULL; /*for random filling */ + const char *sname; /*fill style nam */ + int mdc_nelmts; /*num meta objs to cache*/ + hsize_t i; + int j; + struct stat sb; + + if (!had) had = calloc(cur_size[0], sizeof(int)); + if ((fapl=H5Pcreate(H5P_FILE_ACCESS))<0) goto error; + if (!use_rdcc) { + if (H5Pget_cache(fapl, &mdc_nelmts, NULL, NULL, NULL)<0) goto error; + if (H5Pset_cache(fapl, mdc_nelmts, 0, 0, 0.0)<0) goto error; + } + if ((file=H5Fcreate(FILE_NAME_1, H5F_ACC_TRUNC, H5P_DEFAULT, fapl))<0) { + goto error; + } + if ((dcpl=H5Pcreate(H5P_DATASET_CREATE))<0) goto error; + if (H5Pset_chunk(dcpl, 1, ch_size)<0) goto error; + if ((xfer=H5Pcreate(H5P_DATASET_XFER))<0) goto error; + if (H5Pset_btree_ratios(xfer, splits[0], splits[1], splits[2])<0) { + goto error; + } + if ((fspace=H5Screate_simple(1, cur_size, max_size))<0) goto error; + if ((mspace=H5Screate_simple(1, ch_size, ch_size))<0) goto error; + if ((dset=H5Dcreate(file, "chunked", H5T_NATIVE_INT, + fspace, dcpl))<0) goto error; + +#if !defined( __MWERKS__) + + /* + workaround for a bug in the Metrowerks open function + pvn + */ + if ((fd=open(FILE_NAME_1, O_RDONLY))<0) goto error; +#endif + + for (i=1; i<=cur_size[0]; i++) { + + /* Decide which chunk to write to */ + switch (fill_style) { + case FILL_FORWARD: + hs_start[0] = i-1; + break; + case FILL_REVERSE: + hs_start[0] = cur_size[0]-i; + break; + case FILL_INWARD: + hs_start[0] = i%2 ? i/2 : cur_size[0]-i/2; + break; + case FILL_OUTWARD: + j = (cur_size[0]-i)+1; + hs_start[0] = j%2 ? j/2 : cur_size[0]-j/2; + break; + case FILL_RANDOM: + for (j=rand()%cur_size[0]; had[j]; j=(j+1)%cur_size[0]) /*void*/; + hs_start[0] = j; + had[j] = 1; + break; + case FILL_ALL: + abort(); + } + + /* Write the chunk */ + if (H5Sselect_hyperslab(fspace, H5S_SELECT_SET, hs_start, NULL, + hs_count, NULL)<0) goto error; + if (H5Dwrite(dset, H5T_NATIVE_INT, mspace, fspace, xfer, &i)<0) { + goto error; + } + + +#if !defined( __MWERKS__) + + /* Determine overhead */ + if (verbose) { + if (H5Fflush(file, H5F_SCOPE_LOCAL)<0) goto error; + if (fstat(fd, &sb)<0) goto error; + /* + * The extra cast in the following statement is a bug workaround + * for the Win32 version 5.0 compiler. + * 1998-11-06 ptl + */ + printf("%4lu %8.3f ***\n", + (unsigned long)i, + (double)(hssize_t)(sb.st_size-i*sizeof(int))/(hssize_t)i); + } +#endif + + + } + + H5Dclose(dset); + H5Sclose(mspace); + H5Sclose(fspace); + H5Pclose(dcpl); + H5Fclose(file); + + if (!verbose) { + switch (fill_style) { + case FILL_FORWARD: + sname = "forward"; + break; + case FILL_REVERSE: + sname = "reverse"; + break; + case FILL_INWARD: + sname = "inward"; + break; + case FILL_OUTWARD: + sname = "outward"; + break; + case FILL_RANDOM: + sname = "random"; + break; + case FILL_ALL: + abort(); + } + +#if !defined( __MWERKS__) + + if (fstat(fd, &sb)<0) goto error; + printf("%-7s %8.3f\n", sname, + (double)(hssize_t)(sb.st_size-cur_size[0]*sizeof(int))/ + (hssize_t)cur_size[0]); +#endif + + } + + +#if !defined( __MWERKS__) + close(fd); +#endif + + return 0; + + error: + H5Dclose(dset); + H5Sclose(mspace); + H5Sclose(fspace); + H5Pclose(dcpl); + H5Fclose(file); + free(had); + close(fd); + return 1; +} + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: + * + * Return: Success: zero + * + * Failure: non-zero + * + * Programmer: Robb Matzke + * Monday, September 28, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main(int argc, char *argv[]) +{ + hid_t xfer; + fill_t fill_style = FILL_ALL; + hbool_t use_cache = FALSE; + double splits[3]; + int i, j, nerrors=0; + + /* Default split ratios */ + H5Eset_auto(display_error_cb, NULL); + if ((xfer=H5Pcreate(H5P_DATASET_XFER))<0) goto error; + if (H5Pget_btree_ratios(xfer, splits+0, splits+1, splits+2)<0) { + goto error; + } + if (H5Pclose(xfer)<0) goto error; + + /* Parse command-line options */ + for (i=1, j=0; i<argc; i++) { + if (!strcmp(argv[i], "forward")) { + fill_style = FILL_FORWARD; + } else if (!strcmp(argv[i], "reverse")) { + fill_style = FILL_REVERSE; + } else if (!strcmp(argv[i], "inward")) { + fill_style = FILL_INWARD; + } else if (!strcmp(argv[i], "outward")) { + fill_style = FILL_OUTWARD; + } else if (!strcmp(argv[i], "random")) { + fill_style = FILL_RANDOM; + } else if (!strcmp(argv[i], "cache")) { + use_cache = TRUE; + } else if (j<3 && (isdigit(argv[i][0]) || '.'==argv[i][0])) { + splits[j++] = strtod(argv[i], NULL); + } else { + usage(argv[0]); + } + } + + if (FILL_ALL==fill_style) { + printf("%-7s %8s\n", "Style", "Bytes/Chunk"); + printf("%-7s %8s\n", "-----", "-----------"); + nerrors += test(FILL_FORWARD, splits, FALSE, use_cache); + nerrors += test(FILL_REVERSE, splits, FALSE, use_cache); + nerrors += test(FILL_INWARD, splits, FALSE, use_cache); + nerrors += test(FILL_OUTWARD, splits, FALSE, use_cache); + nerrors += test(FILL_RANDOM, splits, FALSE, use_cache); + } else { + if (use_cache) usage(argv[0]); + nerrors += test(fill_style, splits, TRUE, FALSE); + } + if (nerrors>0) goto error; + cleanup(); + return 0; + + error: + fprintf(stderr, "*** ERRORS DETECTED ***\n"); + return 1; +} diff --git a/perform/perf.c b/perform/perf.c new file mode 100644 index 0000000..8ba0e88 --- /dev/null +++ b/perform/perf.c @@ -0,0 +1,453 @@ +/* + * Author: Albert Cheng of NCSA, May 1, 2001. + * This is derived from code given to me by Robert Ross. + * + * NOTE: This code assumes that all command line arguments make it out to all + * the processes that make up the parallel job, which isn't always the case. + * So if it doesn't work on some platform, that might be why. + */ + +#ifdef H5_HAVE_PARALLEL +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/time.h> +#include <mpi.h> +#ifndef MPI_FILE_NULL /*MPIO may be defined in mpi.h already */ +# include <mpio.h> +#endif + + +#include "hdf5.h" +/* Macro definitions */ +/* Verify: + * if val is false (0), print mesg and if fatal is true (non-zero), die. + */ +#define H5FATAL 1 +#define VRFY(val, mesg, fatal) do { \ + if (!val) { \ + printf("Proc %d: ", mynod); \ + printf("*** Assertion failed (%s) at line %4d in %s\n", \ + mesg, (int)__LINE__, __FILE__); \ + if (fatal){ \ + fflush(stdout); \ + goto die_jar_jar_die; \ + } \ + } \ +} while(0) +#define RANK 1 +hsize_t dims[RANK]; /* dataset dim sizes */ +hsize_t block[RANK], stride[RANK], count[RANK]; +hssize_t start[RANK]; +hid_t fid; /* HDF5 file ID */ +hid_t acc_tpl; /* File access templates */ +hid_t sid; /* Dataspace ID */ +hid_t file_dataspace; /* File dataspace ID */ +hid_t mem_dataspace; /* memory dataspace ID */ +hid_t dataset; /* Dataset ID */ +hsize_t opt_alignment = 1; +hsize_t opt_threshold = 1; +int opt_split_vfd = 0; +char *meta_ext, *raw_ext; /* holds the meta and raw file extension if */ + /* opt_split_vfd is set */ + + +/* DEFAULT VALUES FOR OPTIONS */ +int64_t opt_block = 1048576*16; +int opt_iter = 1; +int opt_stripe = -1; +int opt_correct = 0; +int amode = O_RDWR | O_CREAT; +char opt_file[256] = "/foo/test.out\0"; +char opt_pvfstab[256] = "notset\0"; +int opt_pvfstab_set = 0; + +/* function prototypes */ +int parse_args(int argc, char **argv); +double Wtime(void); + +extern int errno; +extern int debug_on; + +/* globals needed for getopt */ +extern char *optarg; +extern int optind, opterr; + +int main(int argc, char **argv) +{ + char *buf, *tmp, *buf2, *tmp2, *check; + int i, j, mynod=0, nprocs=1, err, my_correct = 1, correct, myerrno; + double stim, etim; + double write_tim = 0; + double read_tim = 0; + double read_bw, write_bw; + double max_read_tim, max_write_tim; + double min_read_tim, min_write_tim; + double ave_read_tim, ave_write_tim; + int64_t iter_jump = 0; + int64_t seek_position = 0; + MPI_File fh; + MPI_Status status; + int nchars; + herr_t ret; /* Generic return value */ + + /* startup MPI and determine the rank of this process */ + MPI_Init(&argc,&argv); + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &mynod); + + /* parse the command line arguments */ + parse_args(argc, argv); + + if (mynod == 0) printf("# Using hdf5-io calls.\n"); + + + /* kindof a weird hack- if the location of the pvfstab file was + * specified on the command line, then spit out this location into + * the appropriate environment variable: */ + +#if H5_HAVE_SETENV +/* no setenv or unsetenv */ + if (opt_pvfstab_set) { + if((setenv("PVFSTAB_FILE", opt_pvfstab, 1)) < 0){ + perror("setenv"); + goto die_jar_jar_die; + } + } +#endif + + /* this is how much of the file data is covered on each iteration of + * the test. used to help determine the seek offset on each + * iteration */ + iter_jump = nprocs * opt_block; + + /* setup a buffer of data to write */ + if (!(tmp = (char *) malloc(opt_block + 256))) { + perror("malloc"); + goto die_jar_jar_die; + } + buf = tmp + 128 - (((long)tmp) % 128); /* align buffer */ + + if (opt_correct) { + /* do the same buffer setup for verifiable data */ + if (!(tmp2 = (char *) malloc(opt_block + 256))) { + perror("malloc2"); + goto die_jar_jar_die; + } + buf2 = tmp + 128 - (((long)tmp) % 128); + } + + /* setup file access template with parallel IO access. */ + if (opt_split_vfd){ + hid_t mpio_pl; + + mpio_pl = H5Pcreate (H5P_FILE_ACCESS); + VRFY((acc_tpl >= 0), "", H5FATAL); + ret = H5Pset_fapl_mpio(mpio_pl, MPI_COMM_WORLD, MPI_INFO_NULL); + VRFY((ret >= 0), "", H5FATAL); + + /* set optional allocation alignment */ + if (opt_alignment*opt_threshold != 1){ + ret = H5Pset_alignment(acc_tpl, opt_threshold, opt_alignment ); + VRFY((ret >= 0), "H5Pset_alignment succeeded", !H5FATAL); + } + + /* setup file access template */ + acc_tpl = H5Pcreate (H5P_FILE_ACCESS); + VRFY((acc_tpl >= 0), "", H5FATAL); + ret = H5Pset_fapl_split(acc_tpl, meta_ext, mpio_pl, raw_ext, mpio_pl); + VRFY((ret >= 0), "H5Pset_fapl_split succeeded", H5FATAL); + }else{ + /* setup file access template */ + acc_tpl = H5Pcreate (H5P_FILE_ACCESS); + VRFY((acc_tpl >= 0), "", H5FATAL); + ret = H5Pset_fapl_mpio(acc_tpl, MPI_COMM_WORLD, MPI_INFO_NULL); + VRFY((ret >= 0), "", H5FATAL); + + /* set optional allocation alignment */ + if (opt_alignment*opt_threshold != 1){ + ret = H5Pset_alignment(acc_tpl, opt_threshold, opt_alignment ); + VRFY((ret >= 0), "H5Pset_alignment succeeded", !H5FATAL); + } + } + + /* create the parallel file */ + fid=H5Fcreate(opt_file,H5F_ACC_TRUNC,H5P_DEFAULT,acc_tpl); + VRFY((fid >= 0), "H5Fcreate succeeded", H5FATAL); + + /* define a contiquous dataset of opt_iter*nprocs*opt_block chars */ + dims[0] = opt_iter*nprocs*opt_block; + sid = H5Screate_simple (RANK, dims, NULL); + VRFY((sid >= 0), "H5Screate_simple succeeded", H5FATAL); + dataset = H5Dcreate(fid, "Dataset1", H5T_NATIVE_CHAR, sid, + H5P_DEFAULT); + VRFY((dataset >= 0), "H5Dcreate succeeded", H5FATAL); + + /* create the memory dataspace and the file dataspace */ + dims[0] = opt_block; + mem_dataspace = H5Screate_simple (RANK, dims, NULL); + VRFY((mem_dataspace >= 0), "", H5FATAL); + file_dataspace = H5Dget_space (dataset); + VRFY((file_dataspace >= 0), "H5Dget_space succeeded", H5FATAL); + + /* now each process writes a block of opt_block chars in round robbin + * fashion until the whole dataset is covered. + */ + for (j=0; j < opt_iter; j++) { + /* setup a file dataspace selection */ + start[0] = (j*iter_jump)+(mynod*opt_block); + stride[0] = block[0] = opt_block; + count[0]= 1; + ret=H5Sselect_hyperslab(file_dataspace, H5S_SELECT_SET, start, stride, count, block); + VRFY((ret >= 0), "H5Sset_hyperslab succeeded", H5FATAL); + + if (opt_correct) /* fill in buffer for iteration */ { + for (i=mynod+j, check=buf; i<opt_block; i++,check++) *check=(char)i; + } + + /* discover the starting time of the operation */ + MPI_Barrier(MPI_COMM_WORLD); + stim = MPI_Wtime(); + + /* write data */ + ret = H5Dwrite(dataset, H5T_NATIVE_CHAR, mem_dataspace, file_dataspace, + H5P_DEFAULT, buf); + VRFY((ret >= 0), "H5Dwrite dataset1 succeeded", !H5FATAL); + + /* discover the ending time of the operation */ + etim = MPI_Wtime(); + + write_tim += (etim - stim); + + /* we are done with this "write" iteration */ + } + + /* close dataset and file */ + ret=H5Dclose(dataset); + VRFY((ret >= 0), "H5Dclose succeeded", H5FATAL); + ret=H5Fclose(fid); + VRFY((ret >= 0), "H5Fclose succeeded", H5FATAL); + + + + /* wait for everyone to synchronize at this point */ + MPI_Barrier(MPI_COMM_WORLD); + + /* reopen the file for reading */ + fid=H5Fopen(opt_file,H5F_ACC_RDONLY,acc_tpl); + VRFY((fid >= 0), "", H5FATAL); + + /* open the dataset */ + dataset = H5Dopen(fid, "Dataset1"); + VRFY((dataset >= 0), "H5Dopen succeeded", H5FATAL); + + /* we can re-use the same mem_dataspace and file_dataspace + * the H5Dwrite used since the dimension size is the same. + */ + + /* we are going to repeat the read the same pattern the write used */ + for (j=0; j < opt_iter; j++) { + /* setup a file dataspace selection */ + start[0] = (j*iter_jump)+(mynod*opt_block); + stride[0] = block[0] = opt_block; + count[0]= 1; + ret=H5Sselect_hyperslab(file_dataspace, H5S_SELECT_SET, start, stride, count, block); + VRFY((ret >= 0), "H5Sset_hyperslab succeeded", H5FATAL); + /* seek to the appropriate spot give the current iteration and + * rank within the MPI processes */ + + /* discover the start time */ + MPI_Barrier(MPI_COMM_WORLD); + stim = MPI_Wtime(); + + /* read data */ + /* read in the file data */ + if (!opt_correct){ + ret = H5Dread(dataset, H5T_NATIVE_CHAR, mem_dataspace, file_dataspace, + H5P_DEFAULT, buf); + } + else{ + ret = H5Dread(dataset, H5T_NATIVE_CHAR, mem_dataspace, file_dataspace, + H5P_DEFAULT, buf2); + } + myerrno = errno; + /* discover the end time */ + etim = MPI_Wtime(); + read_tim += (etim - stim); + VRFY((ret >= 0), "H5Dwrite dataset1 succeeded", !H5FATAL); + + + if (ret < 0) fprintf(stderr, "node %d, read error, loc = %Ld: %s\n", + mynod, mynod*opt_block, strerror(myerrno)); + + /* if the user wanted to check correctness, compare the write + * buffer to the read buffer */ + if (opt_correct && memcmp(buf, buf2, opt_block)) { + fprintf(stderr, "node %d, correctness test failed\n", mynod); + my_correct = 0; + MPI_Allreduce(&my_correct, &correct, 1, MPI_INT, MPI_MIN, + MPI_COMM_WORLD); + } + + /* we are done with this read iteration */ + } + + /* close dataset and file */ + ret=H5Dclose(dataset); + VRFY((ret >= 0), "H5Dclose succeeded", H5FATAL); + ret=H5Fclose(fid); + VRFY((ret >= 0), "H5Fclose succeeded", H5FATAL); + + /* compute the read and write times */ + MPI_Allreduce(&read_tim, &max_read_tim, 1, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); + MPI_Allreduce(&read_tim, &min_read_tim, 1, MPI_DOUBLE, MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(&read_tim, &ave_read_tim, 1, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + + /* calculate the average from the sum */ + ave_read_tim = ave_read_tim / nprocs; + + MPI_Allreduce(&write_tim, &max_write_tim, 1, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); + MPI_Allreduce(&write_tim, &min_write_tim, 1, MPI_DOUBLE, MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(&write_tim, &ave_write_tim, 1, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); + + /* calculate the average from the sum */ + ave_write_tim = ave_write_tim / nprocs; + + /* print out the results on one node */ + if (mynod == 0) { + read_bw = ((int64_t)(opt_block*nprocs*opt_iter))/(max_read_tim*1000000.0); + write_bw = ((int64_t)(opt_block*nprocs*opt_iter))/(max_write_tim*1000000.0); + + printf("nr_procs = %d, nr_iter = %d, blk_sz = %ld\n", nprocs, + opt_iter, (long)opt_block); + + printf("# total_size = %ld\n", (long)(opt_block*nprocs*opt_iter)); + + printf("# Write: min_time = %f, max_time = %f, mean_time = %f\n", + min_write_tim, max_write_tim, ave_write_tim); + printf("# Read: min_time = %f, max_time = %f, mean_time = %f\n", + min_read_tim, max_read_tim, ave_read_tim); + + printf("Write bandwidth = %f Mbytes/sec\n", write_bw); + printf("Read bandwidth = %f Mbytes/sec\n", read_bw); + + if (opt_correct) { + printf("Correctness test %s.\n", correct ? "passed" : "failed"); + } + } + + +die_jar_jar_die: + +#if H5_HAVE_SETENV +/* no setenv or unsetenv */ + /* clear the environment variable if it was set earlier */ + if (opt_pvfstab_set){ + unsetenv("PVFSTAB_FILE"); + } +#endif + + free(tmp); + if (opt_correct) free(tmp2); + MPI_Finalize(); + return(0); +} + +int parse_args(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, "s:b:i:f:p:a:2:c")) != EOF) { + switch (c) { + case 's': /* stripe */ + opt_stripe = atoi(optarg); + break; + case 'b': /* block size */ + opt_block = atoi(optarg); + break; + case 'i': /* iterations */ + opt_iter = atoi(optarg); + break; + case 'f': /* filename */ + strncpy(opt_file, optarg, 255); + break; + case 'p': /* pvfstab file */ + strncpy(opt_pvfstab, optarg, 255); + opt_pvfstab_set = 1; + break; + case 'a': /* aligned allocation. + * syntax: -a<alignment>/<threshold> + * e.g., -a4096/512 allocate at 4096 bytes + * boundary if request size >= 512. + */ + {char *p; + opt_alignment = atoi(optarg); + if (p=(char*)strchr(optarg, '/')) + opt_threshold = atoi(p+1); + } + HDfprintf(stdout, + "alignment/threshold=%Hu/%Hu\n", + opt_alignment, opt_threshold); + break; + case '2': /* use 2-files, i.e., split file driver */ + opt_split_vfd=1; + /* get meta and raw file extension. */ + /* syntax is <raw_ext>,<meta_ext> */ + meta_ext = raw_ext = optarg; + while (*raw_ext != '\0'){ + if (*raw_ext == ','){ + *raw_ext = '\0'; + raw_ext++; + break; + } + raw_ext++; + } + printf("split-file-vfd used: %s,%s\n", + meta_ext, raw_ext); + break; + case 'c': /* correctness */ + opt_correct = 1; + break; + case '?': /* unknown */ + default: + break; + } + } + return(0); +} + +/* Wtime() - returns current time in sec., in a double */ +double Wtime() +{ + struct timeval t; + + gettimeofday(&t, NULL); + return((double)t.tv_sec + (double)t.tv_usec / 1000000); +} + +/* + * Local variables: + * c-indent-level: 3 + * c-basic-offset: 3 + * tab-width: 3 + * End: + */ + +#else /* H5_HAVE_PARALLEL */ +/* dummy program since H5_HAVE_PARALLE is not configured in */ +int +main() +{ +printf("No parallel performance because parallel is not configured in\n"); +return(0); +} +#endif /* H5_HAVE_PARALLEL */ |