diff options
author | Albert Cheng <acheng@hdfgroup.org> | 2014-12-17 23:07:24 (GMT) |
---|---|---|
committer | Albert Cheng <acheng@hdfgroup.org> | 2014-12-17 23:07:24 (GMT) |
commit | d9ba0ae35fce3c6fdf2559753093338061588c00 (patch) | |
tree | 4160f420ade0df1ebfc561b4e61d55dfd34dc5e8 /tools | |
parent | 4c24346fe1c5ff4ff9e4a9ae7ffec2b5ef9cd922 (diff) | |
download | hdf5-d9ba0ae35fce3c6fdf2559753093338061588c00.zip hdf5-d9ba0ae35fce3c6fdf2559753093338061588c00.tar.gz hdf5-d9ba0ae35fce3c6fdf2559753093338061588c00.tar.bz2 |
[svn-r25900] HDFFV-9046: reorganize hdf5/perform/ directory
Moved perform/ to tools/perform. Updated all the configure related files for the new location.
Tested: h5committested plus tested in jam and kite.
Diffstat (limited to 'tools')
32 files changed, 15545 insertions, 2 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a9a8c49..2365326 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -44,3 +44,6 @@ add_subdirectory (${HDF5_TOOLS_SOURCE_DIR}/h5stat) #-- Add the h5dump and test executables add_subdirectory (${HDF5_TOOLS_SOURCE_DIR}/h5dump) + +#-- Add the perform and test executables +add_subdirectory (${HDF5_TOOLS_SOURCE_DIR}/perform) diff --git a/tools/Makefile.am b/tools/Makefile.am index 76b32d2..095cc30 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -24,6 +24,7 @@ include $(top_srcdir)/config/commence.am CONFIG=ordered # All subdirectories -SUBDIRS=lib h5diff h5ls h5dump misc h5import h5repack h5jam h5copy h5stat +SUBDIRS=lib h5diff h5ls h5dump misc h5import h5repack h5jam h5copy h5stat \ + perform include $(top_srcdir)/config/conclude.am diff --git a/tools/Makefile.in b/tools/Makefile.in index 0b0e2d0..c4bf941 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -667,7 +667,9 @@ CHECK_CLEANFILES = *.chkexe *.chklog *.clog CONFIG = ordered # All subdirectories -SUBDIRS = lib h5diff h5ls h5dump misc h5import h5repack h5jam h5copy h5stat +SUBDIRS = lib h5diff h5ls h5dump misc h5import h5repack h5jam h5copy h5stat \ + perform + # Automake needs to be taught how to build lib, progs, and tests targets. # These will be filled in automatically for the most part (e.g., diff --git a/tools/perform/CMakeLists.txt b/tools/perform/CMakeLists.txt new file mode 100644 index 0000000..6887c60 --- /dev/null +++ b/tools/perform/CMakeLists.txt @@ -0,0 +1,140 @@ +cmake_minimum_required (VERSION 2.8.11) +PROJECT (HDF5_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_SRC_DIR}/lib ) + +# -------------------------------------------------------------------- +# Add the executables +# -------------------------------------------------------------------- +#-- Adding test for h5perf_serial +set (h5perf_serial_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/sio_timer.c + ${HDF5_PERFORM_SOURCE_DIR}/sio_perf.c + ${HDF5_PERFORM_SOURCE_DIR}/sio_engine.c +) +add_executable (h5perf_serial ${h5perf_serial_SRCS}) +TARGET_NAMING (h5perf_serial ${LIB_TYPE}) +TARGET_C_PROPERTIES (h5perf_serial " " " ") +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_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/sio_timer.c + ${HDF5_PERFORM_SOURCE_DIR}/sio_perf.c + ${HDF5_PERFORM_SOURCE_DIR}/sio_engine.c + ) + add_executable (h5perf_serial_alone ${h5perf_serial_alone_SRCS}) + set_property (TARGET h5perf_serial_alone + APPEND PROPERTY COMPILE_DEFINITIONS STANDALONE + ) + TARGET_NAMING (h5perf_serial_alone ${LIB_TYPE}) + TARGET_C_PROPERTIES (h5perf_serial_alone " " " ") + target_link_libraries (h5perf_serial_alone ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) + set_target_properties (h5perf_serial_alone PROPERTIES FOLDER perform) +endif (HDF5_BUILD_PERFORM_STANDALONE) + +#-- Adding test for chunk +set (chunk_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/chunk.c +) +ADD_EXECUTABLE(chunk ${chunk_SRCS}) +TARGET_NAMING (chunk ${LIB_TYPE}) +TARGET_C_PROPERTIES (chunk " " " ") +TARGET_LINK_LIBRARIES(chunk ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) +set_target_properties (chunk PROPERTIES FOLDER perform) + +#-- Adding test for iopipe +set (iopipe_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/iopipe.c +) +add_executable (iopipe ${iopipe_SRCS}) +TARGET_NAMING (iopipe ${LIB_TYPE}) +TARGET_C_PROPERTIES (iopipe " " " ") +target_link_libraries (iopipe ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET}) +set_target_properties (iopipe PROPERTIES FOLDER perform) + +#-- Adding test for overhead +set (overhead_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/overhead.c +) +add_executable (overhead ${overhead_SRCS}) +TARGET_NAMING (overhead ${LIB_TYPE}) +TARGET_C_PROPERTIES (overhead " " " ") +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_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/perf_meta.c +) +add_executable (perf_meta ${perf_meta_SRCS}) +TARGET_NAMING (perf_meta ${LIB_TYPE}) +TARGET_C_PROPERTIES (perf_meta " " " ") +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_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/zip_perf.c +) +add_executable (zip_perf ${zip_perf_SRCS}) +TARGET_NAMING (zip_perf ${LIB_TYPE}) +TARGET_C_PROPERTIES (zip_perf " " " ") +target_link_libraries (zip_perf ${HDF5_TOOLS_LIB_TARGET} ${HDF5_LIB_TARGET}) +set_target_properties (zip_perf PROPERTIES FOLDER perform) + +if (H5_HAVE_PARALLEL) + #-- Adding test for h5perf + set (h5perf_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/pio_timer.c + ${HDF5_PERFORM_SOURCE_DIR}/pio_perf.c + ${HDF5_PERFORM_SOURCE_DIR}/pio_engine.c + ) + add_executable (h5perf ${h5perf_SRCS}) + TARGET_NAMING (h5perf ${LIB_TYPE}) + TARGET_C_PROPERTIES (h5perf " " " ") + 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_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/pio_timer.c + ${HDF5_PERFORM_SOURCE_DIR}/pio_perf.c + ${HDF5_PERFORM_SOURCE_DIR}/pio_engine.c + ) + add_executable (h5perf_alone ${h5perf_alone_SRCS}) + set_property (TARGET h5perf_alone + APPEND PROPERTY COMPILE_DEFINITIONS STANDALONE + ) + TARGET_NAMING (h5perf_alone ${LIB_TYPE}) + TARGET_C_PROPERTIES (h5perf_alone " " " ") + target_link_libraries (h5perf_alone ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET} ${HDF5_TEST_LIB_TARGET}) + set_target_properties (h5perf_alone PROPERTIES FOLDER perform) + endif (HDF5_BUILD_PERFORM_STANDALONE) + + if (HDF5_BUILD_PARALLEL_ALL) + #-- Adding test for benchpar + set (benchpar_SRCS + ${HDF5_PERFORM_SOURCE_DIR}/benchpar.c + ) + add_executable (benchpar ${benchpar_SRCS}) + TARGET_NAMING (benchpar ${LIB_TYPE}) + TARGET_C_PROPERTIES (benchpar " " " ") + target_link_libraries (benchpar ${HDF5_LIB_TARGET} ${HDF5_TOOLS_LIB_TARGET} ${HDF5_TEST_LIB_TARGET}) + set_target_properties (benchpar PROPERTIES FOLDER perform) + endif (HDF5_BUILD_PARALLEL_ALL) +endif (H5_HAVE_PARALLEL) + +include (CMakeTests.cmake) diff --git a/tools/perform/CMakeTests.cmake b/tools/perform/CMakeTests.cmake new file mode 100644 index 0000000..74055d5 --- /dev/null +++ b/tools/perform/CMakeTests.cmake @@ -0,0 +1,62 @@ + +############################################################################## +############################################################################## +### T E S T I N G ### +############################################################################## +############################################################################## + +add_custom_command ( + TARGET zip_perf + POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E copy_if_different ${HDF5_TOOLS_SRC_DIR}/testfiles/tfilters.h5 ${PROJECT_BINARY_DIR}/tfilters.h5 +) + +#----------------------------------------------------------------------------- +# 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) + + if (HDF5_BUILD_PARALLEL_ALL) + add_test (NAME PERFORM_benchpar COMMAND ${MPIEXEC} ${MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_MAX_NUMPROCS} ${MPIEXEC_POSTFLAGS} $<TARGET_FILE:benchpar>) + endif (HDF5_BUILD_PARALLEL_ALL) +endif (H5_HAVE_PARALLEL) diff --git a/tools/perform/COPYING b/tools/perform/COPYING new file mode 100644 index 0000000..6903daf --- /dev/null +++ b/tools/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/perform/Makefile.am b/tools/perform/Makefile.am new file mode 100644 index 0000000..59c598a --- /dev/null +++ b/tools/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 pio_timer.c +h5perf_serial_SOURCES=sio_perf.c sio_engine.c sio_timer.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/perform/Makefile.in b/tools/perform/Makefile.in new file mode 100644 index 0000000..1244e27 --- /dev/null +++ b/tools/perform/Makefile.in @@ -0,0 +1,1544 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# 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. +# +# HDF5 Library Performance Makefile(.in) +# + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +DIST_COMMON = $(top_srcdir)/config/commence.am \ + $(top_srcdir)/config/conclude.am $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(top_srcdir)/bin/mkinstalldirs \ + $(top_srcdir)/bin/depcomp $(top_srcdir)/bin/test-driver \ + COPYING +@BUILD_PARALLEL_CONDITIONAL_FALSE@bin_PROGRAMS = \ +@BUILD_PARALLEL_CONDITIONAL_FALSE@ h5perf_serial$(EXEEXT) +@BUILD_PARALLEL_CONDITIONAL_TRUE@bin_PROGRAMS = \ +@BUILD_PARALLEL_CONDITIONAL_TRUE@ h5perf_serial$(EXEEXT) \ +@BUILD_PARALLEL_CONDITIONAL_TRUE@ h5perf$(EXEEXT) +check_PROGRAMS = iopipe$(EXEEXT) chunk$(EXEEXT) overhead$(EXEEXT) \ + zip_perf$(EXEEXT) perf_meta$(EXEEXT) $(am__EXEEXT_2) \ + perf$(EXEEXT) +TESTS = $(am__EXEEXT_3) +subdir = tools/perform +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/bin/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/src/H5config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +am__EXEEXT_1 = +@BUILD_ALL_CONDITIONAL_TRUE@am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(bin_PROGRAMS) +chunk_SOURCES = chunk.c +chunk_OBJECTS = chunk.$(OBJEXT) +chunk_LDADD = $(LDADD) +chunk_DEPENDENCIES = $(LIBHDF5) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am_h5perf_OBJECTS = pio_perf.$(OBJEXT) pio_engine.$(OBJEXT) \ + pio_timer.$(OBJEXT) +h5perf_OBJECTS = $(am_h5perf_OBJECTS) +h5perf_DEPENDENCIES = $(LIBH5TOOLS) $(LIBH5TEST) $(LIBHDF5) +h5perf_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(h5perf_LDFLAGS) $(LDFLAGS) -o $@ +am_h5perf_serial_OBJECTS = sio_perf.$(OBJEXT) sio_engine.$(OBJEXT) \ + sio_timer.$(OBJEXT) +h5perf_serial_OBJECTS = $(am_h5perf_serial_OBJECTS) +h5perf_serial_DEPENDENCIES = $(LIBH5TOOLS) $(LIBH5TEST) $(LIBHDF5) +h5perf_serial_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(h5perf_serial_LDFLAGS) $(LDFLAGS) -o $@ +iopipe_SOURCES = iopipe.c +iopipe_OBJECTS = iopipe.$(OBJEXT) +iopipe_DEPENDENCIES = $(LIBH5TEST) $(LIBHDF5) +overhead_SOURCES = overhead.c +overhead_OBJECTS = overhead.$(OBJEXT) +overhead_LDADD = $(LDADD) +overhead_DEPENDENCIES = $(LIBHDF5) +perf_SOURCES = perf.c +perf_OBJECTS = perf.$(OBJEXT) +perf_DEPENDENCIES = $(LIBH5TEST) $(LIBHDF5) +perf_meta_SOURCES = perf_meta.c +perf_meta_OBJECTS = perf_meta.$(OBJEXT) +perf_meta_DEPENDENCIES = $(LIBH5TEST) $(LIBHDF5) +zip_perf_SOURCES = zip_perf.c +zip_perf_OBJECTS = zip_perf.$(OBJEXT) +zip_perf_DEPENDENCIES = $(LIBH5TOOLS) $(LIBH5TEST) $(LIBHDF5) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src +depcomp = $(SHELL) $(top_srcdir)/bin/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = chunk.c $(h5perf_SOURCES) $(h5perf_serial_SOURCES) iopipe.c \ + overhead.c perf.c perf_meta.c zip_perf.c +DIST_SOURCES = chunk.c $(h5perf_SOURCES) $(h5perf_serial_SOURCES) \ + iopipe.c overhead.c perf.c perf_meta.c zip_perf.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +am__EXEEXT_3 = iopipe$(EXEEXT) chunk$(EXEEXT) overhead$(EXEEXT) \ + zip_perf$(EXEEXT) perf_meta$(EXEEXT) h5perf_serial$(EXEEXT) \ + $(am__EXEEXT_2) +TEST_SUITE_LOG = test-suite.log +LOG_DRIVER = $(SHELL) $(top_srcdir)/bin/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.sh.log=.log) +SH_LOG_DRIVER = $(SHELL) $(top_srcdir)/bin/test-driver +SH_LOG_COMPILE = $(SH_LOG_COMPILER) $(AM_SH_LOG_FLAGS) $(SH_LOG_FLAGS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ADD_PARALLEL_FILES = @ADD_PARALLEL_FILES@ +AMTAR = @AMTAR@ + +# H5_CFLAGS holds flags that should be used when building hdf5, +# but which should not be exported to h5cc for building other programs. +# AM_CFLAGS is an automake construct which should be used by Makefiles +# instead of CFLAGS, as CFLAGS is reserved solely for the user to define. +# This applies to FCFLAGS, CXXFLAGS, CPPFLAGS, and LDFLAGS as well. +AM_CFLAGS = @AM_CFLAGS@ @H5_CFLAGS@ +AM_CPPFLAGS = @AM_CPPFLAGS@ @H5_CPPFLAGS@ -I$(top_srcdir)/src \ + -I$(top_srcdir)/test -I$(top_srcdir)/tools/lib +AM_CXXFLAGS = @AM_CXXFLAGS@ @H5_CXXFLAGS@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_FCFLAGS = @AM_FCFLAGS@ @H5_FCFLAGS@ +AM_LDFLAGS = @AM_LDFLAGS@ @H5_LDFLAGS@ +AM_MAKEFLAGS = @AM_MAKEFLAGS@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BYTESEX = @BYTESEX@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_VERSION = @CC_VERSION@ +CFLAGS = @CFLAGS@ +CLEARFILEBUF = @CLEARFILEBUF@ +CODESTACK = @CODESTACK@ +CONFIG_DATE = @CONFIG_DATE@ +CONFIG_MODE = @CONFIG_MODE@ +CONFIG_USER = @CONFIG_USER@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXX_VERSION = @CXX_VERSION@ +CYGPATH_W = @CYGPATH_W@ +DEBUG_PKG = @DEBUG_PKG@ +DEFAULT_API_VERSION = @DEFAULT_API_VERSION@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_SYMBOLS = @DEPRECATED_SYMBOLS@ +DIRECT_VFD = @DIRECT_VFD@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DYNAMIC_DIRS = @DYNAMIC_DIRS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTERNAL_FILTERS = @EXTERNAL_FILTERS@ + +# Make sure that these variables are exported to the Makefiles +F9XMODEXT = @F9XMODEXT@ +F9XMODFLAG = @F9XMODFLAG@ +F9XSUFFIXFLAG = @F9XSUFFIXFLAG@ +FC = @FC@ +FC2003 = @FC2003@ +FCFLAGS = @FCFLAGS@ +FCFLAGS_f90 = @FCFLAGS_f90@ +FCLIBS = @FCLIBS@ +FC_VERSION = @FC_VERSION@ +FGREP = @FGREP@ +FILTERS = @FILTERS@ +FSEARCH_DIRS = @FSEARCH_DIRS@ +GREP = @GREP@ +H5_CFLAGS = @H5_CFLAGS@ +H5_CPPFLAGS = @H5_CPPFLAGS@ +H5_CXXFLAGS = @H5_CXXFLAGS@ +H5_CXX_SHARED = @H5_CXX_SHARED@ +H5_FCFLAGS = @H5_FCFLAGS@ +H5_FORTRAN_SHARED = @H5_FORTRAN_SHARED@ +H5_LDFLAGS = @H5_LDFLAGS@ +H5_LONE_COLON = @H5_LONE_COLON@ +H5_VERSION = @H5_VERSION@ +HADDR_T = @HADDR_T@ +HAVE_DMALLOC = @HAVE_DMALLOC@ +HAVE_FORTRAN_2003 = @HAVE_FORTRAN_2003@ +HAVE_PTHREAD = @HAVE_PTHREAD@ +HDF5_HL = @HDF5_HL@ +HDF5_INTERFACES = @HDF5_INTERFACES@ +HDF_CXX = @HDF_CXX@ +HDF_FORTRAN = @HDF_FORTRAN@ +HDF_FORTRAN2003 = @HDF_FORTRAN2003@ +HID_T = @HID_T@ +HL = @HL@ +HL_FOR = @HL_FOR@ +HSIZE_T = @HSIZE_T@ +HSSIZE_T = @HSSIZE_T@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTRUMENT = @INSTRUMENT@ +INSTRUMENT_LIBRARY = @INSTRUMENT_LIBRARY@ +LARGEFILE = @LARGEFILE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LL_PATH = @LL_PATH@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_STATIC_EXEC = @LT_STATIC_EXEC@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MPE = @MPE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJECT_NAMELEN_DEFAULT_F = @OBJECT_NAMELEN_DEFAULT_F@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PARALLEL = @PARALLEL@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +RANLIB = @RANLIB@ +ROOT = @ROOT@ +RUNPARALLEL = @RUNPARALLEL@ +RUNSERIAL = @RUNSERIAL@ +R_INTEGER = @R_INTEGER@ +R_LARGE = @R_LARGE@ +SEARCH = @SEARCH@ +SED = @SED@ +SETX = @SETX@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIZE_T = @SIZE_T@ +STATIC_EXEC = @STATIC_EXEC@ +STATIC_SHARED = @STATIC_SHARED@ +STRICT_FORMAT_CHECKS = @STRICT_FORMAT_CHECKS@ +STRIP = @STRIP@ +TESTPARALLEL = @TESTPARALLEL@ +THREADSAFE = @THREADSAFE@ +TIME = @TIME@ +TR = @TR@ +TRACE_API = @TRACE_API@ +UNAME_INFO = @UNAME_INFO@ +USE_FILTER_DEFLATE = @USE_FILTER_DEFLATE@ +USE_FILTER_FLETCHER32 = @USE_FILTER_FLETCHER32@ +USE_FILTER_NBIT = @USE_FILTER_NBIT@ +USE_FILTER_SCALEOFFSET = @USE_FILTER_SCALEOFFSET@ +USE_FILTER_SHUFFLE = @USE_FILTER_SHUFFLE@ +USE_FILTER_SZIP = @USE_FILTER_SZIP@ +USINGMEMCHECKER = @USINGMEMCHECKER@ +VERSION = @VERSION@ +WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_FC = @ac_ct_FC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ + +# Install directories that automake doesn't know about +docdir = $(exec_prefix)/doc +dvidir = @dvidir@ +enable_shared = @enable_shared@ +enable_static = @enable_static@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Shell commands used in Makefiles +RM = rm -f +CP = cp + +# Some machines need a command to run executables; this is that command +# so that our tests will run. +# We use RUNEXEC instead of RUNSERIAL directly because it may be that +# some tests need to be run with a different command. Older versions +# of the makefiles used the command +# $(LIBTOOL) --mode=execute +# in some directories, for instance. +RUNEXEC = $(RUNSERIAL) + +# Libraries to link to while building +LIBHDF5 = $(top_builddir)/src/libhdf5.la +LIBH5TEST = $(top_builddir)/test/libh5test.la +LIBH5F = $(top_builddir)/fortran/src/libhdf5_fortran.la +LIBH5FTEST = $(top_builddir)/fortran/test/libh5test_fortran.la +LIBH5CPP = $(top_builddir)/c++/src/libhdf5_cpp.la +LIBH5TOOLS = $(top_builddir)/tools/lib/libh5tools.la +LIBH5_HL = $(top_builddir)/hl/src/libhdf5_hl.la +LIBH5F_HL = $(top_builddir)/hl/fortran/src/libhdf5hl_fortran.la +LIBH5CPP_HL = $(top_builddir)/hl/c++/src/libhdf5_hl_cpp.la + +# Note that in svn revision 19400 the '/' after DESTDIR in H5* variables below +# has been removed. According to the official description of DESTDIR by Gnu at +# http://www.gnu.org/prep/standards/html_node/DESTDIR.html, DESTDIR is +# prepended to the normal and complete install path that it precedes for the +# purpose of installing in a temporary directory which is useful for building +# rpms and other packages. The '/' after ${DESTDIR} will be followed by another +# '/' at the beginning of the normal install path. When DESTDIR is empty the +# path then begins with '//', which is incorrect and causes problems at least for +# Cygwin. + +# Scripts used to build examples +# If only shared libraries have been installed, have h5cc build examples with +# shared libraries instead of static libraries +H5CC = ${DESTDIR}$(bindir)/h5cc +H5CC_PP = ${DESTDIR}$(bindir)/h5pcc +H5FC = ${DESTDIR}$(bindir)/h5fc +H5FC_PP = ${DESTDIR}$(bindir)/h5pfc +H5CPP = ${DESTDIR}$(bindir)/h5c++ +ACLOCAL_AMFLAGS = "-I m4" + +# The trace script; this is used on source files from the C library to +# insert tracing macros. +TRACE = perl $(top_srcdir)/bin/trace + +# .chkexe files are used to mark tests that have run successfully. +# .chklog files are output from those tests. +# *.clog are from the MPE option. +CHECK_CLEANFILES = *.chkexe *.chklog *.clog + +# 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. +@BUILD_PARALLEL_CONDITIONAL_TRUE@PARA_BUILD_ALL = +@BUILD_ALL_CONDITIONAL_TRUE@BUILD_ALL_PROGS = $(PARA_BUILD_ALL) + +# Define programs that will be run in 'make check' +# List them in the order they should be run. +# Parallel test programs. +@BUILD_PARALLEL_CONDITIONAL_TRUE@TEST_PROG_PARA = h5perf perf +# Serial test programs. +TEST_PROG = iopipe chunk overhead zip_perf perf_meta h5perf_serial $(BUILD_ALL_PROGS) +h5perf_SOURCES = pio_perf.c pio_engine.c pio_timer.c +h5perf_serial_SOURCES = sio_perf.c sio_engine.c sio_timer.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) + +# Automake needs to be taught how to build lib, progs, and tests targets. +# These will be filled in automatically for the most part (e.g., +# lib_LIBRARIES are built for lib target), but EXTRA_LIB, EXTRA_PROG, and +# EXTRA_TEST variables are supplied to allow the user to force targets to +# be built at certain times. +LIB = $(lib_LIBRARIES) $(lib_LTLIBRARIES) $(noinst_LIBRARIES) \ + $(noinst_LTLIBRARIES) $(check_LIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LIB) + +PROGS = $(bin_PROGRAMS) $(bin_SCRIPTS) $(noinst_PROGRAMS) $(noinst_SCRIPTS) \ + $(EXTRA_PROG) + +chk_TESTS = $(check_PROGRAMS) $(check_SCRIPTS) $(EXTRA_TEST) +TEST_EXTENSIONS = .sh +SH_LOG_COMPILER = $(SHELL) +AM_SH_LOG_FLAGS = +TEST_PROG_CHKEXE = $(TEST_PROG:=.chkexe_) +TEST_PROG_PARA_CHKEXE = $(TEST_PROG_PARA:=.chkexe_) +TEST_SCRIPT_CHKSH = $(TEST_SCRIPT:=.chkexe_) +TEST_SCRIPT_PARA_CHKSH = $(TEST_SCRIPT_PARA:=.chkexe_) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .sh .sh$(EXEEXT) .trs +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/config/commence.am $(top_srcdir)/config/conclude.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tools/perform/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign tools/perform/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(top_srcdir)/config/commence.am $(top_srcdir)/config/conclude.am: + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +chunk$(EXEEXT): $(chunk_OBJECTS) $(chunk_DEPENDENCIES) $(EXTRA_chunk_DEPENDENCIES) + @rm -f chunk$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(chunk_OBJECTS) $(chunk_LDADD) $(LIBS) + +h5perf$(EXEEXT): $(h5perf_OBJECTS) $(h5perf_DEPENDENCIES) $(EXTRA_h5perf_DEPENDENCIES) + @rm -f h5perf$(EXEEXT) + $(AM_V_CCLD)$(h5perf_LINK) $(h5perf_OBJECTS) $(h5perf_LDADD) $(LIBS) + +h5perf_serial$(EXEEXT): $(h5perf_serial_OBJECTS) $(h5perf_serial_DEPENDENCIES) $(EXTRA_h5perf_serial_DEPENDENCIES) + @rm -f h5perf_serial$(EXEEXT) + $(AM_V_CCLD)$(h5perf_serial_LINK) $(h5perf_serial_OBJECTS) $(h5perf_serial_LDADD) $(LIBS) + +iopipe$(EXEEXT): $(iopipe_OBJECTS) $(iopipe_DEPENDENCIES) $(EXTRA_iopipe_DEPENDENCIES) + @rm -f iopipe$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(iopipe_OBJECTS) $(iopipe_LDADD) $(LIBS) + +overhead$(EXEEXT): $(overhead_OBJECTS) $(overhead_DEPENDENCIES) $(EXTRA_overhead_DEPENDENCIES) + @rm -f overhead$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(overhead_OBJECTS) $(overhead_LDADD) $(LIBS) + +perf$(EXEEXT): $(perf_OBJECTS) $(perf_DEPENDENCIES) $(EXTRA_perf_DEPENDENCIES) + @rm -f perf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(perf_OBJECTS) $(perf_LDADD) $(LIBS) + +perf_meta$(EXEEXT): $(perf_meta_OBJECTS) $(perf_meta_DEPENDENCIES) $(EXTRA_perf_meta_DEPENDENCIES) + @rm -f perf_meta$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(perf_meta_OBJECTS) $(perf_meta_LDADD) $(LIBS) + +zip_perf$(EXEEXT): $(zip_perf_OBJECTS) $(zip_perf_DEPENDENCIES) $(EXTRA_zip_perf_DEPENDENCIES) + @rm -f zip_perf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(zip_perf_OBJECTS) $(zip_perf_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chunk.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iopipe.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/overhead.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perf_meta.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pio_engine.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pio_perf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pio_timer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sio_engine.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sio_perf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sio_timer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zip_perf.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + else \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +iopipe.log: iopipe$(EXEEXT) + @p='iopipe$(EXEEXT)'; \ + b='iopipe'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +chunk.log: chunk$(EXEEXT) + @p='chunk$(EXEEXT)'; \ + b='chunk'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +overhead.log: overhead$(EXEEXT) + @p='overhead$(EXEEXT)'; \ + b='overhead'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +zip_perf.log: zip_perf$(EXEEXT) + @p='zip_perf$(EXEEXT)'; \ + b='zip_perf'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +perf_meta.log: perf_meta$(EXEEXT) + @p='perf_meta$(EXEEXT)'; \ + b='perf_meta'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +h5perf_serial.log: h5perf_serial$(EXEEXT) + @p='h5perf_serial$(EXEEXT)'; \ + b='h5perf_serial'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.sh.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(SH_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_SH_LOG_DRIVER_FLAGS) $(SH_LOG_DRIVER_FLAGS) -- $(SH_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.sh$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(SH_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_SH_LOG_DRIVER_FLAGS) $(SH_LOG_DRIVER_FLAGS) -- $(SH_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(PROGRAMS) all-local +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool mostlyclean-local + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \ + check-am clean clean-binPROGRAMS clean-checkPROGRAMS \ + clean-generic clean-libtool cscopelist-am ctags ctags-am \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool mostlyclean-local pdf \ + pdf-am ps ps-am recheck tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS + + +# List all build rules defined by HDF5 Makefiles as "PHONY" targets here. +# This tells the Makefiles that these targets are not files to be built but +# commands that should be executed even if a file with the same name already +# exists. +.PHONY: build-check-clean build-check-p build-check-s build-lib build-progs \ + build-tests check-clean check-install check-p check-s check-vfd \ + install-doc lib progs tests uninstall-doc _exec_check-s _test help + +help: + @$(top_srcdir)/bin/makehelp + +# lib/progs/tests targets recurse into subdirectories. build-* targets +# build files in this directory. +build-lib: $(LIB) +build-progs: $(LIB) $(PROGS) +build-tests: $(LIB) $(PROGS) $(chk_TESTS) + +# General rule for recursive building targets. +# BUILT_SOURCES contain targets that need to be built before anything else +# in the directory (e.g., for Fortran type detection) +lib progs tests check-s check-p :: $(BUILT_SOURCES) + @$(MAKE) $(AM_MAKEFLAGS) build-$@ || exit 1; + @for d in X $(SUBDIRS); do \ + if test $$d != X && test $$d != .; then \ + (set -x; cd $$d && $(MAKE) $(AM_MAKEFLAGS) $@) || exit 1; \ + fi; \ + done + +# General rule for recursive cleaning targets. Like the rule above, +# but doesn't require building BUILT_SOURCES. +check-clean :: + @$(MAKE) $(AM_MAKEFLAGS) build-$@ || exit 1; + @for d in X $(SUBDIRS); do \ + if test $$d != X && test $$d != .; then \ + (set -x; cd $$d && $(MAKE) $(AM_MAKEFLAGS) $@) || exit 1; \ + fi; \ + done + +# Tell Automake to build tests when the user types `make all' (this is +# not its default behavior). Also build EXTRA_LIB and EXTRA_PROG since +# Automake won't build them automatically, either. +all-local: $(EXTRA_LIB) $(EXTRA_PROG) $(chk_TESTS) + +# make install-doc doesn't do anything outside of doc directory, but +# Makefiles should recognize it. +# UPDATE: docs no longer reside in this build tree, so this target +# is depreciated. +install-doc uninstall-doc: + @echo "Nothing to be done." + +# clean up files generated by tests so they can be re-run. +build-check-clean: + $(RM) -rf $(CHECK_CLEANFILES) + +# run check-clean whenever mostlyclean is run +mostlyclean-local: build-check-clean + +# check-install is just a synonym for installcheck +check-install: installcheck + +# Run each test in order, passing $(TEST_FLAGS) to the program. +# Since tests are done in a shell loop, "make -i" does apply inside it. +# Set HDF5_Make_Ignore to a non-blank string to ignore errors inside the loop. +# The timestamps give a rough idea how much time the tests use. +# +# Note that targets in chk_TESTS (defined above) will be built when the user +# types 'make tests' or 'make check', but only programs in TEST_PROG, +# TEST_PROG_PARA, or TEST_SCRIPT will actually be executed. +check-TESTS: test + +test _test: + @$(MAKE) build-check-s + @$(MAKE) build-check-p + +# Actual execution of check-s. +build-check-s: $(LIB) $(PROGS) $(chk_TESTS) + @if test -n "$(TEST_PROG)$(TEST_SCRIPT)"; then \ + echo "===Serial tests in `echo ${PWD} | sed -e s:.*/::` begin `date`==="; \ + fi + @$(MAKE) $(AM_MAKEFLAGS) _exec_check-s + @if test -n "$(TEST_PROG)$(TEST_SCRIPT)"; then \ + echo "===Serial tests in `echo ${PWD} | sed -e s:.*/::` ended `date`===";\ + fi + +_exec_check-s: $(TEST_PROG_CHKEXE) $(TEST_SCRIPT_CHKSH) + +# The dummy.chkexe here prevents the target from being +# empty if there are no tests in the current directory. +# $${log} is the log file. +# $${tname} is the name of test. +$(TEST_PROG_CHKEXE) $(TEST_PROG_PARA_CHKEXE) dummy.chkexe_: + @if test "X$@" != "X.chkexe_" && test "X$@" != "Xdummy.chkexe_"; then \ + tname=$(@:.chkexe_=)$(EXEEXT);\ + log=$(@:.chkexe_=.chklog); \ + echo "============================"; \ + if $(top_srcdir)/bin/newer $(@:.chkexe_=.chkexe) $${tname}; then \ + echo "No need to test $${tname} again."; \ + else \ + echo "============================" > $${log}; \ + if test "X$(FORTRAN_API)" = "Xyes"; then \ + echo "Fortran API: Testing $(HDF5_DRIVER) $${tname} $(TEST_FLAGS)"; \ + echo "Fortran API: $(HDF5_DRIVER) $${tname} $(TEST_FLAGS) Test Log" >> $${log}; \ + elif test "X$(CXX_API)" = "Xyes"; then \ + echo "C++ API: Testing $(HDF5_DRIVER) $${tname} $(TEST_FLAGS)"; \ + echo "C++ API: $(HDF5_DRIVER) $${tname} $(TEST_FLAGS) Test Log" >> $${log};\ + else \ + echo "Testing $(HDF5_DRIVER) $${tname} $(TEST_FLAGS)"; \ + echo "$(HDF5_DRIVER) $${tname} $(TEST_FLAGS) Test Log" >> $${log}; \ + fi; \ + echo "============================" >> $${log}; \ + srcdir="$(srcdir)" \ + $(TIME) $(RUNEXEC) ./$${tname} $(TEST_FLAGS) >> $${log} 2>&1 \ + && touch $(@:.chkexe_=.chkexe) || \ + (test $$HDF5_Make_Ignore && echo "*** Error ignored") || \ + (cat $${log} && false) || exit 1; \ + echo "" >> $${log}; \ + echo "Finished testing $${tname} $(TEST_FLAGS)" >> $${log}; \ + echo "============================" >> $${log}; \ + echo "Finished testing $${tname} $(TEST_FLAGS)"; \ + cat $${log}; \ + fi; \ + fi + +# The dummysh.chkexe here prevents the target from being +# empty if there are no tests in the current directory. +# $${log} is the log file. +# $${tname} is the name of test. +$(TEST_SCRIPT_CHKSH) $(TEST_SCRIPT_PARA_CHKSH) dummysh.chkexe_: + @if test "X$@" != "X.chkexe_" && test "X$@" != "Xdummysh.chkexe_"; then \ + cmd=$(@:.chkexe_=);\ + tname=`basename $$cmd`;\ + chkname=`basename $(@:.chkexe_=.chkexe)`;\ + log=`basename $(@:.chkexe_=.chklog)`; \ + echo "============================"; \ + if $(top_srcdir)/bin/newer $${chkname} $$cmd $(SCRIPT_DEPEND); then \ + echo "No need to test $${tname} again."; \ + else \ + echo "============================" > $${log}; \ + if test "X$(FORTRAN_API)" = "Xyes"; then \ + echo "Fortran API: Testing $${tname} $(TEST_FLAGS)"; \ + echo "Fortran API: $${tname} $(TEST_FLAGS) Test Log" >> $${log}; \ + elif test "X$(CXX_API)" = "Xyes"; then \ + echo "C++ API: Testing $${tname} $(TEST_FLAGS)"; \ + echo "C++ API: $${tname} $(TEST_FLAGS) Test Log" >> $${log}; \ + else \ + echo "Testing $${tname} $(TEST_FLAGS)"; \ + echo "$${tname} $(TEST_FLAGS) Test Log" >> $${log}; \ + fi; \ + echo "============================" >> $${log}; \ + RUNSERIAL="$(RUNSERIAL)" RUNPARALLEL="$(RUNPARALLEL)" \ + srcdir="$(srcdir)" \ + $(TIME) $(SHELL) $$cmd $(TEST_FLAGS) >> $${log} 2>&1 \ + && touch $${chkname} || \ + (test $$HDF5_Make_Ignore && echo "*** Error ignored") || \ + (cat $${log} && false) || exit 1; \ + echo "" >> $${log}; \ + echo "Finished testing $${tname} $(TEST_FLAGS)" >> $${log}; \ + echo "============================" >> $${log}; \ + echo "Finished testing $${tname} $(TEST_FLAGS)"; \ + cat $${log}; \ + fi; \ + echo "============================"; \ + fi + +# Actual execution of check-p. +build-check-p: $(LIB) $(PROGS) $(chk_TESTS) + @if test -n "$(TEST_PROG_PARA)$(TEST_SCRIPT_PARA)"; then \ + echo "===Parallel tests in `echo ${PWD} | sed -e s:.*/::` begin `date`==="; \ + fi + @if test -n "$(TEST_PROG_PARA)"; then \ + echo "**** Hint ****"; \ + echo "Parallel test files reside in the current directory" \ + "by default."; \ + echo "Set HDF5_PARAPREFIX to use another directory. E.g.,"; \ + echo " HDF5_PARAPREFIX=/PFS/user/me"; \ + echo " export HDF5_PARAPREFIX"; \ + echo " make check"; \ + echo "**** end of Hint ****"; \ + fi + @for test in $(TEST_PROG_PARA) dummy; do \ + if test $$test != dummy; then \ + $(MAKE) $(AM_MAKEFLAGS) $$test.chkexe_ \ + RUNEXEC="$(RUNPARALLEL)" || exit 1; \ + fi; \ + done + @for test in $(TEST_SCRIPT_PARA) dummy; do \ + if test $$test != dummy; then \ + $(MAKE) $(AM_MAKEFLAGS) $$test.chkexe_ || exit 1; \ + fi; \ + done + @if test -n "$(TEST_PROG_PARA)$(TEST_SCRIPT_PARA)"; then \ + echo "===Parallel tests in `echo ${PWD} | sed -e s:.*/::` ended `date`===";\ + fi + +# Run test with different Virtual File Driver +check-vfd: $(LIB) $(PROGS) $(chk_TESTS) + @for vfd in $(VFD_LIST) dummy; do \ + if test $$vfd != dummy; then \ + echo "============================"; \ + echo "Testing Virtual File Driver $$vfd"; \ + echo "============================"; \ + $(MAKE) $(AM_MAKEFLAGS) check-clean || exit 1; \ + HDF5_DRIVER=$$vfd $(MAKE) $(AM_MAKEFLAGS) check || exit 1; \ + fi; \ + done + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tools/perform/benchpar.c b/tools/perform/benchpar.c new file mode 100644 index 0000000..b75006a --- /dev/null +++ b/tools/perform/benchpar.c @@ -0,0 +1,488 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifdef H5_HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#include <assert.h> +#include <math.h> +#include <float.h> +#include <string.h> + +#include "hdf5.h" + +/* Local macros */ +#ifdef H5_HAVE_VISUAL_STUDIO +#define HDgetlogin() Wgetlogin() +#else /* H5_HAVE_VISUAL_STUDIO */ +#define HDgetlogin() getlogin() +#endif /* H5_HAVE_VISUAL_STUDIO */ + +/* + * HDF Boolean type. + */ +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef TRUE +# define TRUE 1 +#endif + +/* defines for type of VFL driver to use */ +#define FACC_DEFAULT 0 +#define FACC_MPIO 1 + +/* Defines for computing performance information */ +#define ONE_KB 1024 +#define ONE_MB (ONE_KB * ONE_KB) +#define ONE_GB (ONE_MB * ONE_KB) + +/* report 0.0 in case t is zero too */ +#define MB_PER_SEC(bytes,t) ((fabs(t)<0.0000000001) ? 0.0 : ((((double)bytes) / ONE_MB) / (t))) + +/* Control default behavior (with no command line arguments) */ +#define DEFAULT_RANK 3 +#define DEFAULT_DIM 1024 +#define DEFAULT_PREFIX "/tmp" +#define DEFAULT_USERNAME "koziol" +#define DEFAULT_FILENAME "benchpar.h5" +#define DEFAULT_SLICE 0 +#define DEFAULT_C_TYPE int +#define DEFAULT_HDF5_DATATYPE H5T_NATIVE_INT /* Keep this in sync with the DEFAULT_C_TYPE */ +#define DEFAULT_DATASET_NAME "Dataset" +#define DEFAULT_VFL_DRIVER FACC_MPIO +#define DEFAULT_PAR_MODE H5FD_MPIO_COLLECTIVE +#define DEFAULT_CHUNK_STORAGE 0 +#define DEFAULT_ITER 3 + +/* MPI info */ +int mpi_rank, mpi_size; +int mpi_namelen; +char mpi_name[MPI_MAX_PROCESSOR_NAME]; + +/* Usage information */ +static void usage(void) +{ + printf("usage: benchpar [-d <# of dims>] [-s <dim_size>] [-f <file name>] [-h]\n"); + printf(" [-S <slice dim>] [-I] [-c] [-i <# of iterations>\n"); + printf(" -c - Use chunked storage for dataset with 1-1 exact\n"); + printf(" mapping of chunks to hyperslabs\n"); + printf(" Default: off (i.e. contiguous storage)\n"); + printf(" -d <# of dims> - Number of dimensions of the dataset\n"); + printf(" Default: 3\n"); + printf(" -f <file name> - Set the name of the test file\n"); + printf(" Default: /tmp/<login>/benchpar.h5\n"); + printf(" -h - Prints usage information\n"); + printf(" -i <# of iters> - Set the number of test iterations to perform\n"); + printf(" Default: 3\n"); + printf(" -I - Use independent parallel I/O\n"); + printf(" Default: use collective parallel I/O\n"); + printf(" -s <dim_size> - Set the size of each of the dataset's dimensions\n"); + printf(" Default: 1024\n"); + printf(" -S <slice dim> - Set the dimension to slice the dataset along\n"); + printf(" Default: 0\n"); +} /* end usage() */ + +/* Create & initialize file creation property list with appropriate properties */ +static hid_t create_fcpl(void) +{ + hid_t fcpl; /* File creation property list */ + + fcpl=H5Pcreate(H5P_FILE_CREATE); + assert(fcpl>0); + + return(fcpl); +} /* end create_fcpl() */ + +/* Create & initialize file access property list with appropriate properties */ +static hid_t create_fapl(MPI_Comm comm, MPI_Info info, int acc_type ) +{ + hid_t fapl; /* File access property list */ + herr_t ret; /* Generic return value */ + + fapl = H5Pcreate (H5P_FILE_ACCESS); + assert(fapl>0); + + /* set parallel access with communicator, using MPI-I/O driver */ + if (acc_type == FACC_MPIO) { + ret = H5Pset_fapl_mpio(fapl, comm, info); + assert(ret>=0); + } /* end if */ + + return (fapl); +} /* end create_fapl() */ + +/* Create & initialize dataset creation property list with appropriate properties */ +static hid_t create_dcpl(unsigned use_chunks, int rank, hsize_t *dims) +{ + hid_t dcpl; /* Dataset creation property list */ + herr_t ret; /* Generic return value */ + + dcpl=H5Pcreate(H5P_DATASET_CREATE); + assert(dcpl>0); + + /* Check if the dataset should be chunked */ + if(use_chunks) { + ret = H5Pset_chunk(dcpl, rank, dims); + assert(ret>=0); + } /* end if */ + + return(dcpl); +} /* end create_dcpl() */ + +/* Create & initialize dataset transfer property list with appropriate properties */ +static hid_t create_dxpl(H5FD_mpio_xfer_t par_mode) +{ + hid_t dxpl; /* Dataset creation property list */ + herr_t ret; /* Generic return value */ + + dxpl=H5Pcreate(H5P_DATASET_XFER); + assert(dxpl>0); + + /* Set collective I/O on this transfer */ + ret=H5Pset_dxpl_mpio(dxpl, par_mode); + assert(ret>=0); + + return(dxpl); +} /* end create_dcpl() */ + +int main(int argc, char *argv[]) +{ + int curr_arg; /* Current command line argument being processed */ + int rank; /* Number of dimensions of the dataset */ + hsize_t dim_size; /* Dimension size of each dimension */ + hsize_t dims[H5S_MAX_RANK]; /* Pointer to array of dimensions */ + hsize_t start[H5S_MAX_RANK]; /* Pointer to array of starting locations for hyperslab selection */ + hsize_t count[H5S_MAX_RANK]; /* Pointer to array of counts for hyperslab selection */ + unsigned slice_dim; /* Dimension to slice up */ + char *file_name=NULL; /* Name of file to put data into */ + hid_t fcpl; /* HDF5 File creation property list ID */ + hid_t fapl; /* HDF5 File access property list ID */ + hid_t dcpl; /* HDF5 Dataset creation property list ID */ + hid_t dxpl; /* HDF5 Dataset transfer property list ID */ + hid_t fid; /* HDF5 file ID */ + hid_t dsid; /* HDF5 dataset ID */ + hid_t file_sid; /* HDF5 dataspace ID for dataset on disk */ + hid_t mem_sid; /* HDF5 dataspace ID for dataset in memory */ + DEFAULT_C_TYPE *buf; /* Buffer to write out */ + hsize_t buf_size; /* Size of buffer to write */ + int i; /* Local index variable */ + herr_t ret; /* Generic return value */ + double start_write_time, end_write_time, elap_write_time; /* Start, end and elapsed time to write raw data */ + double tmp_max_write_time; /* Temporary holders for maximum time for all nodes to perform raw data I/O */ + double max_write_time=-DBL_MAX, min_write_time=DBL_MAX; /* Minimum & maximum time for all nodes to perform raw data I/O */ + double start_file_time, end_file_time, elap_file_time; /* Start, end and elapsed time from file open to file close */ + double tmp_max_file_time; /* Temporary holders for maximum time for all nodes from file open to file close */ + double max_file_time=-DBL_MAX, min_file_time=DBL_MAX; /* Minimum & maximum time for all nodes from file open to file close */ + int vfl_type; /* Type of VFL driver to use */ + H5FD_mpio_xfer_t par_mode; /* Type of parallel I/O to perform */ + unsigned use_chunks; /* Whether to use chunks for dataset or not */ + unsigned num_iter; /* Number of iterations to perform */ + unsigned u; /* Local index variable */ + + /* Un-buffer the stdout and stderr */ + setbuf(stderr, NULL); + setbuf(stdout, NULL); + + /* MPI initialization */ + MPI_Init(&argc,&argv); + MPI_Comm_size(MPI_COMM_WORLD,&mpi_size); + MPI_Comm_rank(MPI_COMM_WORLD,&mpi_rank); + MPI_Get_processor_name(mpi_name,&mpi_namelen); + + /* Set some defaults */ + rank=DEFAULT_RANK; + dim_size=DEFAULT_DIM; + slice_dim=DEFAULT_SLICE; + vfl_type=DEFAULT_VFL_DRIVER; + par_mode=DEFAULT_PAR_MODE; + use_chunks=DEFAULT_CHUNK_STORAGE; + num_iter=DEFAULT_ITER; + + /* Parse command line arguments */ + if(argc>1) { + curr_arg=1; + while(curr_arg<argc) { + /* Trap any unknown command line parameters */ + if(argv[curr_arg][0]!='-') { + printf("unknown command line parameter: %s\n",argv[curr_arg]); + goto done; + } /* end if */ + + /* Skip over command line flag */ + curr_arg++; + + switch(argv[curr_arg-1][1]) { + case 'c': /* Use chunked storage for dataset */ + use_chunks=1; + break; + + case 'd': /* Change number of dimensions */ + /* Get new number of dimensions */ + rank=atoi(argv[curr_arg]); + curr_arg++; /* Skip over number of dimensions */ + + /* Sanity check */ + if(rank<=0) { + printf("rank=%d, invalid number of dimensions: %d\n",mpi_rank,rank); + goto done; + } /* end if */ + break; + + case 'f': /* Change test file name */ + /* Get new file name */ + file_name=strdup(argv[curr_arg]); + curr_arg++; /* Skip over file name from command line */ + break; + + case 'h': /* Print usage information */ + usage(); + goto done; + break; + + case 'i': /* Change number of iterations */ + /* Get new number of dimensions */ + num_iter=atoi(argv[curr_arg]); + curr_arg++; /* Skip over number of iterations */ + + /* Sanity check */ + if(num_iter<1) { + printf("rank=%d, invalid number of iterations: %u\n",mpi_rank,num_iter); + goto done; + } /* end if */ + break; + + case 'I': /* Use independent I/O for parallel I/O */ + par_mode=H5FD_MPIO_INDEPENDENT; + break; + + case 's': /* Change dimension size */ + /* Get new dimension size */ + dim_size=atoi(argv[curr_arg]); + curr_arg++; /* Skip over dimension size from command line */ + + /* Sanity check */ + if(dim_size<=0) { + printf("rank=%d, invalid dimension size: %ld\n",mpi_rank,(long)dim_size); + goto done; + } /* end if */ + break; + + case 'S': /* Change dimension to slice */ + /* Get new dimension to slice */ + slice_dim=atoi(argv[curr_arg]); + curr_arg++; /* Skip over slice dimension from command line */ + break; + + default: + printf("rank=%d, unknown command line parameter: %s\n",mpi_rank,argv[curr_arg-1]); + goto done; + } /* end switch */ + } /* end while */ + } /* end if */ + + /* Sanity check */ + if(slice_dim>=rank) { + printf("rank=%d, error, slice dim larger than rank: slice_dim=%d, rank=%d\n",mpi_rank,slice_dim,rank); + goto done; + } /* end if */ + + /* Set rest of defaults */ + if(file_name==NULL) { + char *login; /* Pointer to login name */ + + /* Get the login name for this user */ + login=HDgetlogin(); + if(login==NULL) + login=DEFAULT_USERNAME; + + /* Allocate enough room for the prefix, the login name, two '/'s, the filename and the string terminator */ + file_name=malloc(strlen(DEFAULT_PREFIX)+1+strlen(login)+1+strlen(DEFAULT_FILENAME)+1); + strcpy(file_name,DEFAULT_PREFIX); + strcat(file_name,"/"); + strcat(file_name,login); + strcat(file_name,"/"); + strcat(file_name,DEFAULT_FILENAME); + } /* end if */ + + /* Allocate memory for this process's portion of dataset */ + buf_size=sizeof(DEFAULT_C_TYPE); + for(i=0; i<rank; i++) + buf_size *= dim_size; + buf_size /= mpi_size; + + /* Sanity check for overflow */ + assert((hsize_t)((size_t)buf_size)==buf_size); + + buf=malloc((size_t)buf_size); + assert(buf); + + /* Initialize dataset portion to something unique for each process */ + memset(buf,mpi_rank,(size_t)buf_size); + + for(u=0; u<num_iter; u++) { + /* Create file creation property list */ + fcpl=create_fcpl(); + assert(fcpl>0); + + /* Create file access property list */ + fapl=create_fapl(MPI_COMM_WORLD,MPI_INFO_NULL,vfl_type); + assert(fapl>0); + + /* Get file start time */ + start_file_time = MPI_Wtime(); + + /* Create file */ + fid=H5Fcreate(file_name,H5F_ACC_TRUNC,fcpl,fapl); + assert(fid>0); + + /* Close file creation property list */ + ret=H5Pclose(fcpl); + assert(ret>=0); + + /* Close file access property list */ + ret=H5Pclose(fapl); + assert(ret>=0); + + /* Create dataspace for dataset on disk */ + for(i=0; i<rank; i++) + dims[i]=dim_size; + + file_sid=H5Screate_simple(rank,dims,NULL); + assert(file_sid>0); + + /* Create dataspace for buffer in memory */ + for(i=0; i<rank; i++) + dims[i]=dim_size; + dims[slice_dim] /= mpi_size; + + mem_sid=H5Screate_simple(rank,dims,NULL); + assert(mem_sid>0); + + /* Create dataset creation property list */ + dcpl=create_dcpl(use_chunks,rank,dims); + assert(dcpl>0); + + /* Create dataset */ + dsid = H5Dcreate2(fid, DEFAULT_DATASET_NAME, DEFAULT_HDF5_DATATYPE, file_sid, H5P_DEFAULT, dcpl, H5P_DEFAULT); + assert(dsid > 0); + + /* Close dataset creation property list */ + ret=H5Pclose(dcpl); + assert(ret>=0); + + /* Select hyperslab for file dataspace */ + for(i=0; i<rank; i++) { + start[i]=0; + count[i]=dim_size; + } /* end for */ + start[slice_dim]=mpi_rank*(dim_size/mpi_size); + count[slice_dim]=dim_size/mpi_size; + + ret = H5Sselect_hyperslab(file_sid,H5S_SELECT_SET,start,NULL,count,NULL); + assert(ret>=0); + + /* Create dataset transfer property list */ + dxpl=create_dxpl(par_mode); + assert(dxpl>0); + + /* Get raw data start time */ + start_write_time = MPI_Wtime(); + + /* Write hyperslab to dataset */ + ret = H5Dwrite(dsid, DEFAULT_HDF5_DATATYPE, mem_sid, + file_sid, dxpl, buf); + assert(ret>=0); + + /* Get stop time for raw data timer */ + end_write_time = MPI_Wtime(); + + /* Close dataset transfer property list */ + ret=H5Pclose(dxpl); + assert(ret>=0); + + /* Close memory dataspace */ + ret=H5Sclose(mem_sid); + assert(ret>=0); + + /* Close file dataspace */ + ret=H5Sclose(file_sid); + assert(ret>=0); + + /* Close dataset */ + ret=H5Dclose(dsid); + assert(ret>=0); + + /* Close file */ + ret=H5Fclose(fid); + assert(ret>=0); + + /* Get stop time for file timer */ + end_file_time = MPI_Wtime(); + + /* Compute timing results */ + elap_write_time=end_write_time-start_write_time; + elap_file_time=end_file_time-start_file_time; + + /* Collect the minimum and maximum times by MPI reduces */ + MPI_Allreduce(&elap_write_time, &tmp_max_write_time, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce(&elap_file_time, &tmp_max_file_time, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + + /* Track the fastest & slowest total runs */ + if(tmp_max_write_time>max_write_time) + max_write_time=tmp_max_write_time; + if(tmp_max_write_time<min_write_time) + min_write_time=tmp_max_write_time; + if(tmp_max_file_time>max_file_time) + max_file_time=tmp_max_file_time; + if(tmp_max_file_time<min_file_time) + min_file_time=tmp_max_file_time; + } /* end for */ + + /* Only print information from one node */ + if(mpi_rank==0) { + /* Print information about test */ + printf("File driver used: %s\n",vfl_type==FACC_MPIO ? "MPI-I/O" : "Unknown"); + printf("Type of parallel access: %s\n",par_mode==H5FD_MPIO_COLLECTIVE ? "Collective" : "Independent"); + printf("Type of dataset storage: %s\n",use_chunks ? "Chunked" : "Contiguous"); + printf("Number of processes: %d\n",mpi_size); + printf("Element size: %u\n",(unsigned)sizeof(DEFAULT_C_TYPE)); + printf("# of dimensions: %d\n",rank); + printf("Dimension size: %ld\n",(long)dim_size); + printf("Dimension sliced: %u\n",slice_dim); + printf("Number of elements: %lu\n",(unsigned long)((buf_size/sizeof(DEFAULT_C_TYPE))*mpi_size)); + printf("Total dataset size (bytes): %lu\n",(unsigned long)(buf_size*mpi_size)); + printf("Dataset size per process (bytes): %lu\n",(unsigned long)buf_size); + + /* Print timing results */ + printf("# of iterations: %u\n",num_iter); + printf("Maximum raw data write throughput=%6.2f MB/s (%7.3f s)\n",MB_PER_SEC((buf_size*mpi_size),min_write_time),min_write_time); + printf("Minimum raw data write throughput=%6.2f MB/s (%7.3f s)\n",MB_PER_SEC((buf_size*mpi_size),max_write_time),max_write_time); + printf("Maximum file throughput=%6.2f MB/s (%7.3f s)\n",MB_PER_SEC((buf_size*mpi_size),min_file_time),min_file_time); + printf("Minimum file throughput=%6.2f MB/s (%7.3f s)\n",MB_PER_SEC((buf_size*mpi_size),max_file_time),max_file_time); + } /* end if */ + +done: + /* Free buffers allocated */ + if(file_name) + free(file_name); + if(buf) + free(buf); + + /* MPI termination */ + MPI_Finalize(); + return(0); +} /* end main() */ diff --git a/tools/perform/build_h5perf_alone.sh b/tools/perform/build_h5perf_alone.sh new file mode 100755 index 0000000..b65e863 --- /dev/null +++ b/tools/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/perform/build_h5perf_serial_alone.sh b/tools/perform/build_h5perf_serial_alone.sh new file mode 100755 index 0000000..2566609 --- /dev/null +++ b/tools/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/perform/chunk.c b/tools/perform/chunk.c new file mode 100644 index 0000000..a3c11c1 --- /dev/null +++ b/tools/perform/chunk.c @@ -0,0 +1,552 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + + +#if !defined(H5_HAVE_ATTRIBUTE) || defined __cplusplus +# undef __attribute__ +# define __attribute__(X) /*void*/ +# define UNUSED /*void*/ +#else +# define UNUSED __attribute__((unused)) +#endif + +#define FILE_NAME "chunk.h5" +#define LINESPOINTS "lines" +#define CH_SIZE 100 /*squared in terms of bytes */ +#define DS_SIZE 20 /*squared in terms of chunks */ +#define FILTER_COUNTER 305 +#define READ 0 +#define WRITE 1 +#define MIN(X,Y) ((X)<(Y)?(X):(Y)) +#define MAX(X,Y) ((X)>(Y)?(X):(Y)) +#define SQUARE(X) ((X)*(X)) + +/* The row-major test */ +#define RM_CACHE_STRT 25 +#define RM_CACHE_END 25 +#define RM_CACHE_DELT 5 +#define RM_START 0.50 +#define RM_END 5.00 +#define RM_DELTA 0.50 +#define RM_W0 0.0 +#define RM_NRDCC 521 + +/* Diagonal test */ +#define DIAG_CACHE_STRT 25 +#define DIAG_CACHE_END 25 +#define DIAG_CACHE_DELT 5 +#define DIAG_START 0.50 +#define DIAG_END 5.00 +#define DIAG_DELTA 0.50 +/* #define DIAG_W0 0.65 */ +/* #define DIAG_NRDCC 521 */ + +static size_t nio_g; +static hid_t fapl_g = -1; + +/* Local function prototypes */ +static size_t +counter (unsigned 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 UNUSED flags, size_t UNUSED cd_nelmts, + const unsigned UNUSED *cd_values, size_t nbytes, + size_t UNUSED *buf_size, void UNUSED **buf) +{ + nio_g += nbytes; + return nbytes; +} + + +/*------------------------------------------------------------------------- + * Function: create_dataset + * + * Purpose: Creates a square dataset with square chunks, registers a + * stupid compress/uncompress pair for counting I/O, and + * initializes the dataset. The chunk size is in bytes, the + * dataset size is in terms of chunks. + * + * Return: void + * + * Programmer: Robb Matzke + * Thursday, May 14, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +create_dataset (void) +{ + hid_t file, space, dcpl, dset; + hsize_t size[2]; + signed char *buf; + + /* The file */ + file = H5Fcreate (FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_g); + + /* The data space */ + size[0] = size[1] = DS_SIZE * CH_SIZE; + space = H5Screate_simple(2, size, size); + + /* The storage layout and compression */ + dcpl = H5Pcreate(H5P_DATASET_CREATE); + size[0] = size[1] = CH_SIZE; + H5Pset_chunk(dcpl, 2, size); + H5Zregister(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 = 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 = 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 = 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)(hssize_t)nio/(hssize_t)nio_g; +} + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: See file prologue. + * + * Return: Success: + * + * Failure: + * + * Programmer: Robb Matzke + * Thursday, May 14, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main (void) +{ + 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/perform/gen_report.pl b/tools/perform/gen_report.pl new file mode 100755 index 0000000..285f5d7 --- /dev/null +++ b/tools/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/perform/iopipe.c b/tools/perform/iopipe.c new file mode 100644 index 0000000..85063fa --- /dev/null +++ b/tools/perform/iopipe.c @@ -0,0 +1,503 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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)/1000000.0) - + ((double)(r_start->ru_utime.tv_sec)+ + (double)(r_start->ru_utime.tv_usec)/1000000.0); + + s_time = ((double)(r_stop->ru_stime.tv_sec)+ + (double)(r_stop->ru_stime.tv_usec)/1000000.0) - + ((double)(r_start->ru_stime.tv_sec)+ + (double)(r_start->ru_stime.tv_usec)/1000000.0); +#endif +#ifndef H5_HAVE_SYS_TIMEB + e_time = ((double)(t_stop->tv_sec)+ + (double)(t_stop->tv_usec)/1000000.0) - + ((double)(t_start->tv_sec)+ + (double)(t_start->tv_usec)/1000000.0); +#else + e_time = ((double)(t_stop->tv_sec)+ + (double)(t_stop->tv_usec)/1000.0) - + ((double)(t_start->tv_sec)+ + (double)(t_start->tv_usec)/1000.0); +#endif + bw = (double)nbytes / e_time; + +#ifdef H5_HAVE_GETRUSAGE + printf (HEADING "%1.2fuser %1.2fsystem %1.2felapsed %1.2fMB/s\n", + prefix, u_time, s_time, e_time, bw/(1024*1024)); +#else + printf (HEADING "%1.2felapsed %1.2fMB/s\n", + prefix, e_time, bw/(1024*1024)); +#endif + +} + + +/*------------------------------------------------------------------------- + * Function: synchronize + * + * Purpose: + * + * Return: void + * + * Programmer: Robb Matzke + * Thursday, March 12, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +synchronize (void) +{ +#ifdef H5_HAVE_SYSTEM +#if defined(H5_HAVE_WIN32_API) && ! defined(__CYGWIN__) + _flushall(); +#else + HDsystem("sync"); + HDsystem("df >/dev/null"); +#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])/1024.0*1024); + + /* Open the files */ + file = H5Fcreate (HDF5_FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + assert (file>=0); + fd = HDopen (RAW_FILE_NAME, O_RDWR|O_CREAT|O_TRUNC, 0666); + assert (fd>=0); + + /* Create the dataset */ + file_space = H5Screate_simple (2, size, size); + assert(file_space >= 0); + dset = 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/perform/overhead.c b/tools/perform/overhead.c new file mode 100644 index 0000000..9341e64 --- /dev/null +++ b/tools/perform/overhead.c @@ -0,0 +1,416 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +#ifdef H5_HAVE_IO_H +# include <io.h> +#endif + +#ifndef H5_HAVE_ATTRIBUTE +# undef __attribute__ +# define __attribute__(X) /*void*/ +# define UNUSED /*void*/ +#else +# define UNUSED __attribute__((unused)) +#endif + +#define FILE_NAME_1 "overhead.h5" +#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 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; + 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.0) < 0) goto error; + } + if((file = H5Fcreate(FILE_NAME_1, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) goto error; + if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) goto error; + if(H5Pset_chunk(dcpl, 1, ch_size) < 0) goto error; + if((xfer = H5Pcreate(H5P_DATASET_XFER)) < 0) goto error; + if(H5Pset_btree_ratios(xfer, splits[0], splits[1], splits[2]) < 0) goto error; + if((fspace = H5Screate_simple(1, cur_size, max_size)) < 0) goto error; + if((mspace = H5Screate_simple(1, ch_size, ch_size)) < 0) goto error; + if((dset = 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 = 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: + j = (int)(cur_size[0]-i)+1; + hs_start[0] = j%2 ? j/2 : (hssize_t)cur_size[0]-j/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] = 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; + /* + * The extra cast in the following statement is a bug workaround + * for the Win32 version 5.0 compiler. + * 1998-11-06 ptl + */ + printf("%4lu %8.3f ***\n", + (unsigned long)i, + (double)(hssize_t)(sb.st_size-i*sizeof(int))/(hssize_t)i); + } + } + + 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)(hssize_t)(sb.st_size-cur_size[0]*sizeof(int))/ + (hssize_t)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/perform/perf.c b/tools/perform/perf.c new file mode 100644 index 0000000..7d329f0 --- /dev/null +++ b/tools/perform/perf.c @@ -0,0 +1,482 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#ifdef H5_HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <string.h> +#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 UNUSED argc, char UNUSED **argv) +{ + printf("No parallel performance because parallel is not configured in\n"); + return(0); +} +#endif /* H5_HAVE_PARALLEL */ + diff --git a/tools/perform/perf_meta.c b/tools/perform/perf_meta.c new file mode 100644 index 0000000..b52871e --- /dev/null +++ b/tools/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_cleanup(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/perform/pio_engine.c b/tools/perform/pio_engine.c new file mode 100644 index 0000000..3535fb4 --- /dev/null +++ b/tools/perform/pio_engine.c @@ -0,0 +1,2678 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#ifdef H5_HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> + +#include "hdf5.h" + +#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" +#include "pio_timer.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 = pio_time_new(MPI_TIMER); + break; + case POSIXIO: + fd.posixfd = -1; + res.timers = pio_time_new(MPI_TIMER); + break; + case PHDF5: + fd.h5fd = -1; + res.timers = pio_time_new(MPI_TIMER); + 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/perform/pio_perf.c b/tools/perform/pio_perf.c new file mode 100644 index 0000000..cf41cbe --- /dev/null +++ b/tools/perform/pio_perf.c @@ -0,0 +1,1696 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 MBs\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 MBs\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 KBs\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 MBs\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 MBs\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; + + } + + pio_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); + } +} + +#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 + * Modifications: + */ +int +main(void) +{ + printf("No parallel IO performance because parallel is not configured\n"); + return EXIT_SUCCESS; +} + +#endif /* !H5_HAVE_PARALLEL */ diff --git a/tools/perform/pio_perf.h b/tools/perform/pio_perf.h new file mode 100644 index 0000000..3295e2b --- /dev/null +++ b/tools/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 "pio_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; + pio_time *timers; +} results; + +#ifndef SUCCESS +#define SUCCESS 0 +#endif /* !SUCCESS */ + +#ifndef FAIL +#define FAIL -1 +#endif /* !FAIL */ + +extern FILE *output; /* output file */ +extern pio_time *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/perform/pio_standalone.c b/tools/perform/pio_standalone.c new file mode 100644 index 0000000..e404274 --- /dev/null +++ b/tools/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/perform/pio_standalone.h b/tools/perform/pio_standalone.h new file mode 100644 index 0000000..584a057 --- /dev/null +++ b/tools/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 0 +#endif +#ifndef TRUE +# define TRUE 1 +#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/perform/pio_timer.c b/tools/perform/pio_timer.c new file mode 100644 index 0000000..aba219e --- /dev/null +++ b/tools/perform/pio_timer.c @@ -0,0 +1,258 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: + * + * This is a module of useful timing functions for performance testing. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "hdf5.h" + +#ifdef H5_HAVE_PARALLEL + +#include <mpi.h> + +#include "pio_perf.h" + +/* + * The number to divide the tv_usec field with to get a nice decimal to add to + * the number of seconds. + */ +#define MICROSECOND 1000000.0 + +/* global variables */ +pio_time *timer_g; /* timer: global for stub functions */ + +/* + * Function: sub_time + * Purpose: Struct two time values, and return the difference, in microseconds + * + * Note that the function assumes that a > b + * Programmer: Leon Arber, 1/27/06 + */ +static double sub_time(struct timeval* a, struct timeval* b) +{ + return (((double)a->tv_sec + + ((double)a->tv_usec) / MICROSECOND) - + ((double)b->tv_sec + + ((double)b->tv_usec) / MICROSECOND)); +} + + +/* + * Function: pio_time_new + * Purpose: Build us a brand, spankin', new performance time object. + * The object is a black box to the user. They just tell us + * what type of timer they want (MPI_TIMER for MPI_Wtime or + * SYS_TIMER for system time). + * Return: Pointer to pio_time object + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +pio_time * +pio_time_new(clock_type type) +{ + pio_time *pt = (pio_time *)calloc(1, sizeof(struct pio_time_)); + + /* set global timer variable */ + timer_g = pt; + + pt->type = type; + return pt; +} + +/* + * Function: pio_time_destroy + * Purpose: Remove the memory allocated for the pio_time object. Only + * need to call on a pointer allocated with the ``pio_time_new'' + * function. + * Return: Nothing + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +void +pio_time_destroy(pio_time *pt) +{ + HDfree(pt); + /* reset the global timer pointer too. */ + timer_g = NULL; +} + +/* + * Function: set_timer_type + * Purpose: Set the type of the timer to either MPI_TIMER or SYS_TIMER. + * This really only needs to be called if you didn't construct a + * timer with the pio_timer_new function (shame!). + * Return: Nothing + * Programmer: Bill Wendling, 04. October 2001 + * Modifications: + */ +void +set_timer_type(pio_time *pt, clock_type type) +{ + pt->type = type; +} + +/* + * Function: get_timer_type + * Purpose: Get the type of the timer. + * Return: MPI_TIMER or SYS_TIMER. + * Programmer: Bill Wendling, 04. October 2001 + * Modifications: + */ +clock_type +get_timer_type(pio_time *pt) +{ + return pt->type; +} + +/* + * Function: set_time + * Purpose: Set the time in a ``pio_time'' object. + * Return: Pointer to the passed in ``pio_time'' object. + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +pio_time * +set_time(pio_time *pt, timer_type t, int start_stop) +{ + if (pt) { + if (pt->type == MPI_TIMER) { + if (start_stop == TSTART) { + pt->mpi_timer[t] = MPI_Wtime(); + + /* When we start the timer for HDF5_FINE_WRITE_FIXED_DIMS or HDF5_FINE_READ_FIXED_DIMS + * we compute the time it took to only open the file */ + if(t == HDF5_FINE_WRITE_FIXED_DIMS) + pt->total_time[HDF5_FILE_WRITE_OPEN] += pt->mpi_timer[t] - pt->mpi_timer[HDF5_GROSS_WRITE_FIXED_DIMS]; + else if(t == HDF5_FINE_READ_FIXED_DIMS) + pt->total_time[HDF5_FILE_READ_OPEN] += pt->mpi_timer[t] - pt->mpi_timer[HDF5_GROSS_READ_FIXED_DIMS]; + + } else { + pt->total_time[t] += MPI_Wtime() - pt->mpi_timer[t]; + pt->mpi_timer[t] = MPI_Wtime(); + + /* When we stop the timer for HDF5_GROSS_WRITE_FIXED_DIMS or HDF5_GROSS_READ_FIXED_DIMS + * we compute the time it took to close the file after the last read/write finished */ + if(t == HDF5_GROSS_WRITE_FIXED_DIMS) + pt->total_time[HDF5_FILE_WRITE_CLOSE] += pt->mpi_timer[t] - pt->mpi_timer[HDF5_FINE_WRITE_FIXED_DIMS]; + else if(t == HDF5_GROSS_READ_FIXED_DIMS) + pt->total_time[HDF5_FILE_READ_CLOSE] += pt->mpi_timer[t] - pt->mpi_timer[HDF5_FINE_READ_FIXED_DIMS]; + } + } else { + if (start_stop == TSTART) { + HDgettimeofday(&pt->sys_timer[t], NULL); + + /* When we start the timer for HDF5_FINE_WRITE_FIXED_DIMS or HDF5_FINE_READ_FIXED_DIMS + * we compute the time it took to only open the file */ + if(t == HDF5_FINE_WRITE_FIXED_DIMS) + pt->total_time[HDF5_FILE_WRITE_OPEN] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_GROSS_WRITE_FIXED_DIMS])); + else if(t == HDF5_FINE_READ_FIXED_DIMS) + pt->total_time[HDF5_FILE_READ_OPEN] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_GROSS_READ_FIXED_DIMS])); + + + } else { + struct timeval sys_t; + + HDgettimeofday(&sys_t, NULL); + pt->total_time[t] += sub_time(&sys_t, &(pt->sys_timer[t])); + +/* ((double)sys_t.tv_sec + + ((double)sys_t.tv_usec) / MICROSECOND) - + ((double)pt->sys_timer[t].tv_sec + + ((double)pt->sys_timer[t].tv_usec) / MICROSECOND);*/ + + /* When we stop the timer for HDF5_GROSS_WRITE_FIXED_DIMS or HDF5_GROSS_READ_FIXED_DIMS + * we compute the time it took to close the file after the last read/write finished */ + if(t == HDF5_GROSS_WRITE_FIXED_DIMS) + pt->total_time[HDF5_FILE_WRITE_CLOSE] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_FINE_WRITE_FIXED_DIMS])); + else if(t == HDF5_GROSS_READ_FIXED_DIMS) + pt->total_time[HDF5_FILE_READ_CLOSE] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_FINE_READ_FIXED_DIMS])); + + } + } + + if (pio_debug_level >= 4) { + const char *msg; + int myrank; + + MPI_Comm_rank(pio_comm_g, &myrank); + + switch (t) { + case HDF5_FILE_OPENCLOSE: + msg = "File Open/Close"; + break; + case HDF5_DATASET_CREATE: + msg = "Dataset Create"; + break; + case HDF5_MPI_WRITE: + msg = "MPI Write"; + break; + case HDF5_MPI_READ: + msg = "MPI Read"; + break; + case HDF5_FINE_WRITE_FIXED_DIMS: + msg = "Fine Write"; + break; + case HDF5_FINE_READ_FIXED_DIMS: + msg = "Fine Read"; + break; + case HDF5_GROSS_WRITE_FIXED_DIMS: + msg = "Gross Write"; + break; + case HDF5_GROSS_READ_FIXED_DIMS: + msg = "Gross Read"; + break; + case HDF5_RAW_WRITE_FIXED_DIMS: + msg = "Raw Write"; + break; + case HDF5_RAW_READ_FIXED_DIMS: + msg = "Raw Read"; + break; + default: + msg = "Unknown Timer"; + break; + } + + fprintf(output, " Proc %d: %s %s: %.2f\n", myrank, msg, + (start_stop == TSTART ? "Start" : "Stop"), + pt->total_time[t]); + } + } + + return pt; +} + +/* + * Function: get_time + * Purpose: Get the time from a ``pio_time'' object. + * Return: The number of seconds as a DOUBLE. + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +double +get_time(pio_time *pt, timer_type t) +{ + return pt->total_time[t]; +} + +#endif /* H5_HAVE_PARALLEL */ +#ifdef STANDALONE +#include "pio_standalone.c" +#endif diff --git a/tools/perform/pio_timer.h b/tools/perform/pio_timer.h new file mode 100644 index 0000000..a5ee6d7 --- /dev/null +++ b/tools/perform/pio_timer.h @@ -0,0 +1,82 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_TIMER__ +#define PIO_TIMER__ + +#include "hdf5.h" + +#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 + +/* The different types of timers we can have */ +typedef enum timer_type_ { + HDF5_FILE_OPENCLOSE, + HDF5_DATASET_CREATE, + HDF5_MPI_WRITE, + HDF5_MPI_READ, + HDF5_FILE_READ_OPEN, + HDF5_FILE_READ_CLOSE, + HDF5_FILE_WRITE_OPEN, + HDF5_FILE_WRITE_CLOSE, + HDF5_FINE_WRITE_FIXED_DIMS, + HDF5_FINE_READ_FIXED_DIMS, + HDF5_GROSS_WRITE_FIXED_DIMS, + HDF5_GROSS_READ_FIXED_DIMS, + HDF5_RAW_WRITE_FIXED_DIMS, + HDF5_RAW_READ_FIXED_DIMS, + NUM_TIMERS +} timer_type; + +typedef enum clock_type_ { + MPI_TIMER = 0, /* Use MPI timer to measure time */ + SYS_TIMER = 1 /* Use system clock to measure time */ +} clock_type; + +/* Miscellaneous identifiers */ +enum { + TSTART, /* Start a specified timer */ + TSTOP /* Stop a specified timer */ +}; + +/* The performance time structure */ +typedef struct pio_time_ { + clock_type type; + double total_time[NUM_TIMERS]; + double mpi_timer[NUM_TIMERS]; + struct timeval sys_timer[NUM_TIMERS]; +} pio_time; + +/* External function declarations */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +extern pio_time *pio_time_new(clock_type t); +extern void pio_time_destroy(pio_time *pt); +extern void set_timer_type(pio_time *pt, clock_type type); +extern clock_type get_timer_type(pio_time *pt); +extern pio_time *set_time(pio_time *pt, timer_type t, int start_stop); +extern double get_time(pio_time *pt, timer_type t); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PIO_TIMER__ */ diff --git a/tools/perform/sio_engine.c b/tools/perform/sio_engine.c new file mode 100644 index 0000000..07e6b16c --- /dev/null +++ b/tools/perform/sio_engine.c @@ -0,0 +1,1310 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#ifdef H5_HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <errno.h> + +#include "hdf5.h" + +#include "sio_perf.h" +#include "sio_timer.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 off_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: + */ + results +do_sio(parameters param) +{ + 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 */ + results res; + + 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 = sio_time_new(); + break; + case HDF5: + fd.h5fd = -1; + res.timers = sio_time_new(); + 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 = 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; + return res; +} + +/* + * 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]; + long i; + /* 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 (i=0; i < linear_buf_size; i++) + buf_p[i]=i%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]; + long i,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] = offset[cur_dim] = 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++){ + + if (dims[k] <= 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 dtype_size = 1; + int ret_code = SUCCESS; + long i; + size_t d_offset; + size_t linear_dset_offset = 0; + int j, rc; + + /* if dimension is not contiguous, call recursively */ + if (local_dim < parms->rank-1 && local_dim != cont_dim) { + + for (i=0; i < parms->buf_size[local_dim]; i += dtype_size) { + buf_offset[local_dim] = i; + 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 { + + 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 *= parms->dset_size[j]; + + linear_dset_offset += (offset[i]+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]; + long i; + /* 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(i = 0; i < linear_buf_size; i++) + buffer2[i] = i % 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; + int ret_code = SUCCESS; + long i,j; + herr_t hrc; + + /* iterate on the current dimension */ + for (i=0; i < parms->dset_size[cur_dim]; i += parms->buf_size[cur_dim]){ + + h5offset[cur_dim] = offset[cur_dim] = 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"); +#if 0 + for (j=0; j<linear_buf_size; j++) { + buf_p = (unsigned char*)buffer; + if (buf_p[j]!=buffer2[j]) + printf("Inconsistent data in %d\n", j); + } +#endif + 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 dtype_size = 1; + int ret_code = SUCCESS; + long i; + size_t d_offset; + size_t linear_dset_offset = 0; + int j, rc; + + /* if local dimension is not contiguous, recurse */ + if (local_dim < parms->rank-1 && local_dim != cont_dim) { + + for (i=0; i < parms->buf_size[local_dim]; i += dtype_size) { + buf_offset[local_dim] = i; + ret_code = posix_buffer_read(local_dim+1, fd, parms, buffer); + if (local_dim+1==cont_dim) + break; + } + /* otherwise, perform contiguous POSIX access */ + } else { + + 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 *= parms->dset_size[j]; + + linear_dset_offset += (offset[i]+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] = 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/perform/sio_perf.c b/tools/perform/sio_perf.c new file mode 100644 index 0000000..26cec6d --- /dev/null +++ b/tools/perform/sio_perf.c @@ -0,0 +1,1409 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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) (((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 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 */ + off_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 */ + 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 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 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 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.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_sys_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}; + + 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 = 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)); + + if (!parms.h5_write_only) { + read_sys_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)); + } + + /* Do IO iteration times, collecting statistics each time */ + for (i = 0; i < parms.num_iters; ++i) { + double t; + res = do_sio(parms); + + /* 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; + } + sio_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 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: 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"); +} + +/* + * 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[]) +{ + register int opt; + struct options *cl_opts; + int i, default_rank, actual_rank, ranks[4]; + 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_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]=(i+1)*10; + cl_opts->dset_size[i]=(i+1)*100; + cl_opts->chk_size[i]=(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]; + + memset(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]; + + memset(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]; + + 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, "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]; + + memset(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]; + + memset(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]; + + memset(buf, '\0', sizeof(buf)); + + for (i = 0; *end != '\0' && *end != ','; ++end) + if (isalnum(*end) && i < 10) + buf[i++] = *end; + + cl_opts->order[j] = 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 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: + */ +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); +} + diff --git a/tools/perform/sio_perf.h b/tools/perform/sio_perf.h new file mode 100644 index 0000000..b40fed3 --- /dev/null +++ b/tools/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 "sio_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 */ + off_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 */ + off_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; + sio_time *timers; +} results; + +#ifndef SUCCESS +#define SUCCESS 0 +#endif /* !SUCCESS */ + +#ifndef FAIL +#define FAIL -1 +#endif /* !FAIL */ + +extern FILE *output; /* output file */ +extern sio_time *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 results do_sio(parameters param); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PIO_PERF_H__ */ diff --git a/tools/perform/sio_standalone.c b/tools/perform/sio_standalone.c new file mode 100644 index 0000000..d92ed30 --- /dev/null +++ b/tools/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/perform/sio_standalone.h b/tools/perform/sio_standalone.h new file mode 100644 index 0000000..b2f8220 --- /dev/null +++ b/tools/perform/sio_standalone.h @@ -0,0 +1,528 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +/* + * 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 HDBSDgettimeofday(S,P) BSDgettimeofday(S,P) +#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; +#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; +#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; +#endif + +#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_VISUAL_STUDIO +#define HDgetlogin() Wgetlogin() +#else /* H5_HAVE_VISUAL_STUDIO */ +#define HDgetlogin() getlogin() +#endif /* H5_HAVE_VISUAL_STUDIO */ +#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_VISUAL_STUDIO + H5_DLL int Wgettimeofday(struct timeval *tv, struct timezone *tz); +#define HDgettimeofday(V,Z) Wgettimeofday(V,Z) +#else /* H5_HAVE_VISUAL_STUDIO */ +#define HDgettimeofday(S,P) gettimeofday(S,P) +#endif /* H5_HAVE_VISUAL_STUDIO */ +#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) +#ifdef H5_VMS +#ifdef __cplusplus +extern "C" { +#endif +int HDremove_all(const char * fname); +#ifdef __cplusplus +} +#endif +#define HDremove(S) HDremove_all(S) +#else +#define HDremove(S) remove(S) +#endif /*H5_VMS*/ +#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) +#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 0 +#endif +#ifndef TRUE +# define TRUE 1 +#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/perform/sio_timer.c b/tools/perform/sio_timer.c new file mode 100644 index 0000000..4e42ee6 --- /dev/null +++ b/tools/perform/sio_timer.c @@ -0,0 +1,197 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: + * + * This is a module of useful timing functions for performance testing. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "sio_timer.h" + + +#include "sio_perf.h" + +/* + * The number to divide the tv_usec field with to get a nice decimal to add to + * the number of seconds. + */ +#define MICROSECOND 1000000.0 + +/* global variables */ +sio_time *timer_g; /* timer: global for stub functions */ + +/* + * Function: sub_time + * Purpose: Struct two time values, and return the difference, in microseconds + * + * Note that the function assumes that a > b + * Programmer: Leon Arber, 1/27/06 + */ +static double sub_time(struct timeval* a, struct timeval* b) +{ + return (((double)a->tv_sec + + ((double)a->tv_usec) / MICROSECOND) - + ((double)b->tv_sec + + ((double)b->tv_usec) / MICROSECOND)); +} + + +/* + * Function: sio_time_new + * Purpose: Build us a brand, spankin', new performance time object. + * The object is a black box to the user. + * Return: Pointer to sio_time object + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +sio_time * +sio_time_new(void) +{ + sio_time *pt = (sio_time *)calloc(1, sizeof(struct sio_time_)); + + /* set global timer variable */ + timer_g = pt; + + return pt; +} + +/* + * Function: sio_time_destroy + * Purpose: Remove the memory allocated for the sio_time object. Only + * need to call on a pointer allocated with the ``sio_time_new'' + * function. + * Return: Nothing + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +void +sio_time_destroy(sio_time *pt) +{ + HDfree(pt); + /* reset the global timer pointer too. */ + timer_g = NULL; +} + + + +/* + * Function: set_time + * Purpose: Set the time in a ``sio_time'' object. + * Return: Pointer to the passed in ``sio_time'' object. + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +sio_time * +set_time(sio_time *pt, timer_type t, int start_stop) +{ + if (pt) { + if (start_stop == TSTART) { + HDgettimeofday(&pt->sys_timer[t], NULL); + + /* When we start the timer for HDF5_FINE_WRITE_FIXED_DIMS or HDF5_FINE_READ_FIXED_DIMS + * we compute the time it took to only open the file */ + if(t == HDF5_FINE_WRITE_FIXED_DIMS) + pt->total_time[HDF5_FILE_WRITE_OPEN] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_GROSS_WRITE_FIXED_DIMS])); + else if(t == HDF5_FINE_READ_FIXED_DIMS) + pt->total_time[HDF5_FILE_READ_OPEN] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_GROSS_READ_FIXED_DIMS])); + + + } else { + struct timeval sys_t; + + HDgettimeofday(&sys_t, NULL); + pt->total_time[t] += sub_time(&sys_t, &(pt->sys_timer[t])); + +/* ((double)sys_t.tv_sec + + ((double)sys_t.tv_usec) / MICROSECOND) - + ((double)pt->sys_timer[t].tv_sec + + ((double)pt->sys_timer[t].tv_usec) / MICROSECOND);*/ + + /* When we stop the timer for HDF5_GROSS_WRITE_FIXED_DIMS or HDF5_GROSS_READ_FIXED_DIMS + * we compute the time it took to close the file after the last read/write finished */ + if(t == HDF5_GROSS_WRITE_FIXED_DIMS) + pt->total_time[HDF5_FILE_WRITE_CLOSE] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_FINE_WRITE_FIXED_DIMS])); + else if(t == HDF5_GROSS_READ_FIXED_DIMS) + pt->total_time[HDF5_FILE_READ_CLOSE] += sub_time(&(pt->sys_timer[t]), &(pt->sys_timer[HDF5_FINE_READ_FIXED_DIMS])); + + } + + if (sio_debug_level >= 4) { + const char *msg; + + switch (t) { + case HDF5_FILE_OPENCLOSE: + msg = "File Open/Close"; + break; + case HDF5_DATASET_CREATE: + msg = "Dataset Create"; + break; + case HDF5_MPI_WRITE: + msg = "MPI Write"; + break; + case HDF5_MPI_READ: + msg = "MPI Read"; + break; + case HDF5_FINE_WRITE_FIXED_DIMS: + msg = "Fine Write"; + break; + case HDF5_FINE_READ_FIXED_DIMS: + msg = "Fine Read"; + break; + case HDF5_GROSS_WRITE_FIXED_DIMS: + msg = "Gross Write"; + break; + case HDF5_GROSS_READ_FIXED_DIMS: + msg = "Gross Read"; + break; + case HDF5_RAW_WRITE_FIXED_DIMS: + msg = "Raw Write"; + break; + case HDF5_RAW_READ_FIXED_DIMS: + msg = "Raw Read"; + break; + default: + msg = "Unknown Timer"; + break; + } + + fprintf(output, " %s %s: %.2f\n", msg, + (start_stop == TSTART ? "Start" : "Stop"), + pt->total_time[t]); + } + } + + return pt; +} + +/* + * Function: get_time + * Purpose: Get the time from a ``sio_time'' object. + * Return: The number of seconds as a DOUBLE. + * Programmer: Bill Wendling, 01. October 2001 + * Modifications: + */ +double +get_time(sio_time *pt, timer_type t) +{ + return pt->total_time[t]; +} +#ifdef STANDALONE +#include "sio_standalone.c" +#endif + diff --git a/tools/perform/sio_timer.h b/tools/perform/sio_timer.h new file mode 100644 index 0000000..46702c3 --- /dev/null +++ b/tools/perform/sio_timer.h @@ -0,0 +1,78 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_TIMER__ +#define SIO_TIMER__ + +#include "hdf5.h" + +#if defined(H5_TIME_WITH_SYS_TIME) +# include <sys/time.h> +# include <time.h> +#elif defined(H5_HAVE_SYS_TIME_H) +# include <sys/time.h> +#else +# include <time.h> +#endif + +#ifdef H5_HAVE_WINSOCK2_H +# include <winsock2.h> +#endif /* H5_HAVE_WINSOCK2_H */ + +/* The different types of timers we can have */ +typedef enum timer_type_ { + HDF5_FILE_OPENCLOSE, + HDF5_DATASET_CREATE, + HDF5_MPI_WRITE, + HDF5_MPI_READ, + HDF5_FILE_READ_OPEN, + HDF5_FILE_READ_CLOSE, + HDF5_FILE_WRITE_OPEN, + HDF5_FILE_WRITE_CLOSE, + HDF5_FINE_WRITE_FIXED_DIMS, + HDF5_FINE_READ_FIXED_DIMS, + HDF5_GROSS_WRITE_FIXED_DIMS, + HDF5_GROSS_READ_FIXED_DIMS, + HDF5_RAW_WRITE_FIXED_DIMS, + HDF5_RAW_READ_FIXED_DIMS, + NUM_TIMERS +} timer_type; + + +/* Miscellaneous identifiers */ +enum { + TSTART, /* Start a specified timer */ + TSTOP /* Stop a specified timer */ +}; + +/* The performance time structure */ +typedef struct sio_time_ { + double total_time[NUM_TIMERS]; + struct timeval sys_timer[NUM_TIMERS]; +} sio_time; + +/* External function declarations */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +extern sio_time *sio_time_new(void); +extern void sio_time_destroy(sio_time *pt); +extern void set_timer_type(sio_time *pt); +extern sio_time *set_time(sio_time *pt, timer_type t, int start_stop); +extern double get_time(sio_time *pt, timer_type t); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SIO_TIMER__ */ diff --git a/tools/perform/zip_perf.c b/tools/perform/zip_perf.c new file mode 100644 index 0000000..edd5b0f --- /dev/null +++ b/tools/perform/zip_perf.c @@ -0,0 +1,653 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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> + +#ifdef VMS +# define unlink delete +#endif /* VMS */ + +#ifdef RISCOS +# define unlink remove +# define fileno(file) file->__file +#endif /* RISCOS */ + +#define ONE_KB 1024 +#define ONE_MB (ONE_KB * ONE_KB) +#define ONE_GB (ONE_MB * ONE_KB) + +#define MICROSECOND 1000000.0 + +/* report 0.0 in case t is zero too */ +#define MB_PER_SEC(bytes,t) ((fabs(t)<0.0000000001) ? 0.0 : ((((double)bytes) / 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 * 0.1)) + 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) / MICROSECOND) - + ((double)timer_start.tv_sec + + ((double)timer_start.tv_usec) / 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 -= 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 -= 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) { + unsigned long s = src_len * 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.0; + + 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 -= rc; + s_ptr += rc; + } + } + + HDclose(output); + HDgettimeofday(&timer_stop, NULL); + + total_time = ((double)timer_stop.tv_sec + + ((double)timer_stop.tv_usec) / MICROSECOND) - + ((double)timer_start.tv_sec + + ((double)timer_start.tv_usec) / 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) / MICROSECOND) - + ((double)timer_start.tv_sec + + ((double)timer_start.tv_usec) / 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 */ |