diff options
Diffstat (limited to 'tools/test/perform')
23 files changed, 12887 insertions, 0 deletions
diff --git a/tools/test/perform/CMakeLists.txt b/tools/test/perform/CMakeLists.txt new file mode 100644 index 0000000..bea2d0c --- /dev/null +++ b/tools/test/perform/CMakeLists.txt @@ -0,0 +1,124 @@ +cmake_minimum_required (VERSION 3.1.0) +PROJECT (HDF5_TOOLS_TEST_PERFORM ) + +#----------------------------------------------------------------------------- +# Apply Definitions to compiler in this directory and below +#----------------------------------------------------------------------------- +add_definitions (${HDF_EXTRA_C_FLAGS}) + +#----------------------------------------------------------------------------- +# Setup include Directories +#----------------------------------------------------------------------------- +INCLUDE_DIRECTORIES (${HDF5_TEST_SRC_DIR}) +INCLUDE_DIRECTORIES (${HDF5_TOOLS_DIR}/lib ) + +# -------------------------------------------------------------------- +# Add the executables +# -------------------------------------------------------------------- +#-- Adding test for h5perf_serial +set (h5perf_serial_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/sio_perf.c + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/sio_engine.c +) +add_executable (h5perf_serial ${h5perf_serial_SOURCES}) +TARGET_NAMING (h5perf_serial STATIC) +TARGET_C_PROPERTIES (h5perf_serial STATIC " " " ") +target_link_libraries (h5perf_serial ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) +set_target_properties (h5perf_serial PROPERTIES FOLDER perform) + +if (HDF5_BUILD_PERFORM_STANDALONE) + #-- Adding test for h5perf_serial_alone + set (h5perf_serial_alone_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/sio_perf.c + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/sio_engine.c + ) + add_executable (h5perf_serial_alone ${h5perf_serial_alone_SOURCES}) + TARGET_NAMING (h5perf_serial_alone STATIC) + TARGET_C_PROPERTIES (h5perf_serial_alone STATIC " " " ") + target_link_libraries (h5perf_serial_alone ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) + set_target_properties (h5perf_serial_alone PROPERTIES FOLDER perform) + set_property (TARGET h5perf_serial_alone + APPEND PROPERTY COMPILE_DEFINITIONS STANDALONE + ) +endif (HDF5_BUILD_PERFORM_STANDALONE) + +#-- Adding test for chunk +set (chunk_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/chunk.c +) +ADD_EXECUTABLE(chunk ${chunk_SOURCES}) +TARGET_NAMING (chunk STATIC) +TARGET_C_PROPERTIES (chunk STATIC " " " ") +TARGET_LINK_LIBRARIES(chunk ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) +set_target_properties (chunk PROPERTIES FOLDER perform) + +#-- Adding test for iopipe +set (iopipe_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/iopipe.c +) +add_executable (iopipe ${iopipe_SOURCES}) +TARGET_NAMING (iopipe STATIC) +TARGET_C_PROPERTIES (iopipe STATIC " " " ") +target_link_libraries (iopipe ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) +set_target_properties (iopipe PROPERTIES FOLDER perform) + +#-- Adding test for overhead +set (overhead_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/overhead.c +) +add_executable (overhead ${overhead_SOURCES}) +TARGET_NAMING (overhead STATIC) +TARGET_C_PROPERTIES (overhead STATIC " " " ") +target_link_libraries (overhead ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) +set_target_properties (overhead PROPERTIES FOLDER perform) + +#-- Adding test for perf_meta + set (perf_meta_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/perf_meta.c + ) + add_executable (perf_meta ${perf_meta_SOURCES}) + TARGET_NAMING (perf_meta STATIC) + TARGET_C_PROPERTIES (perf_meta STATIC " " " ") + target_link_libraries (perf_meta ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET} ${HDF5_TEST_LIB_TARGET}) + set_target_properties (perf_meta PROPERTIES FOLDER perform) + +#-- Adding test for zip_perf +set (zip_perf_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/zip_perf.c +) +add_executable (zip_perf ${zip_perf_SOURCES}) +TARGET_NAMING (zip_perf STATIC) +TARGET_C_PROPERTIES (zip_perf STATIC " " " ") +target_link_libraries (zip_perf ${HDF5_TOOLS_LIB_TARGET} ${HDF5_LIB_TARGET}) +set_target_properties (zip_perf PROPERTIES FOLDER perform) + +if (H5_HAVE_PARALLEL AND BUILD_TESTING) + #-- Adding test for h5perf + set (h5perf_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/pio_perf.c + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/pio_engine.c + ) + add_executable (h5perf ${h5perf_SOURCES}) + TARGET_NAMING (h5perf STATIC) + TARGET_C_PROPERTIES (h5perf STATIC " " " ") + target_link_libraries (h5perf ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET} ${HDF5_TEST_LIB_TARGET}) + set_target_properties (h5perf PROPERTIES FOLDER perform) + + if (HDF5_BUILD_PERFORM_STANDALONE) + #-- Adding test for h5perf + set (h5perf_alone_SOURCES + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/pio_perf.c + ${HDF5_TOOLS_TEST_PERFORM_SOURCE_DIR}/pio_engine.c + ) + add_executable (h5perf_alone ${h5perf_alone_SOURCES}) + TARGET_NAMING (h5perf_alone STATIC) + TARGET_C_PROPERTIES (h5perf_alone STATIC " " " ") + target_link_libraries (h5perf_alone ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET} ${HDF5_TEST_LIB_TARGET}) + set_target_properties (h5perf_alone PROPERTIES FOLDER perform) + set_property (TARGET h5perf_alone + APPEND PROPERTY COMPILE_DEFINITIONS STANDALONE + ) + endif (HDF5_BUILD_PERFORM_STANDALONE) +endif (H5_HAVE_PARALLEL AND BUILD_TESTING) + +include (CMakeTests.cmake) diff --git a/tools/test/perform/CMakeTests.cmake b/tools/test/perform/CMakeTests.cmake new file mode 100644 index 0000000..0ea40a3 --- /dev/null +++ b/tools/test/perform/CMakeTests.cmake @@ -0,0 +1,54 @@ + +############################################################################## +############################################################################## +### T E S T I N G ### +############################################################################## +############################################################################## + +HDFTEST_COPY_FILE("${HDF5_TOOLS_DIR}/testfiles/tfilters.h5" "${PROJECT_BINARY_DIR}/tfilters.h5" "zip_perf_files") +add_custom_target(zip_perf_files ALL COMMENT "Copying files needed by zip_perf tests" DEPENDS ${zip_perf_list}) + +#----------------------------------------------------------------------------- +# Add Tests +#----------------------------------------------------------------------------- + +# Remove any output file left over from previous test run +add_test ( + NAME PERFORM_h5perform-clear-objects + COMMAND ${CMAKE_COMMAND} + -E remove + chunk.h5 + iopipe.h5 + iopipe.raw + x-diag-rd.dat + x-diag-wr.dat + x-rowmaj-rd.dat + x-rowmaj-wr.dat + x-gnuplot +) + +add_test (NAME PERFORM_h5perf_serial COMMAND $<TARGET_FILE:h5perf_serial>) +set_tests_properties (PERFORM_h5perf_serial PROPERTIES TIMEOUT 1800) + +if (HDF5_BUILD_PERFORM_STANDALONE) + add_test (NAME PERFORM_h5perf_serial_alone COMMAND $<TARGET_FILE:h5perf_serial_alone>) +endif (HDF5_BUILD_PERFORM_STANDALONE) + +add_test (NAME PERFORM_chunk COMMAND $<TARGET_FILE:chunk>) + +add_test (NAME PERFORM_iopipe COMMAND $<TARGET_FILE:iopipe>) + +add_test (NAME PERFORM_overhead COMMAND $<TARGET_FILE:overhead>) + +add_test (NAME PERFORM_perf_meta COMMAND $<TARGET_FILE:perf_meta>) + +add_test (NAME PERFORM_zip_perf_help COMMAND $<TARGET_FILE:zip_perf> "-h") +add_test (NAME PERFORM_zip_perf COMMAND $<TARGET_FILE:zip_perf> tfilters.h5) + +if (H5_HAVE_PARALLEL) + add_test (NAME PERFORM_h5perf COMMAND ${MPIEXEC} ${MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS} ${MPIEXEC_POSTFLAGS} $<TARGET_FILE:h5perf>) + + if (HDF5_BUILD_PERFORM_STANDALONE) + add_test (NAME PERFORM_h5perf_alone COMMAND ${MPIEXEC} ${MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS} ${MPIEXEC_POSTFLAGS} $<TARGET_FILE:h5perf_alone>) + endif (HDF5_BUILD_PERFORM_STANDALONE) +endif (H5_HAVE_PARALLEL) diff --git a/tools/test/perform/COPYING b/tools/test/perform/COPYING new file mode 100644 index 0000000..6903daf --- /dev/null +++ b/tools/test/perform/COPYING @@ -0,0 +1,16 @@ + + Copyright by The HDF Group and + The Board of Trustees of the University of Illinois. + All rights reserved. + + The files and subdirectories in this directory are part of HDF5. + The full HDF5 copyright notice, including terms governing use, + modification, and redistribution, is contained in the files COPYING + and Copyright.html. COPYING can be found at the root of the source + code distribution tree; Copyright.html can be found at the root + level of an installed copy of the electronic HDF5 document set and + is linked from the top-level documents page. It can also be found + at http://www.hdfgroup.org/HDF5/doc/Copyright.html. If you do not + have access to either file, you may request a copy from + help@hdfgroup.org. + diff --git a/tools/test/perform/Makefile.am b/tools/test/perform/Makefile.am new file mode 100644 index 0000000..1af0e7f --- /dev/null +++ b/tools/test/perform/Makefile.am @@ -0,0 +1,79 @@ +# +# Copyright by The HDF Group. +# Copyright by the Board of Trustees of the University of Illinois. +# All rights reserved. +# +# This file is part of HDF5. The full HDF5 copyright notice, including +# terms governing use, modification, and redistribution, is contained in +# the files COPYING and Copyright.html. COPYING can be found at the root +# of the source code distribution tree; Copyright.html can be found at the +# root level of an installed copy of the electronic HDF5 document set and +# is linked from the top-level documents page. It can also be found at +# http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have +# access to either file, you may request a copy from help@hdfgroup.org. +## +## Makefile.am +## Run automake to generate a Makefile.in from this file. +## +# +# HDF5 Library Performance Makefile(.in) +# + +include $(top_srcdir)/config/commence.am + +AM_CPPFLAGS+=-I$(top_srcdir)/src -I$(top_srcdir)/test -I$(top_srcdir)/tools/lib + +# bin_PROGRAMS will be installed. +if BUILD_PARALLEL_CONDITIONAL + bin_PROGRAMS=h5perf_serial h5perf +else + bin_PROGRAMS=h5perf_serial +endif + +# Add h5perf and h5perf_serial specific linker flags here +h5perf_LDFLAGS = $(LT_STATIC_EXEC) $(AM_LDFLAGS) +h5perf_serial_LDFLAGS = $(LT_STATIC_EXEC) $(AM_LDFLAGS) + +# Some programs are not built or run by default, but can be built by hand or by +# specifying --enable-build-all at configure time. +# Also, some of these programs should only be built in parallel. +# Currently there is no such program. +if BUILD_PARALLEL_CONDITIONAL + PARA_BUILD_ALL= +endif +if BUILD_ALL_CONDITIONAL + BUILD_ALL_PROGS=$(PARA_BUILD_ALL) +endif + +# Define programs that will be run in 'make check' +# List them in the order they should be run. +# Parallel test programs. +if BUILD_PARALLEL_CONDITIONAL + TEST_PROG_PARA=h5perf perf +endif +# Serial test programs. +TEST_PROG = iopipe chunk overhead zip_perf perf_meta h5perf_serial $(BUILD_ALL_PROGS) + +# check_PROGRAMS will be built but not installed. Do not any executable +# that is in bin_PROGRAMS already. Otherwise, it will be removed twice in +# "make clean" and some systems, e.g., AIX, do not like it. +check_PROGRAMS= iopipe chunk overhead zip_perf perf_meta $(BUILD_ALL_PROGS) perf + +h5perf_SOURCES=pio_perf.c pio_engine.c +h5perf_serial_SOURCES=sio_perf.c sio_engine.c + +# These are the files that `make clean' (and derivatives) will remove from +# this directory. +CLEANFILES=*.h5 *.raw *.dat x-gnuplot perftest.out + +# All of the programs depend on the main hdf5 library, and some of them +# depend on test or tools library. +LDADD=$(LIBHDF5) +h5perf_LDADD=$(LIBH5TOOLS) $(LIBH5TEST) $(LIBHDF5) +h5perf_serial_LDADD=$(LIBH5TOOLS) $(LIBH5TEST) $(LIBHDF5) +perf_LDADD=$(LIBH5TEST) $(LIBHDF5) +iopipe_LDADD=$(LIBH5TEST) $(LIBHDF5) +zip_perf_LDADD=$(LIBH5TOOLS) $(LIBH5TEST) $(LIBHDF5) +perf_meta_LDADD=$(LIBH5TEST) $(LIBHDF5) + +include $(top_srcdir)/config/conclude.am diff --git a/tools/test/perform/build_h5perf_alone.sh b/tools/test/perform/build_h5perf_alone.sh new file mode 100644 index 0000000..b65e863 --- /dev/null +++ b/tools/test/perform/build_h5perf_alone.sh @@ -0,0 +1,28 @@ +#! /bin/sh -x +# +# Copyright by The HDF Group. +# Copyright by the Board of Trustees of the University of Illinois. +# All rights reserved. +# +# This file is part of HDF5. The full HDF5 copyright notice, including +# terms governing use, modification, and redistribution, is contained in +# the files COPYING and Copyright.html. COPYING can be found at the root +# of the source code distribution tree; Copyright.html can be found at the +# root level of an installed copy of the electronic HDF5 document set and +# is linked from the top-level documents page. It can also be found at +# http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have +# access to either file, you may request a copy from help@hdfgroup.org. +# +# Name: build_h5perf_alone.sh +# +# Puporse: Build the h5perf tool by standalone mode. +# +# Created: Albert Cheng, 2005/09/18 +# +# Modification: +# + +# Default to use h5pcc to build h5perf unless $H5CC is defined. +# +h5pcc=${H5CC:-h5pcc} +$h5pcc -DSTANDALONE pio_perf.c pio_engine.c pio_timer.c -o h5perf diff --git a/tools/test/perform/build_h5perf_serial_alone.sh b/tools/test/perform/build_h5perf_serial_alone.sh new file mode 100644 index 0000000..2566609 --- /dev/null +++ b/tools/test/perform/build_h5perf_serial_alone.sh @@ -0,0 +1,28 @@ +#! /bin/sh -x +# +# Copyright by The HDF Group. +# Copyright by the Board of Trustees of the University of Illinois. +# All rights reserved. +# +# This file is part of HDF5. The full HDF5 copyright notice, including +# terms governing use, modification, and redistribution, is contained in +# the files COPYING and Copyright.html. COPYING can be found at the root +# of the source code distribution tree; Copyright.html can be found at the +# root level of an installed copy of the electronic HDF5 document set and +# is linked from the top-level documents page. It can also be found at +# http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have +# access to either file, you may request a copy from help@hdfgroup.org. +# +# Name: build_h5perf_serial_alone.sh +# +# Puporse: Build the h5perf_serial tool by standalone mode. +# +# Created: Christian Chilan, 2008/09/02 +# +# Modification: +# + +# Default to use h5cc to build h5perf_serial unless $H5CC is defined. +# +h5cc=${H5CC:-h5cc} +$h5cc -DSTANDALONE sio_perf.c sio_engine.c sio_timer.c -o h5perf_serial diff --git a/tools/test/perform/chunk.c b/tools/test/perform/chunk.c new file mode 100644 index 0000000..b1419ee --- /dev/null +++ b/tools/test/perform/chunk.c @@ -0,0 +1,550 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * 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 + +/* Solaris Studio defines attribute, but for the attributes we need */ +#if !defined(H5_HAVE_ATTRIBUTE) || defined __cplusplus || defined(__SUNPRO_C) +# undef __attribute__ +# define __attribute__(X) /*void*/ +# define H5_ATTR_UNUSED /*void*/ +#else +# define H5_ATTR_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 (double)0.50F +#define RM_END (double)5.00F +#define RM_DELTA (double)0.50F +#define RM_W0 0.0F +#define RM_NRDCC 521 + +/* Diagonal test */ +#define DIAG_CACHE_STRT 25 +#define DIAG_CACHE_END 25 +#define DIAG_CACHE_DELT 5 +#define DIAG_START (double)0.50F +#define DIAG_END (double)5.00F +#define DIAG_DELTA (double)0.50F +/* #define DIAG_W0 0.65F */ +/* #define DIAG_NRDCC 521 */ + +static size_t nio_g; +static hid_t fapl_g = -1; + +/* Local function prototypes */ +static size_t +counter (unsigned H5_ATTR_UNUSED flags, size_t cd_nelmts, + const unsigned *cd_values, size_t nbytes, + size_t *buf_size, void **buf); + +/* This message derives from H5Z */ +const H5Z_class2_t H5Z_COUNTER[1] = {{ + H5Z_CLASS_T_VERS, /* H5Z_class_t version */ + FILTER_COUNTER, /* Filter id number */ + 1, 1, /* Encoding and decoding enabled */ + "counter", /* Filter name for debugging */ + NULL, /* The "can apply" callback */ + NULL, /* The "set local" callback */ + counter, /* The actual filter function */ +}}; + + +/*------------------------------------------------------------------------- + * 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 H5_ATTR_UNUSED flags, size_t H5_ATTR_UNUSED cd_nelmts, + const unsigned H5_ATTR_UNUSED *cd_values, size_t nbytes, + size_t H5_ATTR_UNUSED *buf_size, void H5_ATTR_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(H5Z_COUNTER); + H5Pset_filter(dcpl, FILTER_COUNTER, 0, 0, NULL); + + /* The dataset */ + dset = H5Dcreate2(file, "dset", H5T_NATIVE_SCHAR, space, H5P_DEFAULT, dcpl, H5P_DEFAULT); + assert(dset>=0); + + /* The data */ + buf = (signed char *)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, size_t cache_size, size_t io_size) +{ + hid_t file, dset, mem_space, file_space; + signed char *buf = (signed char *)calloc (1, (size_t)(SQUARE(io_size))); + hsize_t i, j, hs_size[2]; + hsize_t hs_offset[2]; + int mdc_nelmts; + size_t 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 = H5Dopen2(file, "dset", H5P_DEFAULT); + 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, size_t cache_size, size_t io_size, size_t offset) +{ + hid_t file, dset, mem_space, file_space; + hsize_t i, hs_size[2]; + hsize_t nio = 0; + hsize_t hs_offset[2]; + signed char *buf = (signed char *)calloc(1, (size_t)(SQUARE (io_size))); + int mdc_nelmts; + size_t 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 = H5Dopen2(file, "dset", H5P_DEFAULT); + 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)nio/(double)nio_g; +} + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: See file prologue. + * + * Return: Success: + * + * Failure: + * + * Programmer: Robb Matzke + * Thursday, May 14, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main (void) +{ + size_t io_size; + double effic, io_percent; + FILE *f, *d; + size_t 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, (size_t)(CH_SIZE*io_percent)); + printf ("Rowmaj-rd %8d %8.2f", (int)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, (size_t)(CH_SIZE*io_percent)); + printf ("Rowmaj-wr %8d %8.2f", (int)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, (size_t)(CH_SIZE*io_percent)); + printf ("Diag-rd %8d %8.2f", (int)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, (size_t)(CH_SIZE*io_percent)); + printf ("Diag-wr %8d %8.2f", (int)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/tools/test/perform/gen_report.pl b/tools/test/perform/gen_report.pl new file mode 100644 index 0000000..285f5d7 --- /dev/null +++ b/tools/test/perform/gen_report.pl @@ -0,0 +1,527 @@ +#!/usr/bin/perl +# +# Copyright by The HDF Group. +# Copyright by the Board of Trustees of the University of Illinois. +# All rights reserved. +# +# This file is part of HDF5. The full HDF5 copyright notice, including +# terms governing use, modification, and redistribution, is contained in +# the files COPYING and Copyright.html. COPYING can be found at the root +# of the source code distribution tree; Copyright.html can be found at the +# root level of an installed copy of the electronic HDF5 document set and +# is linked from the top-level documents page. It can also be found at +# http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have +# access to either file, you may request a copy from help@hdfgroup.org. +# + +# +# Generates an ASCII and Excel-importable file of tables representing +# the output of running the "pio_perf" command. The name of the input +# file is important. The name should reflect the command-line options +# used in the performance test. It needs to be of the form: +# +# f#[GMK].i#.d#.X#[GMK].x#[GMK]..* +# +# For example: +# +# PIO_output_f1G.i2.d1.X2M.x128K.frost +# +# for a 1GB sized file ran for 2 iterations with 1 dataset from xfer +# buffer size of 128KB to 2MB on the frost machine. +# +# The output file will have the same name as the input, but will append +# ".ascii" for the ASCII file and ".excel" for the Excel-importable +# file. +# +# The data structure used in this program looks like: +# +# %results = { +# num_proc => ( +# %xfer_size => ( +# %posix = { +# 'write-only' => ##, +# 'write-close' => ##, +# 'read-only' => ##, +# 'read-close' => ## +# }, +# %mpio = { +# 'write-only' => ##, +# 'write-close' => ##, +# 'read-only' => ##, +# 'read-close' => ## +# }, +# %phdf = { +# 'write-only' => ##, +# 'write-close' => ##, +# 'read-only' => ##, +# 'read-close' => ## +# } +# ) +# ) +# } + +use IO::Handle; +use Getopt::Long; +use List::Util qw[max]; + +if ($#ARGV == -1) { + usage(); +} + +my ($ascii_output, $excel_output); + +GetOptions("data_type=s"=>\$data_type, + "buffer_size=i"=>\$transfer_buffer_size, + "procs=i"=>\$num_procs_graph, + "help!"=>\$help, + "throughput=s"=>\$throughput_type, + "io_type=i"=>\$io_type, + "3d!"=>\$plot_3d); + +usage() if $help or !@ARGV; + +$throughput_type = "average" if !$throughput_type; +$io_type = 7 if !$io_type; + +foreach my $arg (@ARGV) { + + if ($arg !~ /^-/) { + $arg =~ /f([0-9]+.)\.i([0-9]+)\.d([0-9]+)\.X([0-9]+.)\.x([0-9]+.)\.(.*)/; + + my $output = $arg . $1 . ".X" . $4 . ".x" . $5 . "." . $6; + + $ascii_output = $output . ".ascii"; + $excel_output = $output . ".excel"; + + open(INPUT, "<$arg") or die "error: cannot open file $arg: $!\n"; + open(ASCII_OUTPUT, ">$ascii_output") or + die "error: cannot open file $ascii_output: $!\n"; + open(EXCEL_OUTPUT, ">$excel_output") or + die "error: cannot open file $excel_output: $!\n"; + } + else + { + die "error: unrecognized option: $arg\n"; + } +} + +my %results; +my $num_procs = 0; +my ($xfer_size, $avg_type, $type); + +my $posix = 0, $mpio = 1, $phdf5 = 2; + +##"==== End of Parameters ====" + +while (<INPUT>) { + if (/Number of processors = ([0-9]+)/) { + $num_procs = $1; + } + + if (/Transfer Buffer Size: ([0-9]+)/) { + $xfer_size = $1; + } + + $type = $posix if /POSIX/; + $type = $mpio if /MPIO/; + $type = $phdf5 if /PHDF5/; + + if (/Write Open/) { + $avg_type = "write-close"; + } elsif (/Write/) { + $avg_type = "write-only"; + } elsif (/Read Open/) { + $avg_type = "read-close"; + } elsif (/Read/) { + $avg_type = "read-only"; + } + + if($throughput_type eq "max") + { + if (/Maximum Throughput: ( {0,2}[0-9]+\.[0-9]{2}) MB\/s/) { + $results{$num_procs}{$xfer_size}[$type]{$avg_type} = $1; + } + } + elsif($throughput_type eq "min") + { + if (/Minimum Throughput: ( {0,2}[0-9]+\.[0-9]{2}) MB\/s/) { + $results{$num_procs}{$xfer_size}[$type]{$avg_type} = $1; + } + } + elsif($throughput_type eq "average") + { + if (/Average Throughput: ( {0,2}[0-9]+\.[0-9]{2}) MB\/s/) { + $results{$num_procs}{$xfer_size}[$type]{$avg_type} = $1; + } + } +} + +sub usage { + print "Usage: gen_reporl.pl [options] FILE + options are:\n + -data_type \"data_type\" plots the results for \"write-only\",\"read-only\", \"write-close\", or \"read-close\" (default is write-only)\n + -buffer_size \"buffer_size\" plots data from this buffer size (in kilobytes, default is 128)\n + -procs \"num_procs\" plots data from the run with num_procs processors (default is the highest number of processors for which there is data).\n + -throughput \"throughput_type\" plots either the \"max\", \"min\", or \"average\" throughput (default is average)\n + -io_type \"io_type\" where \"io_type\" is the bitwise or of the io_type for which plotting is desired (1 for POSIX, 2 for MPIO, 4 for PHDF5 (default is 7 (all))\n + -3d if present, does a 3d plot in addition to the normal ones\n"; + + exit 1; +} + +sub create_excel_output_header { + my $output_header; + my $kb = 1024; + my $mb = $kb * $kb; + + foreach my $key (sort { $a <=> $b } keys(%{$results{$num_procs}})) { + if ($key < $mb) { + $key /= $kb; + $output_header .= "\t". $key . "K"; + } else { + $key /= $mb; + $output_header .= "\t". $key . "M"; + } + } + + $output_header; +} + +sub create_excel_output_string { + my ($t) = @_; + my $output_content = ""; + + foreach my $procs (sort { $b <=> $a } keys(%results)) { + $output_content .= "\n$procs Procs"; + $output_content .= "\n" . create_excel_output_header; + $output_content .= "\n POSIX"; + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + $output_content .= "\t$results{$procs}{$xfer}[0]{$t}"; + } + + $output_content .= "\n MPIO"; + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + $output_content .= "\t$results{$procs}{$xfer}[1]{$t}"; + } + + $output_content .= "\n PHDF5"; + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + $output_content .= "\t$results{$procs}{$xfer}[2]{$t}"; + } + + $output_content .= "\n"; + } + + $output_content; +} + +sub is_defined { + my ($t) = @_; + my $def = 1; + + foreach my $procs (sort { $b <=> $a } keys(%results)) { + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + if (!defined($results{$procs}{$xfer}[0]{$t})) { + $def = 0; + } + } + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + if (!defined($results{$procs}{$xfer}[0]{$t})) { + $def = 0; + } + } + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + if (!defined($results{$procs}{$xfer}[0]{$t})) { + $def = 0; + } + } + } + + $def; +} + +sub write_excel_file { + print EXCEL_OUTPUT "\nWrite-Only\n"; + print EXCEL_OUTPUT create_excel_output_string("write-only"); + print EXCEL_OUTPUT "\nWrite-Close\n"; + print EXCEL_OUTPUT create_excel_output_string("write-close"); + + if (is_defined("read-only")) { + print EXCEL_OUTPUT "\nRead-Only\n"; + print EXCEL_OUTPUT create_excel_output_string("read-only"); + print EXCEL_OUTPUT "\nRead-Close\n"; + print EXCEL_OUTPUT create_excel_output_string("read-close"); + } +} + +sub create_ascii_output_header { + my $output_header = " " x 12 . "|"; + my $kb = 1024; + my $mb = $kb * $kb; + + foreach my $key (sort { $a <=> $b } keys(%{$results{$num_procs}})) { + if ($key < $mb) { + $key /= $kb; + $output_header = sprintf("$output_header %-4s |", $key . "K"); + } else { + $key /= $mb; + $output_header = sprintf("$output_header %-4s |", $key . "M"); + } + } + + $output_header; +} + +sub create_ascii_output_string { + my ($t) = @_; + my $output_content = ""; + my $output_header = create_ascii_output_header; + + foreach my $procs (sort { $b <=> $a } keys(%results)) { + $output_content .= "\n$procs Procs"; + $output_content .= "\n$output_header\n"; + $output_content .= "-" x length($output_header); + $output_content .= "\n POSIX |"; + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + $output_content = sprintf("$output_content %-6s |", + $results{$procs}{$xfer}[0]{$t}); + } + + $output_content .= "\n "; + $output_content .= "-" x (length($output_header) - 4); + $output_content .= "\n MPI/IO |"; + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + $output_content = sprintf("$output_content %-6s |", + $results{$procs}{$xfer}[1]{$t}); + } + + $output_content .= "\n "; + $output_content .= "-" x (length($output_header) - 4); + $output_content .= "\n PHDF5 |"; + + foreach my $xfer (sort { $a <=> $b } keys(%{$results{$procs}})) { + $output_content = sprintf("$output_content %-6s |", + $results{$procs}{$xfer}[2]{$t}); + } + + $output_content .= "\n"; + $output_content .= "=" x length($output_header); + $output_content .= "\n"; + } + + $output_content; +} + +sub write_ascii_file { + print ASCII_OUTPUT "\nWrite-Only"; + print ASCII_OUTPUT "\n----------\n"; + print ASCII_OUTPUT create_ascii_output_string("write-only"); + print ASCII_OUTPUT "\n\nWrite-Close"; + print ASCII_OUTPUT "\n-----------\n"; + print ASCII_OUTPUT create_ascii_output_string("write-close"); + + if (is_defined("read-only")) { + print ASCII_OUTPUT "\n\nRead-Only"; + print ASCII_OUTPUT "\n---------\n"; + print ASCII_OUTPUT create_ascii_output_string("read-only"); + print ASCII_OUTPUT "\n\nRead-Close"; + print ASCII_OUTPUT "\n----------\n"; + print ASCII_OUTPUT create_ascii_output_string("read-close"); + } +} + +sub draw_plot +{ + my($p_3d) = @_; + + if($p_3d) + { + $counter = 3; + print GNUPLOT_PIPE "splot "; + } + else + { + $counter = 2; + print GNUPLOT_PIPE "plot "; + } + + if($io_type & 1) { + print GNUPLOT_PIPE " \"gnuplot.data\" using 1:"; + + if($p_3d) { print GNUPLOT_PIPE "2:"; } + + print GNUPLOT_PIPE $counter . " title 'POSIX' with linespoints"; + $counter = $counter + 1; + } + if($io_type & 2) { + if($io_type & 1) { print GNUPLOT_PIPE ", "; } + print GNUPLOT_PIPE "\"gnuplot.data\" using 1:"; + + if($p_3d) { print GNUPLOT_PIPE "2:"; } + + print GNUPLOT_PIPE $counter . " title 'MPIO' with linespoints"; + $counter = $counter + 1; + if($io_type & 4) { print GNUPLOT_PIPE ", ";} + } + if($io_type & 4) { + print GNUPLOT_PIPE " \"gnuplot.data\" using 1:"; + + if($p_3d) { print GNUPLOT_PIPE "2:"; } + + print GNUPLOT_PIPE $counter . " title 'PHDF5' with linespoints"; + + } + print GNUPLOT_PIPE "\n"; +} + + +sub plot_default_graph1 { + open(GNUPLOT_DATA_OUTPUT, ">gnuplot.data") or + die "error: cannot open file gnuplot.data: $!\n"; + + $transfer_buffer_size = 128 if !$transfer_buffer_size; + $data_type = "write-only" if !$data_type; + + #set up the plot + print GNUPLOT_PIPE "set term x11 1\n"; + print GNUPLOT_PIPE "set xlabel \"Number of Processors\"\n"; + print GNUPLOT_PIPE "set title \"" . $data_type . " Performance (Speed vs. Num. Procs)\"\n"; + print GNUPLOT_PIPE "set ylabel \"Bandwdith (MB/s)\"\n"; + print GNUPLOT_PIPE "set label 1 \"Transfer buffer size: " . $transfer_buffer_size . "K\" at graph 0.7, graph 0.7 left \n"; + +#the next line attempts to hack gnuplot to get around it's inability to linearly scale, but logarithmically label an axis + print GNUPLOT_PIPE "set xtics (\"1\" 1, \"2\" 2, \"4\" 4, \"8\" 8, \"16\" 16, \"32\" 32, \"64\" 64, \"128\" 128, \"256\" 256, \"512\" 512, \"1024\" 1024)\n"; + + + foreach $proc (sort { $a <=> $b }( keys %results )) + { + print GNUPLOT_DATA_OUTPUT $proc . "\t"; + if($io_type & 1) { + print GNUPLOT_DATA_OUTPUT $results{$proc}{$transfer_buffer_size*1024}[0]{$data_type} . "\t"; + } + if($io_type & 2) { + print GNUPLOT_DATA_OUTPUT $results{$proc}{$transfer_buffer_size*1024}[1]{$data_type}. "\t"; + } + if($io_type & 4) { + print GNUPLOT_DATA_OUTPUT $results{$proc}{$transfer_buffer_size*1024}[2]{$data_type}; + } + print GNUPLOT_DATA_OUTPUT "\n"; + + } + + close(GNUPLOT_DATA_OUTPUT); + + draw_plot(0); + + unlink(GNUPLOT_DATA_OUTPUT); + +} + + +sub plot_default_graph2 { + open(GNUPLOT_DATA_OUTPUT, ">gnuplot.data") or + die "error: cannot open file gnuplot.data: $!\n"; + + $num_procs_graph = max(sort { $a <=> $b }( keys %results )) if !$num_procs_graph; + print "min-rpocs: " . $num_procs_graph; + $data_type = "write-only" if !$data_type; + + #set up the plot + print GNUPLOT_PIPE "set term x11 2\n"; + print GNUPLOT_PIPE "set xlabel \"Transfer Buffer Size (in bytes)\"\n"; + print GNUPLOT_PIPE "set title \"" . $data_type . " Performance (Speed vs. Transfer Buffer Size)\"\n"; + print GNUPLOT_PIPE "set ylabel \"Bandwdith (MB/s)\"\n"; + print GNUPLOT_PIPE "set label 1 \"Procs: " . $num_procs_graph . "\" at graph 0.7, graph 0.7 left \n"; + +#the next line attempts to hack gnuplot to get around it's inability to linearly scale, but logarithmically label an axis + print GNUPLOT_PIPE "set xtics (\"4K\" 4*1024, \"8K\" 8*1024, \"16K\" 16*1024, \"32K\" 32*1024, \"64K\" 64*1024, \"128K\" 128*1024, \"256K\" 256*1024, \"512K\" 512*1024, \"1M\" 1024*1024, \"2M\" 2048*1024, \"4M\" 4096*1024, \"8M\" 8192*1024, \"16M\" 16384*1024)\n"; + + foreach $xfer (sort {$a <=> $b} ( keys %{$results{$num_procs_graph}} )) + { + print GNUPLOT_DATA_OUTPUT $xfer . "\t"; + if($io_type & 1) { + print GNUPLOT_DATA_OUTPUT $results{$num_procs_graph}{$xfer}[0]{$data_type} . "\t"; + } + if($io_type & 2) { + print GNUPLOT_DATA_OUTPUT $results{$num_procs_graph}{$xfer}[1]{$data_type}. "\t"; + } + if($io_type & 4) { + print GNUPLOT_DATA_OUTPUT $results{$num_procs_graph}{$xfer}[2]{$data_type}; + } + print GNUPLOT_DATA_OUTPUT "\n"; + + } + + close(GNUPLOT_DATA_OUTPUT); + + draw_plot(0); + + unlink(GNUPLOT_DATA_OUTPUT); +} + +sub plot_3d_graph3 { + open(GNUPLOT_DATA_OUTPUT, ">gnuplot.data") or + die "error: cannot open file gnuplot.data: $!\n"; + + #set up the plot + print GNUPLOT_PIPE "set term x11 3\n"; + print GNUPLOT_PIPE "set xlabel \"Num. Processors\"\n"; + print GNUPLOT_PIPE "set title \"Write Speed v. No. Procs v. Buffer Size\"\n"; + print GNUPLOT_PIPE "set ylabel \"Buffer Size (bytes)\"\n"; + print GNUPLOT_PIPE "set zlabel \"Bandwidth (in MB/s)\"\n"; + print GNUPLOT_PIPE "set nolabel\n"; + print GNUPLOT_PIPE "set dgrid3d 30,30\n"; + print GNUPLOT_PIPE "set hidden3d\n"; + +#the next lines attempts to hack gnuplot to get around it's inability to linearly scale, but logarithmically label an axis + print GNUPLOT_PIPE "set xtics (\"1\" 1, \"2\" 2, \"4\" 4, \"8\" 8, \"16\" 16, \"32\" 32, \"64\" 64, \"128\" 128, \"256\" 256, \"512\" 512, \"1024\" 1024)\n"; + print GNUPLOT_PIPE "set ytics (\"4K\" 4*1024, \"8K\" 8*1024, \"16K\" 16*1024, \"32K\" 32*1024, \"64K\" 64*1024, \"128K\" 128*1024, \"256K\" 256*1024, \"512K\" 512*1024, \"1M\" 1024*1024, \"2M\" 2048*1024, \"4M\" 4096*1024, \"8M\" 8192*1024, \"16M\" 16384*1024)\n"; + + +#Read speed on z-axis, processors on x, buffer size on y. + + foreach $proc (sort { $a <=> $b }( keys %results )) + { + foreach $xfer (sort {$a <=> $b} ( keys %{$results{$proc}} )) + { + print GNUPLOT_DATA_OUTPUT $proc . "\t" . $xfer . "\t"; + if($io_type & 1) { + print GNUPLOT_DATA_OUTPUT $results{$proc}{$xfer}[0]{"write-only"} . "\t"; + } + if($io_type & 2) { + print GNUPLOT_DATA_OUTPUT $results{$proc}{$xfer}[1]{"write-only"}. "\t"; + } + if($io_type & 4) { + print GNUPLOT_DATA_OUTPUT $results{$proc}{$xfer}[2]{"write-only"}; + } + print GNUPLOT_DATA_OUTPUT "\n"; + + } + } + + close(GNUPLOT_DATA_OUTPUT); + + draw_plot(1); + + unlink(GNUPLOT_DATA_OUTPUT); +} + +open(GNUPLOT_PIPE, "| tee gnuplot.script | gnuplot -persist") || die "Couldn't run gnuplot: $!\n"; +GNUPLOT_PIPE->autoflush(1); + +write_excel_file; +write_ascii_file; +plot_default_graph1; +sleep 1; +plot_default_graph2; +sleep 1; + +plot_3d_graph3 if $plot_3d; +close(GNUPLOT_PIPE); diff --git a/tools/test/perform/iopipe.c b/tools/test/perform/iopipe.c new file mode 100644 index 0000000..fd62d37 --- /dev/null +++ b/tools/test/perform/iopipe.c @@ -0,0 +1,508 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Programmer: Robb Matzke <matzke@llnl.gov> + * Thursday, March 12, 1998 + */ + +/* See H5private.h for how to include headers */ +#include "hdf5.h" + +#include "H5private.h" + +#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)/(double)1000000.0F) - + ((double)(r_start->ru_utime.tv_sec)+ + (double)(r_start->ru_utime.tv_usec)/(double)1000000.0F); + + s_time = ((double)(r_stop->ru_stime.tv_sec)+ + (double)(r_stop->ru_stime.tv_usec)/(double)1000000.0F) - + ((double)(r_start->ru_stime.tv_sec)+ + (double)(r_start->ru_stime.tv_usec)/(double)1000000.0F); +#endif +#ifndef H5_HAVE_SYS_TIMEB + e_time = ((double)(t_stop->tv_sec)+ + (double)(t_stop->tv_usec)/(double)1000000.0F) - + ((double)(t_start->tv_sec)+ + (double)(t_start->tv_usec)/(double)1000000.0F); +#else + e_time = ((double)(t_stop->tv_sec)+ + (double)(t_stop->tv_usec)/(double)1000.0F) - + ((double)(t_start->tv_sec)+ + (double)(t_start->tv_usec)/(double)1000.0F); +#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 +#if defined(H5_HAVE_WIN32_API) && ! defined(__CYGWIN__) + _flushall(); +#else + int status; + + status = HDsystem("sync"); + HDassert(status >= 0); + + status = HDsystem("df >/dev/null"); + HDassert(status >= 0); +#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 unsigned 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 fd; + unsigned u; + hssize_t n; + off_t offset; + hsize_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])/(double)1024.0F*(double)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 = H5Dcreate2(file, "dset", H5T_NATIVE_UCHAR, file_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + assert(dset >= 0); + the_data = (unsigned char *)malloc((size_t)(size[0] * size[1])); + + /* initial fill for lazy malloc */ + HDmemset(the_data, 0xAA, (size_t)(size[0] * size[1])); + + /* Fill raw */ + synchronize (); +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "fill raw"); + for(u = 0; u < nwrite; u++) { + putc (PROGRESS, stderr); + HDfflush(stderr); + HDmemset(the_data, 0xAA, (size_t)(size[0]*size[1])); + } +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&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 + HDgetrusage(RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "fill hdf5"); + for(u = 0; u < nread; u++) { + putc (PROGRESS, stderr); + HDfflush(stderr); + status = H5Dread (dset, H5T_NATIVE_UCHAR, file_space, file_space, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&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 + HDgetrusage(RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "out raw"); + for(u = 0; u < nwrite; u++) { + putc (PROGRESS, stderr); + HDfflush(stderr); + offset = HDlseek (fd, (off_t)0, SEEK_SET); + assert (0==offset); + n = HDwrite (fd, the_data, (size_t)(size[0]*size[1])); + assert (n>=0 && (size_t)n==size[0]*size[1]); + } +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&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 + HDgetrusage(RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "out hdf5"); + for(u = 0; u < nwrite; u++) { + putc (PROGRESS, stderr); + HDfflush(stderr); + status = H5Dwrite (dset, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&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 + HDgetrusage(RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "in raw"); + for(u = 0; u < nread; u++) { + putc (PROGRESS, stderr); + HDfflush(stderr); + offset = HDlseek (fd, (off_t)0, SEEK_SET); + assert (0==offset); + n = HDread (fd, the_data, (size_t)(size[0]*size[1])); + assert (n>=0 && (size_t)n==size[0]*size[1]); + } +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&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 + HDgetrusage(RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "in hdf5"); + for(u = 0; u < nread; u++) { + putc (PROGRESS, stderr); + HDfflush(stderr); + status = H5Dread (dset, H5T_NATIVE_UCHAR, file_space, file_space, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&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 + HDgetrusage(RUSAGE_SELF, &r_start); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&t_start, NULL); +#else +#ifdef H5_HAVE_SYS_TIMEB + _ftime(tbstart); +#endif +#endif + fprintf (stderr, HEADING, "in hdf5 partial"); + for(u = 0; u < nread; u++) { + putc (PROGRESS, stderr); + HDfflush(stderr); + status = H5Dread (dset, H5T_NATIVE_UCHAR, file_space, file_space, + H5P_DEFAULT, the_data); + assert (status>=0); + } +#ifdef H5_HAVE_GETRUSAGE + HDgetrusage(RUSAGE_SELF, &r_stop); +#endif +#ifdef H5_HAVE_GETTIMEOFDAY + HDgettimeofday(&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); + free(the_data); + + return 0; +} + diff --git a/tools/test/perform/overhead.c b/tools/test/perform/overhead.c new file mode 100644 index 0000000..98093c7 --- /dev/null +++ b/tools/test/perform/overhead.c @@ -0,0 +1,408 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * 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" +#include "H5private.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 + +/* Solaris Studio defines attribute, but for the attributes we need */ +#if !defined(H5_HAVE_ATTRIBUTE) || defined __cplusplus || defined(__SUNPRO_C) +# undef __attribute__ +# define __attribute__(X) /*void*/ +# define H5_ATTR_UNUSED /*void*/ +#else +# define H5_ATTR_UNUSED __attribute__((unused)) +#endif + +#define FILE_NAME_1 "overhead.h5" +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ + +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 (hid_t estack, void H5_ATTR_UNUSED *client_data) +{ + puts ("*FAILED*"); + H5Eprint2(estack, 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 = (-1), fapl = (-1), dcpl = (-1), xfer = (-1), mspace = (-1), fspace = (-1), dset = (-1); + 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 */ + hsize_t hs_start[1]; /*hyperslab start offset*/ + hsize_t hs_count[1] = {1}; /*hyperslab nelmts */ + int fd = (-1); /*h5 file direct */ + int *had = NULL; /*for random filling */ + const char *sname=NULL; /*fill style nam */ + int mdc_nelmts; /*num meta objs to cache*/ + hsize_t i, k; + int j; + h5_stat_t sb; + + 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.0F) < 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 = H5Dcreate2(file, "chunked", H5T_NATIVE_INT, + fspace, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) goto error; + if ((fd=HDopen(FILE_NAME_1, O_RDONLY, 0666)) < 0) goto error; + + if(FILL_RANDOM==fill_style) + had = (int *)calloc((size_t)cur_size[0], sizeof(int)); + + 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: + k = (cur_size[0] - i) + 1; + hs_start[0] = k % 2 ? (k / 2) : (hsize_t)((hssize_t)cur_size[0] - (hssize_t)(k / 2)); + break; + case FILL_RANDOM: + for (j=HDrand()%(int)cur_size[0]; had[j]; j=(j+1)%(int)cur_size[0]) + /*void*/; + hs_start[0] = (hsize_t)j; + had[j] = 1; + break; + case FILL_ALL: + abort(); + default: + /* unknown request */ + HDfprintf(stderr, "Unknown fill style\n"); + goto error; + break; + } + + /* 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; + } + + /* Determine overhead */ + if (verbose) { + if (H5Fflush(file, H5F_SCOPE_LOCAL) < 0) goto error; + if (HDfstat(fd, &sb) < 0) goto error; + printf("%4lu %8.3f ***\n", + (unsigned long)i, + (double)(sb.st_size - (HDoff_t)(i * sizeof(int))) / (double)i); + } + } + + if(had) { + free(had); + had = NULL; + } /* end if */ + + H5Dclose(dset); + H5Sclose(mspace); + H5Sclose(fspace); + H5Pclose(dcpl); + H5Pclose(xfer); + 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(); + default: + /* unknown request */ + HDfprintf(stderr, "Unknown fill style\n"); + goto error; + break; + } + + if (HDfstat(fd, &sb) < 0) goto error; + printf("%-7s %8.3f\n", sname, + (double)(sb.st_size - (HDoff_t)(cur_size[0] * sizeof(int))) / + (double)cur_size[0]); + } + HDclose(fd); + + return 0; + + error: + H5Dclose(dset); + H5Sclose(mspace); + H5Sclose(fspace); + H5Pclose(dcpl); + H5Pclose(xfer); + H5Fclose(file); + if(had) + free(had); + HDclose(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_auto2(H5E_DEFAULT, 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/tools/test/perform/perf.c b/tools/test/perform/perf.c new file mode 100644 index 0000000..7b9590c --- /dev/null +++ b/tools/test/perform/perf.c @@ -0,0 +1,494 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * 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. + */ + +#include "hdf5.h" +#include "H5private.h" + +#ifdef H5_HAVE_PARALLEL + +#ifdef H5_STDC_HEADERS +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#endif + +#ifdef H5_HAVE_UNISTD_H +#include <sys/types.h> +#include <unistd.h> +#endif + +#ifdef H5_HAVE_SYS_STAT_H +#include <sys/stat.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 + +#include <mpi.h> +#ifndef MPI_FILE_NULL /*MPIO may be defined in mpi.h already */ +# include <mpio.h> +#endif + +/* 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 +#define MAX_PATH 1024 + +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] = "perftest.out"; +char opt_pvfstab[256] = "notset"; +int opt_pvfstab_set = 0; + +const char *FILENAME[] = { + opt_file, + NULL +}; + +/* function prototypes */ +static int parse_args(int argc, char **argv); + +extern int errno; + +/* globals needed for getopt */ +extern char *optarg; + +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; + char filename[MAX_PATH]; + 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); + ret = H5Pclose(mpio_pl); + VRFY((ret >= 0), "H5Pclose mpio_pl 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); + } + } + + h5_fixname_no_suffix(FILENAME[0], acc_tpl, filename, sizeof filename); + + /* create the parallel file */ + fid = H5Fcreate(filename, 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 = H5Dcreate2(fid, "Dataset1", H5T_NATIVE_CHAR, sid, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + VRFY((dataset >= 0), "H5Dcreate2 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(filename,H5F_ACC_RDONLY,acc_tpl); + VRFY((fid >= 0), "", H5FATAL); + + /* open the dataset */ + dataset = H5Dopen2(fid, "Dataset1", H5P_DEFAULT); + 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 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); + ret=H5Pclose(acc_tpl); + VRFY((ret >= 0), "H5Pclose 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); +} + +static 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); + FILENAME[0] = opt_file; + 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); +} + +/* + * Local variables: + * c-indent-level: 3 + * c-basic-offset: 3 + * tab-width: 3 + * End: + */ + +#else /* H5_HAVE_PARALLEL */ +/* dummy program since H5_HAVE_PARALLEL is not configured in */ +int +main(int H5_ATTR_UNUSED argc, char H5_ATTR_UNUSED **argv) +{ + printf("No parallel performance because parallel is not configured in\n"); + return(0); +} +#endif /* H5_HAVE_PARALLEL */ + diff --git a/tools/test/perform/perf_meta.c b/tools/test/perform/perf_meta.c new file mode 100644 index 0000000..2c3a19c --- /dev/null +++ b/tools/test/perform/perf_meta.c @@ -0,0 +1,850 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Programmer: Raymond Lu <slu@ncsa.uiuc.edu> + * Friday, Oct 3, 2004 + * + * Purpose: Tests performance of metadata + */ + +#include "h5test.h" + +#ifdef H5_HAVE_PARALLEL +#define MAINPROCESS (!mpi_rank) /* define process 0 as main process */ +#endif /*H5_HAVE_PARALLEL*/ + +/* File_Access_type bits */ +#define FACC_DEFAULT 0x0 /* serial as default */ +#define FACC_MPIO 0x1 /* MPIO */ + +/* Which test to run */ +int RUN_TEST = 0x0; /* all tests as default */ +int TEST_1 = 0x1; /* Test 1 */ +int TEST_2 = 0x2; /* Test 2 */ +int TEST_3 = 0x4; /* Test 3 */ + + +const char *FILENAME[] = { + "meta_perf_1", + "meta_perf_2", + "meta_perf_3", + NULL +}; + +/* Default values for performance. Can be changed through command line options */ +int NUM_DSETS = 16; +int NUM_ATTRS = 8; +int BATCH_ATTRS = 2; +hbool_t flush_dset = FALSE; +hbool_t flush_attr = FALSE; +int nerrors = 0; /* errors count */ +hid_t fapl; + +/* Data space IDs */ +hid_t space; +hid_t small_space; + +/* Performance data */ +typedef struct p_time { + double total; + double avg; + double max; + double min; + double start; + char func[32]; +} p_time; + +/*Test file access type for parallel. MPIO as default */ +int facc_type = FACC_DEFAULT; + +double retrieve_time(void); +void perf(p_time *perf_t, double start_t, double end_t); +void print_perf(p_time, p_time, p_time); + + +/*------------------------------------------------------------------------- + * Function: parse_options + * + Purpose: Parse command line options + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static int +parse_options(int argc, char **argv) +{ + int t; + + /* Use default values */ + if(argc==1) + return(0); + + while (--argc){ + if (**(++argv) != '-'){ + break; + }else{ + switch(*(*argv+1)){ + case 'h': /* Help page */ + return(1); + + case 'd': /* Number of datasets */ + NUM_DSETS = atoi((*argv+1)+1); + if (NUM_DSETS < 0){ + nerrors++; + return(1); + } + break; + + case 'a': /* Number of attributes per dataset */ + NUM_ATTRS = atoi((*argv+1)+1); + if (NUM_ATTRS < 0){ + nerrors++; + return(1); + } + break; + + case 'n': /* Number of attributes to be created in batch */ + BATCH_ATTRS = atoi((*argv+1)+1); + if (BATCH_ATTRS < 0){ + nerrors++; + return(1); + } + break; + + case 'm': /* Use the MPI-IO driver */ + facc_type = FACC_MPIO; + break; + + case 'f': /* Call H5Fflush for each dataset or attribute */ + if(!strcmp("a", (*argv+2))) + flush_attr = TRUE; + else if(!strcmp("d", (*argv+2))) + flush_dset = TRUE; + else { + nerrors++; + return(1); + } + break; + + case 't': /* Which test to run */ + t = atoi((*argv+1)+1); + if (t < 1 || t > 3){ + nerrors++; + return(1); + } + if(t == 1) + RUN_TEST |= TEST_1; + else if(t == 2) + RUN_TEST |= TEST_2; + else + RUN_TEST |= TEST_3; + + break; + + default: nerrors++; + return(1); + } + } + } /*while*/ + + /* Check valid values */ +#ifndef H5_HAVE_PARALLEL + if(facc_type == FACC_MPIO) + { + nerrors++; + return(1); + } +#endif /*H5_HAVE_PARALLEL*/ + + if(NUM_ATTRS && !BATCH_ATTRS) + NUM_ATTRS = 0; + + if(!NUM_ATTRS && BATCH_ATTRS) + BATCH_ATTRS = 0; + + if(!NUM_DSETS) { + nerrors++; + return(1); + } + + if(NUM_ATTRS && BATCH_ATTRS) { + if(BATCH_ATTRS > NUM_ATTRS || NUM_ATTRS % BATCH_ATTRS) { + nerrors++; + return(1); + } + } + + return(0); +} + + +/*------------------------------------------------------------------------- + * Function: usage + * + Purpose: Prints help page + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +usage(void) +{ + printf("Usage: perf_meta [-h] [-m] [-d<num_datasets>]" + "[-a<num_attributes>]\n" + "\t[-n<batch_attributes>] [-f<option>] [-t<test>]\n"); + printf("\t-h" + "\t\t\thelp page.\n"); + printf("\t-m" + "\t\t\tset MPIO as the file driver when parallel HDF5\n" + "\t\t\t\tis enabled. -m must be specified\n" + "\t\t\t\twhen running parallel program.\n"); + printf("\t-d<num_datasets>" + "\tset number of datasets for meta data \n" + "\t\t\t\tperformance test\n"); + printf("\t-a<num_attributes>" + "\tset number of attributes per dataset for meta \n" + "\t\t\t\tdata performance test.\n"); + printf("\t-n<batch_attributes>" + "\tset batch number of attributes for dataset \n" + "\t\t\t\tfor meta data performance test.\n"); + printf("\t-f<option>" + "\t\tflush data to disk after closing a dataset \n" + "\t\t\t\tor attribute. Valid options are \"d\" for \n" + "\t\t\t\tdataset, \"a\" for attribute. Disabled is \n" + "\t\t\t\tthe default.\n"); + printf("\t-t<tests>" + "\t\trun specific test. Give only one number each \n" + "\t\t\t\ttime. i.e. \"-t1 -t3\" will run test 1 and 3. \n" + "\t\t\t\tDefault is all three tests. The 3 tests are: \n\n" + "\t\t\t\t1. Create <num_attributes> attributes for each \n" + "\t\t\t\t of <num_datasets> existing datasets.\n" + "\t\t\t\t2. Create <num_attributes> attributes for each \n" + "\t\t\t\t of <num_datasets> new datasets.\n" + "\t\t\t\t3. Create <batch_attributes> attributes for \n" + "\t\t\t\t each of <num_dataset> new datasets for \n" + "\t\t\t\t <num_attributes>/<batch_attributes> times.\n"); +} + + +/*------------------------------------------------------------------------- + * Function: create_dspace + * + * Purpose: Attempts to create data space. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +create_dspace(void) +{ + hsize_t dims[2]; + hsize_t small_dims[2]; + + /* Create the data space */ + dims[0] = 256; + dims[1] = 512; + if((space = H5Screate_simple(2, dims, NULL)) < 0) + goto error; + + /* Create a small data space for attributes */ + small_dims[0] = 16; + small_dims[1] = 8; + if((small_space = H5Screate_simple(2, small_dims, NULL)) < 0) + goto error; + + return 0; + +error: + return -1; +} + + +/*------------------------------------------------------------------------- + * Function: create_dsets + * + * Purpose: Attempts to create some datasets. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +create_dsets(hid_t file) +{ + hid_t dataset; + char dset_name[32]; + int i; + + /* + * Create a dataset using the default dataset creation properties. + */ + for(i = 0; i < NUM_DSETS; i++) { + sprintf(dset_name, "dataset %d", i); + if((dataset = H5Dcreate2(file, dset_name, H5T_NATIVE_DOUBLE, space, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) + goto error; + + if(H5Dclose(dataset) < 0) + goto error; + } /* end for */ + + return 0; + +error: + return -1; + +} + + +/*------------------------------------------------------------------------- + * Function: create_attrs_1 + * + * Purpose: Attempts to create all attributes for each existing dataset. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +create_attrs_1(void) +{ + hid_t file, dataset, attr; + char filename[128]; + char dset_name[64]; + char attr_name[128]; + int i, j; + p_time attr_t = {0, 0, 0, 1000000, 0, ""}; + p_time open_t = {0, 0, 0, 1000000, 0, "H5Dopen2"}; + p_time close_t = {0, 0, 0, 1000000, 0, ""}; + +#ifdef H5_HAVE_PARALLEL + /* need the rank for printing data */ + int mpi_rank; + if(facc_type == FACC_MPIO) + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); +#endif /*H5_HAVE_PARALLEL*/ + + h5_fixname(FILENAME[0], fapl, filename, sizeof filename); + + if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, + fapl)) < 0) + goto error; + + if(create_dsets(file) < 0) + goto error; + + /* + * Create all(user specifies the number) attributes for each dataset + */ + for(i = 0; i < NUM_DSETS; i++) { + sprintf(dset_name, "dataset %d", i); + open_t.start = retrieve_time(); + if((dataset = H5Dopen2(file, dset_name, H5P_DEFAULT)) < 0) + goto error; + perf(&open_t, open_t.start, retrieve_time()); + + for(j = 0; j < NUM_ATTRS; j++) { + sprintf(attr_name, "all attrs for each dset %d", j); + attr_t.start = retrieve_time(); + if((attr = H5Acreate2(dataset, attr_name, H5T_NATIVE_DOUBLE, + small_space, H5P_DEFAULT, H5P_DEFAULT)) < 0) + goto error; + if(H5Aclose(attr) < 0) + goto error; + perf(&attr_t, attr_t.start, retrieve_time()); + if(flush_attr && H5Fflush(file, H5F_SCOPE_LOCAL) < 0) + goto error; + } /* end for */ + + close_t.start = retrieve_time(); + if(H5Dclose(dataset) < 0) + goto error; + perf(&close_t, close_t.start, retrieve_time()); + if(flush_dset && H5Fflush(file, H5F_SCOPE_LOCAL) < 0) + goto error; + } /* end for */ + + if(facc_type == FACC_MPIO) { +#ifdef H5_HAVE_PARALLEL + MPI_Barrier(MPI_COMM_WORLD); +#endif /*H5_HAVE_PARALLEL*/ + } + +#ifdef H5_HAVE_PARALLEL + if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS)) /* only process 0 reports */ +#endif /*H5_HAVE_PARALLEL*/ + { + /* Calculate the average time */ + open_t.avg = open_t.total / NUM_DSETS; + close_t.avg = close_t.total / NUM_DSETS; + if(NUM_ATTRS) + attr_t.avg = attr_t.total / (NUM_ATTRS*NUM_DSETS); + + /* Print out the performance result */ + fprintf(stderr, "1. Create %d attributes for each of %d existing datasets\n", + NUM_ATTRS, NUM_DSETS); + print_perf(open_t, close_t, attr_t); + } + + if (H5Fclose(file) < 0) goto error; + + return 0; + +error: + return -1; +} + + +/*------------------------------------------------------------------------- + * Function: create_attrs_2 + * + * Purpose: Attempts to create all attributes for each new dataset. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +create_attrs_2(void) +{ + hid_t file, dataset, attr; + char filename[128]; + char dset_name[64]; + char attr_name[128]; + int i, j; + p_time attr_t = {0, 0, 0, 1000000, 0, ""}; + p_time create_t = {0, 0, 0, 1000000, 0, "H5Dcreate2"}; + p_time close_t = {0, 0, 0, 1000000, 0, ""}; + +#ifdef H5_HAVE_PARALLEL + /* need the rank for printing data */ + int mpi_rank; + if(facc_type == FACC_MPIO) + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); +#endif /*H5_HAVE_PARALLEL*/ + + h5_fixname(FILENAME[1], fapl, filename, sizeof filename); + + if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) + goto error; + + /* + * Create all(user specifies the number) attributes for each new dataset + */ + for(i = 0; i < NUM_DSETS; i++) { + sprintf(dset_name, "dataset %d", i); + create_t.start = retrieve_time(); + if((dataset = H5Dcreate2(file, dset_name, H5T_NATIVE_DOUBLE, + space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) + goto error; + perf(&create_t, create_t.start, retrieve_time()); + + for(j = 0; j < NUM_ATTRS; j++) { + sprintf(attr_name, "all attrs for each dset %d", j); + attr_t.start = retrieve_time(); + if((attr = H5Acreate2(dataset, attr_name, H5T_NATIVE_DOUBLE, + small_space, H5P_DEFAULT, H5P_DEFAULT)) < 0) + goto error; + if(H5Aclose(attr) < 0) + goto error; + perf(&attr_t, attr_t.start, retrieve_time()); + if(flush_attr && H5Fflush(file, H5F_SCOPE_LOCAL) < 0) + goto error; + } /* end for */ + + close_t.start = retrieve_time(); + if(H5Dclose(dataset) < 0) + goto error; + perf(&close_t, close_t.start, retrieve_time()); + if(flush_dset && H5Fflush(file, H5F_SCOPE_LOCAL) < 0) + goto error; + } /* end for */ + +#ifdef H5_HAVE_PARALLEL + if(facc_type == FACC_MPIO) + MPI_Barrier(MPI_COMM_WORLD); +#endif /*H5_HAVE_PARALLEL*/ + +#ifdef H5_HAVE_PARALLEL + /* only process 0 reports if parallel */ + if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS)) +#endif /*H5_HAVE_PARALLEL*/ + { + /* Calculate the average time */ + create_t.avg = create_t.total / NUM_DSETS; + close_t.avg = close_t.total / NUM_DSETS; + if(NUM_ATTRS) + attr_t.avg = attr_t.total / (NUM_ATTRS*NUM_DSETS); + + /* Print out the performance result */ + fprintf(stderr, "2. Create %d attributes for each of %d new datasets\n", + NUM_ATTRS, NUM_DSETS); + print_perf(create_t, close_t, attr_t); + } + + if (H5Fclose(file) < 0) goto error; + + return 0; + +error: + return -1; +} + + +/*------------------------------------------------------------------------- + * Function: create_attrs_3 + * + * Purpose: Attempts to create some attributes for each dataset in a + * loop. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +create_attrs_3(void) +{ + hid_t file, dataset, attr; + char filename[128]; + char dset_name[64]; + char attr_name[128]; + int loop_num; + int i, j, k; + p_time attr_t = {0, 0, 0, 1000000, 0, ""}; + p_time open_t = {0, 0, 0, 1000000, 0, "H5Dopen2"}; + p_time close_t = {0, 0, 0, 1000000, 0, ""}; + +#ifdef H5_HAVE_PARALLEL + /* need the rank for printing data */ + int mpi_rank; + if(facc_type == FACC_MPIO) + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); +#endif /*H5_HAVE_PARALLEL*/ + + h5_fixname(FILENAME[2], fapl, filename, sizeof filename); + + if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, + fapl)) < 0) + goto error; + + if(create_dsets(file) < 0) + goto error; + + /* + * Create some(user specifies the number) attributes for each dataset + * in a loop + */ + loop_num = NUM_ATTRS/BATCH_ATTRS; + + for(i = 0; i < loop_num; i++) { + for(j = 0; j < NUM_DSETS; j++) { + sprintf(dset_name, "dataset %d", j); + open_t.start = retrieve_time(); + if((dataset = H5Dopen2(file, dset_name, H5P_DEFAULT)) < 0) + goto error; + perf(&open_t, open_t.start, retrieve_time()); + + for(k = 0; k < BATCH_ATTRS; k++) { + sprintf(attr_name, "some attrs for each dset %d %d", i, k); + attr_t.start = retrieve_time(); + if((attr = H5Acreate2(dataset, attr_name, H5T_NATIVE_DOUBLE, + small_space, H5P_DEFAULT, H5P_DEFAULT)) < 0) + goto error; + if(H5Aclose(attr) < 0) + goto error; + perf(&attr_t, attr_t.start, retrieve_time()); + if(flush_attr && H5Fflush(file, H5F_SCOPE_LOCAL) < 0) + goto error; + } /* end for */ + + close_t.start = retrieve_time(); + if(H5Dclose(dataset) < 0) + goto error; + perf(&close_t, close_t.start, retrieve_time()); + if(flush_dset && H5Fflush(file, H5F_SCOPE_LOCAL) < 0) + goto error; + } /* end for */ + } /* end for */ + +#ifdef H5_HAVE_PARALLEL + if(facc_type == FACC_MPIO) + MPI_Barrier(MPI_COMM_WORLD); +#endif /*H5_HAVE_PARALLEL*/ + +#ifdef H5_HAVE_PARALLEL + /* only process 0 reports if parallel */ + if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS)) +#endif /*H5_HAVE_PARALLEL*/ + { + /* Calculate the average time */ + open_t.avg = open_t.total / (loop_num*NUM_DSETS); + close_t.avg = close_t.total / (loop_num*NUM_DSETS); + attr_t.avg = attr_t.total / (NUM_ATTRS*NUM_DSETS); + + /* Print out the performance result */ + fprintf(stderr, "3. Create %d attributes for each of %d existing datasets for %d times\n", + BATCH_ATTRS, NUM_DSETS, loop_num); + print_perf(open_t, close_t, attr_t); + } + + if (H5Fclose(file) < 0) goto error; + + return 0; + +error: + return -1; +} + + +/*------------------------------------------------------------------------- + * Function: retrieve_time + * + * Purpose: Returns time in seconds, in a double number. + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +double retrieve_time(void) +{ +#ifdef H5_HAVE_PARALLEL + if(facc_type == FACC_DEFAULT) { +#endif /*H5_HAVE_PARALLEL*/ + struct timeval t; + HDgettimeofday(&t, NULL); + return ((double)t.tv_sec + (double)t.tv_usec / 1000000); +#ifdef H5_HAVE_PARALLEL + } else { + return MPI_Wtime(); + } +#endif /*H5_HAVE_PARALLEL*/ +} + + +/*------------------------------------------------------------------------- + * Function: perf + * + * Purpose: Calculate total time, maximal and minimal time of + * performance. + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +void perf(p_time *perf_t, double start_t, double end_t) +{ + double t = end_t - start_t; + +#ifdef H5_HAVE_PARALLEL + if(facc_type == FACC_MPIO) { + double reduced_t; + double t_max, t_min; + int mpi_size, mpi_rank; + + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + MPI_Barrier(MPI_COMM_WORLD); + + MPI_Reduce(&t, &reduced_t, 1, MPI_DOUBLE, MPI_SUM, 0, + MPI_COMM_WORLD); + reduced_t /= mpi_size; + + MPI_Reduce(&t, &t_max, 1, MPI_DOUBLE, MPI_MAX, 0, + MPI_COMM_WORLD); + MPI_Reduce(&t, &t_min, 1, MPI_DOUBLE, MPI_MIN, 0, + MPI_COMM_WORLD); + + if (MAINPROCESS) { + perf_t->total += reduced_t; + + if(t_max > perf_t->max) + perf_t->max = t_max; + if(t_min < perf_t->min) + perf_t->min = t_min; + } + } else +#endif /*H5_HAVE_PARALLEL*/ + { + perf_t->total += t; + + if(t > perf_t->max) + perf_t->max = t; + if(t < perf_t->min) + perf_t->min = t; + } +} + + +/*------------------------------------------------------------------------- + * Function: print_perf + * + * Purpose: Print out performance data. + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +void print_perf(p_time open_t, p_time close_t, p_time attr_t) +{ + fprintf(stderr, "\t%s:\t\tavg=%.6fs;\tmax=%.6fs;\tmin=%.6fs\n", + open_t.func, open_t.avg, open_t.max, open_t.min); + fprintf(stderr, "\tH5Dclose:\t\tavg=%.6fs;\tmax=%.6fs;\tmin=%.6fs\n", + close_t.avg, close_t.max, close_t.min); + if(NUM_ATTRS) + fprintf(stderr, "\tH5A(create & close):\tavg=%.6fs;\tmax=%.6fs;\tmin=%.6fs\n", + attr_t.avg, attr_t.max, attr_t.min); +} + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: Tests + * + * Return: Success: exit(0) + * + * Failure: exit(1) + * + * Programmer: Raymond Lu + * Friday, Oct 3, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main(int argc, char **argv) +{ +#ifdef H5_HAVE_PARALLEL + int mpi_size, mpi_rank; /* mpi variables */ +#endif /*H5_HAVE_PARALLEL*/ + + if(parse_options(argc, argv) != 0) { + usage(); + return 0; + } + +#ifdef H5_HAVE_PARALLEL + if(facc_type == FACC_MPIO) { + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + } +#endif /*H5_HAVE_PARALLEL*/ + +#ifdef H5_HAVE_PARALLEL + if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS)) +#endif /*H5_HAVE_PARALLEL*/ + fprintf(stderr, "\t\tPerformance result of metadata for datasets and attributes\n\n"); + + fapl = H5Pcreate (H5P_FILE_ACCESS); +#ifdef H5_HAVE_PARALLEL + if(facc_type == FACC_MPIO) + H5Pset_fapl_mpio(fapl, MPI_COMM_WORLD, MPI_INFO_NULL); +#endif /*H5_HAVE_PARALLEL*/ + + nerrors += create_dspace() < 0 ?1:0; + + if((RUN_TEST & TEST_1) || !RUN_TEST) + nerrors += create_attrs_1() < 0 ?1:0; + if((RUN_TEST & TEST_2) || !RUN_TEST) + nerrors += create_attrs_2() < 0 ?1:0; + if(((RUN_TEST & TEST_3) || !RUN_TEST) && BATCH_ATTRS && NUM_ATTRS) + nerrors += create_attrs_3() < 0 ?1:0; + + if (H5Sclose(space) < 0) goto error; + if (H5Sclose(small_space) < 0) goto error; + + h5_clean_files(FILENAME, fapl); + +#ifdef H5_HAVE_PARALLEL + if(facc_type == FACC_MPIO) + /* MPI_Finalize must be called AFTER H5close which may use MPI calls */ + MPI_Finalize(); +#endif /*H5_HAVE_PARALLEL*/ + + if (nerrors) goto error; +#ifdef H5_HAVE_PARALLEL + if (facc_type != FACC_DEFAULT && MAINPROCESS) +#endif /*H5_HAVE_PARALLEL*/ + printf("All metadata performance tests passed.\n"); + + return 0; + + error: + nerrors = MAX(1, nerrors); +#ifdef H5_HAVE_PARALLEL + if (facc_type != FACC_DEFAULT && MAINPROCESS) +#endif /*H5_HAVE_PARALLEL*/ + printf("***** %d PERFORMANCE TEST%s FAILED! *****\n", + nerrors, 1 == nerrors ? "" : "S"); + + return 1; +} + diff --git a/tools/test/perform/pio_engine.c b/tools/test/perform/pio_engine.c new file mode 100644 index 0000000..eaedcb8 --- /dev/null +++ b/tools/test/perform/pio_engine.c @@ -0,0 +1,2683 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Author: Albert Cheng of NCSA, Oct 24, 2001. + */ + +#include "hdf5.h" + +#ifdef H5_STDC_HEADERS +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#endif + +#ifdef H5_HAVE_UNISTD_H +#include <sys/types.h> +#include <unistd.h> +#endif + +#ifdef H5_HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef H5_HAVE_PARALLEL + +#include <mpi.h> + +#ifndef MPI_FILE_NULL /*MPIO may be defined in mpi.h already */ +# include <mpio.h> +#endif /* !MPI_FILE_NULL */ + +#include "pio_perf.h" + +/* Macro definitions */ + +#if H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 6 +# define H5DCREATE(fd, name, type, space, dcpl) H5Dcreate(fd, name, type, space, dcpl) +# define H5DOPEN(fd, name) H5Dopen(fd, name) +#else +# define H5DCREATE(fd, name, type, space, dcpl) H5Dcreate2(fd, name, type, space, H5P_DEFAULT, dcpl, H5P_DEFAULT) +# define H5DOPEN(fd, name) H5Dopen2(fd, name, H5P_DEFAULT) +#endif + +/* sizes of various items. these sizes won't change during program execution */ +/* The following three must have the same type */ +#define ELMT_SIZE (sizeof(unsigned char)) /* we're doing bytes */ +#define ELMT_MPI_TYPE MPI_BYTE +#define ELMT_H5_TYPE H5T_NATIVE_UCHAR + +#define GOTOERROR(errcode) { ret_code = errcode; goto done; } +#define GOTODONE { goto done; } +#define ERRMSG(mesg) { \ + fprintf(stderr, "Proc %d: ", pio_mpi_rank_g); \ + fprintf(stderr, "*** Assertion failed (%s) at line %4d in %s\n", \ + mesg, (int)__LINE__, __FILE__); \ +} + +#define MSG(mesg) { \ + fprintf(stderr, "Proc %d: ", pio_mpi_rank_g); \ + fprintf(stderr, "(%s) at line %4d in %s\n", \ + mesg, (int)__LINE__, __FILE__); \ +} + +/* verify: if val is false (0), print mesg. */ +#define VRFY(val, mesg) do { \ + if (!val) { \ + ERRMSG(mesg); \ + GOTOERROR(FAIL); \ + } \ +} while(0) + + +/* POSIX I/O macros */ +#define POSIXCREATE(fn) HDopen(fn, O_CREAT|O_TRUNC|O_RDWR, 0600) +#define POSIXOPEN(fn, F) HDopen(fn, F, 0600) +#define POSIXCLOSE(F) HDclose(F) +#define POSIXSEEK(F,L) HDlseek(F, L, SEEK_SET) +#define POSIXWRITE(F,B,S) HDwrite(F,B,S) +#define POSIXREAD(F,B,S) HDread(F,B,S) + +enum { + PIO_CREATE = 1, + PIO_WRITE = 2, + PIO_READ = 4 +}; + +/* Global variables */ +static int clean_file_g = -1; /*whether to cleanup temporary test */ +/*files. -1 is not defined; */ +/*0 is no cleanup; 1 is do cleanup */ + +/* + * In a parallel machine, the filesystem suitable for compiling is + * unlikely a parallel file system that is suitable for parallel I/O. + * There is no standard pathname for the parallel file system. /tmp + * is about the best guess. + */ +#ifndef HDF5_PARAPREFIX +# define HDF5_PARAPREFIX "" +#endif /* !HDF5_PARAPREFIX */ + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif /* !MIN */ + +/* the different types of file descriptors we can expect */ +typedef union _file_descr { + int posixfd; /* POSIX file handle*/ + MPI_File mpifd; /* MPI file */ + hid_t h5fd; /* HDF5 file */ +} file_descr; + +/* local functions */ +static char *pio_create_filename(iotype iot, const char *base_name, + char *fullname, size_t size); +static herr_t do_write(results *res, file_descr *fd, parameters *parms, + long ndsets, off_t nelmts, size_t buf_size, void *buffer); +static herr_t do_read(results *res, file_descr *fd, parameters *parms, + long ndsets, off_t nelmts, size_t buf_size, void *buffer /*out*/); +static herr_t do_fopen(parameters *param, char *fname, file_descr *fd /*out*/, + int flags); +static herr_t do_fclose(iotype iot, file_descr *fd); +static void do_cleanupfile(iotype iot, char *fname); + +/* + * Function: do_pio + * Purpose: PIO Engine where Parallel IO are executed. + * Return: results + * Programmer: Albert Cheng, Bill Wendling 2001/12/12 + * Modifications: + * Added 2D testing (Christian Chilan, 10. August 2005) + */ + results +do_pio(parameters param) +{ + /* return codes */ + herr_t ret_code = 0; /*return code */ + results res; + + file_descr fd; + iotype iot; + + char fname[FILENAME_MAX]; + long nf; + long ndsets; + off_t nbytes; /*number of bytes per dataset */ + off_t snbytes; /*general dataset size */ + /*for 1D, it is the actual dataset size */ + /*for 2D, it is the size of a side of the dataset square */ + char *buffer = NULL; /*data buffer pointer */ + size_t buf_size; /*general buffer size in bytes */ + /*for 1D, it is the actual buffer size */ + /*for 2D, it is the length of the buffer rectangle */ + size_t blk_size; /*data block size in bytes */ + size_t bsize; /*actual buffer size */ + + /* HDF5 variables */ + herr_t hrc; /*HDF5 return code */ + + /* Sanity check parameters */ + + /* IO type */ + iot = param.io_type; + + switch (iot) { + case MPIO: + fd.mpifd = MPI_FILE_NULL; + res.timers = io_time_new(MPI_CLOCK); + break; + case POSIXIO: + fd.posixfd = -1; + res.timers = io_time_new(MPI_CLOCK); + break; + case PHDF5: + fd.h5fd = -1; + res.timers = io_time_new(MPI_CLOCK); + break; + default: + /* unknown request */ + fprintf(stderr, "Unknown IO type request (%d)\n", iot); + GOTOERROR(FAIL); + } + + ndsets = param.num_dsets; /* number of datasets per file */ + nbytes = param.num_bytes; /* number of bytes per dataset */ + buf_size = param.buf_size; + blk_size = param.blk_size; + + if (!param.dim2d){ + snbytes = nbytes; /* General dataset size */ + bsize = buf_size; /* Actual buffer size */ + } + else { + snbytes = (off_t)sqrt(nbytes); /* General dataset size */ + bsize = buf_size * blk_size; /* Actual buffer size */ + } + + if (param.num_files < 0 ) { + fprintf(stderr, + "number of files must be >= 0 (%ld)\n", + param.num_files); + GOTOERROR(FAIL); + } + + if (ndsets < 0 ) { + fprintf(stderr, + "number of datasets per file must be >= 0 (%ld)\n", + ndsets); + GOTOERROR(FAIL); + } + + if (param.num_procs <= 0 ) { + fprintf(stderr, + "maximum number of process to use must be > 0 (%d)\n", + param.num_procs); + GOTOERROR(FAIL); + } + + /* Validate transfer buffer size & block size*/ + if(blk_size<=0) { + HDfprintf(stderr, + "Transfer block size (%zu) must be > 0\n", blk_size); + GOTOERROR(FAIL); + } + if(buf_size<=0) { + HDfprintf(stderr, + "Transfer buffer size (%zu) must be > 0\n", buf_size); + GOTOERROR(FAIL); + } + if ((buf_size % blk_size) != 0){ + HDfprintf(stderr, + "Transfer buffer size (%zu) must be a multiple of the " + "interleaved I/O block size (%zu)\n", + buf_size, blk_size); + GOTOERROR(FAIL); + } + if((snbytes%pio_mpi_nprocs_g)!=0) { + HDfprintf(stderr, + "Dataset size (%" H5_PRINTF_LL_WIDTH "d) must be a multiple of the " + "number of processes (%d)\n", + (long long)snbytes, pio_mpi_nprocs_g); + GOTOERROR(FAIL); + } + + if (!param.dim2d){ + if(((snbytes/pio_mpi_nprocs_g)%buf_size)!=0) { + HDfprintf(stderr, + "Dataset size/process (%" H5_PRINTF_LL_WIDTH "d) must be a multiple of the " + "trasfer buffer size (%zu)\n", + (long long)(snbytes/pio_mpi_nprocs_g), buf_size); + GOTOERROR(FAIL); + } + } + else { + if((snbytes%buf_size)!=0) { + HDfprintf(stderr, + "Dataset side size (%" H5_PRINTF_LL_WIDTH "d) must be a multiple of the " + "trasfer buffer size (%zu)\n", + (long long)snbytes, buf_size); + GOTOERROR(FAIL); + } + } + + /* Allocate transfer buffer */ + if ((buffer = malloc(bsize)) == NULL){ + HDfprintf(stderr, "malloc for transfer buffer size (%zu) failed\n", + bsize); + GOTOERROR(FAIL); + } + + if (pio_debug_level >= 4) { + int myrank; + + MPI_Comm_rank(pio_comm_g, &myrank); + + /* output all of the times for all iterations */ + if (myrank == 0) + fprintf(output, "Timer details:\n"); + } + + for (nf = 1; nf <= param.num_files; nf++) { + /* + * Write performance measurement + */ + /* Open file for write */ + char base_name[256]; + + sprintf(base_name, "#pio_tmp_%lu", nf); + pio_create_filename(iot, base_name, fname, sizeof(fname)); + if (pio_debug_level > 0) + HDfprintf(output, "rank %d: data filename=%s\n", + pio_mpi_rank_g, fname); + + /* Need barrier to make sure everyone starts at the same time */ + MPI_Barrier(pio_comm_g); + + set_time(res.timers, HDF5_GROSS_WRITE_FIXED_DIMS, TSTART); + hrc = do_fopen(¶m, fname, &fd, PIO_CREATE | PIO_WRITE); + + VRFY((hrc == SUCCESS), "do_fopen failed"); + + set_time(res.timers, HDF5_FINE_WRITE_FIXED_DIMS, TSTART); + hrc = do_write(&res, &fd, ¶m, ndsets, nbytes, buf_size, buffer); + hrc == SUCCESS; + set_time(res.timers, HDF5_FINE_WRITE_FIXED_DIMS, TSTOP); + + VRFY((hrc == SUCCESS), "do_write failed"); + + /* Close file for write */ + hrc = do_fclose(iot, &fd); + + set_time(res.timers, HDF5_GROSS_WRITE_FIXED_DIMS, TSTOP); + VRFY((hrc == SUCCESS), "do_fclose failed"); + + if (!param.h5_write_only) { + /* + * Read performance measurement + */ + /* Need barrier to make sure everyone is done writing and has + * closed the file. Also to make sure everyone starts reading + * at the same time. + */ + MPI_Barrier(pio_comm_g); + + /* Open file for read */ + set_time(res.timers, HDF5_GROSS_READ_FIXED_DIMS, TSTART); + hrc = do_fopen(¶m, fname, &fd, PIO_READ); + + VRFY((hrc == SUCCESS), "do_fopen failed"); + + set_time(res.timers, HDF5_FINE_READ_FIXED_DIMS, TSTART); + hrc = do_read(&res, &fd, ¶m, ndsets, nbytes, buf_size, buffer); + set_time(res.timers, HDF5_FINE_READ_FIXED_DIMS, TSTOP); + VRFY((hrc == SUCCESS), "do_read failed"); + + /* Close file for read */ + hrc = do_fclose(iot, &fd); + + set_time(res.timers, HDF5_GROSS_READ_FIXED_DIMS, TSTOP); + VRFY((hrc == SUCCESS), "do_fclose failed"); + } + + /* Need barrier to make sure everyone is done with the file */ + /* before it may be removed by do_cleanupfile */ + MPI_Barrier(pio_comm_g); + do_cleanupfile(iot, fname); + } + +done: + /* clean up */ + /* release HDF5 objects */ + + /* close any opened files */ + /* no remove(fname) because that should have happened normally. */ + switch (iot) { + case POSIXIO: + if (fd.posixfd != -1) + hrc = do_fclose(iot, &fd); + break; + case MPIO: + if (fd.mpifd != MPI_FILE_NULL) + hrc = do_fclose(iot, &fd); + break; + case PHDF5: + if (fd.h5fd != -1) + hrc = do_fclose(iot, &fd); + break; + } + + /* release generic resources */ + if(buffer) + free(buffer); + res.ret_code = ret_code; + return res; +} + +/* + * Function: pio_create_filename + * Purpose: Create a new filename to write to. Determine the correct + * suffix to append to the filename by the type of I/O we're + * doing. Also, place in the /tmp/{$USER,$LOGIN} directory if + * USER or LOGIN are specified in the environment. + * Return: Pointer to filename or NULL + * Programmer: Bill Wendling, 21. November 2001 + * Modifications: + */ + static char * +pio_create_filename(iotype iot, const char *base_name, char *fullname, size_t size) +{ + const char *prefix, *suffix = ""; + char *ptr, last = '\0'; + size_t i, j; + + if (!base_name || !fullname || size < 1) + return NULL; + + HDmemset(fullname, 0, size); + + switch (iot) { + case POSIXIO: + suffix = ".posix"; + break; + case MPIO: + suffix = ".mpio"; + break; + case PHDF5: + suffix = ".h5"; + break; + } + + /* First use the environment variable and then try the constant */ + prefix = HDgetenv("HDF5_PARAPREFIX"); + +#ifdef HDF5_PARAPREFIX + if (!prefix) + prefix = HDF5_PARAPREFIX; +#endif /* HDF5_PARAPREFIX */ + + /* Prepend the prefix value to the base name */ + if (prefix && *prefix) { + /* If the prefix specifies the HDF5_PARAPREFIX directory, then + * default to using the "/tmp/$USER" or "/tmp/$LOGIN" + * directory instead. */ + register char *user, *login, *subdir; + + user = HDgetenv("USER"); + login = HDgetenv("LOGIN"); + subdir = (user ? user : login); + + if (subdir) { + for (i = 0; i < size-1 && prefix[i]; i++) + fullname[i] = prefix[i]; + + fullname[i++] = '/'; + + for (j = 0; i < size && subdir[j]; i++, j++) + fullname[i] = subdir[j]; + } + else { + /* We didn't append the prefix yet */ + HDstrncpy(fullname, prefix, size); + fullname[size - 1] = '\0'; + } + + if ((HDstrlen(fullname) + HDstrlen(base_name) + 1) < size) { + /* Append the base_name with a slash first. Multiple slashes are + * handled below. */ + h5_stat_t buf; + + if (HDstat(fullname, &buf) < 0) + /* The directory doesn't exist just yet */ + if (HDmkdir(fullname, (mode_t) 0755) < 0 && errno != EEXIST) { + /* We couldn't make the "/tmp/${USER,LOGIN}" subdirectory. + * Default to PREFIX's original prefix value. */ + HDstrcpy(fullname, prefix); + } + + HDstrcat(fullname, "/"); + HDstrcat(fullname, base_name); + } + else { + /* Buffer is too small */ + return NULL; + } + } + else if (HDstrlen(base_name) >= size) { + /* Buffer is too small */ + return NULL; + } + else { + HDstrcpy(fullname, base_name); + } + + /* Append a suffix */ + if (suffix) { + if (HDstrlen(fullname) + HDstrlen(suffix) >= size) + return NULL; + + HDstrcat(fullname, suffix); + } + + /* Remove any double slashes in the filename */ + for (ptr = fullname, i = j = 0; ptr && i < size; i++, ptr++) { + if (*ptr != '/' || last != '/') + fullname[j++] = *ptr; + + last = *ptr; + } + + return fullname; +} + +/* + * Function: do_write + * Purpose: Write the required amount of data to the file. + * Return: SUCCESS or FAIL + * Programmer: Albert Cheng, Bill Wendling, 2001/12/13 + * Modifications: + * Added 2D testing (Christian Chilan, 10. August 2005) + */ + static herr_t +do_write(results *res, file_descr *fd, parameters *parms, long ndsets, + off_t nbytes, size_t buf_size, void *buffer) +{ + int ret_code = SUCCESS; + int rc; /*routine return code */ + long ndset; + size_t blk_size; /* The block size to subdivide the xfer buffer into */ + off_t nbytes_xfer; /* Total number of bytes transferred so far */ + size_t nbytes_xfer_advance; /* Number of bytes transferred in a single I/O operation */ + size_t nbytes_toxfer; /* Number of bytes to transfer a particular time */ + char dname[64]; + off_t dset_offset=0; /*dataset offset in a file */ + off_t bytes_begin[2]; /*first elmt this process transfer */ + off_t bytes_count; /*number of elmts this process transfer */ + off_t snbytes=0; /*size of a side of the dataset square */ + unsigned char *buf_p; /* Current buffer pointer */ + + /* POSIX variables */ + off_t file_offset; /* File offset of the next transfer */ + off_t file_offset_advance; /* File offset advance after each I/O operation */ + off_t posix_file_offset; /* Base file offset of the next transfer */ + + /* MPI variables */ + MPI_Offset mpi_file_offset; /* Base file offset of the next transfer*/ + MPI_Offset mpi_offset; /* Offset in MPI file */ + MPI_Offset mpi_offset_advance; /* Offset advance after each I/O operation */ + MPI_Datatype mpi_file_type; /* MPI derived type for 1D file */ + MPI_Datatype mpi_blk_type; /* MPI derived type for 1D buffer */ + MPI_Datatype mpi_cont_type; /* MPI derived type for 2D contiguous file */ + MPI_Datatype mpi_partial_buffer_cont; /* MPI derived type for partial 2D contiguous buffer */ + MPI_Datatype mpi_inter_type; /* MPI derived type for 2D interleaved file */ + MPI_Datatype mpi_partial_buffer_inter; /* MPI derived type for partial 2D interleaved buffer */ + MPI_Datatype mpi_full_buffer; /* MPI derived type for 2D full buffer */ + MPI_Datatype mpi_full_chunk; /* MPI derived type for 2D full chunk */ + MPI_Datatype mpi_chunk_inter_type; /* MPI derived type for 2D chunk interleaved file */ + MPI_Datatype mpi_collective_type; /* Generic MPI derived type for 2D collective access */ + MPI_Status mpi_status; + int mrc; /* MPI return code */ + + /* HDF5 variables */ + herr_t hrc; /*HDF5 return code */ + hsize_t h5dims[2]; /*dataset dim sizes */ + hid_t h5dset_space_id = -1; /*dataset space ID */ + hid_t h5mem_space_id = -1; /*memory dataspace ID */ + hid_t h5ds_id = -1; /*dataset handle */ + hsize_t h5block[2]; /*dataspace selection */ + hsize_t h5stride[2]; + hsize_t h5count[2]; + hsize_t h5start[2]; + hssize_t h5offset[2]; /* Selection offset within dataspace */ + hid_t h5dcpl = -1; /* Dataset creation property list */ + hid_t h5dxpl = -1; /* Dataset transfer property list */ + + /* Get the parameters from the parameter block */ + blk_size=parms->blk_size; + + /* There are two kinds of transfer patterns, contiguous and interleaved. + * Let 0,1,2,...,n be data accessed by process 0,1,2,...,n + * where n is rank of the last process. + * In contiguous pattern, data are accessed as + * 000...111...222...nnn... + * In interleaved pattern, data are accessed as + * 012...n012...n... + * These are all in the scope of one dataset. + */ + + /* 1D dataspace */ + if (!parms->dim2d){ + /* Contiguous Pattern: */ + if (!parms->interleaved) { + bytes_begin[0] = (off_t)(((double)nbytes*pio_mpi_rank_g)/pio_mpi_nprocs_g); + } /* end if */ + /* Interleaved Pattern: */ + else { + bytes_begin[0] = (off_t)(blk_size*pio_mpi_rank_g); + } /* end else */ + + /* Prepare buffer for verifying data */ + if (parms->verify) + memset(buffer,pio_mpi_rank_g+1,buf_size); + }/* end if */ + /* 2D dataspace */ + else { + /* nbytes is always the number of bytes per dataset (1D or 2D). If the + dataspace is 2D, snbytes is the size of a side of the dataset square. + */ + snbytes = (off_t)sqrt(nbytes); + + /* Contiguous Pattern: */ + if (!parms->interleaved) { + bytes_begin[0] = (off_t)((double)snbytes*pio_mpi_rank_g / pio_mpi_nprocs_g); + bytes_begin[1] = 0; + } /* end if */ + /* Interleaved Pattern: */ + else { + bytes_begin[0] = 0; + + if(!parms->h5_use_chunks || parms->io_type==PHDF5) + bytes_begin[1] = (off_t)(blk_size*pio_mpi_rank_g); + else + bytes_begin[1] = (off_t)(blk_size*blk_size*pio_mpi_rank_g); + } /* end else */ + + /* Prepare buffer for verifying data */ + if (parms->verify) + memset(buffer,pio_mpi_rank_g+1,buf_size*blk_size); + } /* end else */ + + + /* Calculate the total number of bytes (bytes_count) to be + * transferred by this process. It may be different for different + * transfer pattern due to rounding to integral values. + */ + /* + * Calculate the beginning bytes of this process and the next. + * bytes_count is the difference between these two beginnings. + * This way, it eliminates any rounding errors. + * (This is tricky, don't mess with the formula, rounding errors + * can easily get introduced) */ + bytes_count = (off_t)(((double)nbytes*(pio_mpi_rank_g+1)) / pio_mpi_nprocs_g) + - (off_t)(((double)nbytes*pio_mpi_rank_g) / pio_mpi_nprocs_g); + + /* debug */ + if (pio_debug_level >= 4) { + HDprint_rank(output); + if (!parms->dim2d) { + HDfprintf(output, "Debug(do_write): " + "buf_size=%zu, bytes_begin=%" H5_PRINTF_LL_WIDTH "d, bytes_count=%" H5_PRINTF_LL_WIDTH "d\n", + buf_size, (long long)bytes_begin[0], + (long long)bytes_count); + } else { + HDfprintf(output, "Debug(do_write): " + "linear buf_size=%zu, bytes_begin=(%" H5_PRINTF_LL_WIDTH "d,%" H5_PRINTF_LL_WIDTH "d), bytes_count=%" H5_PRINTF_LL_WIDTH "d\n", + buf_size*blk_size, (long long)bytes_begin[0], + (long long)bytes_begin[1], (long long)bytes_count); + } + } + + /* I/O Access specific setup */ + switch (parms->io_type) { + case POSIXIO: + /* No extra setup */ + break; + + case MPIO: /* MPI-I/O setup */ + /* 1D dataspace */ + if (!parms->dim2d){ + /* Build block's derived type */ + mrc = MPI_Type_contiguous((int)blk_size, + MPI_BYTE, &mpi_blk_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Build file's derived type */ + mrc = MPI_Type_vector((int)(buf_size/blk_size), (int)1, + (int)pio_mpi_nprocs_g, mpi_blk_type, &mpi_file_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit file type */ + mrc = MPI_Type_commit( &mpi_file_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Commit buffer type */ + mrc = MPI_Type_commit( &mpi_blk_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + } /* end if */ + /* 2D dataspace */ + else { + /* Build partial buffer derived type for contiguous access */ + + mrc = MPI_Type_contiguous((int)buf_size, MPI_BYTE, + &mpi_partial_buffer_cont); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit partial buffer derived type */ + mrc = MPI_Type_commit(&mpi_partial_buffer_cont); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build contiguous file's derived type */ + mrc = MPI_Type_vector((int)blk_size, (int)1, (int)(snbytes/buf_size), + mpi_partial_buffer_cont, &mpi_cont_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit contiguous file type */ + mrc = MPI_Type_commit(&mpi_cont_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build partial buffer derived type for interleaved access */ + mrc = MPI_Type_contiguous((int)blk_size, MPI_BYTE, + &mpi_partial_buffer_inter); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit partial buffer derived type */ + mrc = MPI_Type_commit(&mpi_partial_buffer_inter); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build interleaved file's derived type */ + mrc = MPI_Type_vector((int)buf_size, (int)1, (int)(snbytes/blk_size), + mpi_partial_buffer_inter, &mpi_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit interleaved file type */ + mrc = MPI_Type_commit(&mpi_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build full buffer derived type */ + mrc = MPI_Type_contiguous((int)(blk_size*buf_size), MPI_BYTE, + &mpi_full_buffer); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit full buffer derived type */ + mrc = MPI_Type_commit(&mpi_full_buffer); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build full chunk derived type */ + mrc = MPI_Type_contiguous((int)(blk_size*blk_size), MPI_BYTE, + &mpi_full_chunk); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit full chunk derived type */ + mrc = MPI_Type_commit(&mpi_full_chunk); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build chunk interleaved file's derived type */ + mrc = MPI_Type_vector((int)(buf_size/blk_size), (int)1, (int)(snbytes/blk_size), + mpi_full_chunk, &mpi_chunk_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit chunk interleaved file type */ + mrc = MPI_Type_commit(&mpi_chunk_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + } /* end else */ + break; + + case PHDF5: /* HDF5 setup */ + /* 1D dataspace */ + if (!parms->dim2d){ + if(nbytes>0) { + /* define a contiguous dataset of nbytes native bytes */ + h5dims[0] = nbytes; + h5dset_space_id = H5Screate_simple(1, h5dims, NULL); + VRFY((h5dset_space_id >= 0), "H5Screate_simple"); + + /* Set up the file dset space id to select the pattern to access */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5start[0] = bytes_begin[0]; + h5stride[0] = h5block[0] = blk_size; + h5count[0] = buf_size/blk_size; + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5start[0] = bytes_begin[0]; + h5stride[0] = blk_size*pio_mpi_nprocs_g; + h5block[0] = blk_size; + h5count[0] = buf_size/blk_size; + } /* end else */ + hrc = H5Sselect_hyperslab(h5dset_space_id, H5S_SELECT_SET, + h5start, h5stride, h5count, h5block); + VRFY((hrc >= 0), "H5Sselect_hyperslab"); + } /* end if */ + else { + h5dset_space_id = H5Screate(H5S_SCALAR); + VRFY((h5dset_space_id >= 0), "H5Screate"); + } /* end else */ + + /* Create the memory dataspace that corresponds to the xfer buffer */ + if(buf_size>0) { + h5dims[0] = buf_size; + h5mem_space_id = H5Screate_simple(1, h5dims, NULL); + VRFY((h5mem_space_id >= 0), "H5Screate_simple"); + } /* end if */ + else { + h5mem_space_id = H5Screate(H5S_SCALAR); + VRFY((h5mem_space_id >= 0), "H5Screate"); + } /* end else */ + } /* end if */ + /* 2D dataspace */ + else { + if(nbytes>0) { + /* define a contiguous dataset of nbytes native bytes */ + h5dims[0] = snbytes; + h5dims[1] = snbytes; + h5dset_space_id = H5Screate_simple(2, h5dims, NULL); + VRFY((h5dset_space_id >= 0), "H5Screate_simple"); + + /* Set up the file dset space id to select the pattern to access */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5start[0] = bytes_begin[0]; + h5start[1] = bytes_begin[1]; + h5stride[0] = 1; + h5stride[1] = h5block[0] = h5block[1] = blk_size; + h5count[0] = 1; + h5count[1] = buf_size/blk_size; + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5start[0] = bytes_begin[0]; + h5start[1] = bytes_begin[1]; + h5stride[0] = blk_size; + h5stride[1] = blk_size*pio_mpi_nprocs_g; + h5block[0] = h5block[1] = blk_size; + h5count[0] = buf_size/blk_size; + h5count[1] = 1; + } /* end else */ + hrc = H5Sselect_hyperslab(h5dset_space_id, H5S_SELECT_SET, + h5start, h5stride, h5count, h5block); + VRFY((hrc >= 0), "H5Sselect_hyperslab"); + } /* end if */ + else { + h5dset_space_id = H5Screate(H5S_SCALAR); + VRFY((h5dset_space_id >= 0), "H5Screate"); + } /* end else */ + + /* Create the memory dataspace that corresponds to the xfer buffer */ + if(buf_size>0) { + if (!parms->interleaved){ + h5dims[0] = blk_size; + h5dims[1] = buf_size; + }else{ + h5dims[0] = buf_size; + h5dims[1] = blk_size; + } + h5mem_space_id = H5Screate_simple(2, h5dims, NULL); + VRFY((h5mem_space_id >= 0), "H5Screate_simple"); + } /* end if */ + else { + h5mem_space_id = H5Screate(H5S_SCALAR); + VRFY((h5mem_space_id >= 0), "H5Screate"); + } /* end else */ + } /* end else */ + + /* Create the dataset transfer property list */ + h5dxpl = H5Pcreate(H5P_DATASET_XFER); + if (h5dxpl < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + + /* Change to collective I/O, if asked */ + if(parms->collective) { + hrc = H5Pset_dxpl_mpio(h5dxpl, H5FD_MPIO_COLLECTIVE); + if (hrc < 0) { + fprintf(stderr, "HDF5 Property List Set failed\n"); + GOTOERROR(FAIL); + } /* end if */ + } /* end if */ + break; + } /* end switch */ + + for (ndset = 1; ndset <= ndsets; ++ndset) { + + /* Calculate dataset offset within a file */ + + /* create dataset */ + switch (parms->io_type) { + case POSIXIO: + case MPIO: + /* both posix and mpi io just need dataset offset in file*/ + dset_offset = (ndset - 1) * nbytes; + break; + + case PHDF5: + h5dcpl = H5Pcreate(H5P_DATASET_CREATE); + if (h5dcpl < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + /* 1D dataspace */ + if (!parms->dim2d){ + /* Make the dataset chunked if asked */ + if(parms->h5_use_chunks) { + /* Set the chunk size to be the same as the buffer size */ + h5dims[0] = blk_size; + hrc = H5Pset_chunk(h5dcpl, 1, h5dims); + if (hrc < 0) { + fprintf(stderr, "HDF5 Property List Set failed\n"); + GOTOERROR(FAIL); + } /* end if */ + } /* end if */ + }/* end if */ + else{ + /* 2D dataspace */ + if(parms->h5_use_chunks) { + /* Set the chunk size to be the same as the block size */ + h5dims[0] = blk_size; + h5dims[1] = blk_size; + hrc = H5Pset_chunk(h5dcpl, 2, h5dims); + if (hrc < 0) { + fprintf(stderr, "HDF5 Property List Set failed\n"); + GOTOERROR(FAIL); + } /* end if */ + } /* end if */ + }/* end else */ + + sprintf(dname, "Dataset_%ld", ndset); + h5ds_id = H5DCREATE(fd->h5fd, dname, ELMT_H5_TYPE, + h5dset_space_id, h5dcpl); + + if (h5ds_id < 0) { + fprintf(stderr, "HDF5 Dataset Create failed\n"); + GOTOERROR(FAIL); + } + + hrc = H5Pclose(h5dcpl); + /* verifying the close of the dcpl */ + if (hrc < 0) { + fprintf(stderr, "HDF5 Property List Close failed\n"); + GOTOERROR(FAIL); + } + break; + } + + /* The task is to transfer bytes_count bytes, starting at + * bytes_begin position, using transfer buffer of buf_size bytes. + * If interleaved, select buf_size at a time, in round robin + * fashion, according to number of process. Otherwise, select + * all bytes_count in contiguous. + */ + nbytes_xfer = 0 ; + + /* 1D dataspace */ + if (!parms->dim2d){ + /* Set base file offset for all I/O patterns and POSIX access */ + posix_file_offset = dset_offset + bytes_begin[0]; + + /* Set base file offset for all I/O patterns and MPI access */ + mpi_file_offset = (MPI_Offset)(dset_offset + bytes_begin[0]); + } /* end if */ + else { + /* Set base file offset for all I/O patterns and POSIX access */ + posix_file_offset=dset_offset + bytes_begin[0]*snbytes+ + bytes_begin[1]; + + /* Set base file offset for all I/O patterns and MPI access */ + mpi_file_offset=(MPI_Offset)(dset_offset + bytes_begin[0]*snbytes+ + bytes_begin[1]); + } /* end else */ + + /* Start "raw data" write timer */ + set_time(res->timers, HDF5_RAW_WRITE_FIXED_DIMS, TSTART); + + while (nbytes_xfer < bytes_count){ + /* Write */ + /* Calculate offset of write within a dataset/file */ + switch (parms->io_type) { + case POSIXIO: + /* 1D dataspace */ + if (!parms->dim2d){ + /* Contiguous pattern */ + if (!parms->interleaved) { + /* Compute file offset */ + file_offset = posix_file_offset + (off_t)nbytes_xfer; + + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, file_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + + /* check if all bytes are written */ + rc = ((ssize_t)buf_size == + POSIXWRITE(fd->posixfd, buffer, buf_size)); + VRFY((rc != 0), "POSIXWRITE"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Set the base of user's buffer */ + buf_p=(unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size; + + /* Loop over the buffers to write */ + while(nbytes_toxfer>0) { + /* Skip offset over blocks of other processes */ + file_offset = posix_file_offset + + (off_t)(nbytes_xfer*pio_mpi_nprocs_g); + + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, file_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + + /* check if all bytes are written */ + rc = ((ssize_t)blk_size == + POSIXWRITE(fd->posixfd, buf_p, blk_size)); + VRFY((rc != 0), "POSIXWRITE"); + + /* Advance location in buffer */ + buf_p+=blk_size; + + /* Advance global offset in dataset */ + nbytes_xfer+=blk_size; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=blk_size; + } /* end while */ + } /* end else */ + } /* end if */ + /* 2D dataspace */ + else { + /* Contiguous storage */ + if (!parms->h5_use_chunks) { + /* Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute file offset */ + file_offset=posix_file_offset+(off_t)(((nbytes_xfer/blk_size) + /snbytes)*(blk_size*snbytes)+((nbytes_xfer/blk_size)%snbytes)); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = buf_size; + + /* Global offset advance after each I/O operation */ + file_offset_advance = (off_t)snbytes; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Compute file offset */ + file_offset=posix_file_offset+(off_t)((((nbytes_xfer/buf_size) + *pio_mpi_nprocs_g)/snbytes)*(buf_size*snbytes) + +((nbytes_xfer/buf_size)*pio_mpi_nprocs_g)%snbytes); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size; + + /* Global offset advance after each I/O operation */ + file_offset_advance = (off_t)snbytes; + } /* end else */ + } /* end if */ + /* Chunked storage */ + else { + /*Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute file offset */ + file_offset=posix_file_offset+(off_t)nbytes_xfer; + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * buf_size; + + /* Global offset advance after each I/O operation */ + file_offset_advance = 0; + } /* end if */ + /*Interleaved access pattern */ + else { + /* Compute file offset */ + /* Before simplification */ + /* file_offset=posix_file_offset+(off_t)((nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes/blk_size*(blk_size*blk_size))*(buf_size/blk_size + *snbytes/blk_size*(blk_size*blk_size))+((nbytes_xfer/(buf_size/blk_size)) + *pio_mpi_nprocs_g)%(snbytes/blk_size*(blk_size*blk_size))); */ + + file_offset=posix_file_offset+(off_t)(((nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes*blk_size))*(buf_size*snbytes)+((nbytes_xfer/(buf_size/blk_size)) + *pio_mpi_nprocs_g)%(snbytes*blk_size)); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * blk_size; + + /* Global offset advance after each I/O operation */ + /* file_offset_advance = (off_t)(snbytes/blk_size*(blk_size*blk_size)); */ + file_offset_advance = (off_t)(snbytes*blk_size); + } /* end else */ + } /* end else */ + + /* Common code for file access */ + + /* Set the base of user's buffer */ + buf_p = (unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size*blk_size; + + /* Loop over portions of the buffer to write */ + while(nbytes_toxfer>0){ + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, file_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + + /* check if all bytes are written */ + rc = ((ssize_t)nbytes_xfer_advance == + POSIXWRITE(fd->posixfd, buf_p, nbytes_xfer_advance)); + VRFY((rc != 0), "POSIXWRITE"); + + /* Advance location in buffer */ + buf_p+=nbytes_xfer_advance; + + /* Advance global offset in dataset */ + nbytes_xfer+=nbytes_xfer_advance; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=nbytes_xfer_advance; + + /* Partially advance file offset */ + file_offset+=file_offset_advance; + } /* end while */ + + } /* end else */ + + break; + + case MPIO: + /* 1D dataspace */ + if (!parms->dim2d){ + /* Independent file access */ + if(!parms->collective) { + /* Contiguous pattern */ + if (!parms->interleaved){ + /* Compute offset in file */ + mpi_offset = mpi_file_offset + + nbytes_xfer; + + /* Perform independent write */ + mrc = MPI_File_write_at(fd->mpifd, mpi_offset, buffer, + (int)(buf_size/blk_size), mpi_blk_type, + &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_WRITE"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Set the base of user's buffer */ + buf_p=(unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size; + + /* Loop over the buffers to write */ + while(nbytes_toxfer>0) { + /* Skip offset over blocks of other processes */ + mpi_offset = mpi_file_offset + + (nbytes_xfer*pio_mpi_nprocs_g); + + /* Perform independent write */ + mrc = MPI_File_write_at(fd->mpifd, mpi_offset, buf_p, + (int)1, mpi_blk_type, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_WRITE"); + + /* Advance location in buffer */ + buf_p+=blk_size; + + /* Advance global offset in dataset */ + nbytes_xfer+=blk_size; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=blk_size; + } /* end while */ + } /* end else */ + } /* end if */ + /* Collective file access */ + else { + /* Contiguous access pattern */ + if (!parms->interleaved){ + /* Compute offset in file */ + mpi_offset = mpi_file_offset + + nbytes_xfer; + + /* Perform independent write */ + mrc = MPI_File_write_at_all(fd->mpifd, mpi_offset, buffer, + (int)(buf_size/blk_size), mpi_blk_type, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_WRITE"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Compute offset in file */ + mpi_offset = mpi_file_offset + + (nbytes_xfer*pio_mpi_nprocs_g); + + /* Set the file view */ + mrc = MPI_File_set_view(fd->mpifd, mpi_offset, mpi_blk_type, + mpi_file_type, (char*)"native", h5_io_info_g); + VRFY((mrc==MPI_SUCCESS), "MPIO_VIEW"); + + /* Perform write */ + mrc = MPI_File_write_at_all(fd->mpifd, 0, buffer, + (int)(buf_size/blk_size), mpi_blk_type, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_WRITE"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end else */ + } /* end else */ + } /* end if */ + /* 2D dataspace */ + else { + /* Contiguous storage */ + if (!parms->h5_use_chunks) { + /* Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute offset in file */ + mpi_offset=mpi_file_offset+((nbytes_xfer/blk_size)/snbytes)* + (blk_size*snbytes)+((nbytes_xfer/blk_size)%snbytes); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = buf_size; + + /* Global offset advance after each I/O operation */ + mpi_offset_advance = snbytes; + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_cont_type; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Compute offset in file */ + mpi_offset=mpi_file_offset+(((nbytes_xfer/buf_size)*pio_mpi_nprocs_g)/snbytes)* + (buf_size*snbytes)+((nbytes_xfer/buf_size)*pio_mpi_nprocs_g)%snbytes; + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size; + + /* Global offset advance after each I/O operation */ + mpi_offset_advance = snbytes; + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_inter_type; + } /* end else */ + } /* end if */ + /* Chunked storage */ + else { + /*Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute offset in file */ + mpi_offset=mpi_file_offset+nbytes_xfer; + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * buf_size; + + /* Global offset advance after each I/O operation */ + mpi_offset_advance = 0; + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_full_buffer; + } /* end if */ + /*Interleaved access pattern */ + else { + /* Compute offset in file */ + /* Before simplification */ + /* mpi_offset=mpi_file_offset+(nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes/blk_size*(blk_size*blk_size))* + (buf_size/blk_size*snbytes/blk_size*(blk_size*blk_size))+ + ((nbytes_xfer/(buf_size/blk_size))*pio_mpi_nprocs_g)%(snbytes + /blk_size*(blk_size*blk_size)); */ + mpi_offset=mpi_file_offset+((nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes*blk_size))*(buf_size*snbytes) + +((nbytes_xfer/(buf_size/blk_size))*pio_mpi_nprocs_g)%(snbytes*blk_size); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * blk_size; + + /* Global offset advance after each I/O operation */ + /* mpi_offset_advance = (MPI_Offset)(snbytes/blk_size*(blk_size*blk_size)); */ + mpi_offset_advance = (MPI_Offset)(snbytes*blk_size); + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_chunk_inter_type; + } /* end else */ + } /* end else */ + + /* Common code for independent file access */ + if (!parms->collective) { + /* Set the base of user's buffer */ + buf_p = (unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size * blk_size; + + /* Loop over portions of the buffer to write */ + while(nbytes_toxfer>0){ + /* Perform independent write */ + mrc = MPI_File_write_at(fd->mpifd, mpi_offset, buf_p, + (int)nbytes_xfer_advance, MPI_BYTE, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_WRITE"); + + /* Advance location in buffer */ + buf_p+=nbytes_xfer_advance; + + /* Advance global offset in dataset */ + nbytes_xfer+=nbytes_xfer_advance; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=nbytes_xfer_advance; + + /* Partially advance global offset in dataset */ + mpi_offset+=mpi_offset_advance; + } /* end while */ + } /* end if */ + + /* Common code for collective file access */ + else { + /* Set the file view */ + mrc = MPI_File_set_view(fd->mpifd, mpi_offset, MPI_BYTE, + mpi_collective_type, (char *)"native", h5_io_info_g); + VRFY((mrc==MPI_SUCCESS), "MPIO_VIEW"); + + /* Perform write */ + MPI_File_write_at_all(fd->mpifd, 0, buffer,(int)(buf_size*blk_size), + MPI_BYTE, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_WRITE"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size*blk_size; + } /* end else */ + + } /* end else */ + + break; + + case PHDF5: + /* 1D dataspace */ + if (!parms->dim2d){ + /* Set up the file dset space id to move the selection to process */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5offset[0] = nbytes_xfer; + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5offset[0] = (nbytes_xfer*pio_mpi_nprocs_g); + } /* end else */ + hrc = H5Soffset_simple(h5dset_space_id, h5offset); + VRFY((hrc >= 0), "H5Soffset_simple"); + + /* Write the buffer out */ + hrc = H5Dwrite(h5ds_id, ELMT_H5_TYPE, h5mem_space_id, + h5dset_space_id, h5dxpl, buffer); + VRFY((hrc >= 0), "H5Dwrite"); + + /* Increment number of bytes transferred */ + nbytes_xfer += buf_size; + } /* end if */ + /* 2D dataspace */ + else { + /* Set up the file dset space id to move the selection to process */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5offset[0] = (nbytes_xfer/(snbytes*blk_size))*blk_size; + h5offset[1] = (nbytes_xfer%(snbytes*blk_size))/blk_size; + + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5offset[0] = ((nbytes_xfer*pio_mpi_nprocs_g)/(snbytes*buf_size))*buf_size; + h5offset[1] = ((nbytes_xfer*pio_mpi_nprocs_g)%(snbytes*buf_size))/buf_size; + + } /* end else */ + hrc = H5Soffset_simple(h5dset_space_id, h5offset); + VRFY((hrc >= 0), "H5Soffset_simple"); + + /* Write the buffer out */ + hrc = H5Dwrite(h5ds_id, ELMT_H5_TYPE, h5mem_space_id, + h5dset_space_id, h5dxpl, buffer); + VRFY((hrc >= 0), "H5Dwrite"); + + /* Increment number of bytes transferred */ + nbytes_xfer += buf_size*blk_size; + + } /* end else */ + + break; + } /* switch (parms->io_type) */ + } /* end while */ + + /* Stop "raw data" write timer */ + set_time(res->timers, HDF5_RAW_WRITE_FIXED_DIMS, TSTOP); + + /* Calculate write time */ + + /* Close dataset. Only HDF5 needs to do an explicit close. */ + if (parms->io_type == PHDF5) { + hrc = H5Dclose(h5ds_id); + + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Close failed\n"); + GOTOERROR(FAIL); + } + + h5ds_id = -1; + } /* end if */ + } /* end for */ + +done: + /* release MPI-I/O objects */ + if (parms->io_type == MPIO) { + /* 1D dataspace */ + if (!parms->dim2d){ + /* Free file type */ + mrc = MPI_Type_free( &mpi_file_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free buffer type */ + mrc = MPI_Type_free( &mpi_blk_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + } /* end if */ + /* 2D dataspace */ + else { + /* Free partial buffer type for contiguous access */ + mrc = MPI_Type_free( &mpi_partial_buffer_cont ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free contiguous file type */ + mrc = MPI_Type_free( &mpi_cont_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free partial buffer type for interleaved access */ + mrc = MPI_Type_free( &mpi_partial_buffer_inter ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free interleaved file type */ + mrc = MPI_Type_free( &mpi_inter_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free full buffer type */ + mrc = MPI_Type_free(&mpi_full_buffer); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free full chunk type */ + mrc = MPI_Type_free(&mpi_full_chunk); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free chunk interleaved file type */ + mrc = MPI_Type_free(&mpi_chunk_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + } /* end else */ + } /* end if */ + + /* release HDF5 objects */ + if (h5dset_space_id != -1) { + hrc = H5Sclose(h5dset_space_id); + if (hrc < 0){ + fprintf(stderr, "HDF5 Dataset Space Close failed\n"); + ret_code = FAIL; + } else { + h5dset_space_id = -1; + } + } + + if (h5mem_space_id != -1) { + hrc = H5Sclose(h5mem_space_id); + if (hrc < 0) { + fprintf(stderr, "HDF5 Memory Space Close failed\n"); + ret_code = FAIL; + } else { + h5mem_space_id = -1; + } + } + + if (h5dxpl != -1) { + hrc = H5Pclose(h5dxpl); + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Transfer Property List Close failed\n"); + ret_code = FAIL; + } else { + h5dxpl = -1; + } + } + + return ret_code; +} + +/* + * Function: do_read + * Purpose: read the required amount of data from the file. + * Return: SUCCESS or FAIL + * Programmer: Albert Cheng 2001/12/13 + * Modifications: + * Added 2D testing (Christian Chilan, 10. August 2005) + */ + static herr_t +do_read(results *res, file_descr *fd, parameters *parms, long ndsets, + off_t nbytes, size_t buf_size, void *buffer /*out*/) +{ + int ret_code = SUCCESS; + int rc; /*routine return code */ + long ndset; + size_t blk_size; /* The block size to subdivide the xfer buffer into */ + size_t bsize; /* Size of the actual buffer */ + off_t nbytes_xfer; /* Total number of bytes transferred so far */ + size_t nbytes_xfer_advance; /* Number of bytes transferred in a single I/O operation */ + size_t nbytes_toxfer; /* Number of bytes to transfer a particular time */ + char dname[64]; + off_t dset_offset=0; /*dataset offset in a file */ + off_t bytes_begin[2]; /*first elmt this process transfer */ + off_t bytes_count; /*number of elmts this process transfer */ + off_t snbytes=0; /*size of a side of the dataset square */ + unsigned char *buf_p; /* Current buffer pointer */ + + /* POSIX variables */ + off_t file_offset; /* File offset of the next transfer */ + off_t file_offset_advance; /* File offset advance after each I/O operation */ + off_t posix_file_offset; /* Base file offset of the next transfer */ + + /* MPI variables */ + MPI_Offset mpi_file_offset;/* Base file offset of the next transfer*/ + MPI_Offset mpi_offset; /* Offset in MPI file */ + MPI_Offset mpi_offset_advance; /* Offset advance after each I/O operation */ + MPI_Datatype mpi_file_type; /* MPI derived type for 1D file */ + MPI_Datatype mpi_blk_type; /* MPI derived type for 1D buffer */ + MPI_Datatype mpi_cont_type; /* MPI derived type for 2D contiguous file */ + MPI_Datatype mpi_partial_buffer_cont; /* MPI derived type for partial 2D contiguous buffer */ + MPI_Datatype mpi_inter_type; /* MPI derived type for 2D interleaved file */ + MPI_Datatype mpi_partial_buffer_inter; /* MPI derived type for partial 2D interleaved buffer */ + MPI_Datatype mpi_full_buffer; /* MPI derived type for 2D full buffer */ + MPI_Datatype mpi_full_chunk; /* MPI derived type for 2D full chunk */ + MPI_Datatype mpi_chunk_inter_type; /* MPI derived type for 2D chunk interleaved file */ + MPI_Datatype mpi_collective_type; /* Generic MPI derived type for 2D collective access */ + MPI_Status mpi_status; + int mrc; /* MPI return code */ + + /* HDF5 variables */ + herr_t hrc; /*HDF5 return code */ + hsize_t h5dims[2]; /*dataset dim sizes */ + hid_t h5dset_space_id = -1; /*dataset space ID */ + hid_t h5mem_space_id = -1; /*memory dataspace ID */ + hid_t h5ds_id = -1; /*dataset handle */ + hsize_t h5block[2]; /*dataspace selection */ + hsize_t h5stride[2]; + hsize_t h5count[2]; + hsize_t h5start[2]; + hssize_t h5offset[2]; /* Selection offset within dataspace */ + hid_t h5dxpl = -1; /* Dataset transfer property list */ + + /* Get the parameters from the parameter block */ + blk_size=parms->blk_size; + + /* There are two kinds of transfer patterns, contiguous and interleaved. + * Let 0,1,2,...,n be data accessed by process 0,1,2,...,n + * where n is rank of the last process. + * In contiguous pattern, data are accessed as + * 000...111...222...nnn... + * In interleaved pattern, data are accessed as + * 012...n012...n... + * These are all in the scope of one dataset. + */ + + /* 1D dataspace */ + if (!parms->dim2d){ + bsize = buf_size; + /* Contiguous Pattern: */ + if (!parms->interleaved) { + bytes_begin[0] = (off_t)(((double)nbytes*pio_mpi_rank_g)/pio_mpi_nprocs_g); + } /* end if */ + /* Interleaved Pattern: */ + else { + bytes_begin[0] = (off_t)(blk_size*pio_mpi_rank_g); + } /* end else */ + }/* end if */ + /* 2D dataspace */ + else { + /* nbytes is always the number of bytes per dataset (1D or 2D). If the + dataspace is 2D, snbytes is the size of a side of the 'dataset square'. + */ + snbytes = (off_t)sqrt(nbytes); + + bsize = buf_size * blk_size; + + /* Contiguous Pattern: */ + if (!parms->interleaved) { + bytes_begin[0] = (off_t)((double)snbytes*pio_mpi_rank_g / pio_mpi_nprocs_g); + bytes_begin[1] = 0; + } /* end if */ + /* Interleaved Pattern: */ + else { + bytes_begin[0] = 0; + + if (!parms->h5_use_chunks || parms->io_type==PHDF5) + bytes_begin[1] = (off_t)(blk_size*pio_mpi_rank_g); + else + bytes_begin[1] = (off_t)(blk_size*blk_size*pio_mpi_rank_g); + } /* end else */ + } /* end else */ + + /* Calculate the total number of bytes (bytes_count) to be + * transferred by this process. It may be different for different + * transfer pattern due to rounding to integral values. + */ + /* + * Calculate the beginning bytes of this process and the next. + * bytes_count is the difference between these two beginnings. + * This way, it eliminates any rounding errors. + * (This is tricky, don't mess with the formula, rounding errors + * can easily get introduced) */ + bytes_count = (off_t)(((double)nbytes*(pio_mpi_rank_g+1)) / pio_mpi_nprocs_g) + - (off_t)(((double)nbytes*pio_mpi_rank_g) / pio_mpi_nprocs_g); + + /* debug */ + if (pio_debug_level >= 4) { + HDprint_rank(output); + if (!parms->dim2d) { + HDfprintf(output, "Debug(do_write): " + "buf_size=%zu, bytes_begin=%" H5_PRINTF_LL_WIDTH "d, bytes_count=%" H5_PRINTF_LL_WIDTH "d\n", + buf_size, (long long)bytes_begin[0], + (long long)bytes_count); + } else { + HDfprintf(output, "Debug(do_write): " + "linear buf_size=%zu, bytes_begin=(%" H5_PRINTF_LL_WIDTH "d,%" H5_PRINTF_LL_WIDTH "d), bytes_count=%" H5_PRINTF_LL_WIDTH "d\n", + buf_size*blk_size, (long long)bytes_begin[0], + (long long)bytes_begin[1], (long long)bytes_count); + } + } + + /* I/O Access specific setup */ + switch (parms->io_type) { + case POSIXIO: + /* No extra setup */ + break; + + case MPIO: /* MPI-I/O setup */ + /* 1D dataspace */ + if (!parms->dim2d){ + /* Build block's derived type */ + mrc = MPI_Type_contiguous((int)blk_size, + MPI_BYTE, &mpi_blk_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Build file's derived type */ + mrc = MPI_Type_vector((int)(buf_size/blk_size), (int)1, + (int)pio_mpi_nprocs_g, mpi_blk_type, &mpi_file_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit file type */ + mrc = MPI_Type_commit( &mpi_file_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Commit buffer type */ + mrc = MPI_Type_commit( &mpi_blk_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + } /* end if */ + /* 2D dataspace */ + else { + /* Build partial buffer derived type for contiguous access */ + mrc = MPI_Type_contiguous((int)buf_size, MPI_BYTE, + &mpi_partial_buffer_cont); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit partial buffer derived type */ + mrc = MPI_Type_commit(&mpi_partial_buffer_cont); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build contiguous file's derived type */ + mrc = MPI_Type_vector((int)blk_size, (int)1, (int)(snbytes/buf_size), + mpi_partial_buffer_cont, &mpi_cont_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit contiguous file type */ + mrc = MPI_Type_commit(&mpi_cont_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build partial buffer derived type for interleaved access */ + mrc = MPI_Type_contiguous((int)blk_size, MPI_BYTE, + &mpi_partial_buffer_inter); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit partial buffer derived type */ + mrc = MPI_Type_commit(&mpi_partial_buffer_inter); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build interleaved file's derived type */ + mrc = MPI_Type_vector((int)buf_size, (int)1, (int)(snbytes/blk_size), + mpi_partial_buffer_inter, &mpi_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit interleaved file type */ + mrc = MPI_Type_commit(&mpi_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build full buffer derived type */ + mrc = MPI_Type_contiguous((int)(blk_size*buf_size), MPI_BYTE, + &mpi_full_buffer); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit full buffer derived type */ + mrc = MPI_Type_commit(&mpi_full_buffer); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build full chunk derived type */ + mrc = MPI_Type_contiguous((int)(blk_size*blk_size), MPI_BYTE, + &mpi_full_chunk); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit full chunk derived type */ + mrc = MPI_Type_commit(&mpi_full_chunk); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + + /* Build chunk interleaved file's derived type */ + mrc = MPI_Type_vector((int)(buf_size/blk_size), (int)1, (int)(snbytes/blk_size), + mpi_full_chunk, &mpi_chunk_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_CREATE"); + + /* Commit chunk interleaved file type */ + mrc = MPI_Type_commit(&mpi_chunk_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_COMMIT"); + } /* end else */ + break; + + case PHDF5: /* HDF5 setup */ + /* 1D dataspace */ + if (!parms->dim2d){ + if(nbytes>0) { + /* define a contiguous dataset of nbytes native bytes */ + h5dims[0] = nbytes; + h5dset_space_id = H5Screate_simple(1, h5dims, NULL); + VRFY((h5dset_space_id >= 0), "H5Screate_simple"); + + /* Set up the file dset space id to select the pattern to access */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5start[0] = bytes_begin[0]; + h5stride[0] = h5block[0] = blk_size; + h5count[0] = buf_size/blk_size; + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5start[0] = bytes_begin[0]; + h5stride[0] = blk_size*pio_mpi_nprocs_g; + h5block[0] = blk_size; + h5count[0] = buf_size/blk_size; + } /* end else */ + hrc = H5Sselect_hyperslab(h5dset_space_id, H5S_SELECT_SET, + h5start, h5stride, h5count, h5block); + VRFY((hrc >= 0), "H5Sselect_hyperslab"); + } /* end if */ + else { + h5dset_space_id = H5Screate(H5S_SCALAR); + VRFY((h5dset_space_id >= 0), "H5Screate"); + } /* end else */ + + /* Create the memory dataspace that corresponds to the xfer buffer */ + if(buf_size>0) { + h5dims[0] = buf_size; + h5mem_space_id = H5Screate_simple(1, h5dims, NULL); + VRFY((h5mem_space_id >= 0), "H5Screate_simple"); + } /* end if */ + else { + h5mem_space_id = H5Screate(H5S_SCALAR); + VRFY((h5mem_space_id >= 0), "H5Screate"); + } /* end else */ + } /* end if */ + /* 2D dataspace */ + else { + if(nbytes>0) { + /* define a contiguous dataset of nbytes native bytes */ + h5dims[0] = snbytes; + h5dims[1] = snbytes; + h5dset_space_id = H5Screate_simple(2, h5dims, NULL); + VRFY((h5dset_space_id >= 0), "H5Screate_simple"); + + /* Set up the file dset space id to select the pattern to access */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5start[0] = bytes_begin[0]; + h5start[1] = bytes_begin[1]; + h5stride[0] = 1; + h5stride[1] = h5block[0] = h5block[1] = blk_size; + h5count[0] = 1; + h5count[1] = buf_size/blk_size; + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5start[0] = bytes_begin[0]; + h5start[1] = bytes_begin[1]; + h5stride[0] = blk_size; + h5stride[1] = blk_size*pio_mpi_nprocs_g; + h5block[0] = h5block[1] = blk_size; + h5count[0] = buf_size/blk_size; + h5count[1] = 1; + } /* end else */ + hrc = H5Sselect_hyperslab(h5dset_space_id, H5S_SELECT_SET, + h5start, h5stride, h5count, h5block); + VRFY((hrc >= 0), "H5Sselect_hyperslab"); + } /* end if */ + else { + h5dset_space_id = H5Screate(H5S_SCALAR); + VRFY((h5dset_space_id >= 0), "H5Screate"); + } /* end else */ + + /* Create the memory dataspace that corresponds to the xfer buffer */ + if(buf_size>0) { + if (!parms->interleaved){ + h5dims[0] = blk_size; + h5dims[1] = buf_size; + }else{ + h5dims[0] = buf_size; + h5dims[1] = blk_size; + } + h5mem_space_id = H5Screate_simple(2, h5dims, NULL); + VRFY((h5mem_space_id >= 0), "H5Screate_simple"); + } /* end if */ + else { + h5mem_space_id = H5Screate(H5S_SCALAR); + VRFY((h5mem_space_id >= 0), "H5Screate"); + } /* end else */ + } /* end else */ + + /* Create the dataset transfer property list */ + h5dxpl = H5Pcreate(H5P_DATASET_XFER); + if (h5dxpl < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + + /* Change to collective I/O, if asked */ + if(parms->collective) { + hrc = H5Pset_dxpl_mpio(h5dxpl, H5FD_MPIO_COLLECTIVE); + if (hrc < 0) { + fprintf(stderr, "HDF5 Property List Set failed\n"); + GOTOERROR(FAIL); + } /* end if */ + } /* end if */ + break; + } /* end switch */ + + for (ndset = 1; ndset <= ndsets; ++ndset) { + + /* Calculate dataset offset within a file */ + + /* create dataset */ + switch (parms->io_type) { + case POSIXIO: + case MPIO: + /* both posix and mpi io just need dataset offset in file*/ + dset_offset = (ndset - 1) * nbytes; + break; + + case PHDF5: + sprintf(dname, "Dataset_%ld", ndset); + h5ds_id = H5DOPEN(fd->h5fd, dname); + if (h5ds_id < 0) { + fprintf(stderr, "HDF5 Dataset open failed\n"); + GOTOERROR(FAIL); + } + + break; + } + + /* The task is to transfer bytes_count bytes, starting at + * bytes_begin position, using transfer buffer of buf_size bytes. + * If interleaved, select buf_size at a time, in round robin + * fashion, according to number of process. Otherwise, select + * all bytes_count in contiguous. + */ + nbytes_xfer = 0 ; + + /* 1D dataspace */ + if (!parms->dim2d){ + /* Set base file offset for all I/O patterns and POSIX access */ + posix_file_offset = dset_offset + bytes_begin[0]; + + /* Set base file offset for all I/O patterns and MPI access */ + mpi_file_offset = (MPI_Offset)(dset_offset + bytes_begin[0]); + } /* end if */ + else { + /* Set base file offset for all I/O patterns and POSIX access */ + posix_file_offset=dset_offset + bytes_begin[0]*snbytes+ + bytes_begin[1]; + + /* Set base file offset for all I/O patterns and MPI access */ + mpi_file_offset=(MPI_Offset)(dset_offset + bytes_begin[0]*snbytes+ + bytes_begin[1]); + } /* end else */ + + /* Start "raw data" read timer */ + set_time(res->timers, HDF5_RAW_READ_FIXED_DIMS, TSTART); + + while (nbytes_xfer < bytes_count){ + /* Read */ + /* Calculate offset of read within a dataset/file */ + switch (parms->io_type) { + case POSIXIO: + /* 1D dataspace */ + if (!parms->dim2d){ + /* Contiguous pattern */ + if (!parms->interleaved) { + /* Compute file offset */ + file_offset = posix_file_offset + (off_t)nbytes_xfer; + + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, file_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + + /* check if all bytes are read */ + rc = ((ssize_t)buf_size == + POSIXREAD(fd->posixfd, buffer, buf_size)); + VRFY((rc != 0), "POSIXREAD"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Set the base of user's buffer */ + buf_p=(unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size; + + /* Loop over the buffers to read */ + while(nbytes_toxfer>0) { + /* Skip offset over blocks of other processes */ + file_offset = posix_file_offset + + (off_t)(nbytes_xfer*pio_mpi_nprocs_g); + + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, file_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + + /* check if all bytes are read */ + rc = ((ssize_t)blk_size == + POSIXREAD(fd->posixfd, buf_p, blk_size)); + VRFY((rc != 0), "POSIXREAD"); + + /* Advance location in buffer */ + buf_p+=blk_size; + + /* Advance global offset in dataset */ + nbytes_xfer+=blk_size; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=blk_size; + } /* end while */ + } /* end else */ + } /* end if */ + /* 2D dataspace */ + else { + /* Contiguous storage */ + if (!parms->h5_use_chunks) { + /* Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute file offset */ + file_offset=posix_file_offset+(off_t)(((nbytes_xfer/blk_size) + /snbytes)*(blk_size*snbytes)+((nbytes_xfer/blk_size)%snbytes)); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = buf_size; + + /* Global offset advance after each I/O operation */ + file_offset_advance = (off_t)snbytes; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Compute file offset */ + file_offset=posix_file_offset+(off_t)((((nbytes_xfer/buf_size) + *pio_mpi_nprocs_g)/snbytes)*(buf_size*snbytes) + +((nbytes_xfer/buf_size)*pio_mpi_nprocs_g)%snbytes); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size; + + /* Global offset advance after each I/O operation */ + file_offset_advance = (off_t)snbytes; + } /* end else */ + } /* end if */ + /* Chunked storage */ + else { + /*Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute file offset */ + file_offset=posix_file_offset+(off_t)nbytes_xfer; + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * buf_size; + + /* Global offset advance after each I/O operation */ + file_offset_advance = 0; + } /* end if */ + /*Interleaved access pattern */ + else { + /* Compute file offset */ + /* Before simplification */ + /* file_offset=posix_file_offset+(off_t)((nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes/blk_size*(blk_size*blk_size))*(buf_size/blk_size + *snbytes/blk_size*(blk_size*blk_size))+((nbytes_xfer/(buf_size/blk_size)) + *pio_mpi_nprocs_g)%(snbytes/blk_size*(blk_size*blk_size))); */ + + file_offset=posix_file_offset+(off_t)(((nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes*blk_size))*(buf_size*snbytes)+((nbytes_xfer/(buf_size/blk_size)) + *pio_mpi_nprocs_g)%(snbytes*blk_size)); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * blk_size; + + /* Global offset advance after each I/O operation */ + /* file_offset_advance = (off_t)(snbytes/blk_size*(blk_size*blk_size)); */ + file_offset_advance = (off_t)(snbytes*blk_size); + } /* end else */ + } /* end else */ + + /* Common code for file access */ + + /* Set the base of user's buffer */ + buf_p = (unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size*blk_size; + + /* Loop over portions of the buffer to read */ + while(nbytes_toxfer>0){ + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, file_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + + /* check if all bytes are read */ + rc = ((ssize_t)nbytes_xfer_advance == + POSIXREAD(fd->posixfd, buf_p, nbytes_xfer_advance)); + VRFY((rc != 0), "POSIXREAD"); + + /* Advance location in buffer */ + buf_p+=nbytes_xfer_advance; + + /* Advance global offset in dataset */ + nbytes_xfer+=nbytes_xfer_advance; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=nbytes_xfer_advance; + + /* Partially advance file offset */ + file_offset+=file_offset_advance; + } /* end while */ + + } /* end else */ + break; + + case MPIO: + /* 1D dataspace */ + if (!parms->dim2d){ + /* Independent file access */ + if(!parms->collective) { + /* Contiguous pattern */ + if (!parms->interleaved){ + /* Compute offset in file */ + mpi_offset = mpi_file_offset + + nbytes_xfer; + + /* Perform independent read */ + mrc = MPI_File_read_at(fd->mpifd, mpi_offset, buffer, + (int)(buf_size/blk_size), mpi_blk_type, + &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_READ"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Set the base of user's buffer */ + buf_p=(unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size; + + /* Loop over the buffers to read */ + while(nbytes_toxfer>0) { + /* Skip offset over blocks of other processes */ + mpi_offset = mpi_file_offset + + (nbytes_xfer*pio_mpi_nprocs_g); + + /* Perform independent read */ + mrc = MPI_File_read_at(fd->mpifd, mpi_offset, buf_p, + (int)1, mpi_blk_type, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_READ"); + + /* Advance location in buffer */ + buf_p+=blk_size; + + /* Advance global offset in dataset */ + nbytes_xfer+=blk_size; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=blk_size; + } /* end while */ + } /* end else */ + } /* end if */ + /* Collective file access */ + else { + /* Contiguous access pattern */ + if (!parms->interleaved){ + /* Compute offset in file */ + mpi_offset = mpi_file_offset + + nbytes_xfer; + + /* Perform collective read */ + mrc = MPI_File_read_at_all(fd->mpifd, mpi_offset, buffer, + (int)(buf_size/blk_size), mpi_blk_type, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_READ"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Compute offset in file */ + mpi_offset = mpi_file_offset + + (nbytes_xfer*pio_mpi_nprocs_g); + + /* Set the file view */ + mrc = MPI_File_set_view(fd->mpifd, mpi_offset, mpi_blk_type, + mpi_file_type, (char*)"native", h5_io_info_g); + VRFY((mrc==MPI_SUCCESS), "MPIO_VIEW"); + + /* Perform collective read */ + mrc = MPI_File_read_at_all(fd->mpifd, 0, buffer, + (int)(buf_size/blk_size), mpi_blk_type, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_READ"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size; + } /* end else */ + } /* end else */ + } /* end if */ + /* 2D dataspace */ + else { + /* Contiguous storage */ + if (!parms->h5_use_chunks) { + /* Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute offset in file */ + mpi_offset=mpi_file_offset+((nbytes_xfer/blk_size)/snbytes)* + (blk_size*snbytes)+((nbytes_xfer/blk_size)%snbytes); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = buf_size; + + /* Global offset advance after each I/O operation */ + mpi_offset_advance = snbytes; + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_cont_type; + } /* end if */ + /* Interleaved access pattern */ + else { + /* Compute offset in file */ + mpi_offset=mpi_file_offset+(((nbytes_xfer/buf_size)*pio_mpi_nprocs_g)/snbytes)* + (buf_size*snbytes)+((nbytes_xfer/buf_size)*pio_mpi_nprocs_g)%snbytes; + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size; + + /* Global offset advance after each I/O operation */ + mpi_offset_advance = snbytes; + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_inter_type; + } /* end else */ + } /* end if */ + /* Chunked storage */ + else { + /*Contiguous access pattern */ + if (!parms->interleaved) { + /* Compute offset in file */ + mpi_offset=mpi_file_offset+nbytes_xfer; + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * buf_size; + + /* Global offset advance after each I/O operation */ + mpi_offset_advance = 0; + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_full_buffer; + } /* end if */ + /*Interleaved access pattern */ + else { + /* Compute offset in file */ + /* Before simplification */ + /* mpi_offset=mpi_file_offset+(nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes/blk_size*(blk_size*blk_size))* + (buf_size/blk_size*snbytes/blk_size*(blk_size*blk_size))+ + ((nbytes_xfer/(buf_size/blk_size))*pio_mpi_nprocs_g)%(snbytes + /blk_size*(blk_size*blk_size)); */ + mpi_offset=mpi_file_offset+((nbytes_xfer/(buf_size/blk_size) + *pio_mpi_nprocs_g)/(snbytes*blk_size))*(buf_size*snbytes) + +((nbytes_xfer/(buf_size/blk_size))*pio_mpi_nprocs_g)%(snbytes*blk_size); + + /* Number of bytes to be transferred per I/O operation */ + nbytes_xfer_advance = blk_size * blk_size; + + /* Global offset advance after each I/O operation */ + /* mpi_offset_advance = (MPI_Offset)(snbytes/blk_size*(blk_size*blk_size)); */ + mpi_offset_advance = (MPI_Offset)(snbytes*blk_size); + + /* MPI type to be used for collective access */ + mpi_collective_type = mpi_chunk_inter_type; + } /* end else */ + } /* end else */ + + /* Common code for independent file access */ + if (!parms->collective) { + /* Set the base of user's buffer */ + buf_p = (unsigned char *)buffer; + + /* Set the number of bytes to transfer this time */ + nbytes_toxfer = buf_size * blk_size; + + /* Loop over portions of the buffer to read */ + while(nbytes_toxfer>0){ + /* Perform independent read */ + mrc = MPI_File_read_at(fd->mpifd, mpi_offset, buf_p, + (int)nbytes_xfer_advance, MPI_BYTE, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_READ"); + + /* Advance location in buffer */ + buf_p+=nbytes_xfer_advance; + + /* Advance global offset in dataset */ + nbytes_xfer+=nbytes_xfer_advance; + + /* Decrement number of bytes left this time */ + nbytes_toxfer-=nbytes_xfer_advance; + + /* Partially advance global offset in dataset */ + mpi_offset+=mpi_offset_advance; + } /* end while */ + } /* end if */ + + /* Common code for collective file access */ + else { + /* Set the file view */ + mrc = MPI_File_set_view(fd->mpifd, mpi_offset, MPI_BYTE, + mpi_collective_type, (char *)"native", h5_io_info_g); + VRFY((mrc==MPI_SUCCESS), "MPIO_VIEW"); + + /* Perform read */ + MPI_File_read_at_all(fd->mpifd, 0, buffer,(int)(buf_size*blk_size), + MPI_BYTE, &mpi_status); + VRFY((mrc==MPI_SUCCESS), "MPIO_READ"); + + /* Advance global offset in dataset */ + nbytes_xfer+=buf_size*blk_size; + } /* end else */ + + } /* end else */ + break; + + case PHDF5: + /* 1D dataspace */ + if (!parms->dim2d){ + /* Set up the file dset space id to move the selection to process */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5offset[0] = nbytes_xfer; + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5offset[0] = (nbytes_xfer*pio_mpi_nprocs_g); + } /* end else */ + hrc = H5Soffset_simple(h5dset_space_id, h5offset); + VRFY((hrc >= 0), "H5Soffset_simple"); + + /* Read the buffer in */ + hrc = H5Dread(h5ds_id, ELMT_H5_TYPE, h5mem_space_id, + h5dset_space_id, h5dxpl, buffer); + VRFY((hrc >= 0), "H5Dread"); + + /* Increment number of bytes transferred */ + nbytes_xfer += buf_size; + } /* end if */ + /* 2D dataspace */ + else { + /* Set up the file dset space id to move the selection to process */ + if (!parms->interleaved){ + /* Contiguous pattern */ + h5offset[0] = (nbytes_xfer/(snbytes*blk_size))*blk_size; + h5offset[1] = (nbytes_xfer%(snbytes*blk_size))/blk_size; + } /* end if */ + else { + /* Interleaved access pattern */ + /* Skip offset over blocks of other processes */ + h5offset[0] = ((nbytes_xfer*pio_mpi_nprocs_g)/(snbytes*buf_size))*buf_size; + h5offset[1] = ((nbytes_xfer*pio_mpi_nprocs_g)%(snbytes*buf_size))/buf_size; + + } /* end else */ + hrc = H5Soffset_simple(h5dset_space_id, h5offset); + VRFY((hrc >= 0), "H5Soffset_simple"); + + /* Write the buffer out */ + hrc = H5Dread(h5ds_id, ELMT_H5_TYPE, h5mem_space_id, + h5dset_space_id, h5dxpl, buffer); + VRFY((hrc >= 0), "H5Dread"); + + /* Increment number of bytes transferred */ + nbytes_xfer += buf_size*blk_size; + + } /* end else */ + break; + } /* switch (parms->io_type) */ + + /* Verify raw data, if asked */ + if (parms->verify) { + /* Verify data read */ + unsigned char *ucharptr = (unsigned char *)buffer; + size_t i; + int nerror=0; + + for (i = 0; i < bsize; ++i){ + if (*ucharptr++ != pio_mpi_rank_g+1) { + if (++nerror < 20){ + /* report at most 20 errors */ + HDprint_rank(output); + HDfprintf(output, "read data error, expected (%d), " + "got (%d)\n", + pio_mpi_rank_g+1, + (int)*(ucharptr-1)); + } /* end if */ + } /* end if */ + } /* end for */ + if (nerror >= 20) { + HDprint_rank(output); + HDfprintf(output, "..."); + HDfprintf(output, "total read data errors=%d\n", + nerror); + } /* end if */ + } /* if (parms->verify) */ + + } /* end while */ + + /* Stop "raw data" read timer */ + set_time(res->timers, HDF5_RAW_READ_FIXED_DIMS, TSTOP); + + /* Calculate read time */ + + /* Close dataset. Only HDF5 needs to do an explicit close. */ + if (parms->io_type == PHDF5) { + hrc = H5Dclose(h5ds_id); + + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Close failed\n"); + GOTOERROR(FAIL); + } + + h5ds_id = -1; + } /* end if */ + } /* end for */ + +done: + /* release MPI-I/O objects */ + if (parms->io_type == MPIO) { + /* 1D dataspace */ + if (!parms->dim2d){ + /* Free file type */ + mrc = MPI_Type_free( &mpi_file_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free buffer type */ + mrc = MPI_Type_free( &mpi_blk_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + } /* end if */ + /* 2D dataspace */ + else { + /* Free partial buffer type for contiguous access */ + mrc = MPI_Type_free( &mpi_partial_buffer_cont ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free contiguous file type */ + mrc = MPI_Type_free( &mpi_cont_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free partial buffer type for interleaved access */ + mrc = MPI_Type_free( &mpi_partial_buffer_inter ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free interleaved file type */ + mrc = MPI_Type_free( &mpi_inter_type ); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free full buffer type */ + mrc = MPI_Type_free(&mpi_full_buffer); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free full chunk type */ + mrc = MPI_Type_free(&mpi_full_chunk); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + + /* Free chunk interleaved file type */ + mrc = MPI_Type_free(&mpi_chunk_inter_type); + VRFY((mrc==MPI_SUCCESS), "MPIO_TYPE_FREE"); + } /* end else */ + } /* end if */ + + /* release HDF5 objects */ + if (h5dset_space_id != -1) { + hrc = H5Sclose(h5dset_space_id); + if (hrc < 0){ + fprintf(stderr, "HDF5 Dataset Space Close failed\n"); + ret_code = FAIL; + } else { + h5dset_space_id = -1; + } + } + + if (h5mem_space_id != -1) { + hrc = H5Sclose(h5mem_space_id); + if (hrc < 0) { + fprintf(stderr, "HDF5 Memory Space Close failed\n"); + ret_code = FAIL; + } else { + h5mem_space_id = -1; + } + } + + if (h5dxpl != -1) { + hrc = H5Pclose(h5dxpl); + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Transfer Property List Close failed\n"); + ret_code = FAIL; + } else { + h5dxpl = -1; + } + } + + return ret_code; +} + +/* + * Function: do_fopen + * Purpose: Open the specified file. + * Return: SUCCESS or FAIL + * Programmer: Albert Cheng, Bill Wendling, 2001/12/13 + * Modifications: + */ + static herr_t +do_fopen(parameters *param, char *fname, file_descr *fd /*out*/, int flags) +{ + int ret_code = SUCCESS, mrc; + hid_t acc_tpl = -1; /* file access templates */ + + switch (param->io_type) { + case POSIXIO: + if (flags & (PIO_CREATE | PIO_WRITE)) + fd->posixfd = POSIXCREATE(fname); + else + fd->posixfd = POSIXOPEN(fname, O_RDONLY); + + if (fd->posixfd < 0 ) { + fprintf(stderr, "POSIX File Open failed(%s)\n", fname); + GOTOERROR(FAIL); + } + + + /* The perils of POSIX I/O in a parallel environment. The problem is: + * + * - Process n opens a file with truncation and then starts + * writing to the file. + * - Process m also opens the file with truncation, but after + * process n has already started to write to the file. Thus, + * all of the stuff process n wrote is now lost. + */ + MPI_Barrier(pio_comm_g); + + break; + + case MPIO: + if (flags & (PIO_CREATE | PIO_WRITE)) { + MPI_File_delete(fname, h5_io_info_g); + mrc = MPI_File_open(pio_comm_g, fname, MPI_MODE_CREATE | MPI_MODE_RDWR, + h5_io_info_g, &fd->mpifd); + + if (mrc != MPI_SUCCESS) { + fprintf(stderr, "MPI File Open failed(%s)\n", fname); + GOTOERROR(FAIL); + } + + /*since MPI_File_open with MPI_MODE_CREATE does not truncate */ + /*filesize , set size to 0 explicitedly. */ + mrc = MPI_File_set_size(fd->mpifd, (MPI_Offset)0); + if (mrc != MPI_SUCCESS) { + fprintf(stderr, "MPI_File_set_size failed\n"); + GOTOERROR(FAIL); + } + } else { + mrc = MPI_File_open(pio_comm_g, fname, MPI_MODE_RDONLY, h5_io_info_g, &fd->mpifd); + if (mrc != MPI_SUCCESS) { + fprintf(stderr, "MPI File Open failed(%s)\n", fname); + GOTOERROR(FAIL); + } + } + + break; + + case PHDF5: + if ((acc_tpl = H5Pcreate(H5P_FILE_ACCESS)) < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + + /* Set the file driver to the MPI-IO driver */ + if (H5Pset_fapl_mpio(acc_tpl, pio_comm_g, h5_io_info_g) < 0) { + fprintf(stderr, "HDF5 Property List Set failed\n"); + GOTOERROR(FAIL); + } + + /* Set the alignment of objects in HDF5 file */ + if (H5Pset_alignment(acc_tpl, param->h5_thresh, param->h5_align) < 0) { + fprintf(stderr, "HDF5 Property List Set failed\n"); + GOTOERROR(FAIL); + } + + /* create the parallel file */ + if (flags & (PIO_CREATE | PIO_WRITE)) + fd->h5fd = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, acc_tpl); + else + fd->h5fd = H5Fopen(fname, H5F_ACC_RDONLY, acc_tpl); + if (fd->h5fd < 0) { + fprintf(stderr, "HDF5 File Create failed(%s)\n", fname); + GOTOERROR(FAIL); + } + + /* verifying the close of the acc_tpl */ + if (H5Pclose(acc_tpl) < 0) { + fprintf(stderr, "HDF5 Property List Close failed\n"); + GOTOERROR(FAIL); + } + + break; + } + +done: + return ret_code; +} + +/* + * Function: do_fclose + * Purpose: Close the specified file descriptor. + * Return: SUCCESS or FAIL + * Programmer: Albert Cheng, Bill Wendling, 2001/12/13 + * Modifications: + */ + static herr_t +do_fclose(iotype iot, file_descr *fd /*out*/) +{ + herr_t ret_code = SUCCESS, hrc; + int mrc = 0, rc = 0; + + switch (iot) { + case POSIXIO: + rc = POSIXCLOSE(fd->posixfd); + + if (rc != 0){ + fprintf(stderr, "POSIX File Close failed\n"); + GOTOERROR(FAIL); + } + + fd->posixfd = -1; + break; + + case MPIO: + mrc = MPI_File_close(&fd->mpifd); + + if (mrc != MPI_SUCCESS){ + fprintf(stderr, "MPI File close failed\n"); + GOTOERROR(FAIL); + } + + fd->mpifd = MPI_FILE_NULL; + break; + + case PHDF5: + hrc = H5Fclose(fd->h5fd); + + if (hrc < 0) { + fprintf(stderr, "HDF5 File Close failed\n"); + GOTOERROR(FAIL); + } + + fd->h5fd = -1; + break; + } + +done: + return ret_code; +} + + +/* + * Function: do_fclose + * Purpose: Cleanup temporary file unless HDF5_NOCLEANUP is set. + * Only Proc 0 of the PIO communicator will do the cleanup. + * Other processes just return. + * Return: void + * Programmer: Albert Cheng 2001/12/12 + * Modifications: + */ + static void +do_cleanupfile(iotype iot, char *fname) +{ + if (pio_mpi_rank_g != 0) + return; + + if (clean_file_g == -1) + clean_file_g = (getenv("HDF5_NOCLEANUP")==NULL) ? 1 : 0; + + if (clean_file_g){ + switch (iot){ + case POSIXIO: + remove(fname); + break; + case MPIO: + case PHDF5: + MPI_File_delete(fname, h5_io_info_g); + break; + } + } +} + +#ifdef TIME_MPI +/* instrument the MPI_File_wrirte_xxx and read_xxx calls to measure + * pure time spent in MPI_File code. + */ +int MPI_File_read_at(MPI_File fh, MPI_Offset offset, void *buf, + int count, MPI_Datatype datatype, MPI_Status *status) +{ + int err; + set_time(timer_g, HDF5_MPI_READ, TSTART); + err=PMPI_File_read_at(fh, offset, buf, count, datatype, status); + set_time(timer_g, HDF5_MPI_READ, TSTOP); + return err; +} + + +int MPI_File_read_at_all(MPI_File fh, MPI_Offset offset, void *buf, + int count, MPI_Datatype datatype, MPI_Status *status) +{ + int err; + set_time(timer_g, HDF5_MPI_READ, TSTART); + err=PMPI_File_read_at_all(fh, offset, buf, count, datatype, status); + set_time(timer_g, HDF5_MPI_READ, TSTOP); + return err; +} + +int MPI_File_write_at(MPI_File fh, MPI_Offset offset, void *buf, + int count, MPI_Datatype datatype, MPI_Status *status) +{ + int err; + set_time(timer_g, HDF5_MPI_WRITE, TSTART); + err=PMPI_File_write_at(fh, offset, buf, count, datatype, status); + set_time(timer_g, HDF5_MPI_WRITE, TSTOP); + return err; +} + +int MPI_File_write_at_all(MPI_File fh, MPI_Offset offset, void *buf, + int count, MPI_Datatype datatype, MPI_Status *status) +{ + int err; + set_time(timer_g, HDF5_MPI_WRITE, TSTART); + err=PMPI_File_write_at_all(fh, offset, buf, count, datatype, status); + set_time(timer_g, HDF5_MPI_WRITE, TSTOP); + return err; +} + +#endif /* TIME_MPI */ +#endif /* H5_HAVE_PARALLEL */ + + + + + diff --git a/tools/test/perform/pio_perf.c b/tools/test/perform/pio_perf.c new file mode 100644 index 0000000..1402ec3 --- /dev/null +++ b/tools/test/perform/pio_perf.c @@ -0,0 +1,1695 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Parallel HDF5 Performance Testing Code + * -------------------------------------- + * + * Portable code to test performance on the different platforms we support. + * This is what the report should look like: + * + * nprocs = Max#Procs + * IO API = POSIXIO + * # Files = 1, # of dsets = 1000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * # Files = 1, # of dsets = 3000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * + * . . . + * + * IO API = MPIO + * # Files = 1, # of dsets = 1000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * # Files = 1, # of dsets = 3000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * + * . . . + * + * IO API = PHDF5 + * # Files = 1, # of dsets = 1000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * # Files = 1, # of dsets = 3000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * + * . . . + * + * nprocs = Max#Procs / 2 + * + * . . . + * + */ + +/* system header files */ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "hdf5.h" + +#ifdef H5_HAVE_PARALLEL + +/* library header files */ +#include <mpi.h> + +/* our header files */ +#include "pio_perf.h" + +/* useful macros */ +#define TAB_SPACE 4 + +#define ONE_KB 1024 +#define ONE_MB (ONE_KB * ONE_KB) +#define ONE_GB (ONE_MB * ONE_KB) + +#define PIO_POSIX 0x1 +#define PIO_MPI 0x2 +#define PIO_HDF5 0x4 + +/* report 0.0 in case t is zero too */ +#define MB_PER_SEC(bytes,t) (((t)==0.0) ? 0.0 : ((((double)bytes) / ONE_MB) / (t))) + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE (!TRUE) +#endif /* FALSE */ + +/* global variables */ +FILE *output; /* output file */ +int comm_world_rank_g; /* my rank in MPI_COMM_RANK */ +int comm_world_nprocs_g;/* num. of processes of MPI_COMM_WORLD */ +MPI_Comm pio_comm_g; /* Communicator to run the PIO */ +int pio_mpi_rank_g; /* MPI rank of pio_comm_g */ +int pio_mpi_nprocs_g; /* Number of processes of pio_comm_g */ +int pio_debug_level = 0;/* The debug level: + * 0 - Off + * 1 - Minimal + * 2 - Some more + * 3 - Maximal + * 4 - Maximal & then some + */ + +/* local variables */ +static const char *progname = "h5perf"; + +/* + * Command-line options: The user can specify short or long-named + * parameters. The long-named ones can be partially spelled. When + * adding more, make sure that they don't clash with each other. + */ +#if 1 +static const char *s_opts = "a:A:B:cCd:D:e:F:ghi:Imno:p:P:stT:wx:X:"; +#else +static const char *s_opts = "a:A:bB:cCd:D:e:F:ghi:Imno:p:P:stT:wx:X:"; +#endif /* 1 */ +static struct long_options l_opts[] = { + { "align", require_arg, 'a' }, + { "alig", require_arg, 'a' }, + { "ali", require_arg, 'a' }, + { "al", require_arg, 'a' }, + { "api", require_arg, 'A' }, + { "ap", require_arg, 'A' }, +#if 0 + /* a sighting of the elusive binary option */ + { "binary", no_arg, 'b' }, + { "binar", no_arg, 'b' }, + { "bina", no_arg, 'b' }, + { "bin", no_arg, 'b' }, + { "bi", no_arg, 'b' }, +#endif /* 0 */ + { "block-size", require_arg, 'B' }, + { "block-siz", require_arg, 'B' }, + { "block-si", require_arg, 'B' }, + { "block-s", require_arg, 'B' }, + { "block-", require_arg, 'B' }, + { "block", require_arg, 'B' }, + { "bloc", require_arg, 'B' }, + { "blo", require_arg, 'B' }, + { "bl", require_arg, 'B' }, + { "chunk", no_arg, 'c' }, + { "chun", no_arg, 'c' }, + { "chu", no_arg, 'c' }, + { "ch", no_arg, 'c' }, + { "collective", no_arg, 'C' }, + { "collectiv", no_arg, 'C' }, + { "collecti", no_arg, 'C' }, + { "collect", no_arg, 'C' }, + { "collec", no_arg, 'C' }, + { "colle", no_arg, 'C' }, + { "coll", no_arg, 'C' }, + { "col", no_arg, 'C' }, + { "co", no_arg, 'C' }, + { "debug", require_arg, 'D' }, + { "debu", require_arg, 'D' }, + { "deb", require_arg, 'D' }, + { "de", require_arg, 'D' }, + { "geometry", no_arg, 'g' }, + { "geometr", no_arg, 'g' }, + { "geomet", no_arg, 'g' }, + { "geome", no_arg, 'g' }, + { "geom", no_arg, 'g' }, + { "geo", no_arg, 'g' }, + { "ge", no_arg, 'g' }, + { "help", no_arg, 'h' }, + { "hel", no_arg, 'h' }, + { "he", no_arg, 'h' }, + { "interleaved", require_arg, 'I' }, + { "interleave", require_arg, 'I' }, + { "interleav", require_arg, 'I' }, + { "interlea", require_arg, 'I' }, + { "interle", require_arg, 'I' }, + { "interl", require_arg, 'I' }, + { "inter", require_arg, 'I' }, + { "inte", require_arg, 'I' }, + { "int", require_arg, 'I' }, + { "in", require_arg, 'I' }, + { "max-num-processes", require_arg, 'P' }, + { "max-num-processe", require_arg, 'P' }, + { "max-num-process", require_arg, 'P' }, + { "max-num-proces", require_arg, 'P' }, + { "max-num-proce", require_arg, 'P' }, + { "max-num-proc", require_arg, 'P' }, + { "max-num-pro", require_arg, 'P' }, + { "max-num-pr", require_arg, 'P' }, + { "max-num-p", require_arg, 'P' }, + { "min-num-processes", require_arg, 'p' }, + { "min-num-processe", require_arg, 'p' }, + { "min-num-process", require_arg, 'p' }, + { "min-num-proces", require_arg, 'p' }, + { "min-num-proce", require_arg, 'p' }, + { "min-num-proc", require_arg, 'p' }, + { "min-num-pro", require_arg, 'p' }, + { "min-num-pr", require_arg, 'p' }, + { "min-num-p", require_arg, 'p' }, + { "max-xfer-size", require_arg, 'X' }, + { "max-xfer-siz", require_arg, 'X' }, + { "max-xfer-si", require_arg, 'X' }, + { "max-xfer-s", require_arg, 'X' }, + { "max-xfer", require_arg, 'X' }, + { "max-xfe", require_arg, 'X' }, + { "max-xf", require_arg, 'X' }, + { "max-x", require_arg, 'X' }, + { "min-xfer-size", require_arg, 'x' }, + { "min-xfer-siz", require_arg, 'x' }, + { "min-xfer-si", require_arg, 'x' }, + { "min-xfer-s", require_arg, 'x' }, + { "min-xfer", require_arg, 'x' }, + { "min-xfe", require_arg, 'x' }, + { "min-xf", require_arg, 'x' }, + { "min-x", require_arg, 'x' }, + { "num-bytes", require_arg, 'e' }, + { "num-byte", require_arg, 'e' }, + { "num-byt", require_arg, 'e' }, + { "num-by", require_arg, 'e' }, + { "num-b", require_arg, 'e' }, + { "num-dsets", require_arg, 'd' }, + { "num-dset", require_arg, 'd' }, + { "num-dse", require_arg, 'd' }, + { "num-ds", require_arg, 'd' }, + { "num-d", require_arg, 'd' }, + { "num-files", require_arg, 'F' }, + { "num-file", require_arg, 'F' }, + { "num-fil", require_arg, 'F' }, + { "num-fi", require_arg, 'F' }, + { "num-f", require_arg, 'F' }, + { "num-iterations", require_arg, 'i' }, + { "num-iteration", require_arg, 'i' }, + { "num-iteratio", require_arg, 'i' }, + { "num-iterati", require_arg, 'i' }, + { "num-iterat", require_arg, 'i' }, + { "num-itera", require_arg, 'i' }, + { "num-iter", require_arg, 'i' }, + { "num-ite", require_arg, 'i' }, + { "num-it", require_arg, 'i' }, + { "num-i", require_arg, 'i' }, + { "output", require_arg, 'o' }, + { "outpu", require_arg, 'o' }, + { "outp", require_arg, 'o' }, + { "out", require_arg, 'o' }, + { "ou", require_arg, 'o' }, + { "threshold", require_arg, 'T' }, + { "threshol", require_arg, 'T' }, + { "thresho", require_arg, 'T' }, + { "thresh", require_arg, 'T' }, + { "thres", require_arg, 'T' }, + { "thre", require_arg, 'T' }, + { "thr", require_arg, 'T' }, + { "th", require_arg, 'T' }, + { "write-only", require_arg, 'w' }, + { "write-onl", require_arg, 'w' }, + { "write-on", require_arg, 'w' }, + { "write-o", require_arg, 'w' }, + { "write", require_arg, 'w' }, + { "writ", require_arg, 'w' }, + { "wri", require_arg, 'w' }, + { "wr", require_arg, 'w' }, + { NULL, 0, '\0' } +}; + +struct options { + long io_types; /* bitmask of which I/O types to test */ + const char *output_file; /* file to print report to */ + long num_dsets; /* number of datasets */ + long num_files; /* number of files */ + off_t num_bpp; /* number of bytes per proc per dset */ + int num_iters; /* number of iterations */ + int max_num_procs; /* maximum number of processes to use */ + int min_num_procs; /* minimum number of processes to use */ + size_t max_xfer_size; /* maximum transfer buffer size */ + size_t min_xfer_size; /* minimum transfer buffer size */ + size_t blk_size; /* Block size */ + unsigned interleaved; /* Interleaved vs. contiguous blocks */ + unsigned collective; /* Collective vs. independent I/O */ + unsigned dim2d; /* 1D vs. 2D geometry */ + int print_times; /* print times as well as throughputs */ + int print_raw; /* print raw data throughput info */ + off_t h5_alignment; /* alignment in HDF5 file */ + off_t h5_threshold; /* threshold for alignment in HDF5 file */ + int h5_use_chunks; /* Make HDF5 dataset chunked */ + int h5_write_only; /* Perform the write tests only */ + int verify; /* Verify data correctness */ +}; + +typedef struct _minmax { + double min; + double max; + double sum; + int num; +} minmax; + +/* local functions */ +static off_t parse_size_directive(const char *size); +static struct options *parse_command_line(int argc, char *argv[]); +static void run_test_loop(struct options *options); +static int run_test(iotype iot, parameters parms, struct options *opts); +static void output_all_info(minmax *mm, int count, int indent_level); +static void get_minmax(minmax *mm, double val); +static minmax accumulate_minmax_stuff(minmax *mm, int count); +static int create_comm_world(int num_procs, int *doing_pio); +static int destroy_comm_world(void); +static void output_results(const struct options *options, const char *name, + minmax *table, int table_size, off_t data_size); +static void output_times(const struct options *options, const char *name, + minmax *table, int table_size); +static void output_report(const char *fmt, ...); +static void print_indent(register int indent); +static void usage(const char *prog); +static void report_parameters(struct options *opts); + +/* + * Function: main + * Purpose: Start things up. Initialize MPI and then call the test looping + * function. + * Return: EXIT_SUCCESS or EXIT_FAILURE + * Programmer: Bill Wendling, 30. October 2001 + * Modifications: + */ +int +main(int argc, char **argv) +{ + int ret; + int exit_value = EXIT_SUCCESS; + struct options *opts = NULL; + +#ifndef STANDALONE + /* Initialize h5tools lib */ + h5tools_init(); +#endif + + output = stdout; + + /* initialize MPI and get the maximum num of processors we started with */ + MPI_Init(&argc, &argv); + ret = MPI_Comm_size(MPI_COMM_WORLD, &comm_world_nprocs_g); + + if (ret != MPI_SUCCESS) { + fprintf(stderr, "%s: MPI_Comm_size call failed\n", progname); + + if (ret == MPI_ERR_COMM) + fprintf(stderr, "invalid MPI communicator\n"); + else + fprintf(stderr, "invalid argument\n"); + + exit_value = EXIT_FAILURE; + goto finish; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &comm_world_rank_g); + + if (ret != MPI_SUCCESS) { + fprintf(stderr, "%s: MPI_Comm_rank call failed\n", progname); + + if (ret == MPI_ERR_COMM) + fprintf(stderr, "invalid MPI communicator\n"); + else + fprintf(stderr, "invalid argument\n"); + + exit_value = EXIT_FAILURE; + goto finish; + } + + pio_comm_g = MPI_COMM_WORLD; + + h5_set_info_object(); + opts = parse_command_line(argc, argv); + + if (!opts) { + exit_value = EXIT_FAILURE; + goto finish; + } + + if (opts->output_file) { + if ((output = fopen(opts->output_file, "w")) == NULL) { + fprintf(stderr, "%s: cannot open output file\n", progname); + perror(opts->output_file); + goto finish; + } + } + + if ((pio_debug_level == 0 && comm_world_rank_g == 0) || pio_debug_level > 0) + report_parameters(opts); + + run_test_loop(opts); + +finish: + MPI_Finalize(); + free(opts); + return exit_value; +} + +/* + * Function: run_test_loop + * Purpose: Run the I/O tests. Write the results to OUTPUT. + * + * - The slowest changing part of the test is the number of + * processors to use. For each loop iteration, we divide that + * number by 2 and rerun the test. + * + * - The second slowest is what type of IO API to perform. We have + * three choices: POSIXIO, MPI-IO, and PHDF5. + * + * - Then we change the size of the buffer. This information is + * inferred from the number of datasets to create and the number + * of integers to put into each dataset. The backend code figures + * this out. + * + * Return: Nothing + * Programmer: Bill Wendling, 30. October 2001 + * Modifications: + * Added 2D testing (Christian Chilan, 10. August 2005) + */ +static void +run_test_loop(struct options *opts) +{ + parameters parms; + int num_procs; + int doing_pio; /* if this process is doing PIO */ + + parms.num_files = opts->num_files; + parms.num_dsets = opts->num_dsets; + parms.num_iters = opts->num_iters; + parms.blk_size = opts->blk_size; + parms.interleaved = opts->interleaved; + parms.collective = opts->collective; + parms.dim2d = opts->dim2d; + parms.h5_align = opts->h5_alignment; + parms.h5_thresh = opts->h5_threshold; + parms.h5_use_chunks = opts->h5_use_chunks; + parms.h5_write_only = opts->h5_write_only; + parms.verify = opts->verify; + + /* start with max_num_procs and decrement it by half for each loop. */ + /* if performance needs restart, fewer processes may be needed. */ + for (num_procs = opts->max_num_procs; + num_procs >= opts->min_num_procs; num_procs >>= 1) { + register size_t buf_size; + + parms.num_procs = num_procs; + + if (create_comm_world(parms.num_procs, &doing_pio) != SUCCESS) { + /* do something harsh */ + } + + /* only processes doing PIO will run the tests */ + if (doing_pio){ + output_report("Number of processors = %ld\n", parms.num_procs); + + /* multiply the xfer buffer size by 2 for each loop iteration */ + for (buf_size = opts->min_xfer_size; + buf_size <= opts->max_xfer_size; buf_size <<= 1) { + parms.buf_size = buf_size; + + if (parms.dim2d){ + parms.num_bytes = (off_t)pow((double)(opts->num_bpp*parms.num_procs),2); + if (parms.interleaved) + output_report("Transfer Buffer Size: %ldx%ld bytes, File size: %.2f MB\n", + buf_size, opts->blk_size, + ((double)parms.num_dsets * (double)parms.num_bytes) + / ONE_MB); + else + output_report("Transfer Buffer Size: %ldx%ld bytes, File size: %.2f MB\n", + opts->blk_size, buf_size, + ((double)parms.num_dsets * (double)parms.num_bytes) + / ONE_MB); + + print_indent(1); + output_report(" # of files: %ld, # of datasets: %ld, dataset size: %.2fx%.2f KB\n", + parms.num_files, parms.num_dsets, (double)(opts->num_bpp*parms.num_procs)/ONE_KB, + (double)(opts->num_bpp*parms.num_procs)/ONE_KB); + } + else{ + parms.num_bytes = (off_t)opts->num_bpp*parms.num_procs; + output_report("Transfer Buffer Size: %ld bytes, File size: %.2f MB\n", + buf_size,((double)parms.num_dsets * (double)parms.num_bytes) / ONE_MB); + + print_indent(1); + output_report(" # of files: %ld, # of datasets: %ld, dataset size: %.2f MB\n", + parms.num_files, parms.num_dsets, (double)(opts->num_bpp*parms.num_procs)/ONE_MB); + } + + if (opts->io_types & PIO_POSIX) + run_test(POSIXIO, parms, opts); + + if (opts->io_types & PIO_MPI) + run_test(MPIO, parms, opts); + + if (opts->io_types & PIO_HDF5) + run_test(PHDF5, parms, opts); + + /* Run the tests once if buf_size==0, but then break out */ + if(buf_size==0) + break; + } + + if (destroy_comm_world() != SUCCESS) { + /* do something harsh */ + } + } + } +} + +/* + * Function: run_test + * Purpose: Inner loop call to actually run the I/O test. + * Return: Nothing + * Programmer: Bill Wendling, 18. December 2001 + * Modifications: + */ +static int +run_test(iotype iot, parameters parms, struct options *opts) +{ + results res; + register int i, ret_value = SUCCESS; + int comm_size; + off_t raw_size; + minmax *write_mpi_mm_table=NULL; + minmax *write_mm_table=NULL; + minmax *write_gross_mm_table=NULL; + minmax *write_raw_mm_table=NULL; + minmax *read_mpi_mm_table=NULL; + minmax *read_mm_table=NULL; + minmax *read_gross_mm_table=NULL; + minmax *read_raw_mm_table=NULL; + minmax *read_open_mm_table=NULL; + minmax *read_close_mm_table=NULL; + minmax *write_open_mm_table=NULL; + minmax *write_close_mm_table=NULL; + minmax write_mpi_mm = {0.0, 0.0, 0.0, 0}; + minmax write_mm = {0.0, 0.0, 0.0, 0}; + minmax write_gross_mm = {0.0, 0.0, 0.0, 0}; + minmax write_raw_mm = {0.0, 0.0, 0.0, 0}; + minmax read_mpi_mm = {0.0, 0.0, 0.0, 0}; + minmax read_mm = {0.0, 0.0, 0.0, 0}; + minmax read_gross_mm = {0.0, 0.0, 0.0, 0}; + minmax read_raw_mm = {0.0, 0.0, 0.0, 0}; + minmax read_open_mm = {0.0, 0.0, 0.0, 0}; + minmax read_close_mm = {0.0, 0.0, 0.0, 0}; + minmax write_open_mm = {0.0, 0.0, 0.0, 0}; + minmax write_close_mm = {0.0, 0.0, 0.0, 0}; + + raw_size = parms.num_files * (off_t)parms.num_dsets * (off_t)parms.num_bytes; + parms.io_type = iot; + print_indent(2); + output_report("IO API = "); + + switch (iot) { + case POSIXIO: + output_report("POSIX\n"); + break; + case MPIO: + output_report("MPIO\n"); + break; + case PHDF5: + output_report("PHDF5 (w/MPI-IO driver)\n"); + break; + } + + MPI_Comm_size(pio_comm_g, &comm_size); + + /* allocate space for tables minmax and that it is sufficient */ + /* to initialize all elements to zeros by calloc. */ + write_mpi_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + write_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + write_gross_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + write_raw_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + write_open_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + write_close_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + if (!parms.h5_write_only) { + read_mpi_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + read_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + read_gross_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + read_raw_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + read_open_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + read_close_mm_table = calloc((size_t)parms.num_iters , sizeof(minmax)); + } + + /* Do IO iteration times, collecting statistics each time */ + for (i = 0; i < parms.num_iters; ++i) { + double t; + + MPI_Barrier(pio_comm_g); + res = do_pio(parms); + + /* gather all of the "mpi write" times */ + t = get_time(res.timers, HDF5_MPI_WRITE); + get_minmax(&write_mpi_mm, t); + + write_mpi_mm_table[i] = write_mpi_mm; + + /* gather all of the "write" times */ + t = get_time(res.timers, HDF5_FINE_WRITE_FIXED_DIMS); + get_minmax(&write_mm, t); + + write_mm_table[i] = write_mm; + + /* gather all of the "write" times from open to close */ + t = get_time(res.timers, HDF5_GROSS_WRITE_FIXED_DIMS); + get_minmax(&write_gross_mm, t); + + write_gross_mm_table[i] = write_gross_mm; + + /* gather all of the raw "write" times */ + t = get_time(res.timers, HDF5_RAW_WRITE_FIXED_DIMS); + get_minmax(&write_raw_mm, t); + + write_raw_mm_table[i] = write_raw_mm; + + /* gather all of the file open times (time from open to first write) */ + t = get_time(res.timers, HDF5_FILE_WRITE_OPEN); + get_minmax(&write_open_mm, t); + + write_open_mm_table[i] = write_open_mm; + + /* gather all of the file close times (time from last write to close) */ + t = get_time(res.timers, HDF5_FILE_WRITE_CLOSE); + get_minmax(&write_close_mm, t); + + write_close_mm_table[i] = write_close_mm; + + if (!parms.h5_write_only) { + /* gather all of the "mpi read" times */ + t = get_time(res.timers, HDF5_MPI_READ); + get_minmax(&read_mpi_mm, t); + + read_mpi_mm_table[i] = read_mpi_mm; + + /* gather all of the "read" times */ + t = get_time(res.timers, HDF5_FINE_READ_FIXED_DIMS); + get_minmax(&read_mm, t); + + read_mm_table[i] = read_mm; + + /* gather all of the "read" times from open to close */ + t = get_time(res.timers, HDF5_GROSS_READ_FIXED_DIMS); + get_minmax(&read_gross_mm, t); + + read_gross_mm_table[i] = read_gross_mm; + + /* gather all of the raw "read" times */ + t = get_time(res.timers, HDF5_RAW_READ_FIXED_DIMS); + get_minmax(&read_raw_mm, t); + + read_raw_mm_table[i] = read_raw_mm; + + /* gather all of the file open times (time from open to first read) */ + t = get_time(res.timers, HDF5_FILE_READ_OPEN); + get_minmax(&read_open_mm, t); + + read_open_mm_table[i] = read_open_mm; + + /* gather all of the file close times (time from last read to close) */ + t = get_time(res.timers, HDF5_FILE_READ_CLOSE); + get_minmax(&read_close_mm, t); + + read_close_mm_table[i] = read_close_mm; + + } + + io_time_destroy(res.timers); + } + + /* + * Show various statistics + */ + /* Write statistics */ + /* Print the raw data throughput if desired */ + if (opts->print_raw) { + /* accumulate and output the max, min, and average "raw write" times */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Raw Data Write details:\n"); + output_all_info(write_raw_mm_table, parms.num_iters, 4); + } + + output_results(opts,"Raw Data Write",write_raw_mm_table,parms.num_iters,raw_size); + } /* end if */ + + /* show mpi write statics */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("MPI Write details:\n"); + output_all_info(write_mpi_mm_table, parms.num_iters, 4); + } + + /* We don't currently output the MPI write results */ + + /* accumulate and output the max, min, and average "write" times */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Write details:\n"); + output_all_info(write_mm_table, parms.num_iters, 4); + } + + output_results(opts,"Write",write_mm_table,parms.num_iters,raw_size); + + /* accumulate and output the max, min, and average "gross write" times */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Write Open-Close details:\n"); + output_all_info(write_gross_mm_table, parms.num_iters, 4); + } + + output_results(opts,"Write Open-Close",write_gross_mm_table,parms.num_iters,raw_size); + + if (opts->print_times) { + output_times(opts,"Write File Open",write_open_mm_table,parms.num_iters); + output_times(opts,"Write File Close",write_close_mm_table,parms.num_iters); + } + + /* Print out time from open to first write */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Write file open details:\n"); + output_all_info(write_open_mm_table, parms.num_iters, 4); + } + + /* Print out time from last write to close */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Write file close details:\n"); + output_all_info(write_close_mm_table, parms.num_iters, 4); + } + + if (!parms.h5_write_only) { + /* Read statistics */ + /* Print the raw data throughput if desired */ + if (opts->print_raw) { + /* accumulate and output the max, min, and average "raw read" times */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Raw Data Read details:\n"); + output_all_info(read_raw_mm_table, parms.num_iters, 4); + } + + output_results(opts, "Raw Data Read", read_raw_mm_table, + parms.num_iters, raw_size); + } /* end if */ + + /* show mpi read statics */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("MPI Read details:\n"); + output_all_info(read_mpi_mm_table, parms.num_iters, 4); + } + + /* We don't currently output the MPI read results */ + + /* accumulate and output the max, min, and average "read" times */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Read details:\n"); + output_all_info(read_mm_table, parms.num_iters, 4); + } + + output_results(opts, "Read", read_mm_table, parms.num_iters, raw_size); + + /* accumulate and output the max, min, and average "gross read" times */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Read Open-Close details:\n"); + output_all_info(read_gross_mm_table, parms.num_iters, 4); + } + + output_results(opts, "Read Open-Close", read_gross_mm_table,parms.num_iters, raw_size); + + if (opts->print_times) { + output_times(opts,"Read File Open",read_open_mm_table,parms.num_iters); + output_times(opts,"Read File Close",read_close_mm_table,parms.num_iters); + } + + /* Print out time from open to first read */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Read file open details:\n"); + output_all_info(read_open_mm_table, parms.num_iters, 4); + } + + /* Print out time from last read to close */ + if (pio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Read file close details:\n"); + output_all_info(read_close_mm_table, parms.num_iters, 4); + } + + } + + /* clean up our mess */ + free(write_mpi_mm_table); + free(write_mm_table); + free(write_gross_mm_table); + free(write_raw_mm_table); + free(write_open_mm_table); + free(write_close_mm_table); + + if (!parms.h5_write_only) { + free(read_mpi_mm_table); + free(read_mm_table); + free(read_gross_mm_table); + free(read_raw_mm_table); + free(read_open_mm_table); + free(read_close_mm_table); + } + + return ret_value; +} + +/* + * Function: output_all_info + * Purpose: + * Return: Nothing + * Programmer: Bill Wendling, 29. January 2002 + * Modifications: + */ +static void +output_all_info(minmax *mm, int count, int indent_level) +{ + int i; + + for (i = 0; i < count; ++i) { + print_indent(indent_level); + output_report("Iteration %d:\n", i + 1); + print_indent(indent_level + 1); + output_report("Minimum Time: %.2fs\n", mm[i].min); + print_indent(indent_level + 1); + output_report("Maximum Time: %.2fs\n", mm[i].max); + } +} + +/* + * Function: get_minmax + * Purpose: Gather all the min, max and total of val. + * Return: Nothing + * Programmer: Bill Wendling, 21. December 2001 + * Modifications: + * Use MPI_Allreduce to do it. -akc, 2002/01/11 + */ +static void +get_minmax(minmax *mm, double val) +{ + int myrank; + + MPI_Comm_rank(pio_comm_g, &myrank); + MPI_Comm_size(pio_comm_g, &mm->num); + + MPI_Allreduce(&val, &mm->max, 1, MPI_DOUBLE, MPI_MAX, pio_comm_g); + MPI_Allreduce(&val, &mm->min, 1, MPI_DOUBLE, MPI_MIN, pio_comm_g); + MPI_Allreduce(&val, &mm->sum, 1, MPI_DOUBLE, MPI_SUM, pio_comm_g); +} + +/* + * Function: accumulate_minmax_stuff + * Purpose: Accumulate the minimum, maximum, and average of the times + * across all processes. + * Return: TOTAL_MM - the total of all of these. + * Programmer: Bill Wendling, 21. December 2001 + * Modifications: + * Changed to use seconds instead of MB/s - QAK, 5/9/02 + */ +static minmax +accumulate_minmax_stuff(minmax *mm, int count) +{ + int i; + minmax total_mm; + + total_mm.sum = 0.0; + total_mm.max = -DBL_MAX; + total_mm.min = DBL_MAX; + total_mm.num = count; + + for (i = 0; i < count; ++i) { + double m = mm[i].max; + + total_mm.sum += m; + + if (m < total_mm.min) + total_mm.min = m; + + if (m > total_mm.max) + total_mm.max = m; + } + + return total_mm; +} + +/* + * Function: create_comm_world + * Purpose: Create an MPI Comm world and store it in pio_comm_g, which + * is a global variable. + * Return: SUCCESS on success. + * FAIL otherwise. + * Programmer: Bill Wendling, 19. December 2001 + * Modifications: + */ +static int +create_comm_world(int num_procs, int *doing_pio) +{ + /* MPI variables */ + int mrc; /* return values */ + int color; /* for communicator creation */ + int myrank, nprocs; + + pio_comm_g = MPI_COMM_NULL; + + /* + * Create a sub communicator for this PIO run. Easier to use the first N + * processes. + */ + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + + if (num_procs > nprocs) { + fprintf(stderr, + "number of process(%d) must be <= number of processes in MPI_COMM_WORLD(%d)\n", + num_procs, nprocs); + goto error_done; + } + + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); + color = (myrank < num_procs); + mrc = MPI_Comm_split(MPI_COMM_WORLD, color, myrank, &pio_comm_g); + + if (mrc != MPI_SUCCESS) { + fprintf(stderr, "MPI_Comm_split failed\n"); + goto error_done; + } + + if (!color) { + /* not involved in this run */ + mrc = destroy_comm_world(); + goto done; + } + + /* determine the MPI rank in the PIO communicator */ + MPI_Comm_size(pio_comm_g, &pio_mpi_nprocs_g); + MPI_Comm_rank(pio_comm_g, &pio_mpi_rank_g); + +done: + *doing_pio = color; + return SUCCESS; + +error_done: + destroy_comm_world(); + return FAIL; +} + +/* + * Function: destroy_comm_world + * Purpose: Destroy the created MPI Comm world which is stored in the + * pio_comm_g global variable. + * Return: SUCCESS on success. + * FAIL otherwise. + * Programmer: Bill Wendling, 19. December 2001 + * Modifications: + */ +static int +destroy_comm_world(void) +{ + int mrc = SUCCESS; /* return code */ + + /* release MPI resources */ + if (pio_comm_g != MPI_COMM_NULL) + mrc = (MPI_Comm_free(&pio_comm_g) == MPI_SUCCESS ? SUCCESS : FAIL); + + return mrc; +} + +/* + * Function: output_results + * Purpose: Print information about the time & bandwidth for a given + * minmax & # of iterations. + * Return: Nothing + * Programmer: Quincey Koziol, 9. May 2002 + * Modifications: + */ +static void +output_results(const struct options *opts, const char *name, minmax *table, + int table_size,off_t data_size) +{ + minmax total_mm; + + total_mm = accumulate_minmax_stuff(table, table_size); + + print_indent(3); + output_report("%s (%d iteration(s)):\n", name,table_size); + + /* Note: The maximum throughput uses the minimum amount of time & vice versa */ + + print_indent(4); + output_report("Maximum Throughput: %6.2f MB/s", MB_PER_SEC(data_size,total_mm.min)); + if(opts->print_times) + output_report(" (%7.3f s)\n", total_mm.min); + else + output_report("\n"); + + print_indent(4); + output_report("Average Throughput: %6.2f MB/s", + MB_PER_SEC(data_size,total_mm.sum / total_mm.num)); + if(opts->print_times) + output_report(" (%7.3f s)\n", (total_mm.sum / total_mm.num)); + else + output_report("\n"); + + print_indent(4); + output_report("Minimum Throughput: %6.2f MB/s", MB_PER_SEC(data_size,total_mm.max)); + if(opts->print_times) + output_report(" (%7.3f s)\n", total_mm.max); + else + output_report("\n"); + +} + +static void +output_times(const struct options *opts, const char *name, minmax *table, + int table_size) +{ + minmax total_mm; + + total_mm = accumulate_minmax_stuff(table, table_size); + + print_indent(3); + output_report("%s (%d iteration(s)):\n", name,table_size); + + /* Note: The maximum throughput uses the minimum amount of time & vice versa */ + + print_indent(4); + output_report("Minimum Accumulated Time using %d file(s): %7.5f s\n", opts->num_files,(total_mm.min)); + + print_indent(4); + output_report("Average Accumulated Time using %d file(s): %7.5f s\n", opts->num_files,(total_mm.sum / total_mm.num)); + + print_indent(4); + output_report("Maximum Accumulated Time using %d file(s): %7.5f s\n", opts->num_files,(total_mm.max)); +} + +/* + * Function: output_report + * Purpose: Print a line of the report. Only do so if I'm the 0 process. + * Return: Nothing + * Programmer: Bill Wendling, 19. December 2001 + * Modifications: + */ +static void +output_report(const char *fmt, ...) +{ + int myrank; + + MPI_Comm_rank(pio_comm_g, &myrank); + + if (myrank == 0) { + va_list ap; + + va_start(ap, fmt); + vfprintf(output, fmt, ap); + va_end(ap); + } +} + +/* + * Function: print_indent + * Purpose: Print spaces to indent a new line of text for pretty printing + * things. + * Return: Nothing + * Programmer: Bill Wendling, 29. October 2001 + * Modifications: + */ +static void +print_indent(register int indent) +{ + int myrank; + + MPI_Comm_rank(pio_comm_g, &myrank); + + if (myrank == 0) { + indent *= TAB_SPACE; + + for (; indent > 0; --indent) + fputc(' ', output); + } +} + +static void +recover_size_and_print(long long val, const char *end) +{ + if (val >= ONE_KB && (val % ONE_KB) == 0) { + if (val >= ONE_MB && (val % ONE_MB) == 0) { + if (val >= ONE_GB && (val % ONE_GB) == 0) + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""GB%s", val / ONE_GB, end); + else + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""MB%s", val / ONE_MB, end); + } else { + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""KB%s", val / ONE_KB, end); + } + } else { + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""%s", val, end); + } +} + +static void +print_io_api(long io_types) +{ + if (io_types & PIO_POSIX) + HDfprintf(output, "posix "); + if (io_types & PIO_MPI) + HDfprintf(output, "mpiio "); + if (io_types & PIO_HDF5) + HDfprintf(output, "phdf5 "); + HDfprintf(output, "\n"); +} + +static void +report_parameters(struct options *opts) +{ + int rank = comm_world_rank_g; + + print_version("HDF5 Library"); /* print library version */ + HDfprintf(output, "rank %d: ==== Parameters ====\n", rank); + + HDfprintf(output, "rank %d: IO API=", rank); + print_io_api(opts->io_types); + + HDfprintf(output, "rank %d: Number of files=%ld\n", rank, + opts->num_files); + HDfprintf(output, "rank %d: Number of datasets=%ld\n", rank, + opts->num_dsets); + HDfprintf(output, "rank %d: Number of iterations=%d\n", rank, + opts->num_iters); + HDfprintf(output, "rank %d: Number of processes=%d:%d\n", rank, + opts->min_num_procs, opts->max_num_procs); + + if (opts->dim2d){ + HDfprintf(output, "rank %d: Number of bytes per process per dataset=", rank); + recover_size_and_print((long long)(opts->num_bpp * opts->num_bpp * opts->min_num_procs), ":"); + recover_size_and_print((long long)(opts->num_bpp * opts->num_bpp * opts->max_num_procs), "\n"); + + HDfprintf(output, "rank %d: Size of dataset(s)=", rank); + recover_size_and_print((long long)(opts->num_bpp * opts->min_num_procs), "x"); + recover_size_and_print((long long)(opts->num_bpp * opts->min_num_procs), ":"); + recover_size_and_print((long long)(opts->num_bpp * opts->max_num_procs), "x"); + recover_size_and_print((long long)(opts->num_bpp * opts->max_num_procs), "\n"); + + HDfprintf(output, "rank %d: File size=", rank); + recover_size_and_print((long long)(pow(opts->num_bpp * opts->min_num_procs,2) + * opts->num_dsets), ":"); + recover_size_and_print((long long)(pow(opts->num_bpp * opts->max_num_procs,2) + * opts->num_dsets), "\n"); + + HDfprintf(output, "rank %d: Transfer buffer size=", rank); + if(opts->interleaved){ + recover_size_and_print((long long)opts->min_xfer_size, "x"); + recover_size_and_print((long long)opts->blk_size, ":"); + recover_size_and_print((long long)opts->max_xfer_size, "x"); + recover_size_and_print((long long)opts->blk_size, "\n"); + } + else{ + recover_size_and_print((long long)opts->blk_size, "x"); + recover_size_and_print((long long)opts->min_xfer_size, ":"); + recover_size_and_print((long long)opts->blk_size, "x"); + recover_size_and_print((long long)opts->max_xfer_size, "\n"); + } + HDfprintf(output, "rank %d: Block size=", rank); + recover_size_and_print((long long)opts->blk_size, "x"); + recover_size_and_print((long long)opts->blk_size, "\n"); + } + else{ + HDfprintf(output, "rank %d: Number of bytes per process per dataset=", rank); + recover_size_and_print((long long)opts->num_bpp, "\n"); + + HDfprintf(output, "rank %d: Size of dataset(s)=", rank); + recover_size_and_print((long long)(opts->num_bpp * opts->min_num_procs), ":"); + recover_size_and_print((long long)(opts->num_bpp * opts->max_num_procs), "\n"); + + HDfprintf(output, "rank %d: File size=", rank); + recover_size_and_print((long long)(opts->num_bpp * opts->min_num_procs + * opts->num_dsets), ":"); + recover_size_and_print((long long)(opts->num_bpp * opts->max_num_procs + * opts->num_dsets), "\n"); + + HDfprintf(output, "rank %d: Transfer buffer size=", rank); + recover_size_and_print((long long)opts->min_xfer_size, ":"); + recover_size_and_print((long long)opts->max_xfer_size, "\n"); + HDfprintf(output, "rank %d: Block size=", rank); + recover_size_and_print((long long)opts->blk_size, "\n"); + } + + HDfprintf(output, "rank %d: Block Pattern in Dataset=", rank); + if(opts->interleaved) + HDfprintf(output, "Interleaved\n"); + else + HDfprintf(output, "Contiguous\n"); + + HDfprintf(output, "rank %d: I/O Method for MPI and HDF5=", rank); + if(opts->collective) + HDfprintf(output, "Collective\n"); + else + HDfprintf(output, "Independent\n"); + + HDfprintf(output, "rank %d: Geometry=", rank); + if(opts->dim2d) + HDfprintf(output, "2D\n"); + else + HDfprintf(output, "1D\n"); + + HDfprintf(output, "rank %d: VFL used for HDF5 I/O=%s\n", rank, "MPI-IO driver"); + + HDfprintf(output, "rank %d: Data storage method in HDF5=", rank); + if(opts->h5_use_chunks) + HDfprintf(output, "Chunked\n"); + else + HDfprintf(output, "Contiguous\n"); + + { + char *prefix = getenv("HDF5_PARAPREFIX"); + + HDfprintf(output, "rank %d: Env HDF5_PARAPREFIX=%s\n", rank, + (prefix ? prefix : "not set")); + } + + HDfprintf(output, "rank %d: ", rank); + h5_dump_info_object(h5_io_info_g); + + HDfprintf(output, "rank %d: ==== End of Parameters ====\n", rank); + HDfprintf(output, "\n"); +} + +/* + * Function: parse_command_line + * Purpose: Parse the command line options and return a STRUCT OPTIONS + * structure which will need to be freed by the calling function. + * Return: Pointer to an OPTIONS structure + * Programmer: Bill Wendling, 31. October 2001 + * Modifications: + * Added 2D testing (Christian Chilan, 10. August 2005) + */ +static struct options * +parse_command_line(int argc, char *argv[]) +{ + register int opt; + struct options *cl_opts; + + cl_opts = (struct options *)malloc(sizeof(struct options)); + + cl_opts->output_file = NULL; + cl_opts->io_types = 0; /* will set default after parsing options */ + cl_opts->num_dsets = 1; + cl_opts->num_files = 1; + cl_opts->num_bpp = 0; + cl_opts->num_iters = 1; + cl_opts->max_num_procs = comm_world_nprocs_g; + cl_opts->min_num_procs = 1; + cl_opts->max_xfer_size = 0; + cl_opts->min_xfer_size = 0; + cl_opts->blk_size = 0; + cl_opts->interleaved = 0; /* Default to contiguous blocks in dataset */ + cl_opts->collective = 0; /* Default to independent I/O access */ + cl_opts->dim2d = 0; /* Default to 1D */ + cl_opts->print_times = FALSE; /* Printing times is off by default */ + cl_opts->print_raw = FALSE; /* Printing raw data throughput is off by default */ + cl_opts->h5_alignment = 1; /* No alignment for HDF5 objects by default */ + cl_opts->h5_threshold = 1; /* No threshold for aligning HDF5 objects by default */ + cl_opts->h5_use_chunks = FALSE; /* Don't chunk the HDF5 dataset by default */ + cl_opts->h5_write_only = FALSE; /* Do both read and write by default */ + cl_opts->verify = FALSE; /* No Verify data correctness by default */ + + while ((opt = get_option(argc, (const char **)argv, s_opts, l_opts)) != EOF) { + switch ((char)opt) { + case 'a': + cl_opts->h5_alignment = parse_size_directive(opt_arg); + break; + case 'A': + { + const char *end = opt_arg; + + while (end && *end != '\0') { + char buf[10]; + int i; + + memset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + if (!HDstrcasecmp(buf, "phdf5")) { + cl_opts->io_types |= PIO_HDF5; + } else if (!HDstrcasecmp(buf, "mpiio")) { + cl_opts->io_types |= PIO_MPI; + } else if (!HDstrcasecmp(buf, "posix")) { + cl_opts->io_types |= PIO_POSIX; + } else { + fprintf(stderr, "pio_perf: invalid --api option %s\n", + buf); + exit(EXIT_FAILURE); + } + + if (*end == '\0') + break; + + end++; + } + } + + break; +#if 0 + case 'b': + /* the future "binary" option */ + break; +#endif /* 0 */ + case 'B': + cl_opts->blk_size = parse_size_directive(opt_arg); + break; + case 'c': + /* Turn on chunked HDF5 dataset creation */ + cl_opts->h5_use_chunks = TRUE; + break; + case 'C': + cl_opts->collective = 1; + break; + case 'd': + cl_opts->num_dsets = atoi(opt_arg); + break; + case 'D': + { + const char *end = opt_arg; + + while (end && *end != '\0') { + char buf[10]; + int i; + + memset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + if (strlen(buf) > 1 || isdigit(buf[0])) { + size_t j; + + for (j = 0; j < 10 && buf[j] != '\0'; ++j) + if (!isdigit(buf[j])) { + fprintf(stderr, "pio_perf: invalid --debug option %s\n", + buf); + exit(EXIT_FAILURE); + } + + pio_debug_level = atoi(buf); + + if (pio_debug_level > 4) + pio_debug_level = 4; + else if (pio_debug_level < 0) + pio_debug_level = 0; + } else { + switch (*buf) { + case 'r': + /* Turn on raw data throughput info */ + cl_opts->print_raw = TRUE; + break; + case 't': + /* Turn on time printing */ + cl_opts->print_times = TRUE; + break; + case 'v': + /* Turn on verify data correctness*/ + cl_opts->verify = TRUE; + break; + default: + fprintf(stderr, "pio_perf: invalid --debug option %s\n", buf); + exit(EXIT_FAILURE); + } + } + + if (*end == '\0') + break; + + end++; + } + } + + break; + case 'e': + cl_opts->num_bpp = parse_size_directive(opt_arg); + break; + case 'F': + cl_opts->num_files = atoi(opt_arg); + break; + case 'g': + cl_opts->dim2d = 1; + break; + case 'i': + cl_opts->num_iters = atoi(opt_arg); + break; + case 'I': + cl_opts->interleaved = 1; + break; + case 'o': + cl_opts->output_file = opt_arg; + break; + case 'p': + cl_opts->min_num_procs = atoi(opt_arg); + break; + case 'P': + cl_opts->max_num_procs = atoi(opt_arg); + break; + case 'T': + cl_opts->h5_threshold = parse_size_directive(opt_arg); + break; + case 'w': + cl_opts->h5_write_only = TRUE; + break; + case 'x': + cl_opts->min_xfer_size = parse_size_directive(opt_arg); + break; + case 'X': + cl_opts->max_xfer_size = parse_size_directive(opt_arg); + break; + case 'h': + case '?': + default: + usage(progname); + free(cl_opts); + return NULL; + } + } + + + if (cl_opts->num_bpp == 0){ + if (cl_opts->dim2d == 0) + cl_opts->num_bpp = 256 * ONE_KB; + else + cl_opts->num_bpp = 8 * ONE_KB; + } + + if (cl_opts->max_xfer_size == 0) + cl_opts->max_xfer_size = cl_opts->num_bpp; + + if (cl_opts->min_xfer_size == 0) + cl_opts->min_xfer_size = (cl_opts->num_bpp)/2; + + if (cl_opts->blk_size == 0) + cl_opts->blk_size = (cl_opts->num_bpp)/2; + + + /* set default if none specified yet */ + if (!cl_opts->io_types) + cl_opts->io_types = PIO_HDF5 | PIO_MPI | PIO_POSIX; /* run all API */ + + /* verify parameters sanity. Adjust if needed. */ + /* cap xfer_size with bytes per process */ + if (!cl_opts->dim2d) { + if (cl_opts->min_xfer_size > cl_opts->num_bpp) + cl_opts->min_xfer_size = cl_opts->num_bpp; + if (cl_opts->max_xfer_size > cl_opts->num_bpp) + cl_opts->max_xfer_size = cl_opts->num_bpp; + } + if (cl_opts->min_xfer_size > cl_opts->max_xfer_size) + cl_opts->min_xfer_size = cl_opts->max_xfer_size; + if (cl_opts->blk_size > cl_opts->num_bpp ) + cl_opts->blk_size = cl_opts->num_bpp; + /* check range of number of processes */ + if (cl_opts->min_num_procs <= 0) + cl_opts->min_num_procs = 1; + if (cl_opts->max_num_procs <= 0) + cl_opts->max_num_procs = 1; + if (cl_opts->min_num_procs > cl_opts->max_num_procs) + cl_opts->min_num_procs = cl_opts->max_num_procs; + /* check iteration */ + if (cl_opts->num_iters <= 0) + cl_opts->num_iters = 1; + + return cl_opts; +} + +/* + * Function: parse_size_directive + * Purpose: Parse the size directive passed on the commandline. The size + * directive is an integer followed by a size indicator: + * + * K, k - Kilobyte + * M, m - Megabyte + * G, g - Gigabyte + * + * Return: The size as a off_t because this is related to file size. + * If an unknown size indicator is used, then the program will + * exit with EXIT_FAILURE as the return value. + * Programmer: Bill Wendling, 18. December 2001 + * Modifications: + */ +static off_t +parse_size_directive(const char *size) +{ + off_t s; + char *endptr; + + s = strtol(size, &endptr, 10); + + if (endptr && *endptr) { + while (*endptr != '\0' && (*endptr == ' ' || *endptr == '\t')) + ++endptr; + + switch (*endptr) { + case 'K': + case 'k': + s *= ONE_KB; + break; + case 'M': + case 'm': + s *= ONE_MB; + break; + case 'G': + case 'g': + s *= ONE_GB; + break; + default: + fprintf(stderr, "Illegal size specifier '%c'\n", *endptr); + exit(EXIT_FAILURE); + } + } + + return s; +} + +/* + * Function: usage + * Purpose: Print a usage message and then exit. + * Return: Nothing + * Programmer: Bill Wendling, 31. October 2001 + * Modifications: + * Added 2D testing (Christian Chilan, 10. August 2005) + */ +static void +usage(const char *prog) +{ + int myrank; + + MPI_Comm_rank(pio_comm_g, &myrank); + + if (myrank == 0) { + print_version(prog); + printf("usage: %s [OPTIONS]\n", prog); + printf(" OPTIONS\n"); + printf(" -h, --help Print a usage message and exit\n"); + printf(" -a S, --align=S Alignment of objects in HDF5 file [default: 1]\n"); + printf(" -A AL, --api=AL Which APIs to test [default: all of them]\n"); +#if 0 + printf(" -b, --binary The elusive binary option\n"); +#endif /* 0 */ + printf(" -B S, --block-size=S Block size within transfer buffer\n"); + printf(" (see below for description)\n"); + printf(" [default: half the number of bytes per process\n"); + printf(" per dataset]\n"); + printf(" -c, --chunk Create HDF5 datasets using chunked storage\n"); + printf(" [default: contiguous storage]\n"); + printf(" -C, --collective Use collective I/O for MPI and HDF5 APIs\n"); + printf(" [default: independent I/O)\n"); + printf(" -d N, --num-dsets=N Number of datasets per file [default: 1]\n"); + printf(" -D DL, --debug=DL Indicate the debugging level\n"); + printf(" [default: no debugging]\n"); + printf(" -e S, --num-bytes=S Number of bytes per process per dataset\n"); + printf(" (see below for description)\n"); + printf(" [default: 256K for 1D, 8K for 2D]\n"); + printf(" -F N, --num-files=N Number of files [default: 1]\n"); + printf(" -g, --geometry Use 2D geometry [default: 1D geometry]\n"); + printf(" -i N, --num-iterations=N Number of iterations to perform [default: 1]\n"); + printf(" -I, --interleaved Interleaved access pattern\n"); + printf(" (see below for example)\n"); + printf(" [default: Contiguous access pattern]\n"); + printf(" -o F, --output=F Output raw data into file F [default: none]\n"); + printf(" -p N, --min-num-processes=N Minimum number of processes to use [default: 1]\n"); + printf(" -P N, --max-num-processes=N Maximum number of processes to use\n"); + printf(" [default: all MPI_COMM_WORLD processes ]\n"); + printf(" -T S, --threshold=S Threshold for alignment of objects in HDF5 file\n"); + printf(" [default: 1]\n"); + printf(" -w, --write-only Perform write tests not the read tests\n"); + printf(" -x S, --min-xfer-size=S Minimum transfer buffer size\n"); + printf(" (see below for description)\n"); + printf(" [default: half the number of bytes per process\n"); + printf(" per dataset]\n"); + printf(" -X S, --max-xfer-size=S Maximum transfer buffer size\n"); + printf(" [default: the number of bytes per process per\n"); + printf(" dataset]\n"); + printf("\n"); + printf(" F - is a filename.\n"); + printf(" N - is an integer >=0.\n"); + printf(" S - is a size specifier, an integer >=0 followed by a size indicator:\n"); + printf(" K - Kilobyte (%d)\n", ONE_KB); + printf(" M - Megabyte (%d)\n", ONE_MB); + printf(" G - Gigabyte (%d)\n", ONE_GB); + printf("\n"); + printf(" Example: '37M' is 37 megabytes or %d bytes\n", 37*ONE_MB); + printf("\n"); + printf(" AL - is an API list. Valid values are:\n"); + printf(" phdf5 - Parallel HDF5\n"); + printf(" mpiio - MPI-I/O\n"); + printf(" posix - POSIX\n"); + printf("\n"); + printf(" Example: --api=mpiio,phdf5\n"); + printf("\n"); + printf(" Dataset size:\n"); + printf(" Depending on the selected geometry, each test dataset is either a linear\n"); + printf(" array of size bytes-per-process * num-processes, or a square array of size\n"); + printf(" (bytes-per-process * num-processes) x (bytes-per-process * num-processes).\n"); + printf("\n"); + printf(" Block size vs. Transfer buffer size:\n"); + printf(" buffer-size controls the size of the memory buffer, which is broken into\n"); + printf(" blocks and written to the file. Depending on the selected geometry, each\n"); + printf(" block can be a linear array of size block-size or a square array of size\n"); + printf(" block-size x block-size. The arrangement in which blocks are written is\n"); + printf(" determined by the access pattern.\n"); + printf("\n"); + printf(" In 1D geometry, the transfer buffer is a linear array of size buffer-size.\n"); + printf(" In 2D geometry, it is a rectangular array of size block-size x buffer-size\n"); + printf(" or buffer-size x block-size if interleaved pattern is selected.\n"); + printf("\n"); + printf(" Interleaved and Contiguous patterns in 1D geometry:\n"); + printf(" When contiguous access pattern is chosen, the dataset is evenly divided\n"); + printf(" into num-processes regions and each process writes data to its own region.\n"); + printf(" When interleaved blocks are written to a dataset, space for the first\n"); + printf(" block of the first process is allocated in the dataset, then space is\n"); + printf(" allocated for the first block of the second process, etc. until space is\n"); + printf(" allocated for the first block of each process, then space is allocated for\n"); + printf(" the second block of the first process, the second block of the second\n"); + printf(" process, etc.\n"); + printf("\n"); + printf(" For example, with a 3 process run, 512KB bytes-per-process, 256KB transfer\n"); + printf(" buffer size, and 64KB block size, each process must issue 2 transfer\n"); + printf(" requests to complete access to the dataset.\n"); + printf(" Contiguous blocks of the first transfer request are written like so:\n"); + printf(" 1111----2222----3333----\n"); + printf(" Interleaved blocks of the first transfer request are written like so:\n"); + printf(" 123123123123------------\n"); + printf(" The actual number of I/O operations involved in a transfer request\n"); + printf(" depends on the access pattern and communication mode.\n"); + printf(" When using independent I/O with interleaved pattern, each process\n"); + printf(" performs 4 small non-contiguous I/O operations per transfer request.\n"); + printf(" If collective I/O is turned on, the combined content of the buffers of\n"); + printf(" the 3 processes will be written using one collective I/O operation\n"); + printf(" per transfer request.\n"); + printf("\n"); + printf(" For information about access patterns in 2D geometry, please refer to the\n"); + printf(" HDF5 Reference Manual.\n"); + printf("\n"); + printf(" DL - is a list of debugging flags. Valid values are:\n"); + printf(" 1 - Minimal\n"); + printf(" 2 - Not quite everything\n"); + printf(" 3 - Everything\n"); + printf(" 4 - The kitchen sink\n"); + printf(" r - Raw data I/O throughput information\n"); + printf(" t - Times as well as throughputs\n"); + printf(" v - Verify data correctness\n"); + printf("\n"); + printf(" Example: --debug=2,r,t\n"); + printf("\n"); + printf(" Environment variables:\n"); + printf(" HDF5_NOCLEANUP Do not remove data files if set [default remove]\n"); + printf(" HDF5_MPI_INFO MPI INFO object key=value separated by ;\n"); + printf(" HDF5_PARAPREFIX Paralllel data files prefix\n"); + fflush(stdout); + } /* end if */ +} /* end usage() */ + +#else /* H5_HAVE_PARALLEL */ + +/* + * Function: main + * Purpose: Dummy main() function for if HDF5 was configured without + * parallel stuff. + * Return: EXIT_SUCCESS + * Programmer: Bill Wendling, 14. November 2001 + */ +int +main(void) +{ + printf("No parallel IO performance because parallel is not configured\n"); + return EXIT_SUCCESS; +} /* end main */ + +#endif /* !H5_HAVE_PARALLEL */ diff --git a/tools/test/perform/pio_perf.h b/tools/test/perform/pio_perf.h new file mode 100644 index 0000000..0fff7a3 --- /dev/null +++ b/tools/test/perform/pio_perf.h @@ -0,0 +1,105 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef PIO_PERF_H__ +#define PIO_PERF_H__ + +#ifndef STANDALONE +#include "H5private.h" +#include "h5test.h" +#include "h5tools.h" +#include "h5tools_utils.h" +#else +#include "pio_standalone.h" +#endif +#include "io_timer.h" + +/* setup the dataset no fill option if this is v1.5 or more */ +#if H5_VERS_MAJOR > 1 || H5_VERS_MINOR > 4 +#define H5_HAVE_NOFILL 1 +#endif + +typedef enum iotype_ { + POSIXIO, + MPIO, + PHDF5 + /*NUM_TYPES*/ +} iotype; + +typedef struct parameters_ { + iotype io_type; /* The type of IO test to perform */ + int num_procs; /* Maximum number of processes to use */ + long num_files; /* Number of files to create */ + long num_dsets; /* Number of datasets to create */ + off_t num_bytes; /* Number of bytes in each dset */ + int num_iters; /* Number of times to loop doing the IO */ + size_t buf_size; /* Buffer size */ + size_t blk_size; /* Block size */ + unsigned interleaved; /* Interleaved vs. contiguous blocks */ + unsigned collective; /* Collective vs. independent I/O */ + unsigned dim2d; /* 1D vs. 2D */ + hsize_t h5_align; /* HDF5 object alignment */ + hsize_t h5_thresh; /* HDF5 object alignment threshold */ + int h5_use_chunks; /* Make HDF5 dataset chunked */ + int h5_write_only; /* Perform the write tests only */ + int verify; /* Verify data correctness */ +} parameters; + +typedef struct results_ { + herr_t ret_code; + io_time_t *timers; +} results; + +#ifndef SUCCESS +#define SUCCESS 0 +#endif /* !SUCCESS */ + +#ifndef FAIL +#define FAIL -1 +#endif /* !FAIL */ + +extern FILE *output; /* output file */ +extern io_time_t *timer_g; /* timer: global for stub functions */ +extern int comm_world_rank_g; /* my rank in MPI_COMM_RANK */ +extern int comm_world_nprocs_g;/* num. of processes of MPI_COMM_WORLD */ +extern MPI_Comm pio_comm_g; /* Communicator to run the PIO */ +extern int pio_mpi_rank_g; /* MPI rank of pio_comm_g */ +extern int pio_mpi_nprocs_g; /* number of processes of pio_comm_g */ +extern int pio_debug_level; /* The debug level: + * 0 - Off + * 1 - Minimal + * 2 - Some more + * 3 - Maximal + * 4 - Even More Debugging (timer stuff) + */ + +#define HDprint_rank(f) /* print rank in MPI_COMM_WORLD */ \ + HDfprintf(f, "%d: ", comm_world_rank_g); +#define HDprint_size(f) /* print size of MPI_COMM_WORLD */ \ + HDfprintf(f, "%d", comm_world_nprocs_g); +#define HDprint_rank_size(f) /* print rank/size of MPI_COMM_WORLD */ \ + HDfprintf(f, "%d/%d: ", comm_world_rank_g, comm_world_nprocs_g); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern results do_pio(parameters param); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PIO_PERF_H__ */ diff --git a/tools/test/perform/pio_standalone.c b/tools/test/perform/pio_standalone.c new file mode 100644 index 0000000..e404274 --- /dev/null +++ b/tools/test/perform/pio_standalone.c @@ -0,0 +1,282 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +/* This file contains the definition of functions required to build h5perf in + * STANDALONE mode. + * Created: Christian Chilan, 2005/5/18. + */ + +#include "pio_perf.h" + + +/** From h5tools_utils.c **/ + +/* global variables */ +int nCols = 80; + +/* ``get_option'' variables */ +int opt_err = 1; /*get_option prints errors if this is on */ +int opt_ind = 1; /*token pointer */ +const char *opt_arg; /*flag argument (or value) */ + + +int +get_option(int argc, const char **argv, const char *opts, const struct long_options *l_opts) +{ + static int sp = 1; /* character index in current token */ + int opt_opt = '?'; /* option character passed back to user */ + + if (sp == 1) { + /* check for more flag-like tokens */ + if (opt_ind >= argc || argv[opt_ind][0] != '-' || argv[opt_ind][1] == '\0') { + return EOF; + } else if (HDstrcmp(argv[opt_ind], "--") == 0) { + opt_ind++; + return EOF; + } + } + + if (sp == 1 && argv[opt_ind][0] == '-' && argv[opt_ind][1] == '-') { + /* long command line option */ + const char *arg = &argv[opt_ind][2]; + int i; + + for (i = 0; l_opts && l_opts[i].name; i++) { + size_t len = HDstrlen(l_opts[i].name); + + if (HDstrncmp(arg, l_opts[i].name, len) == 0) { + /* we've found a matching long command line flag */ + opt_opt = l_opts[i].shortval; + + if (l_opts[i].has_arg != no_arg) { + if (arg[len] == '=') { + opt_arg = &arg[len + 1]; + } else if (opt_ind < (argc - 1) && argv[opt_ind + 1][0] != '-') { + opt_arg = argv[++opt_ind]; + } else if (l_opts[i].has_arg == require_arg) { + if (opt_err) + HDfprintf(stderr, + "%s: option required for \"--%s\" flag\n", + argv[0], arg); + + opt_opt = '?'; + } + } else { + if (arg[len] == '=') { + if (opt_err) + HDfprintf(stderr, + "%s: no option required for \"%s\" flag\n", + argv[0], arg); + + opt_opt = '?'; + } + + opt_arg = NULL; + } + + break; + } + } + + if (l_opts[i].name == NULL) { + /* exhausted all of the l_opts we have and still didn't match */ + if (opt_err) + HDfprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], arg); + + opt_opt = '?'; + } + + opt_ind++; + sp = 1; + } else { + register char *cp; /* pointer into current token */ + + /* short command line option */ + opt_opt = argv[opt_ind][sp]; + + if (opt_opt == ':' || (cp = strchr(opts, opt_opt)) == 0) { + + if (opt_err) + HDfprintf(stderr, "%s: unknown option \"%c\"\n", + argv[0], opt_opt); + + /* if no chars left in this token, move to next token */ + if (argv[opt_ind][++sp] == '\0') { + opt_ind++; + sp = 1; + } + + return '?'; + } + + if (*++cp == ':') { + /* if a value is expected, get it */ + if (argv[opt_ind][sp + 1] != '\0') { + /* flag value is rest of current token */ + opt_arg = &argv[opt_ind++][sp + 1]; + } else if (++opt_ind >= argc) { + if (opt_err) + HDfprintf(stderr, + "%s: value expected for option \"%c\"\n", + argv[0], opt_opt); + + opt_opt = '?'; + } else { + /* flag value is next token */ + opt_arg = argv[opt_ind++]; + } + + sp = 1; + } else { + /* set up to look at next char in token, next time */ + if (argv[opt_ind][++sp] == '\0') { + /* no more in current token, so setup next token */ + opt_ind++; + sp = 1; + } + + opt_arg = NULL; + } + } + + /* return the current flag character found */ + return opt_opt; +} + + +void +print_version(const char *progname) +{ + printf("%s: Version %u.%u.%u%s%s\n", + progname, H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE, + H5_VERS_SUBRELEASE[0] ? "-" : "", H5_VERS_SUBRELEASE); +} + + + +/** From h5test.c **/ + +#ifdef H5_HAVE_PARALLEL +MPI_Info h5_io_info_g=MPI_INFO_NULL;/* MPI INFO object for IO */ +#endif + +int +h5_set_info_object(void) +{ + char *envp; /* environment pointer */ + int ret_value=0; + + /* handle any MPI INFO hints via $HDF5_MPI_INFO */ + if ((envp = getenv("HDF5_MPI_INFO")) != NULL){ + char *next, *valp; + + + valp = envp = next = HDstrdup(envp); + + /* create an INFO object if not created yet */ + if (h5_io_info_g == MPI_INFO_NULL) + MPI_Info_create(&h5_io_info_g); + + do { + size_t len; + char *key_val, *endp, *namep; + + if (*valp == ';') + valp++; + + /* copy key/value pair into temporary buffer */ + len = strcspn(valp, ";"); + next = &valp[len]; + key_val = calloc(1, len + 1); + + /* increment the next pointer past the terminating semicolon */ + if (*next == ';') + ++next; + + namep = HDstrncpy(key_val, valp, len); + + /* pass up any beginning whitespaces */ + while (*namep && (*namep == ' ' || *namep == '\t')) + namep++; + + /* eat up any ending white spaces */ + endp = &namep[strlen(namep) - 1]; + + while (endp && (*endp == ' ' || *endp == '\t')) + *endp-- = '\0'; + + /* find the '=' */ + + valp = HDstrchr(namep, '='); + + if (valp != NULL) { /* it's a valid key/value pairing */ + char *tmp_val = valp + 1; + + /* change '=' to \0, move valp down one */ + *valp-- = '\0'; + + /* eat up ending whitespace on the "key" part */ + while (*valp == ' ' || *valp == '\t') + *valp-- = '\0'; + + valp = tmp_val; + + /* eat up beginning whitespace on the "value" part */ + while (*valp == ' ' || *valp == '\t') + *valp++ = '\0'; + + /* actually set the darned thing */ + if (MPI_SUCCESS != MPI_Info_set(h5_io_info_g, namep, valp)) { + printf("MPI_Info_set failed\n"); + ret_value = -1; + } + } + + valp = next; + HDfree(key_val); + } while (next && *next); + + HDfree(envp); + } + + return ret_value; +} + + +void +h5_dump_info_object(MPI_Info info) +{ + char key[MPI_MAX_INFO_KEY+1]; + char value[MPI_MAX_INFO_VAL+1]; + int flag; + int i, nkeys; + + printf("Dumping MPI Info Object(%d) (up to %d bytes per item):\n", (int)info, + MPI_MAX_INFO_VAL); + if (info==MPI_INFO_NULL){ + printf("object is MPI_INFO_NULL\n"); + } + else { + MPI_Info_get_nkeys(info, &nkeys); + printf("object has %d items\n", nkeys); + for (i=0; i<nkeys; i++){ + MPI_Info_get_nthkey(info, i, key); + MPI_Info_get(info, key, MPI_MAX_INFO_VAL, value, &flag); + printf("%s=%s\n", key, value); + } + + } +} diff --git a/tools/test/perform/pio_standalone.h b/tools/test/perform/pio_standalone.h new file mode 100644 index 0000000..762a564 --- /dev/null +++ b/tools/test/perform/pio_standalone.h @@ -0,0 +1,157 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef PIO_STANDALONE_H__ +#define PIO_PERF_H__ + +/* Header file for building h5perf by standalone mode. + * Created: Christian Chilan, 2005/5/18. + */ + +/** From H5private.h **/ + +#include "H5public.h" /* Include Public Definitions */ + + +/* + * Include ANSI-C header files. + */ +#ifdef H5_STDC_HEADERS +# include <assert.h> +# include <ctype.h> +# include <errno.h> +# include <fcntl.h> +# include <float.h> +# include <limits.h> +# include <math.h> +# include <signal.h> +# include <stdarg.h> +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +#endif + +/* + * And now for a couple non-Posix functions... Watch out for systems that + * define these in terms of macros. + */ +#ifdef H5_HAVE_WIN32_API +#define HDstrdup(S) _strdup(S) +#else /* H5_HAVE_WIN32_API */ + +#if !defined strdup && !defined H5_HAVE_STRDUP +extern char *strdup(const char *s); +#endif + +#define HDstrdup(S) strdup(S) + +#endif /* H5_HAVE_WIN32_API */ + +H5_DLL int HDfprintf (FILE *stream, const char *fmt, ...); +#define HDstrcmp(S,T) strcmp(S,T) +#define HDstrlen(S) strlen(S) +#define HDstrncmp(S,T,L) strncmp(S,T,L) +#define HDstrncpy(X,Y,Z) strncpy(X,Y,Z) +#define HDstrchr(S,C) strchr(S,C) +#define HDfree(M) free(M) + + +#ifdef _O_BINARY +#define HDopen(S,F,M) open(S,F|_O_BINARY,M) +#else +#define HDopen(S,F,M) open(S,F,M) +#endif +#define HDclose(F) close(F) + +#ifdef H5_HAVE_WIN32_API +#define HDlseek(F,O,W) _lseeki64(F,O,W) +#else +#define HDlseek(F,O,W) lseek(F,O,W) +#endif + +#define HDwrite(F,M,Z) write(F,M,Z) + +#define HDread(F,M,Z) read(F,M,Z) + +#ifdef H5_HAVE_WIN32_API + #define HDstat(S,B) _stati64(S,B) +#else +#define HDstat(S,B) stat(S,B) +#endif + +#ifdef H5_HAVE_WIN32_API +#define HDfstat(F,B) _fstati64(F,B) +typedef struct _stati64 h5_stat_t; +typedef __int64 h5_stat_size_t; +#else +#define HDfstat(F,B) fstat(F,B) +typedef struct stat h5_stat_t; +typedef off_t h5_stat_size_t; +#endif + +/* + * HDF Boolean type. + */ +#ifndef FALSE +# define FALSE false +#endif +#ifndef TRUE +# define TRUE true +#endif + + +/** From h5test.h **/ + +#ifdef H5_HAVE_PARALLEL +extern MPI_Info h5_io_info_g; /* MPI INFO object for IO */ +#endif + +#ifdef H5_HAVE_PARALLEL +H5TEST_DLL int h5_set_info_object(void); +H5TEST_DLL void h5_dump_info_object(MPI_Info info); +#endif + + + +/** From h5tools_utils.h **/ + +extern int opt_err; /* getoption prints errors if this is on */ +extern int opt_ind; /* token pointer */ +extern const char *opt_arg; /* flag argument (or value) */ + + +enum { + no_arg = 0, /* doesn't take an argument */ + require_arg, /* requires an argument */ + optional_arg /* argument is optional */ +}; + + +typedef struct long_options { + const char *name; /* name of the long option */ + int has_arg; /* whether we should look for an arg */ + char shortval; /* the shortname equivalent of long arg + * this gets returned from get_option */ +} long_options; + +extern int get_option(int argc, const char **argv, const char *opt, + const struct long_options *l_opt); + +extern int nCols; /*max number of columns for outputting */ + +/* Definitions of useful routines */ +extern void print_version(const char *progname); + +#endif diff --git a/tools/test/perform/sio_engine.c b/tools/test/perform/sio_engine.c new file mode 100644 index 0000000..abf1fa6 --- /dev/null +++ b/tools/test/perform/sio_engine.c @@ -0,0 +1,1313 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Author: Christian Chilan, April 2008 + */ + +#include "hdf5.h" + +#ifdef H5_STDC_HEADERS +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#endif + +#ifdef H5_HAVE_UNISTD_H +#include <sys/types.h> +#include <unistd.h> +#endif + +#ifdef H5_HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#include "sio_perf.h" + +/* Macro definitions */ + +/* sizes of various items. these sizes won't change during program execution */ +#define ELMT_H5_TYPE H5T_NATIVE_UCHAR + +#define GOTOERROR(errcode) { ret_code = errcode; goto done; } +#define ERRMSG(mesg) { \ + fprintf(stderr, "*** Assertion failed (%s) at line %4d in %s\n", \ + mesg, (int)__LINE__, __FILE__); \ +} + +/* verify: if val is false (0), print mesg. */ +#define VRFY(val, mesg) do { \ + if (!val) { \ + ERRMSG(mesg); \ + GOTOERROR(FAIL); \ + } \ +} while(0) + +/* POSIX I/O macros */ +#define POSIXCREATE(fn) HDopen(fn, O_CREAT|O_TRUNC|O_RDWR, 0600) +#define POSIXOPEN(fn, F) HDopen(fn, F, 0600) +#define POSIXCLOSE(F) HDclose(F) +#define POSIXSEEK(F,L) HDlseek(F, L, SEEK_SET) +#define POSIXWRITE(F,B,S) HDwrite(F,B,S) +#define POSIXREAD(F,B,S) HDread(F,B,S) + +enum { + SIO_CREATE = 1, + SIO_WRITE = 2, + SIO_READ = 4 +}; + +/* Global variables */ +static int clean_file_g = -1; /*whether to cleanup temporary test */ +/*files. -1 is not defined; */ +/*0 is no cleanup; 1 is do cleanup */ + +/* the different types of file descriptors we can expect */ +typedef union _file_descr { + int posixfd; /* POSIX file handle*/ + hid_t h5fd; /* HDF5 file */ +} file_descr; + +/* local functions */ +static char *sio_create_filename(iotype iot, const char *base_name, + char *fullname, size_t size, parameters *param); +static herr_t do_write(results *res, file_descr *fd, parameters *parms, void *buffer); +static herr_t do_read(results *res, file_descr *fd, parameters *parms, void *buffer); +static herr_t dset_write(int local_dim, file_descr *fd, parameters *parms, void *buffer); +static herr_t posix_buffer_write(int local_dim, file_descr *fd, parameters *parms, void *buffer); +static herr_t dset_read(int localrank, file_descr *fd, parameters *parms, void *buffer, const char *buffer2); +static herr_t posix_buffer_read(int local_dim, file_descr *fd, parameters *parms, void *buffer); +static herr_t do_fopen(parameters *param, char *fname, file_descr *fd /*out*/, + int flags); +hid_t set_vfd(parameters *param); +static herr_t do_fclose(iotype iot, file_descr *fd); +static void do_cleanupfile(iotype iot, char *fname); + +/* global variables */ +static HDoff_t offset[MAX_DIMS]; /* dataset size in bytes */ +static size_t buf_offset[MAX_DIMS]; /* dataset size in bytes */ +static int order[MAX_DIMS]; /* dimension access order */ +static size_t linear_buf_size; /* linear buffer size */ +static int cont_dim; /* lowest dimension for contiguous POSIX + access */ +static size_t cont_size; /* size of contiguous POSIX access */ +static hid_t fapl; /* file access list */ +static unsigned char *buf_p; /* buffer pointer */ +static const char *multi_letters = "msbrglo"; /* string for multi driver */ + +/* HDF5 global variables */ +static hsize_t h5count[MAX_DIMS]; /*selection count */ +static hssize_t h5offset[MAX_DIMS]; /* Selection offset within dataspace */ +static hid_t h5dset_space_id = -1; /*dataset space ID */ +static hid_t h5mem_space_id = -1; /*memory dataspace ID */ +static hid_t h5ds_id = -1; /*dataset handle */ +static hid_t h5dcpl = -1; /* Dataset creation property list */ +static hid_t h5dxpl = -1; /* Dataset transfer property list */ + +/* + * Function: do_sio + * Purpose: SIO Engine where IO are executed. + * Return: results + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ +void +do_sio(parameters param, results *res) +{ + char *buffer = NULL; /*data buffer pointer */ + size_t buf_size[MAX_DIMS]; /* general buffer size in bytes */ + file_descr fd; /* file handles */ + iotype iot; /* API type */ + char base_name[256]; /* test file base name */ + /* return codes */ + herr_t ret_code = 0; /*return code */ + + char fname[FILENAME_MAX]; /* test file name */ + int i; + /* HDF5 variables */ + herr_t hrc; /*HDF5 return code */ + + /* Sanity check parameters */ + + /* IO type */ + iot = param.io_type; + + switch (iot) { + case POSIXIO: + fd.posixfd = -1; + res->timers = io_time_new(SYS_CLOCK); + break; + case HDF5: + fd.h5fd = -1; + res->timers = io_time_new(SYS_CLOCK); + break; + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)iot); + GOTOERROR(FAIL); + } + + linear_buf_size = 1; + + for (i=0; i<param.rank; i++){ + buf_size[i] = param.buf_size[i]; + order[i] = param.order[i]; + linear_buf_size *= buf_size[i]; + buf_offset[i] = 0; + offset[i] = 0; + + /* Validate transfer buffer size */ + if (param.buf_size[i]<=0) { + HDfprintf(stderr, + "Transfer buffer size[%d] (%zu) must be > 0\n", i,buf_size[i]); + GOTOERROR(FAIL); + } + + if ((param.dset_size[i]%param.buf_size[i])!=0) { + HDfprintf(stderr, + "Dataset size[%d] (%" H5_PRINTF_LL_WIDTH "d) must be a multiple of the " + "trasfer buffer size[%d] (%zu)\n",param.rank, + (long long)param.dset_size[i], param.rank, param.buf_size[i]); + GOTOERROR(FAIL); + } + + } + + /* Allocate transfer buffer */ + if ((buffer = (char *)malloc(linear_buf_size)) == NULL){ + HDfprintf(stderr, "malloc for transfer buffer size (%zu) failed\n", linear_buf_size); + GOTOERROR(FAIL); + } + + if (sio_debug_level >= 4) + + /* output all of the times for all iterations */ + fprintf(output, "Timer details:\n"); + + /* + * Write performance measurement + */ + /* Open file for write */ + + HDstrcpy(base_name, "#sio_tmp"); + sio_create_filename(iot, base_name, fname, sizeof(fname), ¶m); + + if (sio_debug_level > 0) + HDfprintf(output, "data filename=%s\n", + fname); + + set_time(res->timers, HDF5_GROSS_WRITE_FIXED_DIMS, TSTART); + hrc = do_fopen(¶m, fname, &fd, SIO_CREATE | SIO_WRITE); + VRFY((hrc == SUCCESS), "do_fopen failed"); + + set_time(res->timers, HDF5_FINE_WRITE_FIXED_DIMS, TSTART); + hrc = do_write(res, &fd, ¶m, buffer); + set_time(res->timers, HDF5_FINE_WRITE_FIXED_DIMS, TSTOP); + VRFY((hrc == SUCCESS), "do_write failed"); + + /* Close file for write */ + hrc = do_fclose(iot, &fd); + set_time(res->timers, HDF5_GROSS_WRITE_FIXED_DIMS, TSTOP); + VRFY((hrc == SUCCESS), "do_fclose failed"); + + if (!param.h5_write_only) { + /* + * Read performance measurement + */ + + /* Open file for read */ + set_time(res->timers, HDF5_GROSS_READ_FIXED_DIMS, TSTART); + hrc = do_fopen(¶m, fname, &fd, SIO_READ); + VRFY((hrc == SUCCESS), "do_fopen failed"); + + set_time(res->timers, HDF5_FINE_READ_FIXED_DIMS, TSTART); + hrc = do_read(res, &fd, ¶m, buffer); + set_time(res->timers, HDF5_FINE_READ_FIXED_DIMS, TSTOP); + VRFY((hrc == SUCCESS), "do_read failed"); + + /* Close file for read */ + hrc = do_fclose(iot, &fd); + + set_time(res->timers, HDF5_GROSS_READ_FIXED_DIMS, TSTOP); + VRFY((hrc == SUCCESS), "do_fclose failed"); + } + + do_cleanupfile(iot, fname); + +done: + /* clean up */ + /* release HDF5 objects */ + + /* close any opened files */ + /* no remove(fname) because that should have happened normally. */ + switch (iot) { + case POSIXIO: + if (fd.posixfd != -1) + hrc = do_fclose(iot, &fd); + break; + case HDF5: + if (fd.h5fd != -1) + hrc = do_fclose(iot, &fd); + break; + default: + /* unknown request */ + HDassert(0 && "Unknown IO type"); + break; + } + + /* release generic resources */ + if (buffer) + free(buffer); + + res->ret_code = ret_code; +} + +/* + * Function: sio_create_filename + * Purpose: Create a new filename to write to. Determine the correct + * suffix to append to the filename by the type of I/O we're + * doing. Also, place in the /tmp/{$USER,$LOGIN} directory if + * USER or LOGIN are specified in the environment. + * Return: Pointer to filename or NULL + * Programmer: Bill Wendling, 21. November 2001 + * Modifications: Support for file drivers. Christian Chilan, April, 2008 + */ + static char * +sio_create_filename(iotype iot, const char *base_name, char *fullname, size_t size, parameters *param) +{ + const char *prefix, *suffix=""; + char *ptr, last = '\0'; + size_t i, j; + vfdtype vfd; + vfd = param->vfd; + + if (!base_name || !fullname || size < 1) + return NULL; + + memset(fullname, 0, size); + + switch (iot) { + case POSIXIO: + suffix = ".posix"; + break; + case HDF5: + suffix = ".h5"; + if (vfd == family) + suffix = "%05d.h5"; + else if (vfd == multi) + suffix = NULL; + break; + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)iot); + HDassert(0 && "Unknown IO type"); + break; + } + + /* First use the environment variable and then try the constant */ + prefix = HDgetenv("HDF5_PREFIX"); + +#ifdef HDF5_PREFIX + if (!prefix) + prefix = HDF5_PREFIX; +#endif /* HDF5_PREFIX */ + + /* Prepend the prefix value to the base name */ + if (prefix && *prefix) { + /* If the prefix specifies the HDF5_PREFIX directory, then + * default to using the "/tmp/$USER" or "/tmp/$LOGIN" + * directory instead. */ + register char *user, *login, *subdir; + + user = HDgetenv("USER"); + login = HDgetenv("LOGIN"); + subdir = (user ? user : login); + + if (subdir) { + for (i = 0; i < size-1 && prefix[i]; i++) + fullname[i] = prefix[i]; + + fullname[i++] = '/'; + + for (j = 0; i < size && subdir[j]; i++, j++) + fullname[i] = subdir[j]; + } else { + /* We didn't append the prefix yet */ + HDstrncpy(fullname, prefix, size); + fullname[size - 1] = '\0'; + } + + if ((HDstrlen(fullname) + HDstrlen(base_name) + 1) < size) { + /* Append the base_name with a slash first. Multiple slashes are + * handled below. */ + h5_stat_t buf; + + if (HDstat(fullname, &buf) < 0) + /* The directory doesn't exist just yet */ + if (HDmkdir(fullname, 0755) < 0 && errno != EEXIST) { + /* We couldn't make the "/tmp/${USER,LOGIN}" subdirectory. + * Default to PREFIX's original prefix value. */ + HDstrcpy(fullname, prefix); + } + + HDstrcat(fullname, "/"); + HDstrcat(fullname, base_name); + } else { + /* Buffer is too small */ + return NULL; + } + } else if (strlen(base_name) >= size) { + /* Buffer is too small */ + return NULL; + } else { + HDstrcpy(fullname, base_name); + } + + /* Append a suffix */ + if (suffix) { + if (HDstrlen(fullname) + HDstrlen(suffix) >= size) + return NULL; + + HDstrcat(fullname, suffix); + } + + /* Remove any double slashes in the filename */ + for (ptr = fullname, i = j = 0; ptr && (i < size); i++, ptr++) { + if (*ptr != '/' || last != '/') + fullname[j++] = *ptr; + + last = *ptr; + } + + return fullname; +} + +/* + * Function: do_write + * Purpose: Write the required amount of data to the file. + * Return: SUCCESS or FAIL + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ +static herr_t +do_write(results *res, file_descr *fd, parameters *parms, void *buffer) +{ + int ret_code = SUCCESS; + char dname[64]; + int i; + size_t u; + /* HDF5 variables */ + herr_t hrc; /*HDF5 return code */ + hsize_t h5dims[MAX_DIMS]; /*dataset dim sizes */ + hsize_t h5chunk[MAX_DIMS]; /*dataset dim sizes */ + hsize_t h5block[MAX_DIMS]; /*dataspace selection */ + hsize_t h5stride[MAX_DIMS]; /*selection stride */ + hsize_t h5start[MAX_DIMS]; /*selection start */ + hsize_t h5maxdims[MAX_DIMS]; + int rank; /*rank of dataset */ + + /* Prepare buffer for verifying data */ +/* if (parms->verify) + memset(buffer,1,linear_buf_size); */ + + buf_p=(unsigned char *)buffer; + + for(u = 0; u < linear_buf_size; u++) + buf_p[u] = u % 128; + + rank = parms->rank; + + for(i = 0; i < rank; i++) + h5offset[i] = offset[i] = 0; + + /* I/O Access specific setup */ + switch (parms->io_type) { + case POSIXIO: + + /* determine lowest dimension for contiguous POSIX access */ + cont_dim = rank; + + for (i=rank-1; i>=0; i--) { + if (parms->buf_size[i]==parms->dset_size[i]) + cont_dim = i; + else + break; + } + + /* determine size of the contiguous POSIX access */ + cont_size = (!cont_dim)? 1 : parms->buf_size[cont_dim-1]; + for (i=cont_dim; i<rank; i++) + cont_size *= parms->buf_size[i]; + + break; + + case HDF5: /* HDF5 setup */ + + for (i=0; i < rank; i++){ + h5dims[i] = parms->dset_size[i]; + h5start[i] = 0; + h5stride[i] = 1; + h5block[i] = 1; + h5count[i] = parms->buf_size[i]; + h5chunk[i] = parms->chk_size[i]; + h5maxdims[i] = H5S_UNLIMITED; + + } + + if (parms->h5_use_chunks && parms->h5_extendable) { + h5dset_space_id = H5Screate_simple(rank, h5count, h5maxdims); + VRFY((h5dset_space_id >= 0), "H5Screate_simple"); + } + else { + h5dset_space_id = H5Screate_simple(rank, h5dims, NULL); + VRFY((h5dset_space_id >= 0), "H5Screate_simple"); + } + + hrc = H5Sselect_hyperslab(h5dset_space_id, H5S_SELECT_SET, + h5start, h5stride, h5count, h5block); + VRFY((hrc >= 0), "H5Sselect_hyperslab"); + + /* Create the memory dataspace that corresponds to the xfer buffer */ + h5mem_space_id = H5Screate_simple(rank, h5count, NULL); + VRFY((h5mem_space_id >= 0), "H5Screate_simple"); + + /* Create the dataset transfer property list */ + h5dxpl = H5Pcreate(H5P_DATASET_XFER); + if (h5dxpl < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + + break; + + default: + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)parms->io_type); + GOTOERROR(FAIL); + break; + } /* end switch */ + + + /* create dataset */ + switch (parms->io_type) { + case POSIXIO: + break; + + case HDF5: + h5dcpl = H5Pcreate(H5P_DATASET_CREATE); + + if (h5dcpl < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + + if(parms->h5_use_chunks) { + /* Set the chunk size to be the same as the buffer size */ + hrc = H5Pset_chunk(h5dcpl, rank, h5chunk); + if (hrc < 0) { + fprintf(stderr, "HDF5 Property List Set failed\n"); + GOTOERROR(FAIL); + } /* end if */ + } /* end if */ + + sprintf(dname, "Dataset_%ld", (unsigned long)parms->num_bytes); + h5ds_id = H5Dcreate2(fd->h5fd, dname, ELMT_H5_TYPE, + h5dset_space_id, H5P_DEFAULT, h5dcpl, H5P_DEFAULT); + + if (h5ds_id < 0) { + HDfprintf(stderr, "HDF5 Dataset Create failed\n"); + GOTOERROR(FAIL); + } + + hrc = H5Pclose(h5dcpl); + /* verifying the close of the dcpl */ + if (hrc < 0) { + HDfprintf(stderr, "HDF5 Property List Close failed\n"); + GOTOERROR(FAIL); + } + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)parms->io_type); + GOTOERROR(FAIL); + break; + } + + /* Start "raw data" write timer */ + set_time(res->timers, HDF5_RAW_WRITE_FIXED_DIMS, TSTART); + + /* Perform write */ + hrc = dset_write(rank-1, fd, parms, buffer); + + if (hrc < 0) { + fprintf(stderr, "Error in dataset write\n"); + GOTOERROR(FAIL); + } + + + /* Stop "raw data" write timer */ + set_time(res->timers, HDF5_RAW_WRITE_FIXED_DIMS, TSTOP); + + /* Calculate write time */ + + /* Close dataset. Only HDF5 needs to do an explicit close. */ + if (parms->io_type == HDF5) { + hrc = H5Dclose(h5ds_id); + + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Close failed\n"); + GOTOERROR(FAIL); + } + + h5ds_id = -1; + } /* end if */ + +done: + + /* release HDF5 objects */ + if (h5dset_space_id != -1) { + hrc = H5Sclose(h5dset_space_id); + if (hrc < 0){ + fprintf(stderr, "HDF5 Dataset Space Close failed\n"); + ret_code = FAIL; + } else { + h5dset_space_id = -1; + } + } + + if (h5mem_space_id != -1) { + hrc = H5Sclose(h5mem_space_id); + if (hrc < 0) { + fprintf(stderr, "HDF5 Memory Space Close failed\n"); + ret_code = FAIL; + } else { + h5mem_space_id = -1; + } + } + + if (h5dxpl != -1) { + hrc = H5Pclose(h5dxpl); + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Transfer Property List Close failed\n"); + ret_code = FAIL; + } else { + h5dxpl = -1; + } + } + + return ret_code; +} + +/* + * Function: dset_write + * Purpose: Write buffer into the dataset. + * Return: SUCCESS or FAIL + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ +static herr_t +dset_write(int local_dim, file_descr *fd, parameters *parms, void *buffer) +{ + int cur_dim = order[local_dim]-1; + int ret_code = SUCCESS; + int k; + hsize_t dims[MAX_DIMS], maxdims[MAX_DIMS]; + hsize_t i; + int j; + herr_t hrc; + + /* iterates according to the dimensions in order array */ + for (i=0; i < parms->dset_size[cur_dim]; i += parms->buf_size[cur_dim]){ + + h5offset[cur_dim] = (hssize_t)i; + offset[cur_dim] = (HDoff_t)i; + + if (local_dim > 0){ + + dset_write(local_dim-1, fd, parms, buffer); + + }else{ + + switch (parms->io_type) { + + case POSIXIO: + /* initialize POSIX offset in the buffer */ + for(j = 0; j < parms->rank; j++) + buf_offset[j] = 0; + buf_p = (unsigned char *)buffer; + /* write POSIX buffer */ + posix_buffer_write(0, fd, parms, buffer); + break; + + case HDF5: + /* if dimensions are extendable, extend them as needed during access */ + if (parms->h5_use_chunks && parms->h5_extendable) { + + hrc=H5Sget_simple_extent_dims(h5dset_space_id,dims,maxdims); + VRFY((hrc >= 0), "H5Sget_simple_extent_dims"); + + for (k=0; k < parms->rank; k++){ + + HDassert(h5offset[k] >= 0); + if (dims[k] <= (hsize_t)h5offset[k]) { + dims[k] = dims[k]+h5count[k]; + hrc=H5Sset_extent_simple(h5dset_space_id,parms->rank,dims,maxdims); + VRFY((hrc >= 0), "H5Sset_extent_simple"); + hrc=H5Dset_extent(h5ds_id,dims); + VRFY((hrc >= 0), "H5Dextend"); + } + } + } + /* applies offset */ + hrc = H5Soffset_simple(h5dset_space_id, h5offset); + VRFY((hrc >= 0), "H5Soffset_simple"); + + /* Write the buffer out */ + hrc=H5Sget_simple_extent_dims(h5dset_space_id,dims,maxdims); + hrc = H5Dwrite(h5ds_id, ELMT_H5_TYPE, h5mem_space_id, + h5dset_space_id, h5dxpl, buffer); + VRFY((hrc >= 0), "H5Dwrite"); + + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)parms->io_type); + HDassert(0 && "Unknown IO type"); + break; + } /* switch (parms->io_type) */ + } + } +done: + return ret_code; +} + +/* + * Function: posix_buffer_write + * Purpose: Write buffer into the POSIX file considering contiguity. + * Return: SUCCESS or FAIL + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ + +static herr_t +posix_buffer_write(int local_dim, file_descr *fd, parameters *parms, void *buffer) +{ + int ret_code = SUCCESS; + + /* if dimension is not contiguous, call recursively */ + if (local_dim < parms->rank-1 && local_dim != cont_dim) { + size_t u; + + for(u = 0; u < parms->buf_size[local_dim]; u ++) { + buf_offset[local_dim] = u; + posix_buffer_write(local_dim+1, fd, parms, buffer); + + /* if next dimension is cont_dim, it will fill out the buffer + traversing the entire dimension local_dim without the need + of performing iteration */ + if (local_dim+1==cont_dim) + break; + } + /* otherwise, perform contiguous POSIX access */ + } else { + HDoff_t d_offset; + HDoff_t linear_dset_offset = 0; + int i, j, rc; + + buf_offset[local_dim] = 0; + + /* determine offset in the buffer */ + for(i = 0; i < parms->rank; i++) { + d_offset = 1; + + for(j = i + 1; j < parms->rank; j++) + d_offset *= (HDoff_t)parms->dset_size[j]; + + linear_dset_offset += (offset[i] + (HDoff_t)buf_offset[i]) * d_offset; + } + + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, linear_dset_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + /* check if all bytes are written */ + rc = ((ssize_t)cont_size == + POSIXWRITE(fd->posixfd, buf_p, cont_size)); + VRFY((rc != 0), "POSIXWRITE"); + + /* Advance location in buffer */ + buf_p += cont_size; + + } + +done: + return ret_code; +} + +/* + * Function: do_read + * Purpose: Read the required amount of data to the file. + * Return: SUCCESS or FAIL + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ +static herr_t +do_read(results *res, file_descr *fd, parameters *parms, void *buffer) +{ + char *buffer2 = NULL; /* Buffer for data verification */ + int ret_code = SUCCESS; + char dname[64]; + int i; + size_t u; + /* HDF5 variables */ + herr_t hrc; /*HDF5 return code */ + hsize_t h5dims[MAX_DIMS]; /*dataset dim sizes */ + hsize_t h5block[MAX_DIMS]; /*dataspace selection */ + hsize_t h5stride[MAX_DIMS]; /*selection stride */ + hsize_t h5start[MAX_DIMS]; /*selection start */ + int rank; + + /* Allocate data verification buffer */ + if(NULL == (buffer2 = (char *)malloc(linear_buf_size))) { + HDfprintf(stderr, "malloc for data verification buffer size (%Zu) failed\n", linear_buf_size); + GOTOERROR(FAIL); + } /* end if */ + + /* Prepare buffer for verifying data */ + for(u = 0; u < linear_buf_size; u++) + buffer2[u] = (char)(u % 128); + + rank = parms->rank; + for(i = 0; i < rank; i++) + h5offset[i] = offset[i] = 0; + + /* I/O Access specific setup */ + switch (parms->io_type) { + case POSIXIO: + cont_dim = rank; + + for (i=rank-1; i>=0; i--) { + if (parms->buf_size[i]==parms->dset_size[i]) + cont_dim = i; + else + break; + } + cont_size = (!cont_dim)? 1 : parms->buf_size[cont_dim-1]; + for (i=cont_dim; i<rank; i++) + cont_size *= parms->buf_size[i]; + + break; + + case HDF5: /* HDF5 setup */ + for (i=0; i < rank; i++){ + h5dims[i] = parms->dset_size[i]; + h5start[i] = 0; + h5stride[i] = 1; + h5block[i] = 1; + h5count[i] = parms->buf_size[i]; + } + + h5dset_space_id = H5Screate_simple(rank, h5dims, NULL); + VRFY((h5dset_space_id >= 0), "H5Screate_simple"); + + hrc = H5Sselect_hyperslab(h5dset_space_id, H5S_SELECT_SET, + h5start, h5stride, h5count, h5block); + VRFY((hrc >= 0), "H5Sselect_hyperslab"); + + /* Create the memory dataspace that corresponds to the xfer buffer */ + h5mem_space_id = H5Screate_simple(rank, h5count, NULL); + VRFY((h5mem_space_id >= 0), "H5Screate_simple"); + + /* Create the dataset transfer property list */ + h5dxpl = H5Pcreate(H5P_DATASET_XFER); + if (h5dxpl < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)parms->io_type); + GOTOERROR(FAIL); + break; + } /* end switch */ + + + /* create dataset */ + switch (parms->io_type) { + case POSIXIO: + break; + + case HDF5: + sprintf(dname, "Dataset_%ld", (long)parms->num_bytes); + h5ds_id = H5Dopen2(fd->h5fd, dname, H5P_DEFAULT); + if (h5ds_id < 0) { + HDfprintf(stderr, "HDF5 Dataset open failed\n"); + GOTOERROR(FAIL); + } + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)parms->io_type); + GOTOERROR(FAIL); + break; + } /* end switch */ + + /* Start "raw data" read timer */ + set_time(res->timers, HDF5_RAW_READ_FIXED_DIMS, TSTART); + hrc = dset_read(rank-1, fd, parms, buffer, buffer2); + + if (hrc < 0) { + fprintf(stderr, "Error in dataset read\n"); + GOTOERROR(FAIL); + } + + /* Stop "raw data" read timer */ + set_time(res->timers, HDF5_RAW_READ_FIXED_DIMS, TSTOP); + + /* Calculate read time */ + + /* Close dataset. Only HDF5 needs to do an explicit close. */ + if (parms->io_type == HDF5) { + hrc = H5Dclose(h5ds_id); + + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Close failed\n"); + GOTOERROR(FAIL); + } + + h5ds_id = -1; + } /* end if */ + +done: + + /* release HDF5 objects */ + if (h5dset_space_id != -1) { + hrc = H5Sclose(h5dset_space_id); + if (hrc < 0){ + fprintf(stderr, "HDF5 Dataset Space Close failed\n"); + ret_code = FAIL; + } else { + h5dset_space_id = -1; + } + } + + if (h5mem_space_id != -1) { + hrc = H5Sclose(h5mem_space_id); + if (hrc < 0) { + fprintf(stderr, "HDF5 Memory Space Close failed\n"); + ret_code = FAIL; + } else { + h5mem_space_id = -1; + } + } + + if (h5dxpl != -1) { + hrc = H5Pclose(h5dxpl); + if (hrc < 0) { + fprintf(stderr, "HDF5 Dataset Transfer Property List Close failed\n"); + ret_code = FAIL; + } else { + h5dxpl = -1; + } + } + + /* release generic resources */ + if(buffer2) + free(buffer2); + + return ret_code; +} + +/* + * Function: dset_read + * Purpose: Read buffer into the dataset. + * Return: SUCCESS or FAIL + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ + +static herr_t +dset_read(int local_dim, file_descr *fd, parameters *parms, void *buffer, + const char *buffer2) +{ + int cur_dim = order[local_dim]-1; + hsize_t i; + int j; + herr_t hrc; + int ret_code = SUCCESS; + + /* iterate on the current dimension */ + for (i=0; i < parms->dset_size[cur_dim]; i += parms->buf_size[cur_dim]){ + + h5offset[cur_dim] = (hssize_t)i; + offset[cur_dim] = (HDoff_t)i; + + /* if traverse in order array is incomplete, recurse */ + if (local_dim > 0){ + + ret_code = dset_read(local_dim-1, fd, parms, buffer, buffer2); + + /* otherwise, write buffer into dataset */ + }else{ + + switch (parms->io_type) { + + case POSIXIO: + for (j=0; j<parms->rank; j++) { + buf_offset[j] = 0; + } + buf_p = (unsigned char*)buffer; + posix_buffer_read(0, fd, parms, buffer); + break; + + case HDF5: + hrc = H5Soffset_simple(h5dset_space_id, h5offset); + VRFY((hrc >= 0), "H5Soffset_simple"); + /* Read the buffer out */ + hrc = H5Dread(h5ds_id, ELMT_H5_TYPE, h5mem_space_id, + h5dset_space_id, h5dxpl, buffer); + VRFY((hrc >= 0), "H5Dread"); + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)parms->io_type); + HDassert(0 && "Unknown IO type"); + break; + } /* switch (parms->io_type) */ + } + } +done: + return ret_code; +} + +/* + * Function: posix_buffer_read + * Purpose: Read buffer into the POSIX file considering contiguity. + * Return: SUCCESS or FAIL + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ + +static herr_t +posix_buffer_read(int local_dim, file_descr *fd, parameters *parms, void *buffer) +{ + int ret_code = SUCCESS; + + /* if local dimension is not contiguous, recurse */ + if (local_dim < parms->rank-1 && local_dim != cont_dim) { + size_t u; + + for(u = 0; u < parms->buf_size[local_dim]; u++) { + buf_offset[local_dim] = u; + ret_code = posix_buffer_read(local_dim+1, fd, parms, buffer); + if (local_dim+1==cont_dim) + break; + } + /* otherwise, perform contiguous POSIX access */ + } else { + HDoff_t d_offset; + HDoff_t linear_dset_offset = 0; + int i, j, rc; + + buf_offset[local_dim] = 0; + /* determine offset in buffer */ + for (i=0; i<parms->rank; i++){ + d_offset=1; + + for (j=i+1; j<parms->rank; j++) + d_offset *= (HDoff_t)parms->dset_size[j]; + + linear_dset_offset += (offset[i] + (HDoff_t)buf_offset[i]) * d_offset; + } + + /* only care if seek returns error */ + rc = POSIXSEEK(fd->posixfd, linear_dset_offset) < 0 ? -1 : 0; + VRFY((rc==0), "POSIXSEEK"); + /* check if all bytes are read */ + rc = ((ssize_t)cont_size == + POSIXREAD(fd->posixfd, buf_p, cont_size)); + VRFY((rc != 0), "POSIXREAD"); + + /* Advance location in buffer */ + buf_p += cont_size; + + } +done: + return ret_code; +} + + +/* + * Function: do_fopen + * Purpose: Open the specified file. + * Return: SUCCESS or FAIL + * Programmer: Albert Cheng, Bill Wendling, 2001/12/13 + * Modifications: Support for file drivers, Christian Chilan, April, 2008 + */ + static herr_t +do_fopen(parameters *param, char *fname, file_descr *fd /*out*/, int flags) +{ + int ret_code = SUCCESS; + + switch (param->io_type) { + case POSIXIO: + if (flags & (SIO_CREATE | SIO_WRITE)) + fd->posixfd = POSIXCREATE(fname); + else + fd->posixfd = POSIXOPEN(fname, O_RDONLY); + + if (fd->posixfd < 0 ) { + HDfprintf(stderr, "POSIX File Open failed(%s)\n", fname); + GOTOERROR(FAIL); + } + + break; + + case HDF5: + + fapl = set_vfd(param); + + if (fapl < 0) { + fprintf(stderr, "HDF5 Property List Create failed\n"); + GOTOERROR(FAIL); + } + + /* create the parallel file */ + if (flags & (SIO_CREATE | SIO_WRITE)) { + fd->h5fd = H5Fcreate(fname, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); + } else { + fd->h5fd = H5Fopen(fname, H5F_ACC_RDONLY, fapl); + } + + + if (fd->h5fd < 0) { + fprintf(stderr, "HDF5 File Create failed(%s)\n", fname); + GOTOERROR(FAIL); + } + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)param->io_type); + GOTOERROR(FAIL); + break; + } + +done: + return ret_code; +} + +/* + * Function: set_vfd + * Purpose: Sets file driver. + * Return: SUCCESS or FAIL + * Programmer: Christian Chilan, April, 2008 + * Modifications: + */ + +hid_t +set_vfd(parameters *param) +{ + hid_t my_fapl = -1; + vfdtype vfd; + + vfd = param->vfd; + + if ((my_fapl=H5Pcreate(H5P_FILE_ACCESS))<0) return -1; + + if (vfd == sec2) { + /* Unix read() and write() system calls */ + if (H5Pset_fapl_sec2(my_fapl)<0) return -1; + } else if (vfd == stdio) { + /* Standard C fread() and fwrite() system calls */ + if (H5Pset_fapl_stdio(my_fapl)<0) return -1; + } else if (vfd == core) { + /* In-core temporary file with 1MB increment */ + if (H5Pset_fapl_core(my_fapl, (size_t)1024*1024, TRUE)<0) return -1; + } else if (vfd == split) { + /* Split meta data and raw data each using default driver */ + if (H5Pset_fapl_split(my_fapl, + "-m.h5", H5P_DEFAULT, + "-r.h5", H5P_DEFAULT)<0) + return -1; + } else if (vfd == multi) { + /* Multi-file driver, general case of the split driver */ + H5FD_mem_t memb_map[H5FD_MEM_NTYPES]; + hid_t memb_fapl[H5FD_MEM_NTYPES]; + const char *memb_name[H5FD_MEM_NTYPES]; + char sv[H5FD_MEM_NTYPES][1024]; + haddr_t memb_addr[H5FD_MEM_NTYPES]; + H5FD_mem_t mt; + + HDmemset(memb_map, 0, sizeof memb_map); + HDmemset(memb_fapl, 0, sizeof memb_fapl); + HDmemset(memb_name, 0, sizeof memb_name); + HDmemset(memb_addr, 0, sizeof memb_addr); + + HDassert(HDstrlen(multi_letters)==H5FD_MEM_NTYPES); + for (mt=H5FD_MEM_DEFAULT; mt<H5FD_MEM_NTYPES; H5_INC_ENUM(H5FD_mem_t,mt)) { + memb_fapl[mt] = H5P_DEFAULT; + sprintf(sv[mt], "%%s-%c.h5", multi_letters[mt]); + memb_name[mt] = sv[mt]; + memb_addr[mt] = (haddr_t)MAX(mt - 1,0) * (HADDR_MAX / 10); + } + + if (H5Pset_fapl_multi(my_fapl, memb_map, memb_fapl, memb_name, + memb_addr, FALSE)<0) { + return -1; + } + } else if (vfd == family) { + hsize_t fam_size = 1*1024*1024; /*100 MB*/ + + /* Family of files, each 1MB and using the default driver */ + /* if ((val=HDstrtok(NULL, " \t\n\r"))) + fam_size = (hsize_t)(HDstrtod(val, NULL) * 1024*1024); */ + if (H5Pset_fapl_family(my_fapl, fam_size, H5P_DEFAULT)<0) + return -1; + } else if (vfd == direct) { +#ifdef H5_HAVE_DIRECT + /* Linux direct read() and write() system calls. Set memory boundary, file block size, + * and copy buffer size to the default values. */ + if (H5Pset_fapl_direct(my_fapl, 1024, 4096, 8*4096)<0) return -1; +#endif + } else { + /* Unknown driver */ + return -1; + } + + return my_fapl; +} + +/* + * Function: do_fclose + * Purpose: Close the specified file descriptor. + * Return: SUCCESS or FAIL + * Programmer: Albert Cheng, Bill Wendling, 2001/12/13 + * Modifications: + */ + static herr_t +do_fclose(iotype iot, file_descr *fd /*out*/) +{ + herr_t ret_code = SUCCESS, hrc; + int rc = 0; + + switch (iot) { + case POSIXIO: + rc = POSIXCLOSE(fd->posixfd); + + if (rc != 0){ + fprintf(stderr, "POSIX File Close failed\n"); + GOTOERROR(FAIL); + } + + fd->posixfd = -1; + break; + + case HDF5: + hrc = H5Fclose(fd->h5fd); + + if (hrc < 0) { + fprintf(stderr, "HDF5 File Close failed\n"); + GOTOERROR(FAIL); + } + + fd->h5fd = -1; + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)iot); + GOTOERROR(FAIL); + break; + } + +done: + return ret_code; +} + + +/* + * Function: do_cleanupfile + * Purpose: Cleanup temporary file unless HDF5_NOCLEANUP is set. + * Return: void + * Programmer: Albert Cheng 2001/12/12 + * Modifications: Support for file drivers. Christian Chilan, April, 2008 + */ + static void +do_cleanupfile(iotype iot, char *filename) +{ + char temp[2048]; + int j; + hid_t driver; + + if (clean_file_g == -1) + clean_file_g = (HDgetenv("HDF5_NOCLEANUP")==NULL) ? 1 : 0; + + if (clean_file_g){ + + switch (iot) { + case POSIXIO: + HDremove(filename); + break; + + case HDF5: + driver = H5Pget_driver(fapl); + + if (driver == H5FD_FAMILY) { + for (j = 0; /*void*/; j++) { + HDsnprintf(temp, sizeof temp, filename, j); + + if (HDaccess(temp, F_OK) < 0) + break; + + HDremove(temp); + } + } else if (driver == H5FD_CORE) { + hbool_t backing; /* Whether the core file has backing store */ + + H5Pget_fapl_core(fapl,NULL,&backing); + + /* If the file was stored to disk with bacing store, remove it */ + if(backing) + HDremove(filename); + + } else if (driver == H5FD_MULTI) { + H5FD_mem_t mt; + assert(HDstrlen(multi_letters)==H5FD_MEM_NTYPES); + + for (mt = H5FD_MEM_DEFAULT; mt < H5FD_MEM_NTYPES; H5_INC_ENUM(H5FD_mem_t,mt)) { + HDsnprintf(temp, sizeof temp, "%s-%c.h5", + filename, multi_letters[mt]); + HDremove(temp); /*don't care if it fails*/ + } + } else { + HDremove(filename); + } + H5Pclose(fapl); + break; + + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)iot); + HDassert(0 && "Unknown IO type"); + break; + } + } +} + diff --git a/tools/test/perform/sio_perf.c b/tools/test/perform/sio_perf.c new file mode 100644 index 0000000..aceccb0 --- /dev/null +++ b/tools/test/perform/sio_perf.c @@ -0,0 +1,1412 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Serial HDF5 Performance Testing Code + * -------------------------------------- + * + * Portable code to test performance on the different platforms we support. + * This is what the report should look like: + * + * nprocs = Max#Procs + * IO API = POSIXIO + * # Files = 1, # of dsets = 1000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * # Files = 1, # of dsets = 3000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * + * . . . + * + * + * IO API = HDF5 + * # Files = 1, # of dsets = 1000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * # Files = 1, # of dsets = 3000, Elements per dset = 37000 + * Write Results = x MB/s + * Read Results = x MB/s + * + * . . . + * + * + * . . . + * + */ + +/* system header files */ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "hdf5.h" + + +/* our header files */ +#include "sio_perf.h" + +/* useful macros */ +#define TAB_SPACE 4 + +#define ONE_KB 1024 +#define ONE_MB (ONE_KB * ONE_KB) +#define ONE_GB (ONE_MB * ONE_KB) + +#define SIO_POSIX 0x1 +#define SIO_HDF5 0x4 + +/* report 0.0 in case t is zero too */ +#define MB_PER_SEC(bytes,t) (H5_DBL_ABS_EQUAL(t, (double)0.0F) ? (double)0.0F : ((((double)bytes) / (double)ONE_MB) / (t))) + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE (!TRUE) +#endif /* FALSE */ + +/* global variables */ +FILE *output; /* output file */ +int sio_debug_level = 0;/* The debug level: + * 0 - Off + * 1 - Minimal + * 2 - Some more + * 3 - Maximal + * 4 - Maximal & then some + */ + +/* local variables */ +static const char *progname = "h5perf_serial"; + +/* + * Command-line options: The user can specify short or long-named + * parameters. The long-named ones can be partially spelled. When + * adding more, make sure that they don't clash with each other. + */ + +/* + * It seems that only the options that accept additional information + * such as dataset size (-e) require the colon next to it. + */ +#if 1 +static const char *s_opts = "a:A:B:c:Cd:D:e:F:ghi:Imno:p:P:r:stT:v:wx:X:"; +#else +static const char *s_opts = "a:A:bB:c:Cd:D:e:F:ghi:Imno:p:P:r:stT:wx:X:"; +#endif /* 1 */ +static struct long_options l_opts[] = { + { "align", require_arg, 'a' }, + { "alig", require_arg, 'a' }, + { "ali", require_arg, 'a' }, + { "al", require_arg, 'a' }, + { "api", require_arg, 'A' }, + { "ap", require_arg, 'A' }, +#if 0 + /* a sighting of the elusive binary option */ + { "binary", no_arg, 'b' }, + { "binar", no_arg, 'b' }, + { "bina", no_arg, 'b' }, + { "bin", no_arg, 'b' }, + { "bi", no_arg, 'b' }, +#endif /* 0 */ + { "block-size", require_arg, 'B' }, + { "block-siz", require_arg, 'B' }, + { "block-si", require_arg, 'B' }, + { "block-s", require_arg, 'B' }, + { "block-", require_arg, 'B' }, + { "block", require_arg, 'B' }, + { "bloc", require_arg, 'B' }, + { "blo", require_arg, 'B' }, + { "bl", require_arg, 'B' }, + { "chunk", no_arg, 'c' }, + { "chun", no_arg, 'c' }, + { "chu", no_arg, 'c' }, + { "ch", no_arg, 'c' }, + { "collective", no_arg, 'C' }, + { "collectiv", no_arg, 'C' }, + { "collecti", no_arg, 'C' }, + { "collect", no_arg, 'C' }, + { "collec", no_arg, 'C' }, + { "colle", no_arg, 'C' }, + { "coll", no_arg, 'C' }, + { "col", no_arg, 'C' }, + { "co", no_arg, 'C' }, + { "debug", require_arg, 'D' }, + { "debu", require_arg, 'D' }, + { "deb", require_arg, 'D' }, + { "de", require_arg, 'D' }, + { "file-driver", require_arg, 'v' }, + { "file-drive", require_arg, 'v' }, + { "file-driv", require_arg, 'v' }, + { "file-dri", require_arg, 'v' }, + { "file-dr", require_arg, 'v' }, + { "file-d", require_arg, 'v' }, + { "file-", require_arg, 'v' }, + { "file", require_arg, 'v' }, + { "fil", require_arg, 'v' }, + { "fi", require_arg, 'v' }, + { "geometry", no_arg, 'g' }, + { "geometr", no_arg, 'g' }, + { "geomet", no_arg, 'g' }, + { "geome", no_arg, 'g' }, + { "geom", no_arg, 'g' }, + { "geo", no_arg, 'g' }, + { "ge", no_arg, 'g' }, + { "help", no_arg, 'h' }, + { "hel", no_arg, 'h' }, + { "he", no_arg, 'h' }, + { "interleaved", require_arg, 'I' }, + { "interleave", require_arg, 'I' }, + { "interleav", require_arg, 'I' }, + { "interlea", require_arg, 'I' }, + { "interle", require_arg, 'I' }, + { "interl", require_arg, 'I' }, + { "inter", require_arg, 'I' }, + { "inte", require_arg, 'I' }, + { "int", require_arg, 'I' }, + { "in", require_arg, 'I' }, + { "max-num-processes", require_arg, 'P' }, + { "max-num-processe", require_arg, 'P' }, + { "max-num-process", require_arg, 'P' }, + { "max-num-proces", require_arg, 'P' }, + { "max-num-proce", require_arg, 'P' }, + { "max-num-proc", require_arg, 'P' }, + { "max-num-pro", require_arg, 'P' }, + { "max-num-pr", require_arg, 'P' }, + { "max-num-p", require_arg, 'P' }, + { "min-num-processes", require_arg, 'p' }, + { "min-num-processe", require_arg, 'p' }, + { "min-num-process", require_arg, 'p' }, + { "min-num-proces", require_arg, 'p' }, + { "min-num-proce", require_arg, 'p' }, + { "min-num-proc", require_arg, 'p' }, + { "min-num-pro", require_arg, 'p' }, + { "min-num-pr", require_arg, 'p' }, + { "min-num-p", require_arg, 'p' }, + { "max-xfer-size", require_arg, 'X' }, + { "max-xfer-siz", require_arg, 'X' }, + { "max-xfer-si", require_arg, 'X' }, + { "max-xfer-s", require_arg, 'X' }, + { "max-xfer", require_arg, 'X' }, + { "max-xfe", require_arg, 'X' }, + { "max-xf", require_arg, 'X' }, + { "max-x", require_arg, 'X' }, + { "min-xfer-size", require_arg, 'x' }, + { "min-xfer-siz", require_arg, 'x' }, + { "min-xfer-si", require_arg, 'x' }, + { "min-xfer-s", require_arg, 'x' }, + { "min-xfer", require_arg, 'x' }, + { "min-xfe", require_arg, 'x' }, + { "min-xf", require_arg, 'x' }, + { "min-x", require_arg, 'x' }, + { "num-bytes", require_arg, 'e' }, + { "num-byte", require_arg, 'e' }, + { "num-byt", require_arg, 'e' }, + { "num-by", require_arg, 'e' }, + { "num-b", require_arg, 'e' }, + { "num-dsets", require_arg, 'd' }, + { "num-dset", require_arg, 'd' }, + { "num-dse", require_arg, 'd' }, + { "num-ds", require_arg, 'd' }, + { "num-d", require_arg, 'd' }, + { "num-files", require_arg, 'F' }, + { "num-file", require_arg, 'F' }, + { "num-fil", require_arg, 'F' }, + { "num-fi", require_arg, 'F' }, + { "num-f", require_arg, 'F' }, + { "num-iterations", require_arg, 'i' }, + { "num-iteration", require_arg, 'i' }, + { "num-iteratio", require_arg, 'i' }, + { "num-iterati", require_arg, 'i' }, + { "num-iterat", require_arg, 'i' }, + { "num-itera", require_arg, 'i' }, + { "num-iter", require_arg, 'i' }, + { "num-ite", require_arg, 'i' }, + { "num-it", require_arg, 'i' }, + { "num-i", require_arg, 'i' }, + { "order", require_arg, 'r' }, + { "orde", require_arg, 'r' }, + { "ord", require_arg, 'r' }, + { "or", require_arg, 'r' }, + { "output", require_arg, 'o' }, + { "outpu", require_arg, 'o' }, + { "outp", require_arg, 'o' }, + { "out", require_arg, 'o' }, + { "ou", require_arg, 'o' }, + { "extendable", no_arg, 't' }, + { "extendabl", no_arg, 't' }, + { "extendab", no_arg, 't' }, + { "extenda", no_arg, 't' }, + { "extend", no_arg, 't' }, + { "exten", no_arg, 't' }, + { "exte", no_arg, 't' }, + { "ext", no_arg, 't' }, + { "ex", no_arg, 't' }, + { "threshold", require_arg, 'T' }, + { "threshol", require_arg, 'T' }, + { "thresho", require_arg, 'T' }, + { "thresh", require_arg, 'T' }, + { "thres", require_arg, 'T' }, + { "thre", require_arg, 'T' }, + { "thr", require_arg, 'T' }, + { "th", require_arg, 'T' }, + { "write-only", require_arg, 'w' }, + { "write-onl", require_arg, 'w' }, + { "write-on", require_arg, 'w' }, + { "write-o", require_arg, 'w' }, + { "write", require_arg, 'w' }, + { "writ", require_arg, 'w' }, + { "wri", require_arg, 'w' }, + { "wr", require_arg, 'w' }, + { NULL, 0, '\0' } +}; + +struct options { + long io_types; /* bitmask of which I/O types to test */ + const char *output_file; /* file to print report to */ + long num_dsets; /* number of datasets */ + long num_files; /* number of files */ + off_t num_bpp; /* number of bytes per proc per dset */ + int num_iters; /* number of iterations */ + hsize_t dset_size[MAX_DIMS]; /* Dataset size */ + size_t buf_size[MAX_DIMS]; /* Buffer size */ + size_t chk_size[MAX_DIMS]; /* Chunk size */ + int order[MAX_DIMS]; /* Dimension access order */ + int dset_rank; /* Rank */ + int buf_rank; /* Rank */ + int order_rank; /* Rank */ + int chk_rank; /* Rank */ + int print_times; /* print times as well as throughputs */ + int print_raw; /* print raw data throughput info */ + hsize_t h5_alignment; /* alignment in HDF5 file */ + hsize_t h5_threshold; /* threshold for alignment in HDF5 file */ + int h5_use_chunks; /* Make HDF5 dataset chunked */ + int h5_write_only; /* Perform the write tests only */ + int h5_extendable; /* Perform the write tests only */ + int verify; /* Verify data correctness */ + vfdtype vfd; /* File driver */ + +}; + +typedef struct _minmax { + double min; + double max; + double sum; + int num; +} minmax; + +/* local functions */ +static hsize_t parse_size_directive(const char *size); +static struct options *parse_command_line(int argc, char *argv[]); +static void run_test_loop(struct options *options); +static int run_test(iotype iot, parameters parms, struct options *opts); +static void output_all_info(minmax *mm, int count, int indent_level); +static void get_minmax(minmax *mm, double val); +static void accumulate_minmax_stuff(const minmax *mm, int count, minmax *total_mm); +static void output_results(const struct options *options, const char *name, + minmax *table, int table_size, off_t data_size); +static void output_report(const char *fmt, ...); +static void print_indent(register int indent); +static void usage(const char *prog); +static void report_parameters(struct options *opts); + +/* + * Function: main + * Purpose: Start things up. + * Return: EXIT_SUCCESS or EXIT_FAILURE + * Programmer: Bill Wendling, 30. October 2001 + * Modifications: + */ +int +main(int argc, char **argv) +{ + int exit_value = EXIT_SUCCESS; + struct options *opts = NULL; + +#ifndef STANDALONE + /* Initialize h5tools lib */ + h5tools_init(); +#endif + + output = stdout; + + opts = parse_command_line(argc, argv); + + if (!opts) { + exit_value = EXIT_FAILURE; + goto finish; + } + + if (opts->output_file) { + if ((output = HDfopen(opts->output_file, "w")) == NULL) { + fprintf(stderr, "%s: cannot open output file\n", progname); + perror(opts->output_file); + goto finish; + } + } + + report_parameters(opts); + + run_test_loop(opts); + +finish: + free(opts); + return exit_value; +} + +/* + * Function: run_test_loop + * Purpose: Run the I/O tests. Write the results to OUTPUT. + * + * - The slowest changing part of the test is the number of + * processors to use. For each loop iteration, we divide that + * number by 2 and rerun the test. + * + * - The second slowest is what type of IO API to perform. We have + * three choices: POSIXIO, and HDF5. + * + * - Then we change the size of the buffer. This information is + * inferred from the number of datasets to create and the number + * of integers to put into each dataset. The backend code figures + * this out. + * + * Return: Nothing + * Programmer: Bill Wendling, 30. October 2001 + * Modifications: + * Added multidimensional testing (Christian Chilan, April, 2008) + */ +static void +run_test_loop(struct options *opts) +{ + parameters parms; + int i; + size_t buf_bytes; + + /* load options into parameter structure */ + parms.num_files = opts->num_files; + parms.num_dsets = opts->num_dsets; + parms.num_iters = opts->num_iters; + parms.rank = opts->dset_rank; + parms.h5_align = opts->h5_alignment; + parms.h5_thresh = opts->h5_threshold; + parms.h5_use_chunks = opts->h5_use_chunks; + parms.h5_extendable = opts->h5_extendable; + parms.h5_write_only = opts->h5_write_only; + parms.verify = opts->verify; + parms.vfd = opts->vfd; + + /* load multidimensional options */ + parms.num_bytes = 1; + buf_bytes = 1; + for (i=0; i<parms.rank; i++){ + parms.buf_size[i] = opts->buf_size[i]; + parms.dset_size[i] = opts->dset_size[i]; + parms.chk_size[i] = opts->chk_size[i]; + parms.order[i] = opts->order[i]; + parms.num_bytes *= opts->dset_size[i]; + buf_bytes *= opts->buf_size[i]; + } + + /* print size information */ + output_report("Transfer Buffer Size (bytes): %d\n", buf_bytes); + output_report("File Size(MB): %.2f\n",((double)parms.num_bytes) / ONE_MB); + + print_indent(0); + if (opts->io_types & SIO_POSIX) + run_test(POSIXIO, parms, opts); + + print_indent(0); + if (opts->io_types & SIO_HDF5) + run_test(HDF5, parms, opts); +} + +/* + * Function: run_test + * Purpose: Inner loop call to actually run the I/O test. + * Return: Nothing + * Programmer: Bill Wendling, 18. December 2001 + * Modifications: + */ +static int +run_test(iotype iot, parameters parms, struct options *opts) +{ + results res; + register int i, ret_value = SUCCESS; + off_t raw_size; + minmax *write_sys_mm_table=NULL; + minmax *write_mm_table=NULL; + minmax *write_gross_mm_table=NULL; + minmax *write_raw_mm_table=NULL; + minmax *read_sys_mm_table=NULL; + minmax *read_mm_table=NULL; + minmax *read_gross_mm_table=NULL; + minmax *read_raw_mm_table=NULL; + minmax write_sys_mm = {0.0F, 0.0F, 0.0F, 0}; + minmax write_mm = {0.0F, 0.0F, 0.0F, 0}; + minmax write_gross_mm = {0.0F, 0.0F, 0.0F, 0}; + minmax write_raw_mm = {0.0F, 0.0F, 0.0F, 0}; + minmax read_sys_mm = {0.0F, 0.0F, 0.0F, 0}; + minmax read_mm = {0.0F, 0.0F, 0.0F, 0}; + minmax read_gross_mm = {0.0F, 0.0F, 0.0F, 0}; + minmax read_raw_mm = {0.0F, 0.0F, 0.0F, 0}; + + raw_size = (off_t)parms.num_bytes; + parms.io_type = iot; + print_indent(2); + output_report("IO API = "); + + switch (iot) { + case POSIXIO: + output_report("POSIX\n"); + break; + case HDF5: + output_report("HDF5\n"); + break; + default: + /* unknown request */ + HDfprintf(stderr, "Unknown IO type request (%d)\n", (int)iot); + HDassert(0 && "Unknown IO tpe"); + break; + } + + /* allocate space for tables minmax and that it is sufficient */ + /* to initialize all elements to zeros by calloc. */ + write_sys_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + write_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + write_gross_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + write_raw_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + + if (!parms.h5_write_only) { + read_sys_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + read_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + read_gross_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + read_raw_mm_table = (minmax *)calloc((size_t)parms.num_iters , sizeof(minmax)); + } + + /* Do IO iteration times, collecting statistics each time */ + for (i = 0; i < parms.num_iters; ++i) { + double t; + + do_sio(parms, &res); + + /* gather all of the "sys write" times */ + t = get_time(res.timers, HDF5_MPI_WRITE); + get_minmax(&write_sys_mm, t); + + write_sys_mm_table[i] = write_sys_mm; + + /* gather all of the "write" times */ + t = get_time(res.timers, HDF5_FINE_WRITE_FIXED_DIMS); + get_minmax(&write_mm, t); + + write_mm_table[i] = write_mm; + + /* gather all of the "write" times from open to close */ + t = get_time(res.timers, HDF5_GROSS_WRITE_FIXED_DIMS); + get_minmax(&write_gross_mm, t); + + write_gross_mm_table[i] = write_gross_mm; + + /* gather all of the raw "write" times */ + t = get_time(res.timers, HDF5_RAW_WRITE_FIXED_DIMS); + get_minmax(&write_raw_mm, t); + + write_raw_mm_table[i] = write_raw_mm; + + if (!parms.h5_write_only) { + /* gather all of the "mpi read" times */ + t = get_time(res.timers, HDF5_MPI_READ); + get_minmax(&read_sys_mm, t); + + read_sys_mm_table[i] = read_sys_mm; + + /* gather all of the "read" times */ + t = get_time(res.timers, HDF5_FINE_READ_FIXED_DIMS); + get_minmax(&read_mm, t); + + read_mm_table[i] = read_mm; + + /* gather all of the "read" times from open to close */ + t = get_time(res.timers, HDF5_GROSS_READ_FIXED_DIMS); + get_minmax(&read_gross_mm, t); + + read_gross_mm_table[i] = read_gross_mm; + + /* gather all of the raw "read" times */ + t = get_time(res.timers, HDF5_RAW_READ_FIXED_DIMS); + get_minmax(&read_raw_mm, t); + + read_raw_mm_table[i] = read_gross_mm; + } + io_time_destroy(res.timers); + } + + /* + * Show various statistics + */ + /* Write statistics */ + /* Print the raw data throughput if desired */ + if (opts->print_raw) { + /* accumulate and output the max, min, and average "raw write" times */ + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Raw Data Write details:\n"); + output_all_info(write_raw_mm_table, parms.num_iters, 4); + } + + output_results(opts,"Raw Data Write",write_raw_mm_table,parms.num_iters,raw_size); + } /* end if */ + + /* show sys write statics */ +#if 0 + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("MPI Write details:\n"); + output_all_info(write_sys_mm_table, parms.num_iters, 4); + } +#endif + /* We don't currently output the MPI write results */ + + /* accumulate and output the max, min, and average "write" times */ + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Write details:\n"); + output_all_info(write_mm_table, parms.num_iters, 4); + } + + output_results(opts,"Write",write_mm_table,parms.num_iters,raw_size); + + /* accumulate and output the max, min, and average "gross write" times */ + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Write Open-Close details:\n"); + output_all_info(write_gross_mm_table, parms.num_iters, 4); + } + + output_results(opts,"Write Open-Close",write_gross_mm_table,parms.num_iters,raw_size); + + if (!parms.h5_write_only) { + /* Read statistics */ + /* Print the raw data throughput if desired */ + if (opts->print_raw) { + /* accumulate and output the max, min, and average "raw read" times */ + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Raw Data Read details:\n"); + output_all_info(read_raw_mm_table, parms.num_iters, 4); + } + + output_results(opts, "Raw Data Read", read_raw_mm_table, + parms.num_iters, raw_size); + } /* end if */ + + /* show mpi read statics */ +#if 0 + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("MPI Read details:\n"); + output_all_info(read_sys_mm_table, parms.num_iters, 4); + } +#endif + /* We don't currently output the MPI read results */ + + /* accumulate and output the max, min, and average "read" times */ + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Read details:\n"); + output_all_info(read_mm_table, parms.num_iters, 4); + } + + output_results(opts, "Read", read_mm_table, parms.num_iters, raw_size); + + /* accumulate and output the max, min, and average "gross read" times */ + if (sio_debug_level >= 3) { + /* output all of the times for all iterations */ + print_indent(3); + output_report("Read Open-Close details:\n"); + output_all_info(read_gross_mm_table, parms.num_iters, 4); + } + + output_results(opts, "Read Open-Close", read_gross_mm_table, + parms.num_iters, raw_size); + } + + /* clean up our mess */ + free(write_sys_mm_table); + free(write_mm_table); + free(write_gross_mm_table); + free(write_raw_mm_table); + + if (!parms.h5_write_only) { + free(read_sys_mm_table); + free(read_mm_table); + free(read_gross_mm_table); + free(read_raw_mm_table); + } + + return ret_value; +} + +/* + * Function: output_all_info + * Purpose: + * Return: Nothing + * Programmer: Bill Wendling, 29. January 2002 + * Modifications: + */ +static void +output_all_info(minmax *mm, int count, int indent_level) +{ + int i; + + for (i = 0; i < count; ++i) { + print_indent(indent_level); + output_report("Iteration %d:\n", i + 1); + print_indent(indent_level + 1); + output_report("Minimum Time: %.2fs\n", mm[i].min); + print_indent(indent_level + 1); + output_report("Maximum Time: %.2fs\n", mm[i].max); + } +} + +/* + * Function: get_minmax + * Purpose: Gather all the min, max and total of val. + * Return: Nothing + * Programmer: Bill Wendling, 21. December 2001 + * Modifications: + * Use MPI_Allreduce to do it. -akc, 2002/01/11 + */ + +static void +get_minmax(minmax *mm, double val) +{ + mm->max = val; + mm->min = val; + mm->sum = val; +} + +/* + * Function: accumulate_minmax_stuff + * Purpose: Accumulate the minimum, maximum, and average of the times + * across all processes. + * Return: TOTAL_MM - the total of all of these. + * Programmer: Bill Wendling, 21. December 2001 + * Modifications: + * Changed to use seconds instead of MB/s - QAK, 5/9/02 + */ +static void +accumulate_minmax_stuff(const minmax *mm, int count, minmax *total_mm) +{ + int i; + + total_mm->sum = 0.0F; + total_mm->max = -DBL_MAX; + total_mm->min = DBL_MAX; + total_mm->num = count; + + for (i = 0; i < count; ++i) { + double m = mm[i].max; + + total_mm->sum += m; + + if (m < total_mm->min) + total_mm->min = m; + + if (m > total_mm->max) + total_mm->max = m; + } +} + + +/* + * Function: output_results + * Purpose: Print information about the time & bandwidth for a given + * minmax & # of iterations. + * Return: Nothing + * Programmer: Quincey Koziol, 9. May 2002 + * Modifications: + */ +static void +output_results(const struct options *opts, const char *name, minmax *table, + int table_size,off_t data_size) +{ + minmax total_mm; + + accumulate_minmax_stuff(table, table_size, &total_mm); + + print_indent(3); + output_report("%s (%d iteration(s)):\n", name,table_size); + + /* Note: The maximum throughput uses the minimum amount of time & vice versa */ + + print_indent(4); + output_report("Maximum Throughput: %6.2f MB/s", MB_PER_SEC(data_size,total_mm.min)); + if(opts->print_times) + output_report(" (%7.3f s)\n", total_mm.min); + else + output_report("\n"); + + print_indent(4); + output_report("Average Throughput: %6.2f MB/s", + MB_PER_SEC(data_size,total_mm.sum / total_mm.num)); + if(opts->print_times) + output_report(" (%7.3f s)\n", (total_mm.sum / total_mm.num)); + else + output_report("\n"); + + print_indent(4); + output_report("Minimum Throughput: %6.2f MB/s", MB_PER_SEC(data_size,total_mm.max)); + if(opts->print_times) + output_report(" (%7.3f s)\n", total_mm.max); + else + output_report("\n"); +} + +/* + * Function: output_report + * Purpose: Print a line of the report. Only do so if I'm the 0 process. + * Return: Nothing + * Programmer: Bill Wendling, 19. December 2001 + * Modifications: + */ +static void +output_report(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(output, fmt, ap); + va_end(ap); +} + +/* + * Function: print_indent + * Purpose: Print spaces to indent a new line of text for pretty printing + * things. + * Return: Nothing + * Programmer: Bill Wendling, 29. October 2001 + * Modifications: + */ +static void +print_indent(register int indent) +{ + indent *= TAB_SPACE; + + for (; indent > 0; --indent) + fputc(' ', output); +} + +static void +recover_size_and_print(long long val, const char *end) +{ + if (val >= ONE_KB && (val % ONE_KB) == 0) { + if (val >= ONE_MB && (val % ONE_MB) == 0) { + if (val >= ONE_GB && (val % ONE_GB) == 0) + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""GB%s", val / ONE_GB, end); + else + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""MB%s", val / ONE_MB, end); + } else { + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""KB%s", val / ONE_KB, end); + } + } else { + HDfprintf(output, "%" H5_PRINTF_LL_WIDTH "d""%s", val, end); + } +} + +static void +print_io_api(long io_types) +{ + if (io_types & SIO_POSIX) + HDfprintf(output, "posix "); + if (io_types & SIO_HDF5) + HDfprintf(output, "hdf5 "); + HDfprintf(output, "\n"); +} + +static void +report_parameters(struct options *opts) +{ + int i, rank; + rank = opts->dset_rank; + + print_version("HDF5 Library"); /* print library version */ + HDfprintf(output, "==== Parameters ====\n"); + + HDfprintf(output, "IO API="); + print_io_api(opts->io_types); + + HDfprintf(output, "Number of iterations=%d\n", + opts->num_iters); + + HDfprintf(output, "Dataset size="); + + for (i=0; i<rank; i++) + recover_size_and_print((long long)opts->dset_size[i], " "); + HDfprintf(output, "\n"); + + + HDfprintf(output, "Transfer buffer size="); + for (i=0; i<rank; i++) + recover_size_and_print((long long)opts->buf_size[i], " "); + HDfprintf(output, "\n"); + + HDfprintf(output, "Dimension access order="); + for (i=0; i<rank; i++) + recover_size_and_print((long long)opts->order[i], " "); + HDfprintf(output, "\n"); + + if (opts->io_types & SIO_HDF5) { + + HDfprintf(output, "HDF5 data storage method="); + + if (opts->h5_use_chunks){ + + HDfprintf(output, "Chunked\n"); + HDfprintf(output, "HDF5 chunk size="); + for (i=0; i<rank; i++) + recover_size_and_print((long long)opts->chk_size[i], " "); + HDfprintf(output, "\n"); + + HDfprintf(output, "HDF5 dataset dimensions="); + if (opts->h5_extendable) { + HDfprintf(output, "Extendable\n"); + } + else { + HDfprintf(output, "Fixed\n"); + } + } + else { + HDfprintf(output, "Contiguous\n"); + } + + HDfprintf(output, "HDF5 file driver="); + if (opts->vfd==sec2) { + HDfprintf(output, "sec2\n"); + } else if (opts->vfd==stdio) { + HDfprintf(output, "stdio\n"); + } else if (opts->vfd==core) { + HDfprintf(output, "core\n"); + } else if (opts->vfd==split) { + HDfprintf(output, "split\n"); + } else if (opts->vfd==multi) { + HDfprintf(output, "multi\n"); + } else if (opts->vfd==family) { + HDfprintf(output, "family\n"); + } else if (opts->vfd==direct) { + HDfprintf(output, "direct\n"); + } + } + + { + char *prefix = HDgetenv("HDF5_PREFIX"); + + HDfprintf(output, "Env HDF5_PREFIX=%s\n", + (prefix ? prefix : "not set")); + } + + HDfprintf(output, "==== End of Parameters ====\n"); + HDfprintf(output, "\n"); +} + +/* + * Function: parse_command_line + * Purpose: Parse the command line options and return a STRUCT OPTIONS + * structure which will need to be freed by the calling function. + * Return: Pointer to an OPTIONS structure + * Programmer: Bill Wendling, 31. October 2001 + * Modifications: + * Added multidimensional testing (Christian Chilan, April, 2008) + */ +static struct options * +parse_command_line(int argc, char *argv[]) +{ + int opt; + struct options *cl_opts; + int i, default_rank, actual_rank, ranks[4]; + + cl_opts = (struct options *)HDmalloc(sizeof(struct options)); + + cl_opts->output_file = NULL; + cl_opts->io_types = 0; /* will set default after parsing options */ + cl_opts->num_iters = 1; + + default_rank = 2; + + cl_opts->dset_rank = 0; + cl_opts->buf_rank = 0; + cl_opts->chk_rank = 0; + cl_opts->order_rank = 0; + + for(i = 0; i < MAX_DIMS; i++) { + cl_opts->buf_size[i] = (size_t)((i + 1) * 10); + cl_opts->dset_size[i] = (hsize_t)((i + 1) * 100); + cl_opts->chk_size[i] = (size_t)((i + 1) * 10); + cl_opts->order[i] = i + 1; + } + + cl_opts->vfd = sec2; + + cl_opts->print_times = FALSE; /* Printing times is off by default */ + cl_opts->print_raw = FALSE; /* Printing raw data throughput is off by default */ + cl_opts->h5_alignment = 1; /* No alignment for HDF5 objects by default */ + cl_opts->h5_threshold = 1; /* No threshold for aligning HDF5 objects by default */ + cl_opts->h5_use_chunks = FALSE; /* Don't chunk the HDF5 dataset by default */ + cl_opts->h5_write_only = FALSE; /* Do both read and write by default */ + cl_opts->h5_extendable = FALSE; /* Use extendable dataset */ + cl_opts->verify = FALSE; /* No Verify data correctness by default */ + + while ((opt = get_option(argc, (const char **)argv, s_opts, l_opts)) != EOF) { + switch ((char)opt) { + case 'a': + cl_opts->h5_alignment = parse_size_directive(opt_arg); + break; + case 'A': + { + const char *end = opt_arg; + while (end && *end != '\0') { + char buf[10]; + + HDmemset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + if (!HDstrcasecmp(buf, "hdf5")) { + cl_opts->io_types |= SIO_HDF5; + } else if (!HDstrcasecmp(buf, "posix")) { + cl_opts->io_types |= SIO_POSIX; + } else { + fprintf(stderr, "sio_perf: invalid --api option %s\n", + buf); + exit(EXIT_FAILURE); + } + + if (*end == '\0') + break; + + end++; + } + } + + break; +#if 0 + case 'b': + /* the future "binary" option */ + break; +#endif /* 0 */ + case 'c': + /* Turn on chunked HDF5 dataset creation */ + cl_opts->h5_use_chunks = 1; + { + const char *end = opt_arg; + int j = 0; + + while (end && *end != '\0') { + char buf[10]; + + HDmemset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + cl_opts->chk_size[j] = parse_size_directive(buf); + + j++; + + if (*end == '\0') + break; + + end++; + } + cl_opts->chk_rank = j; + } + + break; + + + case 'D': + { + const char *end = opt_arg; + + while (end && *end != '\0') { + char buf[10]; + + HDmemset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + if (strlen(buf) > 1 || isdigit(buf[0])) { + size_t j; + + for (j = 0; j < 10 && buf[j] != '\0'; ++j) + if (!isdigit(buf[j])) { + fprintf(stderr, "sio_perf: invalid --debug option %s\n", + buf); + exit(EXIT_FAILURE); + } + + sio_debug_level = atoi(buf); + + if (sio_debug_level > 4) + sio_debug_level = 4; + else if (sio_debug_level < 0) + sio_debug_level = 0; + } else { + switch (*buf) { + case 'r': + /* Turn on raw data throughput info */ + cl_opts->print_raw = TRUE; + break; + case 't': + /* Turn on time printing */ + cl_opts->print_times = TRUE; + break; + case 'v': + /* Turn on verify data correctness*/ + cl_opts->verify = TRUE; + break; + default: + fprintf(stderr, "sio_perf: invalid --debug option %s\n", buf); + exit(EXIT_FAILURE); + } + } + + if (*end == '\0') + break; + + end++; + } + } + + break; + case 'e': + { + const char *end = opt_arg; + int j = 0; + + while (end && *end != '\0') { + char buf[10]; + + HDmemset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + cl_opts->dset_size[j] = parse_size_directive(buf); + + j++; + + if (*end == '\0') + break; + + end++; + } + cl_opts->dset_rank = j; + } + + break; + + case 'i': + cl_opts->num_iters = atoi(opt_arg); + break; + case 'o': + cl_opts->output_file = opt_arg; + break; + case 'T': + cl_opts->h5_threshold = parse_size_directive(opt_arg); + break; + case 'v': + if (!HDstrcasecmp(opt_arg, "sec2")) { + cl_opts->vfd=sec2; + } else if (!HDstrcasecmp(opt_arg, "stdio")) { + cl_opts->vfd=stdio; + } else if (!HDstrcasecmp(opt_arg, "core")) { + cl_opts->vfd=core; + } else if (!HDstrcasecmp(opt_arg, "split")) { + cl_opts->vfd=split; + } else if (!HDstrcasecmp(opt_arg, "multi")) { + cl_opts->vfd=multi; + } else if (!HDstrcasecmp(opt_arg, "family")) { + cl_opts->vfd=family; + } else if (!HDstrcasecmp(opt_arg, "direct")) { + cl_opts->vfd=direct; + } else { + fprintf(stderr, "sio_perf: invalid --api option %s\n", + opt_arg); + exit(EXIT_FAILURE); + } + break; + case 'w': + cl_opts->h5_write_only = TRUE; + break; + case 't': + cl_opts->h5_extendable = TRUE; + break; + case 'x': + { + const char *end = opt_arg; + int j = 0; + + while (end && *end != '\0') { + char buf[10]; + + HDmemset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + cl_opts->buf_size[j] = parse_size_directive(buf); + + j++; + + if (*end == '\0') + break; + + end++; + } + cl_opts->buf_rank = j; + } + + break; + + case 'r': + { + const char *end = opt_arg; + int j = 0; + + while (end && *end != '\0') { + char buf[10]; + + HDmemset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + cl_opts->order[j] = (int)parse_size_directive(buf); + + j++; + + if (*end == '\0') + break; + + end++; + } + + cl_opts->order_rank = j; + } + + break; + + case 'h': + case '?': + default: + usage(progname); + free(cl_opts); + return NULL; + } + } + + /* perform rank consistency analysis */ + actual_rank = 0; + + ranks[0] = cl_opts->dset_rank; + ranks[1] = cl_opts->buf_rank; + ranks[2] = cl_opts->order_rank; + ranks[3] = cl_opts->chk_rank; + + for (i=0; i<4; i++) { + if (ranks[i]>0) { + if (!actual_rank) { + actual_rank = ranks[i]; + } + else { + if (actual_rank != ranks[i]) + exit(EXIT_FAILURE); + } + } + } + + if (!actual_rank) + actual_rank = default_rank; + + cl_opts->dset_rank = actual_rank; + cl_opts->buf_rank = actual_rank; + cl_opts->order_rank = actual_rank; + cl_opts->chk_rank = actual_rank; + + for (i=0; i<actual_rank; i++) { + if (cl_opts->order[i] > actual_rank) { + exit(EXIT_FAILURE); + } + } + + /* set default if none specified yet */ + if (!cl_opts->io_types) + cl_opts->io_types = SIO_HDF5 | SIO_POSIX; /* run all API */ + + /* verify parameters sanity. Adjust if needed. */ + /* cap xfer_size with bytes per process */ + if (cl_opts->num_iters <= 0) + cl_opts->num_iters = 1; + + return cl_opts; +} + +/* + * Function: parse_size_directive + * Purpose: Parse the size directive passed on the commandline. The size + * directive is an integer followed by a size indicator: + * + * K, k - Kilobyte + * M, m - Megabyte + * G, g - Gigabyte + * + * Return: The size as a off_t because this is related to file size. + * If an unknown size indicator is used, then the program will + * exit with EXIT_FAILURE as the return value. + * Programmer: Bill Wendling, 18. December 2001 + * Modifications: + */ + +static hsize_t +parse_size_directive(const char *size) +{ + hsize_t s; + char *endptr; + + s = HDstrtoull(size, &endptr, 10); + + if (endptr && *endptr) { + while (*endptr != '\0' && (*endptr == ' ' || *endptr == '\t')) + ++endptr; + + switch (*endptr) { + case 'K': + case 'k': + s *= ONE_KB; + break; + + case 'M': + case 'm': + s *= ONE_MB; + break; + + case 'G': + case 'g': + s *= ONE_GB; + break; + + default: + fprintf(stderr, "Illegal size specifier '%c'\n", *endptr); + exit(EXIT_FAILURE); + } + } + + return s; +} + +/* + * Function: usage + * Purpose: Print a usage message and then exit. + * Return: Nothing + * Programmer: Bill Wendling, 31. October 2001 + * Modifications: + */ +static void +usage(const char *prog) +{ + print_version(prog); + printf("usage: %s [OPTIONS]\n", prog); + printf(" OPTIONS\n"); + printf(" -h Print an usage message and exit\n"); + printf(" -A AL Which APIs to test\n"); + printf(" [default: all of them]\n"); + printf(" -c SL Selects chunked storage and defines chunks dimensions\n"); + printf(" and sizes\n"); + printf(" [default: Off]\n"); + printf(" -e SL Dimensions and sizes of dataset\n"); + printf(" [default: 100,200]\n"); + printf(" -i N Number of iterations to perform\n"); + printf(" [default: 1]\n"); + printf(" -r NL Dimension access order (see below for description)\n"); + printf(" [default: 1,2]\n"); + printf(" -t Selects extendable dimensions for HDF5 dataset\n"); + printf(" [default: Off]\n"); + printf(" -v VFD Selects file driver for HDF5 access\n"); + printf(" [default: sec2]\n"); + printf(" -w Perform write tests, not the read tests\n"); + printf(" [default: Off]\n"); + printf(" -x SL Dimensions and sizes of the transfer buffer\n"); + printf(" [default: 10,20]\n"); + printf("\n"); + printf(" N - is an integer > 0.\n"); + printf("\n"); + printf(" S - is a size specifier, an integer > 0 followed by a size indicator:\n"); + printf(" K - Kilobyte (%d)\n", ONE_KB); + printf(" M - Megabyte (%d)\n", ONE_MB); + printf(" G - Gigabyte (%d)\n", ONE_GB); + printf("\n"); + printf(" Example: '37M' is 37 megabytes or %d bytes\n", 37*ONE_MB); + printf("\n"); + printf(" AL - is an API list. Valid values are:\n"); + printf(" hdf5 - HDF5\n"); + printf(" posix - POSIX\n"); + printf("\n"); + printf(" Example: -A posix,hdf5\n"); + printf("\n"); + printf(" NL - is list of integers (N) separated by commas.\n"); + printf("\n"); + printf(" Example: 1,2,3\n"); + printf("\n"); + printf(" SL - is list of size specifiers (S) separated by commas.\n"); + printf("\n"); + printf(" Example: 2K,2K,3K\n"); + printf("\n"); + printf(" The example defines an object (dataset, tranfer buffer) with three\n"); + printf(" dimensions. Be aware that as the number of dimensions increases, the\n"); + printf(" the total size of the object increases exponentially.\n"); + printf("\n"); + printf(" VFD - is an HDF5 file driver specifier. Valid values are:\n"); + printf(" sec2, stdio, core, split, multi, family, direct\n"); + printf("\n"); + printf(" Dimension access order:\n"); + printf(" Data access starts at the cardinal origin of the dataset using the\n"); + printf(" transfer buffer. The next access occurs on a dataset region next to\n"); + printf(" the previous one. For a multidimensional dataset, there are several\n"); + printf(" directions as to where to proceed. This can be specified in the dimension\n"); + printf(" access order. For example, -r 1,2 states that the tool should traverse\n"); + printf(" dimension 1 first, and then dimension 2.\n"); + printf("\n"); + printf(" Environment variables:\n"); + printf(" HDF5_NOCLEANUP Do not remove data files if set [default remove]\n"); + printf(" HDF5_PREFIX Data file prefix\n"); + printf("\n"); + fflush(stdout); +} /* end usage() */ + diff --git a/tools/test/perform/sio_perf.h b/tools/test/perform/sio_perf.h new file mode 100644 index 0000000..7a7d708 --- /dev/null +++ b/tools/test/perform/sio_perf.h @@ -0,0 +1,104 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef SIO_PERF_H__ +#define SIO_PERF_H__ + +#include "io_timer.h" +#ifndef STANDALONE +#include "H5private.h" +#include "h5test.h" +#include "h5tools.h" +#include "h5tools_utils.h" +#else +#include "sio_standalone.h" +#endif + +/* setup the dataset no fill option if this is v1.5 or more */ +#if H5_VERS_MAJOR > 1 || H5_VERS_MINOR > 4 +#define H5_HAVE_NOFILL 1 +#endif + +#define MAX_DIMS 32 + +typedef enum iotype_ { + POSIXIO, + HDF5 + /*NUM_TYPES*/ +} iotype; + +typedef enum vfdtype_ { + sec2, + stdio, + core, + split, + multi, + family, + direct + /*NUM_TYPES*/ +} vfdtype; + +typedef struct parameters_ { + iotype io_type; /* The type of IO test to perform */ + vfdtype vfd; + long num_files; /* Number of files to create */ + long num_dsets; /* Number of datasets to create */ + hsize_t num_bytes; /* Number of bytes in each dset */ + int num_iters; /* Number of times to loop doing the IO */ + int rank; /* Rank of dataset */ + hsize_t dset_size[MAX_DIMS]; /* Dataset size */ + size_t buf_size[MAX_DIMS]; /* Buffer size */ + size_t chk_size[MAX_DIMS]; /* Chunk size */ + int order[MAX_DIMS]; /* Buffer size */ + hsize_t h5_align; /* HDF5 object alignment */ + hsize_t h5_thresh; /* HDF5 object alignment threshold */ + int h5_use_chunks; /* Make HDF5 dataset chunked */ + int h5_extendable; /* Make HDF5 dataset chunked */ + int h5_write_only; /* Perform the write tests only */ + int verify; /* Verify data correctness */ +} parameters; + +typedef struct results_ { + herr_t ret_code; + io_time_t *timers; +} results; + +#ifndef SUCCESS +#define SUCCESS 0 +#endif /* !SUCCESS */ + +#ifndef FAIL +#define FAIL -1 +#endif /* !FAIL */ + +extern FILE *output; /* output file */ +extern io_time_t *timer_g; /* timer: global for stub functions */ +extern int sio_debug_level; /* The debug level: + * 0 - Off + * 1 - Minimal + * 2 - Some more + * 3 - Maximal + * 4 - Even More Debugging (timer stuff) + */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern void do_sio(parameters param, results *res); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PIO_PERF_H__ */ diff --git a/tools/test/perform/sio_standalone.c b/tools/test/perform/sio_standalone.c new file mode 100644 index 0000000..d92ed30 --- /dev/null +++ b/tools/test/perform/sio_standalone.c @@ -0,0 +1,285 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +/* This file contains the definition of functions required to build h5perf in + * STANDALONE mode. + * Created: Christian Chilan, 2005/5/18. + */ + +#include "sio_perf.h" + + +/** From h5tools_utils.c **/ + +/* global variables */ +int nCols = 80; + +/* ``get_option'' variables */ +int opt_err = 1; /*get_option prints errors if this is on */ +int opt_ind = 1; /*token pointer */ +const char *opt_arg; /*flag argument (or value) */ + + +int +get_option(int argc, const char **argv, const char *opts, const struct long_options *l_opts) +{ + static int sp = 1; /* character index in current token */ + int opt_opt = '?'; /* option character passed back to user */ + + if (sp == 1) { + /* check for more flag-like tokens */ + if (opt_ind >= argc || argv[opt_ind][0] != '-' || argv[opt_ind][1] == '\0') { + return EOF; + } else if (HDstrcmp(argv[opt_ind], "--") == 0) { + opt_ind++; + return EOF; + } + } + + if (sp == 1 && argv[opt_ind][0] == '-' && argv[opt_ind][1] == '-') { + /* long command line option */ + const char *arg = &argv[opt_ind][2]; + int i; + + for (i = 0; l_opts && l_opts[i].name; i++) { + size_t len = HDstrlen(l_opts[i].name); + + if (HDstrncmp(arg, l_opts[i].name, len) == 0) { + /* we've found a matching long command line flag */ + opt_opt = l_opts[i].shortval; + + if (l_opts[i].has_arg != no_arg) { + if (arg[len] == '=') { + opt_arg = &arg[len + 1]; + } else if (opt_ind < (argc - 1) && argv[opt_ind + 1][0] != '-') { + opt_arg = argv[++opt_ind]; + } else if (l_opts[i].has_arg == require_arg) { + if (opt_err) + HDfprintf(stderr, + "%s: option required for \"--%s\" flag\n", + argv[0], arg); + + opt_opt = '?'; + } + } else { + if (arg[len] == '=') { + if (opt_err) + HDfprintf(stderr, + "%s: no option required for \"%s\" flag\n", + argv[0], arg); + + opt_opt = '?'; + } + + opt_arg = NULL; + } + + break; + } + } + + if (l_opts[i].name == NULL) { + /* exhausted all of the l_opts we have and still didn't match */ + if (opt_err) + HDfprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], arg); + + opt_opt = '?'; + } + + opt_ind++; + sp = 1; + } else { + register char *cp; /* pointer into current token */ + + /* short command line option */ + opt_opt = argv[opt_ind][sp]; + + if (opt_opt == ':' || (cp = strchr(opts, opt_opt)) == 0) { + + if (opt_err) + HDfprintf(stderr, "%s: unknown option \"%c\"\n", + argv[0], opt_opt); + + /* if no chars left in this token, move to next token */ + if (argv[opt_ind][++sp] == '\0') { + opt_ind++; + sp = 1; + } + + return '?'; + } + + if (*++cp == ':') { + /* if a value is expected, get it */ + if (argv[opt_ind][sp + 1] != '\0') { + /* flag value is rest of current token */ + opt_arg = &argv[opt_ind++][sp + 1]; + } else if (++opt_ind >= argc) { + if (opt_err) + HDfprintf(stderr, + "%s: value expected for option \"%c\"\n", + argv[0], opt_opt); + + opt_opt = '?'; + } else { + /* flag value is next token */ + opt_arg = argv[opt_ind++]; + } + + sp = 1; + } else { + /* set up to look at next char in token, next time */ + if (argv[opt_ind][++sp] == '\0') { + /* no more in current token, so setup next token */ + opt_ind++; + sp = 1; + } + + opt_arg = NULL; + } + } + + /* return the current flag character found */ + return opt_opt; +} + + +void +print_version(const char *progname) +{ + printf("%s: Version %u.%u.%u%s%s\n", + progname, H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE, + H5_VERS_SUBRELEASE[0] ? "-" : "", H5_VERS_SUBRELEASE); +} + + + +/** From h5test.c **/ + +#ifdef H5_HAVE_PARALLEL +MPI_Info h5_io_info_g=MPI_INFO_NULL;/* MPI INFO object for IO */ +#endif + +#if 0 +int +h5_set_info_object(void) +{ + char *envp; /* environment pointer */ + int ret_value=0; + + /* handle any MPI INFO hints via $HDF5_MPI_INFO */ + if ((envp = getenv("HDF5_MPI_INFO")) != NULL){ + char *next, *valp; + + + valp = envp = next = HDstrdup(envp); + + /* create an INFO object if not created yet */ + if (h5_io_info_g == MPI_INFO_NULL) + MPI_Info_create(&h5_io_info_g); + + do { + size_t len; + char *key_val, *endp, *namep; + + if (*valp == ';') + valp++; + + /* copy key/value pair into temporary buffer */ + len = strcspn(valp, ";"); + next = &valp[len]; + key_val = calloc(1, len + 1); + + /* increment the next pointer past the terminating semicolon */ + if (*next == ';') + ++next; + + namep = HDstrncpy(key_val, valp, len); + + /* pass up any beginning whitespaces */ + while (*namep && (*namep == ' ' || *namep == '\t')) + namep++; + + /* eat up any ending white spaces */ + endp = &namep[strlen(namep) - 1]; + + while (endp && (*endp == ' ' || *endp == '\t')) + *endp-- = '\0'; + + /* find the '=' */ + + valp = HDstrchr(namep, '='); + + if (valp != NULL) { /* it's a valid key/value pairing */ + char *tmp_val = valp + 1; + + /* change '=' to \0, move valp down one */ + *valp-- = '\0'; + + /* eat up ending whitespace on the "key" part */ + while (*valp == ' ' || *valp == '\t') + *valp-- = '\0'; + + valp = tmp_val; + + /* eat up beginning whitespace on the "value" part */ + while (*valp == ' ' || *valp == '\t') + *valp++ = '\0'; + + /* actually set the darned thing */ + if (MPI_SUCCESS != MPI_Info_set(h5_io_info_g, namep, valp)) { + printf("MPI_Info_set failed\n"); + ret_value = -1; + } + } + + valp = next; + HDfree(key_val); + } while (next && *next); + + HDfree(envp); + } + + return ret_value; +} + + +void +h5_dump_info_object(MPI_Info info) +{ + char key[MPI_MAX_INFO_KEY+1]; + char value[MPI_MAX_INFO_VAL+1]; + int flag; + int i, nkeys; + + printf("Dumping MPI Info Object(%d) (up to %d bytes per item):\n", (int)info, + MPI_MAX_INFO_VAL); + if (info==MPI_INFO_NULL){ + printf("object is MPI_INFO_NULL\n"); + } + else { + MPI_Info_get_nkeys(info, &nkeys); + printf("object has %d items\n", nkeys); + for (i=0; i<nkeys; i++){ + MPI_Info_get_nthkey(info, i, key); + MPI_Info_get(info, key, MPI_MAX_INFO_VAL, value, &flag); + printf("%s=%s\n", key, value); + } + + } +} + +#endif + diff --git a/tools/test/perform/sio_standalone.h b/tools/test/perform/sio_standalone.h new file mode 100644 index 0000000..63dc7b0 --- /dev/null +++ b/tools/test/perform/sio_standalone.h @@ -0,0 +1,541 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef SIO_STANDALONE_H__ +#define SIO_PERF_H__ + +/* Header file for building h5perf by standalone mode. + * Created: Christian Chilan, 2005/5/18. + */ + +/** From H5private.h **/ + +#include "H5public.h" /* Include Public Definitions */ + + +/* + * Include ANSI-C header files. + */ +#ifdef H5_STDC_HEADERS +# include <assert.h> +# include <ctype.h> +# include <errno.h> +# include <fcntl.h> +# include <float.h> +# include <limits.h> +# include <math.h> +# include <signal.h> +# include <stdarg.h> +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +#endif + +/* maximum of two, three, or four values */ +#undef MAX +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) +#define MAX2(a,b) MAX(a,b) +#define MAX3(a,b,c) MAX(a,MAX(b,c)) +#define MAX4(a,b,c,d) MAX(MAX(a,b),MAX(c,d)) + +/* + * A macro to portably increment enumerated types. + */ +#ifndef H5_INC_ENUM +# define H5_INC_ENUM(TYPE,VAR) (VAR)=((TYPE)((VAR)+1)) +#endif + +#define H5_FLT_ABS_EQUAL(X,Y) (HDfabsf((X)-(Y)) < FLT_EPSILON) +#define H5_DBL_ABS_EQUAL(X,Y) (HDfabs ((X)-(Y)) < DBL_EPSILON) +#define H5_LDBL_ABS_EQUAL(X,Y) (HDfabsl((X)-(Y)) < LDBL_EPSILON) + +#define H5_FLT_REL_EQUAL(X,Y,M) (HDfabsf(((Y)-(X)) / (X)) < (M)) +#define H5_DBL_REL_EQUAL(X,Y,M) (HDfabs (((Y)-(X)) / (X)) < (M)) +#define H5_LDBL_REL_EQUAL(X,Y,M) (HDfabsl(((Y)-(X)) / (X)) < (M)) + +/* + * Redefine all the POSIX functions. We should never see a POSIX + * function (or any other non-HDF5 function) in the source! + */ +#define HDabort() abort() +#define HDabs(X) abs(X) +#ifdef H5_HAVE_WIN32_API +#define HDaccess(F,M) _access(F, M) +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +#define X_OK 1 /* Test for execute permission. */ +#define F_OK 0 /* Test for existence. */ +#else /* H5_HAVE_WIN32_API */ +#define HDaccess(F,M) access(F, M) +#endif /* H5_HAVE_WIN32_API */ +#define HDacos(X) acos(X) +#ifdef H5_HAVE_ALARM +#define HDalarm(N) alarm(N) +#else /* H5_HAVE_ALARM */ +#define HDalarm(N) (0) +#endif /* H5_HAVE_ALARM */ +#define HDasctime(T) asctime(T) +#define HDasin(X) asin(X) +#define HDasprintf asprintf /*varargs*/ +#define HDassert(X) assert(X) +#define HDatan(X) atan(X) +#define HDatan2(X,Y) atan2(X,Y) +#define HDatexit(F) atexit(F) +#define HDatof(S) atof(S) +#define HDatoi(S) atoi(S) +#define HDatol(S) atol(S) +#define HDbsearch(K,B,N,Z,F) bsearch(K,B,N,Z,F) +#define HDcalloc(N,Z) calloc(N,Z) +#define HDceil(X) ceil(X) +#define HDcfgetispeed(T) cfgetispeed(T) +#define HDcfgetospeed(T) cfgetospeed(T) +#define HDcfsetispeed(T,S) cfsetispeed(T,S) +#define HDcfsetospeed(T,S) cfsetospeed(T,S) +#define HDchdir(S) chdir(S) +#define HDchmod(S,M) chmod(S,M) +#define HDchown(S,O,G) chown(S,O,G) +#define HDclearerr(F) clearerr(F) +#define HDclock() clock() +#define HDclose(F) close(F) +#define HDclosedir(D) closedir(D) +#define HDcos(X) cos(X) +#define HDcosh(X) cosh(X) +#define HDcreat(S,M) creat(S,M) +#define HDctermid(S) ctermid(S) +#define HDctime(T) ctime(T) +#define HDcuserid(S) cuserid(S) +#ifdef H5_HAVE_DIFFTIME +#define HDdifftime(X,Y) difftime(X,Y) +#else +#define HDdifftime(X,Y) ((double)(X)-(double)(Y)) +#endif +#define HDdiv(X,Y) div(X,Y) +#define HDdup(F) dup(F) +#define HDdup2(F,I) dup2(F,I) +/* execl() variable arguments */ +/* execle() variable arguments */ +/* execlp() variable arguments */ +#define HDexecv(S,AV) execv(S,AV) +#define HDexecve(S,AV,E) execve(S,AV,E) +#define HDexecvp(S,AV) execvp(S,AV) +#define HDexit(N) exit(N) +#define HD_exit(N) _exit(N) +#define HDexp(X) exp(X) +#define HDfabs(X) fabs(X) +/* use ABS() because fabsf() fabsl() are not common yet. */ +#define HDfabsf(X) ABS(X) +#define HDfabsl(X) ABS(X) +#define HDfclose(F) fclose(F) +/* fcntl() variable arguments */ +#define HDfdopen(N,S) fdopen(N,S) +#define HDfeof(F) feof(F) +#define HDferror(F) ferror(F) +#define HDfflush(F) fflush(F) +#define HDfgetc(F) fgetc(F) +#define HDfgetpos(F,P) fgetpos(F,P) +#define HDfgets(S,N,F) fgets(S,N,F) +#ifdef H5_HAVE_WIN32_API +#define HDfileno(F) _fileno(F) +#else /* H5_HAVE_WIN32_API */ +#define HDfileno(F) fileno(F) +#endif /* H5_HAVE_WIN32_API */ +#define HDfloor(X) floor(X) +#define HDfmod(X,Y) fmod(X,Y) +#define HDfopen(S,M) fopen(S,M) +#define HDfork() fork() +#define HDfpathconf(F,N) fpathconf(F,N) +H5_DLL int HDfprintf (FILE *stream, const char *fmt, ...); +#define HDfputc(C,F) fputc(C,F) +#define HDfputs(S,F) fputs(S,F) +#define HDfread(M,Z,N,F) fread(M,Z,N,F) +#define HDfree(M) free(M) +#define HDfreopen(S,M,F) freopen(S,M,F) +#define HDfrexp(X,N) frexp(X,N) +/* Check for Cray-specific 'frexpf()' and 'frexpl()' routines */ +#ifdef H5_HAVE_FREXPF +#define HDfrexpf(X,N) frexpf(X,N) +#else /* H5_HAVE_FREXPF */ +#define HDfrexpf(X,N) frexp(X,N) +#endif /* H5_HAVE_FREXPF */ +#ifdef H5_HAVE_FREXPL +#define HDfrexpl(X,N) frexpl(X,N) +#else /* H5_HAVE_FREXPL */ +#define HDfrexpl(X,N) frexp(X,N) +#endif /* H5_HAVE_FREXPL */ +/* fscanf() variable arguments */ +#ifdef H5_HAVE_FSEEKO + #define HDfseek(F,O,W) fseeko(F,O,W) +#else + #define HDfseek(F,O,W) fseek(F,O,W) +#endif +#define HDfsetpos(F,P) fsetpos(F,P) +/* definitions related to the file stat utilities. + * Windows have its own function names. + * For Unix, if off_t is not 64bit big, try use the pseudo-standard + * xxx64 versions if available. + */ +#ifdef H5_HAVE_WIN32_API + #define HDfstat(F,B) _fstati64(F,B) + #define HDlstat(S,B) _lstati64(S,B) + #define HDstat(S,B) _stati64(S,B) + typedef struct _stati64 h5_stat_t; + typedef __int64 h5_stat_size_t; + #define HDoff_t __int64 +#elif H5_SIZEOF_OFF_T!=8 && H5_SIZEOF_OFF64_T==8 && defined(H5_HAVE_STAT64) + #define HDfstat(F,B) fstat64(F,B) + #define HDlstat(S,B) lstat64(S,B) + #define HDstat(S,B) stat64(S,B) + typedef struct stat64 h5_stat_t; + typedef off64_t h5_stat_size_t; + #define HDoff_t off64_t +#else + #define HDfstat(F,B) fstat(F,B) + #define HDlstat(S,B) lstat(S,B) + #define HDstat(S,B) stat(S,B) + typedef struct stat h5_stat_t; + typedef off_t h5_stat_size_t; + #define HDoff_t off_t +#endif + +#ifndef H5_HAVE_WIN32_API +/* These definitions differ in Windows and are defined in + * H5win32defs for that platform. + */ +typedef struct stat h5_stat_t; +typedef off_t h5_stat_size_t; +#define HDoff_t off_t +#endif /* H5_HAVE_WIN32_API */ + +#define HDftell(F) ftell(F) +#define HDftruncate(F,L) ftruncate(F,L) +#define HDfwrite(M,Z,N,F) fwrite(M,Z,N,F) +#define HDgetc(F) getc(F) +#define HDgetchar() getchar() +#define HDgetcwd(S,Z) getcwd(S,Z) +#define HDgetegid() getegid() +#define HDgetenv(S) getenv(S) +#define HDgeteuid() geteuid() +#define HDgetgid() getgid() +#define HDgetgrgid(G) getgrgid(G) +#define HDgetgrnam(S) getgrnam(S) +#define HDgetgroups(Z,G) getgroups(Z,G) +#ifdef H5_HAVE_WIN32_API +#define HDgetlogin() Wgetlogin() +#else /* H5_HAVE_WIN32_API */ +#define HDgetlogin() getlogin() +#endif /* H5_HAVE_WIN32_API */ +#define HDgetpgrp() getpgrp() +#define HDgetpid() getpid() +#define HDgetppid() getppid() +#define HDgetpwnam(S) getpwnam(S) +#define HDgetpwuid(U) getpwuid(U) +#define HDgetrusage(X,S) getrusage(X,S) +#define HDgets(S) gets(S) +#ifdef H5_HAVE_WIN32_API + H5_DLL int Wgettimeofday(struct timeval *tv, struct timezone *tz); +#define HDgettimeofday(V,Z) Wgettimeofday(V,Z) +#else /* H5_HAVE_WIN32_API */ +#define HDgettimeofday(S,P) gettimeofday(S,P) +#endif /* H5_HAVE_WIN32_API */ +#define HDgetuid() getuid() +#define HDgmtime(T) gmtime(T) +#define HDisalnum(C) isalnum((int)(C)) /*cast for solaris warning*/ +#define HDisalpha(C) isalpha((int)(C)) /*cast for solaris warning*/ +#define HDisatty(F) isatty(F) +#define HDiscntrl(C) iscntrl((int)(C)) /*cast for solaris warning*/ +#define HDisdigit(C) isdigit((int)(C)) /*cast for solaris warning*/ +#define HDisgraph(C) isgraph((int)(C)) /*cast for solaris warning*/ +#define HDislower(C) islower((int)(C)) /*cast for solaris warning*/ +#define HDisprint(C) isprint((int)(C)) /*cast for solaris warning*/ +#define HDispunct(C) ispunct((int)(C)) /*cast for solaris warning*/ +#define HDisspace(C) isspace((int)(C)) /*cast for solaris warning*/ +#define HDisupper(C) isupper((int)(C)) /*cast for solaris warning*/ +#define HDisxdigit(C) isxdigit((int)(C)) /*cast for solaris warning*/ +#define HDkill(P,S) kill(P,S) +#define HDlabs(X) labs(X) +#define HDldexp(X,N) ldexp(X,N) +#define HDldiv(X,Y) ldiv(X,Y) +#define HDlink(OLD,NEW) link(OLD,NEW) +#define HDlocaleconv() localeconv() +#define HDlocaltime(T) localtime(T) +#define HDlog(X) log(X) +#define HDlog10(X) log10(X) +#define HDlongjmp(J,N) longjmp(J,N) +#ifdef H5_HAVE_WIN32_API + #define HDlseek(F,O,W) _lseeki64(F,O,W) +#else + #ifdef H5_HAVE_LSEEK64 + #define HDlseek(F,O,W) lseek64(F,O,W) + #else + #define HDlseek(F,O,W) lseek(F,O,W) + #endif +#endif +#define HDmalloc(Z) malloc(Z) +#define HDposix_memalign(P,A,Z) posix_memalign(P,A,Z) +#define HDmblen(S,N) mblen(S,N) +#define HDmbstowcs(P,S,Z) mbstowcs(P,S,Z) +#define HDmbtowc(P,S,Z) mbtowc(P,S,Z) +#define HDmemchr(S,C,Z) memchr(S,C,Z) +#define HDmemcmp(X,Y,Z) memcmp(X,Y,Z) +/* + * The (char*) casts are required for the DEC when optimizations are turned + * on and the source and/or destination are not aligned. + */ +#define HDmemcpy(X,Y,Z) memcpy((char*)(X),(const char*)(Y),Z) +#define HDmemmove(X,Y,Z) memmove((char*)(X),(const char*)(Y),Z) +/* + * The (void*) cast just avoids a compiler warning in H5_HAVE_VISUAL_STUDIO + */ +#ifdef H5_HAVE_VISUAL_STUDIO +#define HDmemset(X,C,Z) memset((void*)(X),C,Z) +#else /* H5_HAVE_VISUAL_STUDIO */ +#define HDmemset(X,C,Z) memset(X,C,Z) +#endif /* H5_HAVE_VISUAL_STUDIO */ +#ifdef H5_HAVE_WIN32_API +#define HDmkdir(S,M) _mkdir(S) +#else /* H5_HAVE_WIN32_API */ +#define HDmkdir(S,M) mkdir(S,M) +#endif /* H5_HAVE_WIN32_API */ +#define HDmkfifo(S,M) mkfifo(S,M) +#define HDmktime(T) mktime(T) +#define HDmodf(X,Y) modf(X,Y) +#ifdef _O_BINARY +#define HDopen(S,F,M) open(S,F|_O_BINARY,M) +#else +#define HDopen(S,F,M) open(S,F,M) +#endif +#define HDopendir(S) opendir(S) +#define HDpathconf(S,N) pathconf(S,N) +#define HDpause() pause() +#define HDperror(S) perror(S) +#define HDpipe(F) pipe(F) +#define HDpow(X,Y) pow(X,Y) +/* printf() variable arguments */ +#define HDputc(C,F) putc(C,F) +#define HDputchar(C) putchar(C) +#define HDputs(S) puts(S) +#define HDqsort(M,N,Z,F) qsort(M,N,Z,F) +#define HDraise(N) raise(N) + +#ifdef H5_HAVE_RAND_R +#define HDrandom() HDrand() +H5_DLL int HDrand(void); +#elif H5_HAVE_RANDOM +#define HDrand() random() +#define HDrandom() random() +#else +#define HDrand() rand() +#define HDrandom() rand() +#endif + +#define HDread(F,M,Z) read(F,M,Z) +#define HDreaddir(D) readdir(D) +#define HDrealloc(M,Z) realloc(M,Z) +#define HDremove(S) remove(S) +#define HDrename(OLD,NEW) rename(OLD,NEW) +#define HDrewind(F) rewind(F) +#define HDrewinddir(D) rewinddir(D) +#define HDrmdir(S) rmdir(S) +/* scanf() variable arguments */ +#define HDsetbuf(F,S) setbuf(F,S) +#define HDsetgid(G) setgid(G) +#define HDsetjmp(J) setjmp(J) +#define HDsetlocale(N,S) setlocale(N,S) +#define HDsetpgid(P,PG) setpgid(P,PG) +#define HDsetsid() setsid() +#define HDsetuid(U) setuid(U) +/* Windows does not permit setting the buffer size to values + less than 2. */ +#ifndef H5_HAVE_WIN32_API +#define HDsetvbuf(F,S,M,Z) setvbuf(F,S,M,Z) +#else +#define HDsetvbuf(F,S,M,Z) setvbuf(F,S,M,(Z>1?Z:2)) +#endif +#define HDsigaddset(S,N) sigaddset(S,N) +#define HDsigdelset(S,N) sigdelset(S,N) +#define HDsigemptyset(S) sigemptyset(S) +#define HDsigfillset(S) sigfillset(S) +#define HDsigismember(S,N) sigismember(S,N) +#define HDsiglongjmp(J,N) siglongjmp(J,N) +#define HDsignal(N,F) signal(N,F) +#define HDsigpending(S) sigpending(S) +#define HDsigprocmask(H,S,O) sigprocmask(H,S,O) +#define HDsigsetjmp(J,N) sigsetjmp(J,N) +#define HDsigsuspend(S) sigsuspend(S) +#define HDsin(X) sin(X) +#define HDsinh(X) sinh(X) +#define HDsleep(N) sleep(N) +#ifdef H5_HAVE_WIN32_API +H5_DLL int c99_snprintf(char* str, size_t size, const char* format, ...); +#define HDsnprintf c99_snprintf /*varargs*/ +#else +#define HDsnprintf snprintf /*varargs*/ +#endif +/* sprintf() variable arguments */ +#define HDsqrt(X) sqrt(X) +#ifdef H5_HAVE_RAND_R +H5_DLL void HDsrand(unsigned int seed); +#define HDsrandom(S) HDsrand(S) +#elif H5_HAVE_RANDOM +#define HDsrand(S) srandom(S) +#define HDsrandom(S) srandom(S) +#else +#define HDsrand(S) srand(S) +#define HDsrandom(S) srand(S) +#endif +/* sscanf() variable arguments */ + +#ifdef H5_HAVE_WIN32_API +#define HDstrcasecmp(A,B) _stricmp(A,B) +#else +#define HDstrcasecmp(X,Y) strcasecmp(X,Y) +#endif +#define HDstrcat(X,Y) strcat(X,Y) +#define HDstrchr(S,C) strchr(S,C) +#define HDstrcmp(X,Y) strcmp(X,Y) +#define HDstrcoll(X,Y) strcoll(X,Y) +#define HDstrcpy(X,Y) strcpy(X,Y) +#define HDstrcspn(X,Y) strcspn(X,Y) +#define HDstrerror(N) strerror(N) +#define HDstrftime(S,Z,F,T) strftime(S,Z,F,T) +#define HDstrlen(S) strlen(S) +#define HDstrncat(X,Y,Z) strncat(X,Y,Z) +#define HDstrncmp(X,Y,Z) strncmp(X,Y,Z) +#define HDstrncpy(X,Y,Z) strncpy(X,Y,Z) +#define HDstrpbrk(X,Y) strpbrk(X,Y) +#define HDstrrchr(S,C) strrchr(S,C) +#define HDstrspn(X,Y) strspn(X,Y) +#define HDstrstr(X,Y) strstr(X,Y) +#define HDstrtod(S,R) strtod(S,R) +#define HDstrtok(X,Y) strtok(X,Y) +#define HDstrtol(S,R,N) strtol(S,R,N) +H5_DLL int64_t HDstrtoll (const char *s, const char **rest, int base); +#define HDstrtoul(S,R,N) strtoul(S,R,N) +#ifdef H5_HAVE_WIN32_API +#define HDstrtoull(S,R,N) _strtoui64(S,R,N) +#else +#define HDstrtoull(S,R,N) strtoull(S,R,N) +#endif +#define HDstrxfrm(X,Y,Z) strxfrm(X,Y,Z) +#define HDsysconf(N) sysconf(N) +#define HDsystem(S) system(S) +#define HDtan(X) tan(X) +#define HDtanh(X) tanh(X) +#define HDtcdrain(F) tcdrain(F) +#define HDtcflow(F,A) tcflow(F,A) +#define HDtcflush(F,N) tcflush(F,N) +#define HDtcgetattr(F,T) tcgetattr(F,T) +#define HDtcgetpgrp(F) tcgetpgrp(F) +#define HDtcsendbreak(F,N) tcsendbreak(F,N) +#define HDtcsetattr(F,O,T) tcsetattr(F,O,T) +#define HDtcsetpgrp(F,N) tcsetpgrp(F,N) +#define HDtime(T) time(T) +#define HDtimes(T) times(T) +#define HDtmpfile() tmpfile() +#define HDtmpnam(S) tmpnam(S) +#define HDtolower(C) tolower(C) +#define HDtoupper(C) toupper(C) +#define HDttyname(F) ttyname(F) +#define HDtzset() tzset() +#define HDumask(N) umask(N) +#define HDuname(S) uname(S) +#define HDungetc(C,F) ungetc(C,F) +#ifdef H5_HAVE_WIN32_API +#define HDunlink(S) _unlink(S) +#else +#define HDunlink(S) unlink(S) +#endif +#define HDutime(S,T) utime(S,T) +#define HDva_arg(A,T) va_arg(A,T) +#define HDva_end(A) va_end(A) +#define HDva_start(A,P) va_start(A,P) +#define HDvasprintf(RET,FMT,A) vasprintf(RET,FMT,A) +#define HDvfprintf(F,FMT,A) vfprintf(F,FMT,A) +#define HDvprintf(FMT,A) vprintf(FMT,A) +#define HDvsprintf(S,FMT,A) vsprintf(S,FMT,A) +#ifdef H5_HAVE_WIN32_API +H5_DLL int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap); +#define HDvsnprintf c99_vsnprintf +#else +# define HDvsnprintf(S,N,FMT,A) vsnprintf(S,N,FMT,A) +#endif +#define HDwait(W) wait(W) +#define HDwaitpid(P,W,O) waitpid(P,W,O) +#define HDwcstombs(S,P,Z) wcstombs(S,P,Z) +#define HDwctomb(S,C) wctomb(S,C) +#define HDwrite(F,M,Z) write(F,M,Z) + +/* + * And now for a couple non-Posix functions... Watch out for systems that + * define these in terms of macros. + */ +#ifdef H5_HAVE_WIN32_API +#define HDstrdup(S) _strdup(S) +#else /* H5_HAVE_WIN32_API */ + +#if !defined strdup && !defined H5_HAVE_STRDUP +extern char *strdup(const char *s); +#endif + +#define HDstrdup(S) strdup(S) + +#endif /* H5_HAVE_WIN32_API */ + +/* + * HDF Boolean type. + */ +#ifndef FALSE +# define FALSE false +#endif +#ifndef TRUE +# define TRUE true +#endif + +/** From h5test.h **/ + +#ifdef H5_HAVE_PARALLEL +extern MPI_Info h5_io_info_g; /* MPI INFO object for IO */ +#endif + +#ifdef H5_HAVE_PARALLEL +H5TEST_DLL int h5_set_info_object(void); +H5TEST_DLL void h5_dump_info_object(MPI_Info info); +#endif + + + +/** From h5tools_utils.h **/ + +extern int opt_err; /* getoption prints errors if this is on */ +extern int opt_ind; /* token pointer */ +extern const char *opt_arg; /* flag argument (or value) */ + + +enum { + no_arg = 0, /* doesn't take an argument */ + require_arg, /* requires an argument */ + optional_arg /* argument is optional */ +}; + + +typedef struct long_options { + const char *name; /* name of the long option */ + int has_arg; /* whether we should look for an arg */ + char shortval; /* the shortname equivalent of long arg + * this gets returned from get_option */ +} long_options; + +extern int get_option(int argc, const char **argv, const char *opt, + const struct long_options *l_opt); +#endif diff --git a/tools/test/perform/zip_perf.c b/tools/test/perform/zip_perf.c new file mode 100644 index 0000000..d9b1fa2 --- /dev/null +++ b/tools/test/perform/zip_perf.c @@ -0,0 +1,644 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* =========================================================================== + * Usage: zip_perf [-d] [-f] [-h] [-1 to -9] [files...] + * -d : decompress + * -f : compress with Z_FILTERED + * -h : compress with Z_HUFFMAN_ONLY + * -1 to -9 : compression level + */ + +/* our header files */ +#include "h5test.h" +#include "h5tools.h" +#include "h5tools_utils.h" + +#ifdef H5_HAVE_FILTER_DEFLATE + +#include <zlib.h> + +#define ONE_KB 1024 +#define ONE_MB (ONE_KB * ONE_KB) +#define ONE_GB (ONE_MB * ONE_KB) + +#define MICROSECOND 1000000.0F + +/* report 0.0 in case t is zero too */ +#define MB_PER_SEC(bytes,t) ((fabs(t) < (double)0.0000000001F) ? (double)0.0F : ((((double)bytes) / (double)ONE_MB) / (t))) + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ + +#ifndef FALSE +#define FALSE (!TRUE) +#endif /* FALSE */ + +#ifndef S_IRWXU +#define S_IRWXU (_S_IREAD|_S_IWRITE) +#endif + +/* internal variables */ +static const char *prog=NULL; +static const char *option_prefix=NULL; +static char *filename=NULL; +static int compress_percent = 0; +static int compress_level = Z_DEFAULT_COMPRESSION; +static int output, random_test = FALSE; +static int report_once_flag; +static double compression_time; + +/* internal functions */ +static void error(const char *fmt, ...); +static void compress_buffer(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen); + +/* commandline options : long and short form */ +static const char *s_opts = "hB:b:c:p:rs:0123456789"; +static struct long_options l_opts[] = { + { "help", no_arg, 'h' }, + { "compressability", require_arg, 'c' }, + { "compressabilit", require_arg, 'c' }, + { "compressabili", require_arg, 'c' }, + { "compressabil", require_arg, 'c' }, + { "compressabi", require_arg, 'c' }, + { "compressab", require_arg, 'c' }, + { "compressa", require_arg, 'c' }, + { "compress", require_arg, 'c' }, + { "compres", require_arg, 'c' }, + { "compre", require_arg, 'c' }, + { "compr", require_arg, 'c' }, + { "comp", require_arg, 'c' }, + { "com", require_arg, 'c' }, + { "co", require_arg, 'c' }, + { "file-size", require_arg, 's' }, + { "file-siz", require_arg, 's' }, + { "file-si", require_arg, 's' }, + { "file-s", require_arg, 's' }, + { "file", require_arg, 's' }, + { "fil", require_arg, 's' }, + { "fi", require_arg, 's' }, + { "max-buffer-size", require_arg, 'B' }, + { "max-buffer-siz", require_arg, 'B' }, + { "max-buffer-si", require_arg, 'B' }, + { "max-buffer-s", require_arg, 'B' }, + { "max-buffer", require_arg, 'B' }, + { "max-buffe", require_arg, 'B' }, + { "max-buff", require_arg, 'B' }, + { "max-buf", require_arg, 'B' }, + { "max-bu", require_arg, 'B' }, + { "max-b", require_arg, 'B' }, + { "max", require_arg, 'B' }, + { "min-buffer-size", require_arg, 'b' }, + { "min-buffer-siz", require_arg, 'b' }, + { "min-buffer-si", require_arg, 'b' }, + { "min-buffer-s", require_arg, 'b' }, + { "min-buffer", require_arg, 'b' }, + { "min-buffe", require_arg, 'b' }, + { "min-buff", require_arg, 'b' }, + { "min-buf", require_arg, 'b' }, + { "min-bu", require_arg, 'b' }, + { "min-b", require_arg, 'b' }, + { "min", require_arg, 'b' }, + { "prefix", require_arg, 'p' }, + { "prefi", require_arg, 'p' }, + { "pref", require_arg, 'p' }, + { "pre", require_arg, 'p' }, + { "pr", require_arg, 'p' }, + { "random-test", no_arg, 'r' }, + { "random-tes", no_arg, 'r' }, + { "random-te", no_arg, 'r' }, + { "random-t", no_arg, 'r' }, + { "random", no_arg, 'r' }, + { "rando", no_arg, 'r' }, + { "rand", no_arg, 'r' }, + { "ran", no_arg, 'r' }, + { "ra", no_arg, 'r' }, + { NULL, 0, '\0' } +}; + +/* + * Function: error + * Purpose: Display error message and exit. + * Programmer: Bill Wendling, 05. June 2002 + * Modifications: + */ +static void +error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + HDfprintf(stderr, "%s: error: ", prog); + HDvfprintf(stderr, fmt, ap); + HDfprintf(stderr, "\n"); + va_end(ap); + HDexit(EXIT_FAILURE); +} + +/* + * Function: cleanup + * Purpose: Cleanup the output file. + * Returns: Nothing + * Programmer: Bill Wendling, 06. June 2002 + * Modifications: + */ +static void +cleanup(void) +{ + if (!HDgetenv("HDF5_NOCLEANUP")) + HDunlink(filename); + HDfree(filename); +} + +static void +write_file(Bytef *source, uLongf sourceLen) +{ + Bytef *d_ptr, *dest; + uLongf d_len, destLen; + struct timeval timer_start, timer_stop; + + /* destination buffer needs to be at least 0.1% larger than sourceLen + * plus 12 bytes */ + destLen = (uLongf)((double)sourceLen + ((double)sourceLen * (double)0.1F)) + 12; + dest = (Bytef *)HDmalloc(destLen); + + if (!dest) + error("out of memory"); + + HDgettimeofday(&timer_start, NULL); + compress_buffer(dest, &destLen, source, sourceLen); + HDgettimeofday(&timer_stop, NULL); + + compression_time += ((double)timer_stop.tv_sec + + ((double)timer_stop.tv_usec) / (double)MICROSECOND) - + ((double)timer_start.tv_sec + + ((double)timer_start.tv_usec) / (double)MICROSECOND); + + if (report_once_flag) { + HDfprintf(stdout, "\tCompression Ratio: %g\n", ((double)destLen) / (double)sourceLen); + report_once_flag = 0; + } + + d_ptr = dest; + d_len = destLen; + + /* loop to make sure we write everything out that we want to write */ + for (;;) { + int rc = (int)HDwrite(output, d_ptr, (size_t)d_len); + + if (rc == -1) + error(HDstrerror(errno)); + + if (rc == (int)d_len) + break; + + d_len -= (size_t)rc; + d_ptr += rc; + } + + HDfree(dest); +} + +/* + * Function: compress_buffer + * Purpose: Compress the buffer. + * Returns: Z_OK - success + * Z_MEM_ERROR - not enough memory + * Z_BUF_ERROR - not enough room in the output buffer + * Z_STREAM_ERROR - level parameter is invalid + * Programmer: Bill Wendling, 05. June 2002 + * Modifications: + */ +static void +compress_buffer(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) +{ + int rc = compress2(dest, destLen, source, sourceLen, compress_level); + + if (rc != Z_OK) { + /* compress2 failed - cleanup and tell why */ + cleanup(); + + switch (rc) { + case Z_MEM_ERROR: + error("not enough memory"); + break; + case Z_BUF_ERROR: + error("not enough room in the output buffer"); + break; + case Z_STREAM_ERROR: + error("level parameter (%d) is invalid", compress_level); + break; + default: + error("unknown compression error"); + break; + } + } +} + +#ifdef LATER +/* + * Function: uncompress_buffer + * Purpose: Uncompress the buffer. + * Returns: Z_OK - success + * Z_MEM_ERROR - not enough memory + * Z_BUF_ERROR - not enough room in the output buffer + * Z_DATA_ERROR - the input data was corrupted + * Programmer: Bill Wendling, 05. June 2002 + * Modifications: + */ +static int +uncompress_buffer(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) +{ + int rc = uncompress(dest, destLen, source, sourceLen); + + return rc; +} +#endif /* LATER */ + +/* + * Function: get_unique_name + * Purpose: Create a new file who's name doesn't conflict with + * pre-existing files. + * Returns: Nothing + * Programmer: Bill Wendling, 06. June 2002 + * Modifications: + */ +#define ZIP_PERF_FILE "zip_perf.data" +static void +get_unique_name(void) +{ + const char *prefix = NULL; + const char *env = HDgetenv("HDF5_PREFIX"); + + if (env) + prefix = env; + + if (option_prefix) + prefix = option_prefix; + + if (prefix) + /* 2 = 1 for '/' + 1 for null terminator */ + filename = (char *) HDmalloc(HDstrlen(prefix) + HDstrlen(ZIP_PERF_FILE) + 2); + else + filename = (char *) HDmalloc(HDstrlen(ZIP_PERF_FILE) + 1); + + if (!filename) + error("out of memory"); + + filename[0] = 0; + if (prefix){ + HDstrcpy(filename, prefix); + HDstrcat(filename, "/"); + } + HDstrcat(filename, ZIP_PERF_FILE); +} + +/* + * Function: usage + * Purpose: Print a usage message and then exit. + * Return: Nothing + * Programmer: Bill Wendling, 05. June 2002 + * Modifications: + */ +static void +usage(void) +{ + HDfprintf(stdout, "usage: %s [OPTIONS]\n", prog); + HDfprintf(stdout, " OPTIONS\n"); + HDfprintf(stdout, " -h, --help Print this usage message and exit\n"); + HDfprintf(stdout, " -1...-9 Level of compression, from 1 to 9\n"); + HDfprintf(stdout, " -c P, --compressability=P Percentage of compressability of the random\n"); + HDfprintf(stdout, " data you want [default: 0]"); + HDfprintf(stdout, " -s S, --file-size=S Maximum size of uncompressed file [default: 64M]\n"); + HDfprintf(stdout, " -B S, --max-buffer_size=S Maximum size of buffer [default: 1M]\n"); + HDfprintf(stdout, " -b S, --min-buffer_size=S Minumum size of buffer [default: 128K]\n"); + HDfprintf(stdout, " -p D, --prefix=D The directory prefix to place the file\n"); + HDfprintf(stdout, " -r, --random-test Use random data to write to the file\n"); + HDfprintf(stdout, " [default: no]\n"); + HDfprintf(stdout, "\n"); + HDfprintf(stdout, " D - a directory which exists\n"); + HDfprintf(stdout, " P - a number between 0 and 100\n"); + HDfprintf(stdout, " S - is a size specifier, an integer >=0 followed by a size indicator:\n"); + HDfprintf(stdout, "\n"); + HDfprintf(stdout, " K - Kilobyte (%d)\n", ONE_KB); + HDfprintf(stdout, " M - Megabyte (%d)\n", ONE_MB); + HDfprintf(stdout, " G - Gigabyte (%d)\n", ONE_GB); + HDfprintf(stdout, "\n"); + HDfprintf(stdout, " Example: 37M = 37 Megabytes = %d bytes\n", 37 * ONE_MB); + HDfprintf(stdout, "\n"); + HDfflush(stdout); +} + +/* + * Function: parse_size_directive + * Purpose: Parse the size directive passed on the commandline. The size + * directive is an integer followed by a size indicator: + * + * K, k - Kilobyte + * M, m - Megabyte + * + * Return: The size as a size_t because this is related to buffer size. + * If an unknown size indicator is used, then the program will + * exit with EXIT_FAILURE as the return value. + * Programmer: Bill Wendling, 05. June 2002 + * Modifications: + */ +static unsigned long +parse_size_directive(const char *size) +{ + unsigned long s; + char *endptr; + + s = HDstrtoul(size, &endptr, 10); + + if (endptr && *endptr) { + while (*endptr != '\0' && (*endptr == ' ' || *endptr == '\t')) + ++endptr; + + switch (*endptr) { + case 'K': + case 'k': + s *= ONE_KB; + break; + case 'M': + case 'm': + s *= ONE_MB; + break; + case 'G': + case 'g': + s *= ONE_GB; + break; + default: + error("illegal size specifier '%c'", *endptr); + break; + } + } + + return s; +} + +static void +fill_with_random_data(Bytef *src, uLongf src_len) +{ + register unsigned u; + h5_stat_t stat_buf; + + if (HDstat("/dev/urandom", &stat_buf) == 0) { + uLongf len = src_len; + Bytef *buf = src; + int fd = HDopen("/dev/urandom", O_RDONLY, 0); + + HDfprintf(stdout, "Using /dev/urandom for random data\n"); + + if (fd < 0) + error(HDstrerror(errno)); + + for (;;) { + ssize_t rc = HDread(fd, buf, src_len); + + if (rc == -1) + error(HDstrerror(errno)); + + if (rc == (ssize_t)len) + break; + + buf += rc; + len -= (size_t)rc; + } + } else { + HDfprintf(stdout, "Using random() for random data\n"); + + for (u = 0; u < src_len; ++u) + src[u] = (Bytef)(0xff & HDrandom()); + } + + if (compress_percent) { + size_t s = (size_t)((src_len * (uLongf)compress_percent) / 100); + + HDmemset(src, '\0', s); + } +} + +static void +do_write_test(unsigned long file_size, unsigned long min_buf_size, + unsigned long max_buf_size) +{ + uLongf src_len, total_len; + struct timeval timer_start, timer_stop; + double total_time; + Bytef *src; + + for (src_len = min_buf_size; src_len <= max_buf_size; src_len <<= 1) { + register unsigned long i, iters; + + iters = file_size / src_len; + src = (Bytef *)HDcalloc(1, sizeof(Bytef) * src_len); + + if (!src) { + cleanup(); + error("out of memory"); + } + + compression_time = 0.0F; + + if (random_test) + fill_with_random_data(src, src_len); + + HDfprintf(stdout, "Buffer size == "); + + if (src_len >= ONE_KB && (src_len % ONE_KB) == 0) { + if (src_len >= ONE_MB && (src_len % ONE_MB) == 0) { + HDfprintf(stdout, "%ldMB", src_len / ONE_MB); + } else { + HDfprintf(stdout, "%ldKB", src_len / ONE_KB); + } + } else { + HDfprintf(stdout, "%ld", src_len); + } + + HDfprintf(stdout, "\n"); + + /* do uncompressed data write */ + HDgettimeofday(&timer_start, NULL); + output = HDopen(filename, O_RDWR | O_CREAT, S_IRWXU); + + if (output == -1) + error(HDstrerror(errno)); + + for (i = 0; i <= iters; ++i) { + Bytef *s_ptr = src; + uLong s_len = src_len; + + /* loop to make sure we write everything out that we want to write */ + for (;;) { + ssize_t rc = HDwrite(output, s_ptr, s_len); + + if (rc == -1) + error(HDstrerror(errno)); + + if (rc == (ssize_t)s_len) + break; + + s_len -= (size_t)rc; + s_ptr += rc; + } + } + + HDclose(output); + HDgettimeofday(&timer_stop, NULL); + + total_time = ((double)timer_stop.tv_sec + + ((double)timer_stop.tv_usec) / (double)MICROSECOND) - + ((double)timer_start.tv_sec + + ((double)timer_start.tv_usec) / (double)MICROSECOND); + + HDfprintf(stdout, "\tUncompressed Write Time: %.2fs\n", total_time); + HDfprintf(stdout, "\tUncompressed Write Throughput: %.2fMB/s\n", + MB_PER_SEC(file_size, total_time)); + + HDunlink(filename); + + /* do compressed data write */ + output = HDopen(filename, O_RDWR | O_CREAT, S_IRWXU); + + if (output == -1) + error(HDstrerror(errno)); + + report_once_flag = 1; + HDgettimeofday(&timer_start, NULL); + + for (total_len = 0; total_len < file_size; total_len += src_len) + write_file(src, src_len); + + HDclose(output); + HDgettimeofday(&timer_stop, NULL); + + total_time = ((double)timer_stop.tv_sec + + ((double)timer_stop.tv_usec) / (double)MICROSECOND) - + ((double)timer_start.tv_sec + + ((double)timer_start.tv_usec) / (double)MICROSECOND); + + HDfprintf(stdout, "\tCompressed Write Time: %.2fs\n", total_time); + HDfprintf(stdout, "\tCompressed Write Throughput: %.2fMB/s\n", + MB_PER_SEC(file_size, total_time)); + HDfprintf(stdout, "\tCompression Time: %gs\n", compression_time); + + HDunlink(filename); + HDfree(src); + } +} + +/* + * Function: main + * Purpose: Run the program + * Return: EXIT_SUCCESS or EXIT_FAILURE + * Programmer: Bill Wendling, 05. June 2002 + * Modifications: + */ +int +main(int argc, char **argv) +{ + unsigned long min_buf_size = 128 * ONE_KB, max_buf_size = ONE_MB; + unsigned long file_size = 64 * ONE_MB; + int opt; + + prog = argv[0]; + + /* Initialize h5tools lib */ + h5tools_init(); + + while ((opt = get_option(argc, (const char **)argv, s_opts, l_opts)) > 0) { + switch ((char)opt) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': + compress_level = opt - '0'; + break; + case 'B': + max_buf_size = parse_size_directive(opt_arg); + break; + case 'b': + min_buf_size = parse_size_directive(opt_arg); + break; + case 'c': + compress_percent = (int)HDstrtol(opt_arg, NULL, 10); + + if (compress_percent < 0) + compress_percent = 0; + else if (compress_percent > 100) + compress_percent = 100; + + break; + case 'p': + option_prefix = opt_arg; + break; + case 'r': + random_test = TRUE; + break; + case 's': + file_size = parse_size_directive(opt_arg); + break; + case '?': + usage(); + exit(EXIT_FAILURE); + break; + case 'h': + default: + usage(); + exit(EXIT_SUCCESS); + break; + } + } + + if (min_buf_size > max_buf_size) + error("minmum buffer size (%d) exceeds maximum buffer size (%d)", + min_buf_size, max_buf_size); + + HDfprintf(stdout, "Filesize: %ld\n", file_size); + + if (compress_level == Z_DEFAULT_COMPRESSION) + HDfprintf(stdout, "Compression Level: 6\n"); + else + HDfprintf(stdout, "Compression Level: %d\n", compress_level); + + get_unique_name(); + do_write_test(file_size, min_buf_size, max_buf_size); + cleanup(); + return EXIT_SUCCESS; +} + +#else + +/* + * Function: main + * Purpose: Dummy main() function for if HDF5 was configured without + * zlib stuff. + * Return: EXIT_SUCCESS + * Programmer: Bill Wendling, 10. June 2002 + * Modifications: + */ +int +main(void) +{ + HDfprintf(stdout, "No compression IO performance because zlib was not configured\n"); + return EXIT_SUCCESS; +} + +#endif /* !H5_HAVE_FILTER_DEFLATE */ |