From 22270493b018177ef8686778a6e471ccf39e0a66 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Tue, 14 Aug 2001 13:46:27 -0500 Subject: [svn-r4346] Purpose: New feature Description: Started this directory for performance measurement programs. The programs here got compiled but not automatically run (just like the examples direcotry.) The programs have existed but now gathered to this one directory. iopipe.c, chunk.c and overhead.c were from test. perf.c and mpi-perf.c were from testpar. Platforms tested: eirene (serial and parallel). overhead failed during run due to some property error. This is probably due to the recent change of properties code. perf and mpi-perf do not compile correctly for Parallel mode. Checking them to make them available to others. --- perform/Makefile.in | 55 ++++++ perform/chunk.c | 521 +++++++++++++++++++++++++++++++++++++++++++++++++ perform/iopipe.c | 546 ++++++++++++++++++++++++++++++++++++++++++++++++++++ perform/mpi-perf.c | 357 ++++++++++++++++++++++++++++++++++ perform/overhead.c | 403 ++++++++++++++++++++++++++++++++++++++ perform/perf.c | 453 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 2335 insertions(+) create mode 100644 perform/Makefile.in create mode 100644 perform/chunk.c create mode 100644 perform/iopipe.c create mode 100644 perform/mpi-perf.c create mode 100644 perform/overhead.c create mode 100644 perform/perf.c 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 + * 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 +# include +# include +# include +#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; i0) 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 + * Thursday, March 12, 1998 + */ + +/* See H5private.h for how to include headers */ +#undef NDEBUG +#include "hdf5.h" + +#ifdef H5_HAVE_WINSOCK_H +#include +#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 +# include +# include +# include +# include +#endif + +#ifdef H5_HAVE_UNISTD_H +# include +# include +#endif + +#if defined(H5_TIME_WITH_SYS_TIME) +# include +# include +#elif defined(H5_HAVE_SYS_TIME_H) +# include +#else +# include +#endif + +#ifdef H5_HAVE_SYS_RESOURCE_H +# include +#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 +#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; itime; + 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=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=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=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=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=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=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 +#include +#include +#include +#include +#include +#include +#include +#ifndef MPI_FILE_NULL /*MPIO may be defined in mpi.h already */ +# include +#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 + * 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 +# include +# include +# include +# include +#endif + +#ifdef H5_HAVE_IO_H +# include +#endif + +#ifdef H5_HAVE_UNISTD_H +# include +# include +#endif + +#ifdef H5_HAVE_IO_H +# include +#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; i0) 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 +#include +#include +#include +#include +#include +#include +#include +#ifndef MPI_FILE_NULL /*MPIO may be defined in mpi.h already */ +# include +#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= 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/ + * 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 , */ + 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 */ -- cgit v0.12