diff --git a/.github/workflows/spio_pnetcdf_testing_workflow.yaml b/.github/workflows/spio_pnetcdf_testing_workflow.yaml index 8ae4b736c69..216730a316b 100644 --- a/.github/workflows/spio_pnetcdf_testing_workflow.yaml +++ b/.github/workflows/spio_pnetcdf_testing_workflow.yaml @@ -19,6 +19,9 @@ on: # yamllint disable-line rule:truthy push: branches: - master + # Includes all branches starting with "copilot/" + # A "*" matches any char except "/", so use "**" to handle all branch names + - 'copilot/**' pull_request: branches: - master diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..e5d9e0c0d40 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,49 @@ +# AGENTS.md + +SCORPIO (Software for Caching Output and Reads for Parallel I/O) is a high-level Parallel I/O Library for structured grid applications. + +## Project Overview + +SCORPIO (Software for Caching Output and Reads for Parallel I/O) is a high-level Parallel I/O Library. The library is used by the E3SM (Energy Exascale Earth System Model) earth system model for all I/O. + +- **Website:** https://github.com/E3SM-Project/scorpio +- **Documentation:** https://docs.e3sm.org/scorpio/ +- **DOI:** https://www.osti.gov/doecode/biblio/36752 + +## Architecture + +- The Fortran interface library source is in src/flib directory +- The C (main/core library) library source is in src/clib directory +- The tests are in tests/general and tests/cunit directories +- The examples are in the examples directory + +## Build Commands + +SCORPIO uses CMake for configuring the library. The library requires a C, C++ and Fortran compiler and an MPI library. SCORPIO uses low-level I/O libraries like PnetCDF (https://parallel-netcdf.github.io), NetCDF (https://www.unidata.ucar.edu/software/netcdf), HDF5 (https://www.hdfgroup.org/solutions/hdf5/) and ADIOS (https://adios2.readthedocs.io/en/latest/) for I/O. + +To configure SCORPIO (source in /path/to/scorpio/source directory) using CMake with the PnetCDF library (installed at /path/to/pnetcdf) use the command below, + +``` +CC=mpicc CXX=mpicxx FC=mpif90 cmake -DPnetCDF_PATH=/path/to/pnetcdf /path/to/scorpio/source +``` + +After configuring the library to build it use make, + +``` +make +``` + +## Development Workflow +- All changes are made on a development branch off master +- The development branches are named using the pattern : ```/``` +- The changes in a branch are merged to the develop branch for nightly testing (Run using Jenkins and results published on CDASH at http://my.cdash.org/index.php?project=E3SM\_SCORPIO) +- Once the nightly testing is successful the PR is manually merged to the master branch +- Code formatting for C++ sources is mentioned in the library documentation (https://docs.e3sm.org/scorpio/html/contributing_code.html) + +## Testing instructions +- Find the CI (Github Actions workflow) plan in the .github/workflows directory +- Add/update tests for the code that you change + +## PR instructions +- Run the CI workflow (SCORPIO + PnetCDF - build and test) in the .github/workflows directory before committing any changes +- Do not automatically merge PRs to master or develop branch diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 00000000000..d32c01235c7 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,14 @@ +# Authors +** Current Authors ** + +* Jayesh Krishna [email](jayesh@anl.gov) +* Danqing Wu [email](DanqingWu@hotmail.com) + +** Past Contributors ** + +* Norbert Podhorszki +* Tahsin Kurc +* Dmitry Ganyushin +* Jim Edwards +* Ed Hartnett + diff --git a/CMakeLists.txt b/CMakeLists.txt index c50239e83c1..2c8eb968a97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.7) +cmake_minimum_required (VERSION 3.7...4.0.1) project (SCORPIO C CXX Fortran) #cmake_policy(VERSION 3.5.2) @@ -49,6 +49,7 @@ option (PIO_USE_MALLOC "Use native malloc (instead of bget package)" OFF) option (PIO_USE_INDEP_MODE "Use PnetCDF independent data mode" ON) option (PIO_MICRO_TIMING "Enable internal micro timers" OFF) option (PIO_SAVE_DECOMPS "Dump the decomposition information" OFF) +option (PIO_USE_ASYNC_WR_THREAD "Use an asynchronous thread for writes (Requires C++11 support)" OFF) option (PIO_LIMIT_CACHED_IO_REGIONS "Limit the number of non-contiguous regions in an IO process" OFF) option (WITH_PNETCDF "Require the use of PnetCDF" ON) option (WITH_NETCDF "Require the use of NetCDF" ON) @@ -158,6 +159,12 @@ else() message (STATUS "Disabling saving I/O decompositions (default)") endif() +set(USE_ASYNC_WR_THREAD 0) +if(PIO_USE_ASYNC_WR_THREAD) + message(STATUS "Using asynchronous I/O thread for writes") + set(USE_ASYNC_WR_THREAD 1) +endif() + if(PIO_LIMIT_CACHED_IO_REGIONS) set(LIMIT_CACHED_IO_REGIONS 1) if(NOT DEFINED PIO_MAX_CACHED_IO_REGIONS) @@ -211,6 +218,14 @@ else() endif() endif() +# Default - Do not disable MPI file syncing for HDF5 output files +set(SPIO_DISABLE_HDF5_MPI_FILE_SYNC 0) +if(FQDN_SITENAME MATCHES "^compute-386") + # On ANL compute nodes with NFS+ROMIO, disable MPI file syncing for HDF5 output + message(STATUS "Disabling HDF5 file syncing") + set(SPIO_DISABLE_HDF5_MPI_FILE_SYNC 1) +endif() + set(USE_INDEP_MODE 0) if(PIO_USE_INDEP_MODE) if(FQDN_SITENAME MATCHES "^chrlogin" OR FQDN_SITENAME MATCHES "^compute-240" OR FQDN_SITENAME MATCHES "^compute-386") @@ -251,6 +266,28 @@ else() message(STATUS "Setting chunk size (in bytes) for HDF5/PnetCDF chunked variables, requested bytes = " ${PIO_CHUNK_SIZE} " (default)") endif() +# Expected format for SPIO_DIM_CHUNK_INFO = "DIM1_NAME:DIM1_CHUNK_SZ;DIM2_NAME:DIM2_CHUNK_SZ;" +# e.g. "time:1;lev:8;" ---> Each chunk has 1 time dimension (time) and 8 levels (lev) +if(DEFINED SPIO_DIM_CHUNK_INFO) + message(STATUS "Using user-specified dimension chunk info, " ${SPIO_DIM_CHUNK_INFO}) +endif() + +# Add more constraints on where chunking is enabled. Specify a regex that matches the variable and file name where chunking +# is enabled. Chunking will be disabled for all other variables +# The regex is same as the one used to save decompositions +# e.g. -DSPIO_ENABLE_CHUNKING_REGEX:STRING='(VAR=\".*U.*\")&&(FILE=\".*e3sm_fgi.*\")' +# Chunking is enabled only on variables with names that match ".*U.*" in files with names that match ".*e3sm_fgi.*" +if(DEFINED SPIO_ENABLE_CHUNKING_REGEX) + if(DEFINED SPIO_DIM_CHUNK_INFO) + message(STATUS "Using user-specified regex," ${SPIO_ENABLE_CHUNKING_REGEX} " to decide when to enable chunking of variables") + else() + message(STATUS "WARNING: SPIO_ENABLE_CHUNKING_REGEX was defined but SPIO_DIM_CHUNK_INFO was not defined, disabling SPIO_ENABLE_CHUNKING_REGEX") + set(SPIO_ENABLE_CHUNKING_REGEX "*") + endif() +else() + set(SPIO_ENABLE_CHUNKING_REGEX "*") +endif() + if(WITH_ADIOS2) # Maximum number of I/O decompositions registered with ADIOS type set(DEF_SPIO_MAX_ADIOS_DECOMPS 65536) @@ -325,6 +362,32 @@ else() endif() endif() +# Asynchronous I/O options +# Maximum wait for asynchronous I/O operations (in milliseconds) +if(NOT DEFINED SPIO_ASYNC_OP_WAIT_TIMEOUT) + # 2 minutes = 2 * 60 * 1000 milliseconds + set(SPIO_ASYNC_OP_WAIT_TIMEOUT 120000) + if(PIO_USE_ASYNC_WR_THREAD) + message(STATUS "Setting timeout for asynchronous I/O operations to ${SPIO_ASYNC_OP_WAIT_TIMEOUT} ms (default)") + endif() +else() + if(PIO_USE_ASYNC_WR_THREAD) + message(STATUS "Setting timeout for asynchronous I/O operations to ${SPIO_ASYNC_OP_WAIT_TIMEOUT} ms") + endif() +endif() + +# Maximum number of threads performing asynchronous I/O operations +if(NOT DEFINED SPIO_ASYNC_NTHREADS) + set(SPIO_ASYNC_NTHREADS 1) + if(PIO_USE_ASYNC_WR_THREAD) + message(STATUS "Setting number of asynchronous I/O threads to ${SPIO_ASYNC_NTHREADS} (default)") + endif() +else() + if(PIO_USE_ASYNC_WR_THREAD) + message(STATUS "Setting number of asynchronous I/O threads to ${SPIO_ASYNC_NTHREADS}") + endif() +endif() + if(DEFINED SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX) if(WITH_HDF5 AND HDF5_USE_COMPRESSION) message(STATUS "Overriding HDF5 compression for variables matching regex: ${SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX}") diff --git a/cmake/SPIOUtils.cmake b/cmake/SPIOUtils.cmake index eb2267dfdf5..d8487bb3141 100644 --- a/cmake/SPIOUtils.cmake +++ b/cmake/SPIOUtils.cmake @@ -9,7 +9,8 @@ # Prereq: MPI (MPI serial) & GPTL libraries are searched for (find_library() ) # prior to invoking this macro to add SCORPIO applications macro (add_spio_executable EXE_NAME IS_C_SRC EXE_LINKER_LANGUAGE) - add_executable(${EXE_NAME} ${ARGN}) + cmake_parse_arguments (_SPIO_EXE "" "" "OBJ_LIBS" ${ARGN}) + add_executable(${EXE_NAME} ${_SPIO_EXE_UNPARSED_ARGUMENTS}) if (${IS_C_SRC}) target_link_libraries(${EXE_NAME} PRIVATE pioc) else () @@ -47,5 +48,10 @@ macro (add_spio_executable EXE_NAME IS_C_SRC EXE_LINKER_LANGUAGE) if (NOT ("${EXE_LINKER_LANGUAGE}" STREQUAL "")) set_property(TARGET ${EXE_NAME} PROPERTY LINKER_LANGUAGE ${EXE_LINKER_LANGUAGE}) endif () + if (_SPIO_EXE_OBJ_LIBS) + foreach (_spio_obj_lib ${_SPIO_EXE_OBJ_LIBS}) + target_link_libraries (${EXE_NAME} PRIVATE $) + endforeach () + endif () endmacro () diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index ea35771d41e..fa2a221b4b1 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -72,6 +72,9 @@ endif () #============================================================================== # DEFINE THE TARGETS AND TESTS #============================================================================== +# Test timeout to 180s +set (DEFAULT_TEST_TIMEOUT 180) + add_spio_executable(examplePio TRUE "" examplePio.c) add_spio_executable(example1 TRUE "" example1.c) add_spio_executable(darray_no_async TRUE "" darray_no_async.c) @@ -101,18 +104,18 @@ if (PIO_USE_MPISERIAL) add_test(NAME example1 COMMAND example1) add_test(NAME put_var COMMAND put_var) else () - add_mpi_test(examplePio EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/examplePio NUMPROCS 4 TIMEOUT 60) - add_mpi_test(example1 EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/example1 NUMPROCS 4 TIMEOUT 60) - add_mpi_test(put_var EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/put_var NUMPROCS 4 TIMEOUT 60) - #add_mpi_test(darray_async EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/darray_async NUMPROCS 5 TIMEOUT 60) - add_mpi_test(darray_no_async EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/darray_no_async NUMPROCS 4 TIMEOUT 60) + add_mpi_test(examplePio EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/examplePio NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + add_mpi_test(example1 EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/example1 NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + add_mpi_test(put_var EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/put_var NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + #add_mpi_test(darray_async EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/darray_async NUMPROCS 5 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + add_mpi_test(darray_no_async EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/darray_no_async NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) if (WITH_HDF5) - add_mpi_test(test_hdf5 EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_hdf5 NUMPROCS 4 TIMEOUT 60) + add_mpi_test(test_hdf5 EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_hdf5 NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) endif () if (WITH_ADIOS2) - add_mpi_test(test_adios EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_adios NUMPROCS 4 TIMEOUT 60) + add_mpi_test(test_adios EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_adios NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) endif () if (WITH_NETCDF) - add_mpi_test(test_netcdf4p EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_netcdf4p NUMPROCS 4 TIMEOUT 60) + add_mpi_test(test_netcdf4p EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_netcdf4p NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) endif () endif () diff --git a/examples/cxx/e3sm_fgi.cpp b/examples/cxx/e3sm_fgi.cpp index 6edc0414127..23e6f500c66 100644 --- a/examples/cxx/e3sm_fgi.cpp +++ b/examples/cxx/e3sm_fgi.cpp @@ -154,9 +154,13 @@ static int get_user_options( int main(int argc, char *argv[]) { - int ret = 0, rank = 0; + int ret = 0, rank = 0, tmode; +#if PIO_USE_ASYNC_WR_THREAD + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &tmode); +#else MPI_Init(&argc, &argv); +#endif Util::GVars::logger = Util::Logging::Logger::get_logger(MPI_COMM_WORLD, "STDOUT"); Util::GVars::logger->set_log_level(Util::Logging::LogLevel::STATUS); diff --git a/src/clib/CMakeLists.txt b/src/clib/CMakeLists.txt index 76c92f19378..a2591abf37c 100644 --- a/src/clib/CMakeLists.txt +++ b/src/clib/CMakeLists.txt @@ -1,50 +1,21 @@ -cmake_minimum_required (VERSION 3.7) +cmake_minimum_required (VERSION 3.7...4.0.1) include (CheckFunctionExists) include(CheckTypeSize) include(SPIOTypeUtils) project (PIOC C CXX) -message(STATUS "===== Configuring SCORPIO C library... =====") #============================================================================== -# DEFINE THE TARGET LIBRARY +# INTERFACE LIB : To cache compiler settings #============================================================================== -# PIO API source -set (pio_api_src - api/spio_lib_api.cpp - api/spio_ddata_api.cpp - api/spio_dim_api.cpp - api/spio_err_api.cpp - api/spio_file_api.cpp - api/spio_file_md_api.cpp - api/spio_get_att_api.cpp - api/spio_get_utils.cpp - api/spio_get_var1_api.cpp - api/spio_get_vara_api.cpp - api/spio_get_var_api.cpp - api/spio_get_varm_api.cpp - api/spio_get_vars_api.cpp - api/spio_io_decomp_api.cpp - api/spio_io_sys_api.cpp - api/spio_misc_att_api.cpp - api/spio_misc_var_api.cpp - api/spio_put_att_api.cpp - api/spio_put_var1_api.cpp - api/spio_put_vara_api.cpp - api/spio_put_var_api.cpp - api/spio_put_varm_api.cpp - api/spio_put_vars_api.cpp) - +add_library (spio_default_public_options INTERFACE) +# Include the install include dir for all dep projects +target_include_directories(spio_default_public_options INTERFACE + $) -# Add sources for libpioc.a -add_library (pioc ${pio_api_src} - topology.cpp pio_mpi_timer.cpp pio_timer.cpp pio_file.cpp - pioc_support.cpp pio_lists.cpp pio_print.cpp - pioc.cpp pioc_sc.cpp pio_spmd.cpp pio_rearrange.cpp pio_nc4.cpp bget.cpp - pio_nc.cpp pio_put_nc.cpp pio_get_nc.cpp pio_getput_int.cpp pio_msg.cpp pio_varm.cpp - pio_darray.cpp pio_darray_int.cpp spio_hash.cpp pio_sdecomps_regex.cpp spio_io_summary.cpp - spio_ltimer.cpp spio_serializer.cpp spio_file_mvcache.cpp - spio_tracer.cpp spio_tracer_mdata.cpp spio_tracer_decomp.cpp - spio_rearrange_any.cpp) +add_library (spio_default_private_options INTERFACE) +# Include the install include dir for all dep projects +target_include_directories(spio_default_private_options INTERFACE + $) #============================================================================== # FIND EXTERNAL LIBRARIES/DEPENDENCIES @@ -67,29 +38,29 @@ if (PIO_ENABLE_TIMING) find_package (GPTL COMPONENTS C) if (GPTL_C_FOUND) message (STATUS "GPTL C library dependencies: ${GPTL_C_LIBRARIES}") - target_include_directories (pioc - PUBLIC ${GPTL_C_INCLUDE_DIRS}) - target_link_libraries (pioc - PUBLIC ${GPTL_C_LIBRARIES}) + target_include_directories (spio_default_public_options + INTERFACE ${GPTL_C_INCLUDE_DIRS}) + target_link_libraries (spio_default_public_options INTERFACE ${GPTL_C_LIBRARIES}) # GPTL expects HAVE_MPI, if MPI is available, when using GPTL header files if (NOT PIO_USE_MPISERIAL) - target_compile_definitions (pioc PUBLIC HAVE_MPI) + target_compile_definitions (spio_default_public_options INTERFACE HAVE_MPI) endif () else () message (STATUS "Using internal GPTL C library for timing") - target_include_directories (pioc - PRIVATE ${PROJECT_SOURCE_DIR}/../gptl) - target_link_libraries (pioc - PUBLIC gptl) + target_include_directories (spio_default_private_options + INTERFACE $) + target_link_libraries (spio_default_public_options INTERFACE gptl) endif () - target_compile_definitions (pioc - PUBLIC SPIO_ENABLE_GPTL_TIMING - PRIVATE TIMING) + target_compile_definitions (spio_default_public_options + INTERFACE SPIO_ENABLE_GPTL_TIMING) + target_compile_definitions(spio_default_private_options + INTERFACE TIMING) if (PIO_ENABLE_INTERNAL_TIMING) - target_compile_definitions (pioc - PUBLIC SPIO_ENABLE_GPTL_TIMING_INTERNAL - PRIVATE TIMING_INTERNAL) + target_compile_definitions (spio_default_public_options + INTERFACE SPIO_ENABLE_GPTL_TIMING_INTERNAL) + target_compile_definitions (spio_default_private_options + INTERFACE TIMING_INTERNAL) endif () endif () @@ -107,10 +78,9 @@ if (WITH_NETCDF) if (NetCDF_FOUND) message(STATUS "NetCDF C library dependencies: ${NetCDF_C_LIBRARIES}") set(PIO_USE_NETCDF 1) - target_include_directories (pioc - PUBLIC ${NetCDF_C_INCLUDE_DIRS}) - target_link_libraries (pioc - PUBLIC ${NetCDF_C_LIBRARIES}) + target_include_directories (spio_default_public_options + INTERFACE ${NetCDF_C_INCLUDE_DIRS}) + target_link_libraries (spio_default_public_options INTERFACE ${NetCDF_C_LIBRARIES}) if (${NetCDF_C_HAS_PARALLEL}) set(PIO_USE_NETCDF4 1) set(PIO_USE_NETCDF4_NCZARR 0) @@ -118,8 +88,8 @@ if (WITH_NETCDF) if (PIO_ENABLE_NCZARR) message(STATUS "Enabling support for NCZarr") set(PIO_USE_NETCDF4_NCZARR 1) - target_compile_definitions (pioc - PUBLIC _SPIO_HAS_NETCDF4_NCZARR) + target_compile_definitions (spio_default_public_options + INTERFACE _SPIO_HAS_NETCDF4_NCZARR) else () message(STATUS "Disabling support for NCZarr (default)") endif () @@ -130,15 +100,15 @@ if (WITH_NETCDF) set(PIO_USE_NETCDF4 0) endif () if (${NetCDF_C_LOGGING_ENABLED}) - target_compile_definitions (pioc - PRIVATE NETCDF_C_LOGGING_ENABLED) + target_compile_definitions (spio_default_private_options + INTERFACE NETCDF_C_LOGGING_ENABLED) # netcdf.h needs this to be defined to use netCDF logging. - target_compile_definitions (pioc - PRIVATE LOGGING) + target_compile_definitions (spio_default_private_options + INTERFACE LOGGING) endif() if (${NetCDF_C_NC__ENDDEF_EXISTS}) - target_compile_definitions (pioc - PRIVATE NETCDF_C_NC__ENDDEF_EXISTS) + target_compile_definitions (spio_default_private_options + INTERFACE NETCDF_C_NC__ENDDEF_EXISTS) endif() else () message(STATUS "Could not find NetCDF C library, disabling support for NetCDF") @@ -164,18 +134,18 @@ if (WITH_PNETCDF) if (PnetCDF_FOUND) message(STATUS "PnetCDF C library dependencies: ${PnetCDF_C_LIBRARY}") set(PIO_USE_PNETCDF 1) - target_include_directories (pioc - PUBLIC ${PnetCDF_C_INCLUDE_DIRS}) - target_link_libraries (pioc - PUBLIC ${PnetCDF_C_LIBRARIES}) + target_include_directories (spio_default_public_options + INTERFACE ${PnetCDF_C_INCLUDE_DIRS}) + target_link_libraries (spio_default_public_options INTERFACE ${PnetCDF_C_LIBRARIES}) # Check library for varn functions set (CMAKE_REQUIRED_LIBRARIES ${PnetCDF_C_LIBRARY}) check_function_exists (ncmpi_get_varn PnetCDF_C_HAS_VARN) if (PnetCDF_C_HAS_VARN) - target_compile_definitions(pioc - PRIVATE USE_PNETCDF_VARN - PRIVATE USE_PNETCDF_VARN_ON_READ) + target_compile_definitions(spio_default_private_options + INTERFACE USE_PNETCDF_VARN) + target_compile_definitions(spio_default_private_options + INTERFACE USE_PNETCDF_VARN_ON_READ) endif() else () message(STATUS "Could not find PnetCDF library, disabling support for PnetCDF") @@ -203,14 +173,13 @@ if (WITH_ADIOS2) if (ADIOS2_FOUND) message(STATUS "Found ADIOS library") set(PIO_USE_ADIOS 1) - target_compile_definitions (pioc - PUBLIC _ADIOS2) + target_compile_definitions (spio_default_public_options + INTERFACE _ADIOS2) # 2001 edition of the POSIX standard (IEEE Standard 1003.1-2001) # Required for symlink() support/decl in unistd.h - target_compile_definitions (pioc - PRIVATE _POSIX_C_SOURCE=200112L) - target_link_libraries (pioc - PUBLIC adios2::adios2 adios2pio-nm-lib) + target_compile_definitions (spio_default_private_options + INTERFACE _POSIX_C_SOURCE=200112L) + target_link_libraries (spio_default_public_options INTERFACE adios2::adios2) else () message(STATUS "Could not find ADIOS library, disabling support for ADIOS") set(PIO_USE_ADIOS 0) @@ -226,12 +195,11 @@ if (WITH_HDF5) if (HDF5_C_FOUND) message(STATUS "HDF5 C library dependencies: ${HDF5_C_LIBRARIES} ${HDF5_HL_LIBRARIES}") set(PIO_USE_HDF5 1) - target_compile_definitions (pioc - PUBLIC _HDF5) - target_include_directories (pioc - PUBLIC ${HDF5_C_INCLUDE_DIRS} ${HDF5_HL_INCLUDE_DIRS}) - target_link_libraries (pioc - PUBLIC ${HDF5_C_LIBRARIES} ${HDF5_HL_LIBRARIES} ${CMAKE_DL_LIBS}) + target_compile_definitions (spio_default_public_options + INTERFACE _HDF5) + target_include_directories (spio_default_public_options + INTERFACE ${HDF5_C_INCLUDE_DIRS} ${HDF5_HL_INCLUDE_DIRS}) + target_link_libraries (spio_default_public_options INTERFACE ${HDF5_C_LIBRARIES} ${HDF5_HL_LIBRARIES} ${CMAKE_DL_LIBS}) # Look for HDF5 ZFP (compression library) filter library set(H5Z_ZFP_USE_STATIC_LIBS ON) @@ -247,8 +215,9 @@ if (WITH_HDF5) find_package(H5Z_ZFP) if (H5Z_ZFP_FOUND) message (STATUS "Found ZFP filter for HDF5") - target_compile_definitions (pioc PUBLIC _SPIO_HAS_H5Z_ZFP) - target_link_libraries (pioc PUBLIC h5z_zfp::h5z_zfp) + target_compile_definitions (spio_default_public_options INTERFACE _SPIO_HAS_H5Z_ZFP) + target_include_directories (spio_default_public_options INTERFACE ${H5Z_ZFP_INCLUDE_DIR}) + target_link_libraries (spio_default_public_options INTERFACE h5z_zfp::h5z_zfp) else () message (STATUS "Could not find ZFP filter for HDF5, disabling ZFP with HDF5") endif () @@ -262,8 +231,9 @@ if (WITH_HDF5) find_package(H5Z_BLOSC2) if (H5Z_BLOSC2_FOUND) message (STATUS "Found BLOSC2 filter for HDF5") - target_compile_definitions (pioc PUBLIC _SPIO_HAS_H5Z_BLOSC2) - target_link_libraries (pioc PUBLIC h5z_blosc2::h5z_blosc2) + target_compile_definitions (spio_default_public_options INTERFACE _SPIO_HAS_H5Z_BLOSC2) + target_include_directories (spio_default_public_options INTERFACE ${H5Z_BLOSC2_INCLUDE_DIR}) + target_link_libraries (spio_default_public_options INTERFACE h5z_blosc2::h5z_blosc2) else () message (STATUS "Could not find Blosc2 filter, disabling HDF5 filter for Blosc2") endif () @@ -325,50 +295,46 @@ include_directories( "${PROJECT_BINARY_DIR}") # to find foo/config.h # Include the clib source and binary directory -target_include_directories (pioc - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_include_directories (pioc - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -# Include the install include dir for all dep projects -target_include_directories (pioc - PUBLIC $) +target_include_directories (spio_default_private_options + INTERFACE $) +target_include_directories (spio_default_private_options + INTERFACE $) # System and compiler CPP directives -target_compile_definitions (pioc - PRIVATE ${CMAKE_SYSTEM_DIRECTIVE}) -target_compile_definitions (pioc - PUBLIC ${CMAKE_C_COMPILER_DIRECTIVE}) +target_compile_definitions (spio_default_public_options + INTERFACE ${CMAKE_SYSTEM_DIRECTIVE}) +target_compile_definitions (spio_default_public_options + INTERFACE ${CMAKE_C_COMPILER_DIRECTIVE}) # Skip MPI C++ headers/bindings for MPICH lib -target_compile_definitions (pioc - PUBLIC MPICH_SKIP_MPICXX) +target_compile_definitions (spio_default_public_options + INTERFACE MPICH_SKIP_MPICXX) # Skip MPI C++ headers/bindings for OpenMPI lib -target_compile_definitions (pioc - PUBLIC OMPI_SKIP_MPICXX) +target_compile_definitions (spio_default_public_options + INTERFACE OMPI_SKIP_MPICXX) # Skip MPI C++ headers/bindings for SGI MPT lib -target_compile_definitions (pioc - PUBLIC MPI_NO_CPPBIND) +target_compile_definitions (spio_default_public_options + INTERFACE MPI_NO_CPPBIND) # Add user-specified include/libs/compiler/link options -target_include_directories (pioc - PUBLIC ${PIO_C_EXTRA_INCLUDE_DIRS}) -target_link_libraries (pioc - PUBLIC ${PIO_C_EXTRA_LIBRARIES}) -target_compile_options (pioc - PRIVATE ${PIO_C_EXTRA_COMPILE_OPTIONS}) -target_compile_definitions (pioc - PUBLIC ${PIO_C_EXTRA_COMPILE_DEFINITIONS}) +target_include_directories (spio_default_public_options + INTERFACE ${PIO_C_EXTRA_INCLUDE_DIRS}) +target_link_libraries (spio_default_public_options + INTERFACE ${PIO_C_EXTRA_LIBRARIES}) +target_compile_options (spio_default_public_options + INTERFACE ${PIO_C_EXTRA_COMPILE_OPTIONS}) +target_compile_definitions (spio_default_public_options + INTERFACE ${PIO_C_EXTRA_COMPILE_DEFINITIONS}) if (PIO_C_EXTRA_LINK_FLAGS) - set_target_properties(pioc PROPERTIES - LINK_FLAGS ${PIO_C_EXTRA_LINK_FLAGS}) + target_link_options(spio_default_public_options + INTERFACE ${PIO_C_EXTRA_LINK_FLAGS}) endif () # At least on Titan + Cray MPI, MPI_Irsends are buggy # causing hangs during I/O # Force Scorpio to use MPI_Isends instead of the default # MPI_Irsends -target_compile_definitions (pioc - PRIVATE USE_MPI_ISEND_FOR_FC) +target_compile_definitions (spio_default_private_options + INTERFACE USE_MPI_ISEND_FOR_FC) # Compiler-specific compiler options string (TOUPPER "${CMAKE_C_COMPILER_ID}" CMAKE_C_COMPILER_NAME) @@ -391,14 +357,14 @@ endif () if (ADIOS_BP2NC_TEST) message(STATUS "Building ADIOS to NetCDF conversion tool") - target_compile_definitions (pioc - PUBLIC _ADIOS_BP2NC_TEST) + target_compile_definitions (spio_default_public_options + INTERFACE _ADIOS_BP2NC_TEST) endif () if (ADIOS_NO_DECOMPS) message(STATUS "No decomposition data will be saved to ADIOS BP files to reduce disk usage") - target_compile_definitions (pioc - PUBLIC _SPIO_ADIOS_NO_DECOMPS) + target_compile_definitions (spio_default_public_options + INTERFACE _SPIO_ADIOS_NO_DECOMPS) endif () if (ADIOS_USE_COMPRESSION) @@ -408,8 +374,8 @@ if (ADIOS_USE_COMPRESSION) message(STATUS "Blosc2 compression level (range: 0 – 9) = 1 (default)") message(STATUS "Blosc2 shuffle mode (option: BLOSC_SHUFFLE, BLOSC_NOSHUFFLE, BLOSC_BITSHUFFLE) = BLOSC_BITSHUFFLE (default)") message(STATUS "These are the default settings for lossless compression. Use -DSPIO_COMPRESSION_OPTIONS to change the defaults") - target_compile_definitions (pioc - PUBLIC _SPIO_ADIOS_USE_COMPRESSION) + target_compile_definitions (spio_default_public_options + INTERFACE _SPIO_ADIOS_USE_COMPRESSION) endif () if (ADIOS_USE_LOSSY_COMPRESSION) @@ -418,8 +384,8 @@ if (ADIOS_USE_LOSSY_COMPRESSION) message(STATUS "Lossy compression error bound = 0.001 (default)") message(STATUS "These are the default settings for lossy compression. Use -DSPIO_COMPRESSION_OPTIONS to change the defaults") message(WARNING "The SZ or MGARD methods only support float or double types. For other types, lossless compression will be used instead") - target_compile_definitions (pioc - PRIVATE _SPIO_ADIOS_USE_LOSSY_COMPRESSION) + target_compile_definitions (spio_default_public_options + INTERFACE _SPIO_ADIOS_USE_LOSSY_COMPRESSION) endif () if (HDF5_USE_COMPRESSION) @@ -429,8 +395,8 @@ if (HDF5_USE_COMPRESSION) message(STATUS "Blosc2 compression level (range: 0 – 9) = 1 (default)") message(STATUS "Blosc2 shuffle mode (option: BLOSC_SHUFFLE, BLOSC_NOSHUFFLE, BLOSC_BITSHUFFLE) = BLOSC_BITSHUFFLE (default)") message(STATUS "These are the default settings for lossless compression. Use -DSPIO_COMPRESSION_OPTIONS to change the defaults") - target_compile_definitions (pioc - PUBLIC _SPIO_HDF5_USE_COMPRESSION) + target_compile_definitions (spio_default_public_options + INTERFACE _SPIO_HDF5_USE_COMPRESSION) endif () if (HDF5_USE_LOSSY_COMPRESSION) @@ -438,8 +404,8 @@ if (HDF5_USE_LOSSY_COMPRESSION) message(STATUS "Currently supported lossy compression methods : ZFP (default)") message(STATUS "Lossy compression error bound = 0.001 (default)") message(STATUS "These are the default settings for lossy compression. Use -DSPIO_COMPRESSION_OPTIONS to change the defaults") - target_compile_definitions (pioc - PRIVATE _SPIO_HDF5_USE_LOSSY_COMPRESSION) + target_compile_definitions (spio_default_public_options + INTERFACE _SPIO_HDF5_USE_LOSSY_COMPRESSION) endif () if (PIO_ENABLE_API_TRACING) @@ -460,12 +426,11 @@ endif () # The MPI library detection was done in the top level if (MPISERIAL_C_FOUND) - target_compile_definitions (pioc - PUBLIC MPI_SERIAL) - target_include_directories (pioc - PUBLIC ${MPISERIAL_C_INCLUDE_DIRS}) - target_link_libraries (pioc - PUBLIC ${MPISERIAL_C_LIBRARIES}) + target_compile_definitions (spio_default_public_options + INTERFACE MPI_SERIAL) + target_include_directories (spio_default_public_options + INTERFACE ${MPISERIAL_C_INCLUDE_DIRS}) + target_link_libraries (spio_default_public_options INTERFACE ${MPISERIAL_C_LIBRARIES}) set (WITH_PNETCDF FALSE) endif () @@ -489,12 +454,55 @@ else () set(USE_MICRO_TIMING 0) endif () +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C library... =====") +# Add subdirectories +add_subdirectory (api) +add_subdirectory (util) +add_subdirectory (core) + +# FIXME : Use interface libs for subdir sources ? +# Add sources for libpioc.a +set (SPIO_PIOC_OBJS + $ + $ + $ + $ + $ + $ + $ + $) + +if (ADIOS2_FOUND) + set (SPIO_PIOC_OBJS ${SPIO_PIOC_OBJS} $) +endif () + +add_library (pioc ${SPIO_PIOC_OBJS}) + +# PUBLIC ${spio_default_public_libs} +# PRIVATE ${spio_default_private_libs} + +target_link_libraries (pioc + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) + +# FIXME: CMake 3.26+ can completely hide the local private interface lib +# (No need to export it) +# PRIVATE $) + #============================================================================== # INSTALL #============================================================================== -# Install libpioc.a -install (TARGETS pioc +# 1. Install Interface library, spio_default_public_options, that contains +# all the public compiler options +# 2. Installing private interface library, spio_default_private_options, that +# contains all private compiler options. CMake 3.26+ can get rid of exporting +# this interface library using BUILD_LOCAL_INTERFACE generator expression +# 3. Install pioc (libpioc.a) +install (TARGETS pioc spio_default_public_options spio_default_private_options EXPORT spio-targets-pioc DESTINATION lib) diff --git a/src/clib/api/CMakeLists.txt b/src/clib/api/CMakeLists.txt new file mode 100644 index 00000000000..8d5368bfaa3 --- /dev/null +++ b/src/clib/api/CMakeLists.txt @@ -0,0 +1,37 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C API ... =====") +# PIO API source +set (pio_api_src + spio_lib_api.cpp + spio_ddata_api.cpp + spio_dim_api.cpp + spio_err_api.cpp + spio_file_api.cpp + spio_file_md_api.cpp + spio_get_att_api.cpp + spio_get_utils.cpp + spio_get_var1_api.cpp + spio_get_vara_api.cpp + spio_get_var_api.cpp + spio_get_varm_api.cpp + spio_get_vars_api.cpp + spio_io_decomp_api.cpp + spio_io_sys_api.cpp + spio_misc_att_api.cpp + spio_misc_var_api.cpp + spio_put_att_api.cpp + spio_put_var1_api.cpp + spio_put_vara_api.cpp + spio_put_var_api.cpp + spio_put_varm_api.cpp + spio_put_vars_api.cpp) + +add_library (pioc_api OBJECT ${pio_api_src}) +target_include_directories(pioc_api + PUBLIC . + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../util ${SCORPIO_SOURCE_DIR}/util) +target_link_libraries(pioc_api + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/api/spio_ddata_api.cpp b/src/clib/api/spio_ddata_api.cpp index 98ebfb2fbad..dc905fae62d 100644 --- a/src/clib/api/spio_ddata_api.cpp +++ b/src/clib/api/spio_ddata_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* ================= Read/Write APIs for distributed data ================ */ int PIOc_advanceframe(int ncid, int varid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_advanceframe"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_advanceframe"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid).flush(); @@ -16,6 +18,7 @@ int PIOc_advanceframe(int ncid, int varid) int PIOc_setframe(int ncid, int varid, int frame) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_setframe"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_setframe"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -27,6 +30,7 @@ int PIOc_setframe(int ncid, int varid, int frame) int PIOc_write_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, const void *array, const void *fillvalue) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_write_darray"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_write_darray"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -39,6 +43,7 @@ int PIOc_write_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, const int PIOc_write_darray_multi(int ncid, const int *varids, int ioid, int nvars, PIO_Offset arraylen, const void *array, const int *frame, const void **fillvalue, bool flushtodisk) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_write_darray_multi"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_write_darray_multi"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("*varids", varids). @@ -54,6 +59,7 @@ int PIOc_write_darray_multi(int ncid, const int *varids, int ioid, int nvars, PI int PIOc_read_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, void *array) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_read_darray"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_read_darray"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -65,6 +71,7 @@ int PIOc_read_darray(int ncid, int varid, int ioid, PIO_Offset arraylen, void *a int PIOc_get_local_array_size(int ioid) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_local_array_size"); /* FIXME: How should we trace these non I/O system specific functions? */ return PIOc_get_local_array_size_impl(ioid); } diff --git a/src/clib/api/spio_dim_api.cpp b/src/clib/api/spio_dim_api.cpp index 437a7b9eb2d..b520a3f6d01 100644 --- a/src/clib/api/spio_dim_api.cpp +++ b/src/clib/api/spio_dim_api.cpp @@ -3,11 +3,13 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* ================= File meta-data APIs ============== */ /* APIs for variable dimensions */ int PIOc_inq_dim(int ncid, int dimid, char *name, PIO_Offset *lenp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_dimx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_dim"); @@ -24,6 +26,7 @@ int PIOc_inq_dim(int ncid, int dimid, char *name, PIO_Offset *lenp) int PIOc_inq_dimid(int ncid, const char *name, int *idp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_dimx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_dimid"); @@ -41,6 +44,7 @@ int PIOc_inq_dimid(int ncid, const char *name, int *idp) int PIOc_inq_dimname(int ncid, int dimid, char *name) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_dimx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_dimname"); @@ -57,6 +61,7 @@ int PIOc_inq_dimname(int ncid, int dimid, char *name) int PIOc_inq_dimlen(int ncid, int dimid, PIO_Offset *lenp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_dimx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_dimlen"); @@ -73,6 +78,7 @@ int PIOc_inq_dimlen(int ncid, int dimid, PIO_Offset *lenp) int PIOc_rename_dim(int ncid, int dimid, const char *name) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_rename_dim"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_rename_dim"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("dimid", dimid). @@ -83,6 +89,7 @@ int PIOc_rename_dim(int ncid, int dimid, const char *name) int PIOc_def_dim(int ncid, const char *name, PIO_Offset len, int *idp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_def_dim"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_def_dim"); diff --git a/src/clib/api/spio_err_api.cpp b/src/clib/api/spio_err_api.cpp index 53fa1b71621..a7afecc1a5c 100644 --- a/src/clib/api/spio_err_api.cpp +++ b/src/clib/api/spio_err_api.cpp @@ -2,16 +2,19 @@ #include "pio.h" #include "pio_internal.h" #include "pio_api_impl.h" +//#include "spio_gptl_utils.hpp" /* ========== Error handling APIs =========== */ int PIOc_strerror(int pioerr, char *errmsg, size_t errmsg_sz) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_strerror"); /* FIXME: Handle non I/O system specific calls */ return PIOc_strerror_impl(pioerr, errmsg, errmsg_sz); } int PIOc_set_log_level(int level) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_log_level"); /* FIXME: Handle non I/O system specific calls */ return PIOc_set_log_level_impl(level); } diff --git a/src/clib/api/spio_file_api.cpp b/src/clib/api/spio_file_api.cpp index b1eeb23a326..8a13130f958 100644 --- a/src/clib/api/spio_file_api.cpp +++ b/src/clib/api/spio_file_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* ================= File APIs ================= */ int PIOc_deletefile(int iosysid, const char *filename) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_deletefile"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_deletefile"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid). @@ -17,6 +19,7 @@ int PIOc_deletefile(int iosysid, const char *filename) int PIOc_createfile(int iosysid, int *ncidp, const int *iotype, const char *fname, int mode) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_createfile"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_createfile"); @@ -34,6 +37,7 @@ int PIOc_createfile(int iosysid, int *ncidp, const int *iotype, const char *fnam int PIOc_create(int iosysid, const char *path, int cmode, int *ncidp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_create"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_create"); @@ -51,6 +55,7 @@ int PIOc_create(int iosysid, const char *path, int cmode, int *ncidp) int PIOc_openfile(int iosysid, int *ncidp, int *iotype, const char *fname, int mode) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_openfile"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_openfile"); @@ -68,6 +73,7 @@ int PIOc_openfile(int iosysid, int *ncidp, int *iotype, const char *fname, int m int PIOc_openfile2(int iosysid, int *ncidp, int *iotype, const char *fname, int mode) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_openfile2"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_openfile2"); @@ -86,6 +92,7 @@ int PIOc_openfile2(int iosysid, int *ncidp, int *iotype, const char *fname, int int PIOc_openfile_retry(int iosysid, int *ncidp, int *iotype, const char *filename, int mode, int retry) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_openfile_retry"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_openfile_retry"); @@ -103,6 +110,7 @@ int PIOc_openfile_retry(int iosysid, int *ncidp, int *iotype, int PIOc_open(int iosysid, const char *path, int mode, int *ncidp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_open"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_open"); @@ -120,6 +128,7 @@ int PIOc_open(int iosysid, const char *path, int mode, int *ncidp) int PIOc_closefile(int ncid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_closefile"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_closefile"); tr.set_file_id(ncid).add_arg("ncid", ncid).flush(); @@ -129,6 +138,7 @@ int PIOc_closefile(int ncid) int PIOc_File_is_Open(int ncid) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_File_is_Open"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_File_is_Open"); tr.set_file_id(ncid).add_arg("ncid", ncid).flush(); @@ -139,6 +149,7 @@ int PIOc_File_is_Open(int ncid) /* Set the error hanlding for a file. */ int PIOc_Set_File_Error_Handling(int ncid, int method) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_Set_File_Error_Handling"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_Set_File_Error_Handling"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("method", method).flush(); @@ -148,6 +159,7 @@ int PIOc_Set_File_Error_Handling(int ncid, int method) int PIOc_sync(int ncid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_sync"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_sync"); tr.set_file_id(ncid).add_arg("ncid", ncid).flush(); diff --git a/src/clib/api/spio_file_md_api.cpp b/src/clib/api/spio_file_md_api.cpp index 79258c0eb7f..85e2c05fbb3 100644 --- a/src/clib/api/spio_file_md_api.cpp +++ b/src/clib/api/spio_file_md_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* ================= File meta-data APIs ============== */ int PIOc_redef(int ncid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_redef"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_redef"); tr.set_file_id(ncid).add_arg("ncid", ncid).flush(); @@ -16,6 +18,7 @@ int PIOc_redef(int ncid) int PIOc_enddef(int ncid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_enddef"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_enddef"); tr.set_file_id(ncid).add_arg("ncid", ncid).flush(); @@ -25,6 +28,7 @@ int PIOc_enddef(int ncid) int PIOc_inq_format(int ncid, int *formatp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING @@ -42,6 +46,7 @@ int PIOc_inq_format(int ncid, int *formatp) int PIOc_inq(int ncid, int *ndimsp, int *nvarsp, int *ngattsp, int *unlimdimidp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING @@ -61,6 +66,7 @@ int PIOc_inq(int ncid, int *ndimsp, int *nvarsp, int *ngattsp, int *unlimdimidp) int PIOc_inq_ndims(int ncid, int *ndimsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING @@ -78,6 +84,7 @@ int PIOc_inq_ndims(int ncid, int *ndimsp) int PIOc_inq_nvars(int ncid, int *nvarsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING @@ -95,6 +102,7 @@ int PIOc_inq_nvars(int ncid, int *nvarsp) int PIOc_inq_natts(int ncid, int *ngattsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING @@ -112,6 +120,7 @@ int PIOc_inq_natts(int ncid, int *ngattsp) int PIOc_inq_unlimdim(int ncid, int *unlimdimidp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING @@ -129,6 +138,7 @@ int PIOc_inq_unlimdim(int ncid, int *unlimdimidp) int PIOc_inq_unlimdims(int ncid, int *nunlimdimsp, int *unlimdimidsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING @@ -150,6 +160,7 @@ int PIOc_inq_unlimdims(int ncid, int *nunlimdimsp, int *unlimdimidsp) int PIOc_inq_type(int ncid, nc_type xtype, char *name, PIO_Offset *sizep) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inqx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING diff --git a/src/clib/api/spio_get_att_api.cpp b/src/clib/api/spio_get_att_api.cpp index 4ec8cb59880..a0f2d9ac0b5 100644 --- a/src/clib/api/spio_get_att_api.cpp +++ b/src/clib/api/spio_get_att_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* APIs for reading file/variable attributes */ int PIOc_get_att(int ncid, int varid, const char *name, void *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -18,6 +20,7 @@ int PIOc_get_att(int ncid, int varid, const char *name, void *ip) int PIOc_get_att_text(int ncid, int varid, const char *name, char *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_text"); @@ -35,6 +38,7 @@ int PIOc_get_att_text(int ncid, int varid, const char *name, char *ip) int PIOc_get_att_schar(int ncid, int varid, const char *name, signed char *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_schar"); @@ -52,6 +56,7 @@ int PIOc_get_att_schar(int ncid, int varid, const char *name, signed char *ip) int PIOc_get_att_short(int ncid, int varid, const char *name, short *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_short"); @@ -69,6 +74,7 @@ int PIOc_get_att_short(int ncid, int varid, const char *name, short *ip) int PIOc_get_att_int(int ncid, int varid, const char *name, int *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_int"); @@ -86,6 +92,7 @@ int PIOc_get_att_int(int ncid, int varid, const char *name, int *ip) int PIOc_get_att_long(int ncid, int varid, const char *name, long *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_long"); @@ -103,6 +110,7 @@ int PIOc_get_att_long(int ncid, int varid, const char *name, long *ip) int PIOc_get_att_float(int ncid, int varid, const char *name, float *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_float"); @@ -120,6 +128,7 @@ int PIOc_get_att_float(int ncid, int varid, const char *name, float *ip) int PIOc_get_att_double(int ncid, int varid, const char *name, double *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_double"); @@ -137,6 +146,7 @@ int PIOc_get_att_double(int ncid, int varid, const char *name, double *ip) int PIOc_get_att_uchar(int ncid, int varid, const char *name, unsigned char *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_uchar"); @@ -154,6 +164,7 @@ int PIOc_get_att_uchar(int ncid, int varid, const char *name, unsigned char *ip) int PIOc_get_att_ushort(int ncid, int varid, const char *name, unsigned short *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_ushort"); @@ -171,6 +182,7 @@ int PIOc_get_att_ushort(int ncid, int varid, const char *name, unsigned short *i int PIOc_get_att_uint(int ncid, int varid, const char *name, unsigned int *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_uint"); @@ -188,6 +200,7 @@ int PIOc_get_att_uint(int ncid, int varid, const char *name, unsigned int *ip) int PIOc_get_att_longlong(int ncid, int varid, const char *name, long long *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_longlong"); @@ -205,6 +218,7 @@ int PIOc_get_att_longlong(int ncid, int varid, const char *name, long long *ip) int PIOc_get_att_ulonglong(int ncid, int varid, const char *name, unsigned long long *ip) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_att_ulonglong"); diff --git a/src/clib/api/spio_get_var1_api.cpp b/src/clib/api/spio_get_var1_api.cpp index 61d976da1c8..32b4bb1b8a0 100644 --- a/src/clib/api/spio_get_var1_api.cpp +++ b/src/clib/api/spio_get_var1_api.cpp @@ -4,10 +4,12 @@ #include "pio_api_impl.h" #include "spio_tracer.hpp" #include "spio_get_utils.hpp" +#include "spio_gptl_utils.hpp" /* APIs for reading non-distributed data/variable at a specified index */ int PIOc_get_var1(int ncid, int varid, const PIO_Offset *index, void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -19,6 +21,7 @@ int PIOc_get_var1(int ncid, int varid, const PIO_Offset *index, void *buf) int PIOc_get_var1_text(int ncid, int varid, const PIO_Offset *index, char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_text"); @@ -36,6 +39,7 @@ int PIOc_get_var1_text(int ncid, int varid, const PIO_Offset *index, char *buf) int PIOc_get_var1_schar(int ncid, int varid, const PIO_Offset *index, signed char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_schar"); @@ -53,6 +57,7 @@ int PIOc_get_var1_schar(int ncid, int varid, const PIO_Offset *index, signed cha int PIOc_get_var1_short(int ncid, int varid, const PIO_Offset *index, short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_short"); @@ -70,6 +75,7 @@ int PIOc_get_var1_short(int ncid, int varid, const PIO_Offset *index, short *buf int PIOc_get_var1_int(int ncid, int varid, const PIO_Offset *index, int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_int"); @@ -87,6 +93,7 @@ int PIOc_get_var1_int(int ncid, int varid, const PIO_Offset *index, int *buf) int PIOc_get_var1_long(int ncid, int varid, const PIO_Offset *index, long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_long"); @@ -104,6 +111,7 @@ int PIOc_get_var1_long(int ncid, int varid, const PIO_Offset *index, long *buf) int PIOc_get_var1_float(int ncid, int varid, const PIO_Offset *index, float *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_float"); @@ -121,6 +129,7 @@ int PIOc_get_var1_float(int ncid, int varid, const PIO_Offset *index, float *buf int PIOc_get_var1_double(int ncid, int varid, const PIO_Offset *index, double *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_double"); @@ -138,6 +147,7 @@ int PIOc_get_var1_double(int ncid, int varid, const PIO_Offset *index, double *b int PIOc_get_var1_uchar(int ncid, int varid, const PIO_Offset *index, unsigned char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_uchar"); @@ -155,6 +165,7 @@ int PIOc_get_var1_uchar(int ncid, int varid, const PIO_Offset *index, unsigned c int PIOc_get_var1_ushort(int ncid, int varid, const PIO_Offset *index, unsigned short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_ushort"); @@ -172,6 +183,7 @@ int PIOc_get_var1_ushort(int ncid, int varid, const PIO_Offset *index, unsigned int PIOc_get_var1_uint(int ncid, int varid, const PIO_Offset *index, unsigned int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_uint"); @@ -189,6 +201,7 @@ int PIOc_get_var1_uint(int ncid, int varid, const PIO_Offset *index, unsigned in int PIOc_get_var1_longlong(int ncid, int varid, const PIO_Offset *index, long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_longlong"); @@ -206,6 +219,7 @@ int PIOc_get_var1_longlong(int ncid, int varid, const PIO_Offset *index, long lo int PIOc_get_var1_ulonglong(int ncid, int varid, const PIO_Offset *index, unsigned long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var1_ulonglong"); diff --git a/src/clib/api/spio_get_var_api.cpp b/src/clib/api/spio_get_var_api.cpp index a95d8497113..339d376b1d2 100644 --- a/src/clib/api/spio_get_var_api.cpp +++ b/src/clib/api/spio_get_var_api.cpp @@ -4,10 +4,12 @@ #include "pio_api_impl.h" #include "spio_tracer.hpp" #include "spio_get_utils.hpp" +#include "spio_gptl_utils.hpp" /* APIs for reading entire non-distributed data/variable */ int PIOc_get_var(int ncid, int varid, void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -18,6 +20,7 @@ int PIOc_get_var(int ncid, int varid, void *buf) int PIOc_get_var_text(int ncid, int varid, char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_text"); @@ -35,6 +38,7 @@ int PIOc_get_var_text(int ncid, int varid, char *buf) int PIOc_get_var_schar(int ncid, int varid, signed char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_schar"); @@ -51,6 +55,7 @@ int PIOc_get_var_schar(int ncid, int varid, signed char *buf) int PIOc_get_var_short(int ncid, int varid, short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_short"); @@ -67,6 +72,7 @@ int PIOc_get_var_short(int ncid, int varid, short *buf) int PIOc_get_var_int(int ncid, int varid, int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_int"); @@ -83,6 +89,7 @@ int PIOc_get_var_int(int ncid, int varid, int *buf) int PIOc_get_var_long(int ncid, int varid, long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_long"); @@ -99,6 +106,7 @@ int PIOc_get_var_long(int ncid, int varid, long *buf) int PIOc_get_var_float(int ncid, int varid, float *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_float"); @@ -115,6 +123,7 @@ int PIOc_get_var_float(int ncid, int varid, float *buf) int PIOc_get_var_double(int ncid, int varid, double *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_double"); @@ -131,6 +140,7 @@ int PIOc_get_var_double(int ncid, int varid, double *buf) int PIOc_get_var_uchar(int ncid, int varid, unsigned char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_uchar"); @@ -147,6 +157,7 @@ int PIOc_get_var_uchar(int ncid, int varid, unsigned char *buf) int PIOc_get_var_ushort(int ncid, int varid, unsigned short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_ushort"); @@ -163,6 +174,7 @@ int PIOc_get_var_ushort(int ncid, int varid, unsigned short *buf) int PIOc_get_var_uint(int ncid, int varid, unsigned int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_uint"); @@ -179,6 +191,7 @@ int PIOc_get_var_uint(int ncid, int varid, unsigned int *buf) int PIOc_get_var_longlong(int ncid, int varid, long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_longlong"); @@ -195,6 +208,7 @@ int PIOc_get_var_longlong(int ncid, int varid, long long *buf) int PIOc_get_var_ulonglong(int ncid, int varid, unsigned long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_ulonglong"); diff --git a/src/clib/api/spio_get_vara_api.cpp b/src/clib/api/spio_get_vara_api.cpp index 14b91f2d03e..59188d9ab4d 100644 --- a/src/clib/api/spio_get_vara_api.cpp +++ b/src/clib/api/spio_get_vara_api.cpp @@ -4,11 +4,13 @@ #include "pio_api_impl.h" #include "spio_tracer.hpp" #include "spio_get_utils.hpp" +#include "spio_gptl_utils.hpp" #include /* APIs for reading a hyperslab of non-distributed data/variable */ int PIOc_get_vara(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -22,6 +24,7 @@ int PIOc_get_vara(int ncid, int varid, const PIO_Offset *start, const PIO_Offset int PIOc_get_vara_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_text"); @@ -40,6 +43,7 @@ int PIOc_get_vara_text(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_get_vara_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, signed char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_schar"); @@ -58,6 +62,7 @@ int PIOc_get_vara_schar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vara_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_short"); @@ -76,6 +81,7 @@ int PIOc_get_vara_short(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vara_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_int"); @@ -94,6 +100,7 @@ int PIOc_get_vara_int(int ncid, int varid, const PIO_Offset *start, const PIO_Of int PIOc_get_vara_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, float *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_float"); @@ -112,6 +119,7 @@ int PIOc_get_vara_float(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vara_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_long"); @@ -130,6 +138,7 @@ int PIOc_get_vara_long(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_get_vara_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, double *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_double"); @@ -148,6 +157,7 @@ int PIOc_get_vara_double(int ncid, int varid, const PIO_Offset *start, int PIOc_get_vara_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_uchar"); @@ -166,6 +176,7 @@ int PIOc_get_vara_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vara_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_ushort"); @@ -184,6 +195,7 @@ int PIOc_get_vara_ushort(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_get_vara_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_uint"); @@ -202,6 +214,7 @@ int PIOc_get_vara_uint(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_get_vara_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_longlong"); @@ -220,6 +233,7 @@ int PIOc_get_vara_longlong(int ncid, int varid, const PIO_Offset *start, const P int PIOc_get_vara_ulonglong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vara_ulonglong"); diff --git a/src/clib/api/spio_get_varm_api.cpp b/src/clib/api/spio_get_varm_api.cpp index 968ad655fad..3ce3325b18e 100644 --- a/src/clib/api/spio_get_varm_api.cpp +++ b/src/clib/api/spio_get_varm_api.cpp @@ -4,6 +4,7 @@ #include "pio_api_impl.h" #include "spio_tracer.hpp" #include "spio_get_utils.hpp" +#include "spio_gptl_utils.hpp" /* APIs for reading/writing a hyperslab of strided non-distributed data/variable * with a mapped array. The mapped array maps between memory and variable data @@ -14,6 +15,7 @@ int PIOc_get_varm_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, signed char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_schar"); @@ -32,6 +34,7 @@ int PIOc_get_varm_schar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_varm_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_short"); @@ -50,6 +53,7 @@ int PIOc_get_varm_short(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_varm_ulonglong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, unsigned long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_ulonglong"); @@ -68,6 +72,7 @@ int PIOc_get_varm_ulonglong(int ncid, int varid, const PIO_Offset *start, const int PIOc_get_varm_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, unsigned short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_ushort"); @@ -86,6 +91,7 @@ int PIOc_get_varm_ushort(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_get_varm_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_longlong"); @@ -104,6 +110,7 @@ int PIOc_get_varm_longlong(int ncid, int varid, const PIO_Offset *start, const P int PIOc_get_varm_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, double *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_double"); @@ -122,6 +129,7 @@ int PIOc_get_varm_double(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_get_varm_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_text"); @@ -140,6 +148,7 @@ int PIOc_get_varm_text(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_get_varm_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_int"); @@ -158,6 +167,7 @@ int PIOc_get_varm_int(int ncid, int varid, const PIO_Offset *start, const PIO_Of int PIOc_get_varm_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, unsigned int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_uint"); @@ -177,6 +187,7 @@ int PIOc_get_varm(int ncid, int varid, const PIO_Offset *start, const PIO_Offset const PIO_Offset *stride, const PIO_Offset *imap, void *buf, PIO_Offset bufcount, MPI_Datatype buftype) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -191,6 +202,7 @@ int PIOc_get_varm(int ncid, int varid, const PIO_Offset *start, const PIO_Offset int PIOc_get_varm_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, float *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_float"); @@ -209,6 +221,7 @@ int PIOc_get_varm_float(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_varm_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_varm_long"); diff --git a/src/clib/api/spio_get_vars_api.cpp b/src/clib/api/spio_get_vars_api.cpp index 2ce44bd535b..b289e040c6f 100644 --- a/src/clib/api/spio_get_vars_api.cpp +++ b/src/clib/api/spio_get_vars_api.cpp @@ -4,11 +4,13 @@ #include "pio_api_impl.h" #include "spio_tracer.hpp" #include "spio_get_utils.hpp" +#include "spio_gptl_utils.hpp" /* APIs for reading a hyperslab of strided non-distributed data/variable */ int PIOc_get_vars(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -21,6 +23,7 @@ int PIOc_get_vars(int ncid, int varid, const PIO_Offset *start, const PIO_Offset int PIOc_get_vars_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_text"); @@ -39,6 +42,7 @@ int PIOc_get_vars_text(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_get_vars_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, signed char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_schar"); @@ -57,6 +61,7 @@ int PIOc_get_vars_schar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vars_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_short"); @@ -75,6 +80,7 @@ int PIOc_get_vars_short(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vars_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_int"); @@ -93,6 +99,7 @@ int PIOc_get_vars_int(int ncid, int varid, const PIO_Offset *start, const PIO_Of int PIOc_get_vars_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_long"); @@ -111,6 +118,7 @@ int PIOc_get_vars_long(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_get_vars_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, float *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_float"); @@ -129,6 +137,7 @@ int PIOc_get_vars_float(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vars_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, double *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_double"); @@ -147,6 +156,7 @@ int PIOc_get_vars_double(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_get_vars_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, unsigned char *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_uchar"); @@ -165,6 +175,7 @@ int PIOc_get_vars_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_get_vars_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, unsigned short *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_ushort"); @@ -183,6 +194,7 @@ int PIOc_get_vars_ushort(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_get_vars_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, unsigned int *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_uint"); @@ -201,6 +213,7 @@ int PIOc_get_vars_uint(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_get_vars_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_longlong"); @@ -220,6 +233,7 @@ int PIOc_get_vars_ulonglong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, unsigned long long *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_vars_ulonglong"); diff --git a/src/clib/api/spio_io_decomp_api.cpp b/src/clib/api/spio_io_decomp_api.cpp index af37eaf4f9b..33c7abfca04 100644 --- a/src/clib/api/spio_io_decomp_api.cpp +++ b/src/clib/api/spio_io_decomp_api.cpp @@ -3,6 +3,7 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* ========== APIs to handle I/O Decomposition =============== */ @@ -11,6 +12,7 @@ int PIOc_InitDecomp(int iosysid, int pio_type, int ndims, const int *gdimlen, in const PIO_Offset *compmap, int *ioidp, const int *rearr, const PIO_Offset *iostart, const PIO_Offset *iocount) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_InitDecomp"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_InitDecomp"); @@ -33,6 +35,7 @@ int PIOc_InitDecomp(int iosysid, int pio_type, int ndims, const int *gdimlen, in int PIOc_InitDecomp_bc(int iosysid, int basetype, int ndims, const int *gdimlen, const long int *start, const long int *count, int *ioidp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_InitDecomp_bc"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_InitDecomp_bc"); @@ -56,6 +59,7 @@ int PIOc_init_decomp(int iosysid, int pio_type, int ndims, const int *gdimlen, i const PIO_Offset *compmap, int *ioidp, int rearranger, const PIO_Offset *iostart, const PIO_Offset *iocount) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_init_decomp"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_init_decomp"); @@ -80,6 +84,7 @@ int PIOc_init_decomp(int iosysid, int pio_type, int ndims, const int *gdimlen, i /* Free resources associated with a decomposition. */ int PIOc_freedecomp(int iosysid, int ioid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_freedecomp"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_freedecomp"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid).add_arg("ioid", ioid).flush(); @@ -92,6 +97,7 @@ int PIOc_freedecomp(int iosysid, int ioid) int PIOc_readmap(const char *file, int *ndims, int **gdims, PIO_Offset *fmaplen, PIO_Offset **map, MPI_Comm comm) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_readmap"); /* FIXME: Add support for tracing comm-specific calls */ return PIOc_readmap_impl(file, ndims, gdims, fmaplen, map, comm); } @@ -99,6 +105,7 @@ int PIOc_readmap(const char *file, int *ndims, int **gdims, PIO_Offset *fmaplen, int PIOc_readmap_from_f90(const char *file,int *ndims, int **gdims, PIO_Offset *maplen, PIO_Offset **map, int f90_comm) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_readmap_from_f90"); /* FIXME: Add support for tracing comm-specific calls */ return PIOc_readmap_from_f90_impl(file, ndims, gdims, maplen, map, f90_comm); } @@ -106,6 +113,7 @@ int PIOc_readmap_from_f90(const char *file,int *ndims, int **gdims, PIO_Offset * int PIOc_writemap(const char *file, int ioid, int ndims, const int *gdims, PIO_Offset maplen, const PIO_Offset *map, MPI_Comm comm) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_writemap"); /* FIXME: Add support for tracing comm-specific calls */ return PIOc_writemap_impl(file, ioid, ndims, gdims, maplen, map, comm); } @@ -113,6 +121,7 @@ int PIOc_writemap(const char *file, int ioid, int ndims, const int *gdims, PIO_O int PIOc_writemap_from_f90(const char *file, int ioid, int ndims, const int *gdims, PIO_Offset maplen, const PIO_Offset *map, int f90_comm) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_writemap_from_f90"); /* FIXME: Add support for tracing comm-specific calls */ return PIOc_writemap_from_f90_impl(file, ioid, ndims, gdims, maplen, map, f90_comm); } @@ -121,6 +130,7 @@ int PIOc_writemap_from_f90(const char *file, int ioid, int ndims, const int *gdi /* Write a decomposition file. */ int PIOc_write_decomp(const char *file, int iosysid, int ioid, MPI_Comm comm) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_write_decomp"); /* FIXME: Add support for tracing comm-specific calls */ return PIOc_write_decomp_impl(file, iosysid, ioid, comm); } @@ -130,6 +140,7 @@ int PIOc_write_decomp(const char *file, int iosysid, int ioid, MPI_Comm comm) int PIOc_write_nc_decomp(int iosysid, const char *filename, int cmode, int ioid, const char *title, const char *history, int fortran_order) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_write_nc_decomp"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_write_nc_decomp"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid). @@ -146,6 +157,7 @@ int PIOc_write_nc_decomp(int iosysid, const char *filename, int cmode, int ioid, int PIOc_read_nc_decomp(int iosysid, const char *filename, int *ioid, MPI_Comm comm, int pio_type, char *title, char *history, int *fortran_order) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_read_nc_decomp"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_read_nc_decomp"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid). diff --git a/src/clib/api/spio_io_sys_api.cpp b/src/clib/api/spio_io_sys_api.cpp index bca8ce8792e..b394b8efa4f 100644 --- a/src/clib/api/spio_io_sys_api.cpp +++ b/src/clib/api/spio_io_sys_api.cpp @@ -3,6 +3,7 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* ============= APIs for an I/O system (like MPI communicators) =================== */ /* Initializing I/O system for asynchronous I/O */ @@ -10,6 +11,12 @@ int PIOc_init_async(MPI_Comm world, int num_io_procs, const int *io_proc_list, i const int *num_procs_per_comp, const int **proc_list, MPI_Comm *io_comm, MPI_Comm *comp_comm, int rearranger, int *iosysidp) { +#ifdef TIMING +#ifdef TIMING_INTERNAL + pio_init_gptl(); +#endif +#endif + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_init_async"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_init_async"); @@ -34,6 +41,12 @@ int PIOc_init_intercomm(int component_count, const MPI_Comm peer_comm, const MPI_Comm *ucomp_comms, const MPI_Comm uio_comm, int rearranger, int *iosysidps) { +#ifdef TIMING +#ifdef TIMING_INTERNAL + pio_init_gptl(); +#endif +#endif + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_Init_Intercommx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_init_intercomm"); @@ -70,6 +83,12 @@ int PIOc_Init_Intercomm_from_F90(int component_count, int f90_peer_comm, const int *f90_comp_comms, int f90_io_comm, int rearranger, int *iosysidps) { +#ifdef TIMING +#ifdef TIMING_INTERNAL + pio_init_gptl(); +#endif +#endif + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_Init_Intercommx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_Init_Intercomm_from_F90"); @@ -107,6 +126,7 @@ int PIOc_Init_Intercomm_from_F90(int component_count, int f90_peer_comm, int PIOc_get_numiotasks(int iosysid, int *numiotasks) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_numiotasks"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_num_iotasks"); @@ -124,6 +144,12 @@ int PIOc_get_numiotasks(int iosysid, int *numiotasks) int PIOc_Init_Intracomm(MPI_Comm comp_comm, int num_iotasks, int stride, int base, int rearr, int *iosysidp) { +#ifdef TIMING +#ifdef TIMING_INTERNAL + pio_init_gptl(); +#endif +#endif + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_Init_Intracommx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_Init_Intracomm"); @@ -145,6 +171,12 @@ int PIOc_Init_Intracomm_from_F90(int f90_comp_comm, const int base, const int rearr, rearr_opt_t *rearr_opts, int *iosysidp) { +#ifdef TIMING +#ifdef TIMING_INTERNAL + pio_init_gptl(); +#endif +#endif + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_Init_Intracommx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_Init_Intracomm_from_F90"); @@ -165,6 +197,7 @@ int PIOc_Init_Intracomm_from_F90(int f90_comp_comm, /* Finalize an I/O system */ int PIOc_finalize(int iosysid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_finalize"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_finalize"); @@ -179,6 +212,7 @@ int PIOc_finalize(int iosysid) /* Set error handling for entire io system. */ int PIOc_Set_IOSystem_Error_Handling(int iosysid, int method) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_Set_IOSystem_Error_Handling"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_Set_IOSystem_Error_Handling"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid).add_arg("method", method).flush(); @@ -190,6 +224,7 @@ int PIOc_Set_IOSystem_Error_Handling(int iosysid, int method) /* Set error handling for entire io system. */ int PIOc_set_iosystem_error_handling(int iosysid, int method, int *old_method) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_iosystem_error_handling"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_set_iosystem_error_handling"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid).add_arg("method", method). @@ -201,6 +236,7 @@ int PIOc_set_iosystem_error_handling(int iosysid, int method, int *old_method) int PIOc_iam_iotask(int iosysid, bool *ioproc) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_iam_iotask"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_iam_iotask"); @@ -216,6 +252,7 @@ int PIOc_iam_iotask(int iosysid, bool *ioproc) int PIOc_iotask_rank(int iosysid, int *iorank) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_iotask_rank"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_iotask_rank"); @@ -231,6 +268,7 @@ int PIOc_iotask_rank(int iosysid, int *iorank) int PIOc_iosystem_is_active(int iosysid, bool *active) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_iosystem_is_active"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_iosystem_is_active"); @@ -246,6 +284,7 @@ int PIOc_iosystem_is_active(int iosysid, bool *active) int PIOc_iotype_available(int iotype) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_iotype_available"); /* FIXME: Figure out how to trace these non I/O system specific calls */ return PIOc_iotype_available_impl(iotype); } @@ -256,6 +295,7 @@ int PIOc_set_rearr_opts(int iosysid, int comm_type, int fcd, bool enable_hs_i2c, bool enable_isend_i2c, int max_pend_req_i2c) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_rearr_opts"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_set_rearr_opts"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid). @@ -276,6 +316,7 @@ int PIOc_set_rearr_opts(int iosysid, int comm_type, int fcd, int PIOc_set_hint(int iosysid, const char *hint, const char *hintval) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_hint"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_set_hint"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid). @@ -287,6 +328,7 @@ int PIOc_set_hint(int iosysid, const char *hint, const char *hintval) int PIOc_set_chunk_cache(int iosysid, int iotype, PIO_Offset size, PIO_Offset nelems, float preemption) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_chunk_cache"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_set_chunk_cache"); tr.set_iosys_id(iosysid).add_arg("iosysid", iosysid).add_arg("iotype", iotype). @@ -300,6 +342,7 @@ int PIOc_set_chunk_cache(int iosysid, int iotype, PIO_Offset size, PIO_Offset ne int PIOc_get_chunk_cache(int iosysid, int iotype, PIO_Offset *sizep, PIO_Offset *nelemsp, float *preemptionp) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_get_chunk_cache"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_chunk_cache"); @@ -319,6 +362,7 @@ int PIOc_get_chunk_cache(int iosysid, int iotype, PIO_Offset *sizep, PIO_Offset int PIOc_set_blocksize(int newblocksize) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_blocksize"); /* FIXME: Figure out how to trace these non I/O system specific calls */ return PIOc_set_blocksize_impl(newblocksize); } @@ -326,6 +370,7 @@ int PIOc_set_blocksize(int newblocksize) /* Set the IO node data buffer size limit. */ PIO_Offset PIOc_set_buffer_size_limit(PIO_Offset limit) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_buffer_size_limit"); /* FIXME: Figure out how to trace these non I/O system specific calls */ return PIOc_set_buffer_size_limit_impl(limit); } diff --git a/src/clib/api/spio_misc_att_api.cpp b/src/clib/api/spio_misc_att_api.cpp index 753f12a05f1..aba7b4846ee 100644 --- a/src/clib/api/spio_misc_att_api.cpp +++ b/src/clib/api/spio_misc_att_api.cpp @@ -3,11 +3,13 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* ================= File meta-data APIs ============== */ /* APIs for file/variable attributes */ int PIOc_rename_att(int ncid, int varid, const char *name, const char *newname) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_rename_att"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_rename_att"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -19,6 +21,7 @@ int PIOc_rename_att(int ncid, int varid, const char *name, const char *newname) int PIOc_del_att(int ncid, int varid, const char *name) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_del_att"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_del_att"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -30,6 +33,7 @@ int PIOc_del_att(int ncid, int varid, const char *name) int PIOc_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, PIO_Offset *lenp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_att"); @@ -48,6 +52,7 @@ int PIOc_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, int PIOc_inq_attid(int ncid, int varid, const char *name, int *idp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_attid"); @@ -65,6 +70,7 @@ int PIOc_inq_attid(int ncid, int varid, const char *name, int *idp) int PIOc_inq_attlen(int ncid, int varid, const char *name, PIO_Offset *lenp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_attlen"); @@ -82,6 +88,7 @@ int PIOc_inq_attlen(int ncid, int varid, const char *name, PIO_Offset *lenp) int PIOc_inq_atttype(int ncid, int varid, const char *name, nc_type *xtypep) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_atttype"); @@ -99,6 +106,7 @@ int PIOc_inq_atttype(int ncid, int varid, const char *name, nc_type *xtypep) int PIOc_inq_attname(int ncid, int varid, int attnum, char *name) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_attx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_attname"); @@ -116,6 +124,7 @@ int PIOc_inq_attname(int ncid, int varid, int attnum, char *name) int PIOc_copy_att(int incid, int ivarid, const char *name, int oncid, int ovarid) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_copy_att"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_copy_att"); tr.set_file_id(incid).add_arg("incid", incid). diff --git a/src/clib/api/spio_misc_var_api.cpp b/src/clib/api/spio_misc_var_api.cpp index 36ac255371d..94d62b3f7ad 100644 --- a/src/clib/api/spio_misc_var_api.cpp +++ b/src/clib/api/spio_misc_var_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* APIs for file variables */ int PIOc_inq_varid(int ncid, const char *name, int *varidp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_varid"); @@ -25,6 +27,7 @@ int PIOc_inq_varid(int ncid, const char *name, int *varidp) int PIOc_inq_var(int ncid, int varid, char *name, int namelen, nc_type *xtypep, int *ndimsp, int *dimidsp, int *nattsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_var"); @@ -55,6 +58,7 @@ int PIOc_inq_var(int ncid, int varid, char *name, int namelen, nc_type *xtypep, int PIOc_inq_varname(int ncid, int varid, char *name, int namelen) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_varname"); @@ -71,6 +75,7 @@ int PIOc_inq_varname(int ncid, int varid, char *name, int namelen) int PIOc_inq_vartype(int ncid, int varid, nc_type *xtypep) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_vartype"); @@ -87,6 +92,7 @@ int PIOc_inq_vartype(int ncid, int varid, nc_type *xtypep) int PIOc_inq_varndims(int ncid, int varid, int *ndimsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_varndims"); @@ -103,6 +109,7 @@ int PIOc_inq_varndims(int ncid, int varid, int *ndimsp) int PIOc_inq_vardimid(int ncid, int varid, int *dimidsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_vardimid"); @@ -113,7 +120,7 @@ int PIOc_inq_vardimid(int ncid, int varid, int *dimidsp) #if SPIO_ENABLE_API_TRACING int ndims = 0; - int rval = PIOc_inq_varndims(ncid, varid, &ndims); + int rval = PIOc_inq_varndims_impl(ncid, varid, &ndims); if((rval == PIO_NOERR) && dimidsp){ tr.add_rval("*dimidsp", dimidsp, ndims); } @@ -123,6 +130,7 @@ int PIOc_inq_vardimid(int ncid, int varid, int *dimidsp) int PIOc_inq_varnatts(int ncid, int varid, int *nattsp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_varnatts"); @@ -140,6 +148,7 @@ int PIOc_inq_varnatts(int ncid, int varid, int *nattsp) int PIOc_def_var(int ncid, const char *name, nc_type xtype, int ndims, const int *dimidsp, int *varidp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_def_var"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING assert(ndims >= 0); @@ -160,6 +169,7 @@ int PIOc_def_var(int ncid, const char *name, nc_type xtype, int ndims, int PIOc_set_fill(int ncid, int fillmode, int *old_modep) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_fill"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_set_fill"); @@ -176,6 +186,7 @@ int PIOc_set_fill(int ncid, int fillmode, int *old_modep) int PIOc_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_def_var_fill"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_def_var_fill"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -186,6 +197,7 @@ int PIOc_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value) int PIOc_inq_var_fill(int ncid, int varid, int *no_fill, void *fill_valuep) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_var_fill"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -196,6 +208,7 @@ int PIOc_inq_var_fill(int ncid, int varid, int *no_fill, void *fill_valuep) int PIOc_rename_var(int ncid, int varid, const char *name) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_rename_var"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_rename_var"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -208,6 +221,7 @@ int PIOc_rename_var(int ncid, int varid, const char *name) int PIOc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_level) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_def_var_deflate"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_def_var_deflate"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -220,6 +234,7 @@ int PIOc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int PIOc_inq_var_deflate(int ncid, int varid, int *shufflep, int *deflatep, int *deflate_levelp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_var_deflate"); @@ -246,6 +261,7 @@ int PIOc_inq_var_szip(int ncid, int varid, int *options_maskp, int *pixels_per_b int PIOc_def_var_chunking(int ncid, int varid, int storage, const PIO_Offset *chunksizesp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_def_var_chunking"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_def_var_chunking"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -256,6 +272,7 @@ int PIOc_def_var_chunking(int ncid, int varid, int storage, const PIO_Offset *ch int PIOc_inq_var_chunking(int ncid, int varid, int *storagep, PIO_Offset *chunksizesp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_var_chunking"); @@ -273,6 +290,7 @@ int PIOc_inq_var_chunking(int ncid, int varid, int *storagep, PIO_Offset *chunks int PIOc_def_var_endian(int ncid, int varid, int endian) { + //SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_def_var_endian"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_def_var_endian"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -283,6 +301,7 @@ int PIOc_def_var_endian(int ncid, int varid, int endian) int PIOc_inq_var_endian(int ncid, int varid, int *endianp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_inq_var_endian"); @@ -300,6 +319,7 @@ int PIOc_inq_var_endian(int ncid, int varid, int *endianp) int PIOc_set_var_chunk_cache(int ncid, int varid, PIO_Offset size, PIO_Offset nelems, float preemption) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_set_var_chunk_cache"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_set_var_chunk_cache"); tr.set_file_id(ncid).add_arg("ncid", ncid).add_arg("varid", varid). @@ -313,6 +333,7 @@ int PIOc_set_var_chunk_cache(int ncid, int varid, PIO_Offset size, PIO_Offset ne int PIOc_get_var_chunk_cache(int ncid, int varid, PIO_Offset *sizep, PIO_Offset *nelemsp, float *preemptionp) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_inq_varx"); int ret = PIO_NOERR; #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_get_var_chunk_cache"); diff --git a/src/clib/api/spio_put_att_api.cpp b/src/clib/api/spio_put_att_api.cpp index 44ce5dc3c01..d17a831cd37 100644 --- a/src/clib/api/spio_put_att_api.cpp +++ b/src/clib/api/spio_put_att_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* Write APIs for file/variable attributes */ int PIOc_put_att(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const void *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -20,6 +22,7 @@ int PIOc_put_att(int ncid, int varid, const char *name, nc_type xtype, PIO_Offse int PIOc_put_att_text(int ncid, int varid, const char *name, PIO_Offset len, const char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_text"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -33,6 +36,7 @@ int PIOc_put_att_text(int ncid, int varid, const char *name, PIO_Offset len, con int PIOc_put_att_schar(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const signed char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_schar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -47,6 +51,7 @@ int PIOc_put_att_schar(int ncid, int varid, const char *name, nc_type xtype, PIO int PIOc_put_att_short(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_short"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -61,6 +66,7 @@ int PIOc_put_att_short(int ncid, int varid, const char *name, nc_type xtype, PIO int PIOc_put_att_int(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_int"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -75,6 +81,7 @@ int PIOc_put_att_int(int ncid, int varid, const char *name, nc_type xtype, PIO_O int PIOc_put_att_long(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_long"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -89,6 +96,7 @@ int PIOc_put_att_long(int ncid, int varid, const char *name, nc_type xtype, PIO_ int PIOc_put_att_float(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const float *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_float"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -103,6 +111,7 @@ int PIOc_put_att_float(int ncid, int varid, const char *name, nc_type xtype, PIO int PIOc_put_att_double(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const double *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_double"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -117,6 +126,7 @@ int PIOc_put_att_double(int ncid, int varid, const char *name, nc_type xtype, PI int PIOc_put_att_uchar(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_uchar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -131,6 +141,7 @@ int PIOc_put_att_uchar(int ncid, int varid, const char *name, nc_type xtype, PIO int PIOc_put_att_ushort(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_ushort"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -145,6 +156,7 @@ int PIOc_put_att_ushort(int ncid, int varid, const char *name, nc_type xtype, PI int PIOc_put_att_uint(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_uint"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -159,6 +171,7 @@ int PIOc_put_att_uint(int ncid, int varid, const char *name, nc_type xtype, PIO_ int PIOc_put_att_longlong(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_longlong"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -173,6 +186,7 @@ int PIOc_put_att_longlong(int ncid, int varid, const char *name, nc_type xtype, int PIOc_put_att_ulonglong(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_attx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_att_ulonglong"); tr.set_file_id(ncid).add_arg("ncid", ncid). diff --git a/src/clib/api/spio_put_var1_api.cpp b/src/clib/api/spio_put_var1_api.cpp index d71719a30b8..3837279d0e5 100644 --- a/src/clib/api/spio_put_var1_api.cpp +++ b/src/clib/api/spio_put_var1_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* APIs for writing non-distributed data/variable at a specified index */ int PIOc_put_var1(int ncid, int varid, const PIO_Offset *index, const void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -18,6 +20,7 @@ int PIOc_put_var1(int ncid, int varid, const PIO_Offset *index, const void *buf) int PIOc_put_var1_text(int ncid, int varid, const PIO_Offset *index, const char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_text"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -29,6 +32,7 @@ int PIOc_put_var1_text(int ncid, int varid, const PIO_Offset *index, const char int PIOc_put_var1_schar(int ncid, int varid, const PIO_Offset *index, const signed char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_schar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -40,6 +44,7 @@ int PIOc_put_var1_schar(int ncid, int varid, const PIO_Offset *index, const sign int PIOc_put_var1_short(int ncid, int varid, const PIO_Offset *index, const short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_short"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -51,6 +56,7 @@ int PIOc_put_var1_short(int ncid, int varid, const PIO_Offset *index, const shor int PIOc_put_var1_int(int ncid, int varid, const PIO_Offset *index, const int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_int"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -62,6 +68,7 @@ int PIOc_put_var1_int(int ncid, int varid, const PIO_Offset *index, const int *o int PIOc_put_var1_long(int ncid, int varid, const PIO_Offset *index, const long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_long"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -73,6 +80,7 @@ int PIOc_put_var1_long(int ncid, int varid, const PIO_Offset *index, const long int PIOc_put_var1_float(int ncid, int varid, const PIO_Offset *index, const float *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_float"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -84,6 +92,7 @@ int PIOc_put_var1_float(int ncid, int varid, const PIO_Offset *index, const floa int PIOc_put_var1_double(int ncid, int varid, const PIO_Offset *index, const double *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_double"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -96,6 +105,7 @@ int PIOc_put_var1_double(int ncid, int varid, const PIO_Offset *index, const dou int PIOc_put_var1_uchar(int ncid, int varid, const PIO_Offset *index, const unsigned char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_uchar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -108,6 +118,7 @@ int PIOc_put_var1_uchar(int ncid, int varid, const PIO_Offset *index, int PIOc_put_var1_ushort(int ncid, int varid, const PIO_Offset *index, const unsigned short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_ushort"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -120,6 +131,7 @@ int PIOc_put_var1_ushort(int ncid, int varid, const PIO_Offset *index, int PIOc_put_var1_uint(int ncid, int varid, const PIO_Offset *index, const unsigned int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_uint"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -131,6 +143,7 @@ int PIOc_put_var1_uint(int ncid, int varid, const PIO_Offset *index, int PIOc_put_var1_longlong(int ncid, int varid, const PIO_Offset *index, const long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_longlong"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -143,6 +156,7 @@ int PIOc_put_var1_longlong(int ncid, int varid, const PIO_Offset *index, const l int PIOc_put_var1_ulonglong(int ncid, int varid, const PIO_Offset *index, const unsigned long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var1_ulonglong"); tr.set_file_id(ncid).add_arg("ncid", ncid). diff --git a/src/clib/api/spio_put_var_api.cpp b/src/clib/api/spio_put_var_api.cpp index e861a48f7ec..6dfab124d78 100644 --- a/src/clib/api/spio_put_var_api.cpp +++ b/src/clib/api/spio_put_var_api.cpp @@ -3,10 +3,12 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* APIs for writing entire non-distributed data/variable */ int PIOc_put_var(int ncid, int varid, const void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -17,6 +19,7 @@ int PIOc_put_var(int ncid, int varid, const void *buf) int PIOc_put_var_text(int ncid, int varid, const char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_text"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -27,6 +30,7 @@ int PIOc_put_var_text(int ncid, int varid, const char *op) int PIOc_put_var_schar(int ncid, int varid, const signed char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_schar"); /* Both bytes and chars are written using this interface, trying to trace/log @@ -40,6 +44,7 @@ int PIOc_put_var_schar(int ncid, int varid, const signed char *op) int PIOc_put_var_short(int ncid, int varid, const short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_short"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -50,6 +55,7 @@ int PIOc_put_var_short(int ncid, int varid, const short *op) int PIOc_put_var_int(int ncid, int varid, const int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_int"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -60,6 +66,7 @@ int PIOc_put_var_int(int ncid, int varid, const int *op) int PIOc_put_var_long(int ncid, int varid, const long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_long"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -70,6 +77,7 @@ int PIOc_put_var_long(int ncid, int varid, const long *op) int PIOc_put_var_float(int ncid, int varid, const float *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_float"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -80,6 +88,7 @@ int PIOc_put_var_float(int ncid, int varid, const float *op) int PIOc_put_var_double(int ncid, int varid, const double *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_double"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -90,6 +99,7 @@ int PIOc_put_var_double(int ncid, int varid, const double *op) int PIOc_put_var_uchar(int ncid, int varid, const unsigned char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_uchar"); /* Both bytes and chars are written using this interface, trying to trace/log @@ -103,6 +113,7 @@ int PIOc_put_var_uchar(int ncid, int varid, const unsigned char *op) int PIOc_put_var_ushort(int ncid, int varid, const unsigned short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_ushort"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -113,6 +124,7 @@ int PIOc_put_var_ushort(int ncid, int varid, const unsigned short *op) int PIOc_put_var_uint(int ncid, int varid, const unsigned int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_uint"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -123,6 +135,7 @@ int PIOc_put_var_uint(int ncid, int varid, const unsigned int *op) int PIOc_put_var_longlong(int ncid, int varid, const long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_longlong"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -133,6 +146,7 @@ int PIOc_put_var_longlong(int ncid, int varid, const long long *op) int PIOc_put_var_ulonglong(int ncid, int varid, const unsigned long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_var_ulonglong"); tr.set_file_id(ncid).add_arg("ncid", ncid). diff --git a/src/clib/api/spio_put_vara_api.cpp b/src/clib/api/spio_put_vara_api.cpp index 4e028f7d53c..f0c567f58b5 100644 --- a/src/clib/api/spio_put_vara_api.cpp +++ b/src/clib/api/spio_put_vara_api.cpp @@ -3,11 +3,13 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* APIs for writing a hyperslab of non-distributed data/variable */ int PIOc_put_vara(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -20,6 +22,7 @@ int PIOc_put_vara(int ncid, int varid, const PIO_Offset *start, const PIO_Offset int PIOc_put_vara_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_text"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -32,6 +35,7 @@ int PIOc_put_vara_text(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vara_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const signed char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_schar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -44,6 +48,7 @@ int PIOc_put_vara_schar(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vara_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_short"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -56,6 +61,7 @@ int PIOc_put_vara_short(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_put_vara_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_int"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -68,6 +74,7 @@ int PIOc_put_vara_int(int ncid, int varid, const PIO_Offset *start, const PIO_Of int PIOc_put_vara_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_long"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -80,6 +87,7 @@ int PIOc_put_vara_long(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_put_vara_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const float *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_float"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -92,6 +100,7 @@ int PIOc_put_vara_float(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vara_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const double *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_double"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -104,6 +113,7 @@ int PIOc_put_vara_double(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_put_vara_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_uchar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -116,6 +126,7 @@ int PIOc_put_vara_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_put_vara_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_ushort"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -128,6 +139,7 @@ int PIOc_put_vara_ushort(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vara_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_uint"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -140,6 +152,7 @@ int PIOc_put_vara_uint(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vara_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_longlong"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -152,6 +165,7 @@ int PIOc_put_vara_longlong(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vara_ulonglong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vara_ulonglong"); tr.set_file_id(ncid).add_arg("ncid", ncid). diff --git a/src/clib/api/spio_put_varm_api.cpp b/src/clib/api/spio_put_varm_api.cpp index a8e55a1f0fa..036a5cb193c 100644 --- a/src/clib/api/spio_put_varm_api.cpp +++ b/src/clib/api/spio_put_varm_api.cpp @@ -3,11 +3,13 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" int PIOc_put_varm(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const void *buf, PIO_Offset bufcount, MPI_Datatype buftype) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -23,6 +25,7 @@ int PIOc_put_varm_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_ const PIO_Offset *stride, const PIO_Offset *imap, const unsigned char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_uchar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -36,6 +39,7 @@ int PIOc_put_varm_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_put_varm_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_ushort"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -50,6 +54,7 @@ int PIOc_put_varm_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_text"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -63,6 +68,7 @@ int PIOc_put_varm_text(int ncid, int varid, const PIO_Offset *start, int PIOc_put_varm_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const unsigned short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_ushort"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -77,6 +83,7 @@ int PIOc_put_varm_ulonglong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const unsigned long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_ulonglong"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -91,6 +98,7 @@ int PIOc_put_varm_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_int"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -105,6 +113,7 @@ int PIOc_put_varm_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const float *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_float"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -119,6 +128,7 @@ int PIOc_put_varm_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_long"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -133,6 +143,7 @@ int PIOc_put_varm_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const unsigned int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_uint"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -146,6 +157,7 @@ int PIOc_put_varm_uint(int ncid, int varid, const PIO_Offset *start, int PIOc_put_varm_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const double *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_double"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -159,6 +171,7 @@ int PIOc_put_varm_double(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_put_varm_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const signed char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_schar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -172,6 +185,7 @@ int PIOc_put_varm_schar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_put_varm_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const PIO_Offset *imap, const long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_varm_longlong"); tr.set_file_id(ncid).add_arg("ncid", ncid). diff --git a/src/clib/api/spio_put_vars_api.cpp b/src/clib/api/spio_put_vars_api.cpp index 8ed83f5cb15..00667dd506f 100644 --- a/src/clib/api/spio_put_vars_api.cpp +++ b/src/clib/api/spio_put_vars_api.cpp @@ -3,11 +3,13 @@ #include "pio_internal.h" #include "pio_api_impl.h" #include "spio_tracer.hpp" +#include "spio_gptl_utils.hpp" /* APIs for writing a hyperslab of strided non-distributed data/variable */ int PIOc_put_vars(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const void *buf) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -20,6 +22,7 @@ int PIOc_put_vars(int ncid, int varid, const PIO_Offset *start, const PIO_Offset int PIOc_put_vars_text(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_text"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -33,6 +36,7 @@ int PIOc_put_vars_schar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const signed char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_shcar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -45,6 +49,7 @@ int PIOc_put_vars_schar(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vars_short(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_short"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -57,6 +62,7 @@ int PIOc_put_vars_short(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vars_int(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_int"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -69,6 +75,7 @@ int PIOc_put_vars_int(int ncid, int varid, const PIO_Offset *start, const PIO_Of int PIOc_put_vars_float(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const float *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_float"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -81,6 +88,7 @@ int PIOc_put_vars_float(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_put_vars_double(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const double *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_double"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -93,6 +101,7 @@ int PIOc_put_vars_double(int ncid, int varid, const PIO_Offset *start, int PIOc_put_vars_long(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_long"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -105,6 +114,7 @@ int PIOc_put_vars_long(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_put_vars_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const unsigned char *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_uchar"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -117,6 +127,7 @@ int PIOc_put_vars_uchar(int ncid, int varid, const PIO_Offset *start, const PIO_ int PIOc_put_vars_ushort(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const unsigned short *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_ushort"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -129,6 +140,7 @@ int PIOc_put_vars_ushort(int ncid, int varid, const PIO_Offset *start, const PIO int PIOc_put_vars_uint(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const unsigned int *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_uint"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -141,6 +153,7 @@ int PIOc_put_vars_uint(int ncid, int varid, const PIO_Offset *start, const PIO_O int PIOc_put_vars_longlong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_longlong"); tr.set_file_id(ncid).add_arg("ncid", ncid). @@ -154,6 +167,7 @@ int PIOc_put_vars_ulonglong(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const unsigned long long *op) { + SPIO_Util::GPTL_Util::GPTL_timer func_timer("SPIO:PIOc_put_varx"); #if SPIO_ENABLE_API_TRACING SPIO_Util::Tracer::Timed_func_call_tracer tr("PIOc_put_vars_ulonglong"); tr.set_file_id(ncid).add_arg("ncid", ncid). diff --git a/src/clib/core/CMakeLists.txt b/src/clib/core/CMakeLists.txt new file mode 100644 index 00000000000..09fb86ec063 --- /dev/null +++ b/src/clib/core/CMakeLists.txt @@ -0,0 +1,44 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C Core... =====") +# SCORPIO C library - core +set (spio_core_src + pio_spmd.cpp + pioc_support.cpp + pioc.cpp + pio_nc.cpp + pio_getput_int.cpp + pio_file.cpp + pio_darray_int.cpp + pio_darray.cpp + pio_varm.cpp + pio_nc4.cpp + pioc_sc.cpp + pio_put_nc.cpp + pio_msg.cpp + pio_get_nc.cpp) + +add_subdirectory (util) +add_subdirectory (progress_engine) +add_subdirectory (rearr) +add_subdirectory (iolib) + +add_library (spio_core OBJECT + $ + $ + $ + $ + $ + ${spio_core_src}) + +set (spio_clib_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/..") +set (spio_clib_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/..") + +target_include_directories(spio_core + PUBLIC . + PRIVATE ${spio_clib_src_dir} ${spio_clib_bin_dir} ${spio_clib_src_dir}/util ${CMAKE_CURRENT_SOURCE_DIR}/util ${CMAKE_CURRENT_SOURCE_DIR}/progress_engine ${CMAKE_CURRENT_SOURCE_DIR}/rearr ${CMAKE_CURRENT_SOURCE_DIR}/iolib/hdf5) + +target_link_libraries(spio_core + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/README_trace_and_replay.txt b/src/clib/core/README_trace_and_replay.txt similarity index 100% rename from src/clib/README_trace_and_replay.txt rename to src/clib/core/README_trace_and_replay.txt diff --git a/src/clib/core/iolib/CMakeLists.txt b/src/clib/core/iolib/CMakeLists.txt new file mode 100644 index 00000000000..86ed9db22b3 --- /dev/null +++ b/src/clib/core/iolib/CMakeLists.txt @@ -0,0 +1,17 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C Core (I/O libraries)... =====") + +add_subdirectory (hdf5) + +add_library (spio_core_iolib OBJECT + $) + +target_include_directories(spio_core_iolib + PUBLIC . + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/../.. ${CMAKE_CURRENT_SOURCE_DIR}/../../util) + +target_link_libraries(spio_core_iolib + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/core/iolib/hdf5/CMakeLists.txt b/src/clib/core/iolib/hdf5/CMakeLists.txt new file mode 100644 index 00000000000..d37607e15df --- /dev/null +++ b/src/clib/core/iolib/hdf5/CMakeLists.txt @@ -0,0 +1,19 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C Core (hdf5)... =====") +# HDF5 Support +set (spio_core_iolib_hdf5_src + spio_async_hdf5_utils.cpp + spio_hdf5_utils.cpp) + +set (spio_clib_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/../../..") +set (spio_clib_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/../../..") + +add_library (spio_core_iolib_hdf5 OBJECT ${spio_core_iolib_hdf5_src}) +target_include_directories(spio_core_iolib_hdf5 + PUBLIC . + PRIVATE ${spio_clib_src_dir} ${spio_clib_bin_dir} ${spio_clib_src_dir}/util ${spio_clib_src_dir}/core ${spio_clib_src_dir}/core/util ${spio_clib_src_dir}/core/progress_engine) +target_link_libraries(spio_core_iolib_hdf5 + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/core/iolib/hdf5/spio_async_hdf5_utils.cpp b/src/clib/core/iolib/hdf5/spio_async_hdf5_utils.cpp new file mode 100644 index 00000000000..7d259d9a9da --- /dev/null +++ b/src/clib/core/iolib/hdf5/spio_async_hdf5_utils.cpp @@ -0,0 +1,894 @@ +/** @file + * Utility functions for Asynchronous I/O using HDF5 + */ + +#include +#include +#include +#include +#include +#include + +extern "C"{ + +#include +#include +#include +#include +#if PIO_ENABLE_LOGGING +#include +#endif /* PIO_ENABLE_LOGGING */ +#include +} // extern "C" +#include "pio_timer.h" +#include +#include "spio_async_op.hpp" +#include "spio_async_utils.hpp" +#include "spio_async_tpool.hpp" +#include "spio_file_mvcache.h" +#include "spio_dbg_utils.hpp" +#include "spio_dt_converter.hpp" +#include "spio_hdf5_utils.hpp" + +struct Hdf5_create_info{ + file_desc_t *file; + std::string fname; +}; + +struct Hdf5_def_var_info{ + file_desc_t *file; + std::string vname; + nc_type xtype; + int ndims; + std::vector dimids; + int varid; +}; + +struct Hdf5_put_att_info{ + file_desc_t *file; + int varid; + std::string aname; + nc_type atype; + PIO_Offset alen; + void *abuf; +}; + +struct Hdf5_enddef_info{ + file_desc_t *file; +}; + +struct Hdf5_put_var_info{ + file_desc_t *file; + int varid; + std::vector start; + std::vector count; + std::vector stride; + nc_type xtype; + PIO_Offset vbuf_sz; + void *vbuf; +}; + +struct Hdf5_set_frame_info{ + file_desc_t *file; + int varid; + int frame; +}; + +struct Hdf5_wcache{ + file_desc_t *file; + int nvars; + int fndims; + std::vector varids; + io_desc_t *iodesc; + std::vector frame; + + bool wr_fillbuf; + void *iobuf; + std::size_t iobuf_sz; + void *fillbuf; + std::size_t fillbuf_sz; +}; + +/* Global vars */ +std::atomic SPIO_Util::GVars::npend_hdf5_async_ops; + +#ifdef _HDF5 +namespace Util{ + /* Each variable writes out one or more regions of data. However all variables have the + * same I/O decomposition and write the same regions (within the variable) of data. The + * region info is the same across all variables + * Note: Different variables (among nvars variables) could be writing out different + * timesteps/records (while still writing the same region - within the record) + */ + struct RInfo{ + /* The starts(:) for this region - local to this process */ + std::vector starts; + /* The counts(:) for this region - local to this process */ + std::vector counts; + /* Total number of elements written out, locally, for this region */ + std::size_t nelems; + }; +} // namespace Util + +/* Update the the start frame for all regions in reg_infos, based on the variable's current frame */ +static inline void update_reg_infos_start_frame(std::vector ®_infos, const var_desc_t *vdesc, const int frame) +{ + /* If the variable has records/frames then update the starts(:) to the provided frame/record number */ + if(vdesc->record >= 0){ + for(std::vector::iterator iter = reg_infos.begin(); iter != reg_infos.end(); ++iter){ + assert(iter->starts.size() > 0); + iter->starts[0] = frame; + } + } +} + +void spio_iosys_async_op_hdf5_create_free(void *pdata) +{ + if(pdata){ + delete(static_cast(pdata)); + } +} + +int spio_iosys_async_op_hdf5_create(void *pdata) +{ + int ret = PIO_NOERR; + + Hdf5_create_info *cinfo = static_cast(pdata); + + assert(cinfo && cinfo->file && cinfo->file->iosystem); + + ret = spio_hdf5_create(cinfo->file->iosystem, cinfo->file, cinfo->fname.c_str()); + + cinfo->file->npend_ops--; + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return ret; +} + +int spio_iosys_async_hdf5_create_op_add(file_desc_t *file, const char *filename) +{ + int ret = PIO_NOERR; + + assert(file && file->iosystem && filename); + + iosystem_desc_t *ios = file->iosystem; + + if(!ios->ioproc){ + return PIO_NOERR; + } + + Hdf5_create_info *cinfo = new Hdf5_create_info{file, filename}; + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_HDF5_CREATE_OP, + static_cast(cinfo), + spio_iosys_async_op_hdf5_create, + pio_async_poke_func_unavail, + spio_iosys_async_op_hdf5_create_free}; + + /* One more pending op using this iodesc & file */ + file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation (to create file = %s) to the thread pool (iosystem = %d). Adding the asynchronous operation failed", filename, ios->iosysid); + } + + return PIO_NOERR; +} + +void spio_iosys_async_op_hdf5_def_var_free(void *pdata) +{ + if(pdata){ + delete(static_cast(pdata)); + } +} + +int spio_iosys_async_op_hdf5_def_var(void *pdata) +{ + int ret = PIO_NOERR; + + Hdf5_def_var_info *def_var_info = static_cast(pdata); + + assert(def_var_info && def_var_info->file && def_var_info->file->iosystem); + + ret = spio_hdf5_def_var(def_var_info->file->iosystem, def_var_info->file, + def_var_info->vname.c_str(), def_var_info->xtype, def_var_info->ndims, + def_var_info->dimids.data(), def_var_info->varid); + + def_var_info->file->npend_ops--; + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return ret; +} + +int spio_iosys_async_hdf5_def_var_op_add(file_desc_t *file, const char *name, + nc_type xtype, int ndims, const int *dimidsp, int varid) +{ + int ret = PIO_NOERR; + + assert(file && file->iosystem && name); + + iosystem_desc_t *ios = file->iosystem; + + if(!ios->ioproc){ + return PIO_NOERR; + } + + Hdf5_def_var_info *def_var_info = new Hdf5_def_var_info{file, name, xtype, ndims, + {dimidsp, dimidsp + ndims}, varid}; + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_HDF5_DEF_VAR_OP, + static_cast(def_var_info), + spio_iosys_async_op_hdf5_def_var, + pio_async_poke_func_unavail, + spio_iosys_async_op_hdf5_def_var_free}; + + /* One more pending op using this iodesc & file */ + file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation (to define var = %s, in file = %s) to the thread pool (iosystem = %d). Adding the asynchronous operation failed", name, pio_get_fname_from_file(file), ios->iosysid); + } + + return PIO_NOERR; +} + +void spio_iosys_async_op_hdf5_put_att_free(void *pdata) +{ + if(pdata){ + Hdf5_put_att_info *info = static_cast(pdata); + free(info->abuf); + delete(info); + } +} + +int spio_iosys_async_op_hdf5_put_att(void *pdata) +{ + int ret = PIO_NOERR; + + Hdf5_put_att_info *info = static_cast(pdata); + + assert(info && info->file && info->file->iosystem); + assert((info->varid >= 0) || (info->varid == PIO_GLOBAL)); + assert(info->alen >= 0); + + ret = spio_hdf5_put_att(info->file->iosystem, info->file, + info->varid, info->aname.c_str(), info->atype, + info->alen, info->abuf); + + info->file->npend_ops--; + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return ret; +} + +int spio_iosys_async_hdf5_put_att_op_add(file_desc_t *file, int varid, + const char *aname, nc_type atype, PIO_Offset alen, const void *abuf) +{ + int ret = PIO_NOERR; + + assert(file && file->iosystem && aname && (alen >= 0) && ((alen == 0) || abuf)); + assert((varid >= 0) || (varid = PIO_GLOBAL)); + + iosystem_desc_t *ios = file->iosystem; + + if(!ios->ioproc){ + return PIO_NOERR; + } + + void *buf = malloc(alen * spio_get_nc_type_size(atype)); + if(buf == NULL){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Queuing asynchronous op/task for writing attribute (%s, variable=%s, file=%s) using PIO_IOTYPE_HDF5x failed. Unable to allocate memory (%lld bytes) for caching attribute value", aname, pio_get_vname_from_file(file, varid), pio_get_fname_from_file(file), static_cast(alen)); + } + + memcpy(buf, abuf, alen * spio_get_nc_type_size(atype)); + + Hdf5_put_att_info *info = new Hdf5_put_att_info{file, varid, aname, atype, alen, buf}; + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_HDF5_PUT_ATT_OP, + static_cast(info), + spio_iosys_async_op_hdf5_put_att, + pio_async_poke_func_unavail, + spio_iosys_async_op_hdf5_put_att_free}; + + /* One more pending op using this iodesc & file */ + file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation (to define attribute = %s, for var = %s in file = %s) to the thread pool (iosystem = %d). Adding the asynchronous operation failed", aname, pio_get_vname_from_file(file, varid), pio_get_fname_from_file(file), ios->iosysid); + } + + return PIO_NOERR; +} + +void spio_iosys_async_op_hdf5_enddef_free(void *pdata) +{ + if(pdata){ + delete(static_cast(pdata)); + } +} + +int spio_iosys_async_op_hdf5_enddef(void *pdata) +{ + int ret = PIO_NOERR; + + Hdf5_enddef_info *info = static_cast(pdata); + + assert(info && info->file && info->file->iosystem); + + ret = spio_hdf5_enddef(info->file->iosystem, info->file); + + info->file->npend_ops--; + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return ret; +} + +int spio_iosys_async_hdf5_enddef_op_add(file_desc_t *file) +{ + int ret = PIO_NOERR; + + assert(file && file->iosystem); + + iosystem_desc_t *ios = file->iosystem; + + if(!ios->ioproc){ + return PIO_NOERR; + } + + Hdf5_enddef_info *info = new Hdf5_enddef_info{file}; + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_HDF5_ENDDEF_OP, + static_cast(info), + spio_iosys_async_op_hdf5_enddef, + pio_async_poke_func_unavail, + spio_iosys_async_op_hdf5_enddef_free}; + + /* One more pending op using this file */ + file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation (to end define mode in file = %s) to the thread pool (iosystem = %d). Adding the asynchronous operation failed", pio_get_fname_from_file(file), ios->iosysid); + } + + return PIO_NOERR; +} + +void spio_iosys_async_op_hdf5_put_var_free(void *pdata) +{ + if(pdata){ + Hdf5_put_var_info *info = static_cast(pdata); + free(info->vbuf); + delete(info); + } +} + +int spio_iosys_async_op_hdf5_put_var(void *pdata) +{ + int ret = PIO_NOERR; + + Hdf5_put_var_info *info = static_cast(pdata); + + assert(info && info->file && info->file->iosystem); + assert((info->varid >= 0) || (info->varid == PIO_GLOBAL)); + + ret = spio_hdf5_put_var(info->file->iosystem, info->file, + info->varid, + (!info->start.empty()) ? (info->start.data()) : NULL, + (!info->count.empty()) ? (info->count.data()) : NULL, + (!info->stride.empty()) ? (info->stride.data()) : NULL, + info->xtype, info->vbuf); + + info->file->npend_ops--; + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return ret; +} + +int spio_iosys_async_hdf5_put_var_op_add(file_desc_t *file, int varid, + const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, + nc_type xtype, const void *vbuf) +{ + int ret = PIO_NOERR; + + assert(file && file->iosystem && (varid >= 0) && vbuf); + + iosystem_desc_t *ios = file->iosystem; + + if(!ios->ioproc){ + return PIO_NOERR; + } + + int ndims = file->hdf5_vars[varid].ndims; + PIO_Offset vbuf_sz = 0; + if(count){ + vbuf_sz = std::accumulate(count, count + ndims, 1, std::multiplies()); + } + else{ + int *dimids = file->hdf5_vars[varid].hdf5_dimids; + vbuf_sz = std::accumulate(dimids, dimids + ndims, 1, + [file](PIO_Offset acc_sz, const int &dimid){ + return acc_sz * file->hdf5_dims[dimid].len; + }); + } + + vbuf_sz *= spio_get_nc_type_size(xtype); + + void *buf = malloc(vbuf_sz); + if(buf == NULL){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Queuing asynchronous op/task for writing variable(%s, varid=%d, file=%s) using PIO_IOTYPE_HDF5x failed. Unable to allocate memory (%lld bytes) for caching variable value", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), static_cast(vbuf_sz)); + } + + memcpy(buf, vbuf, vbuf_sz); + + Hdf5_put_var_info *info = new Hdf5_put_var_info{file, varid, + (start) ? std::vector{start, start + ndims} : std::vector{}, + (count) ? std::vector{count, count + ndims} : std::vector{}, + (stride) ? std::vector{stride, stride + ndims} : std::vector{}, + xtype, vbuf_sz, buf}; + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_HDF5_PUT_VAR_OP, + static_cast(info), + spio_iosys_async_op_hdf5_put_var, + pio_async_poke_func_unavail, + spio_iosys_async_op_hdf5_put_var_free}; + + /* One more pending op using this file */ + file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation (to write/put var = %s in file = %s) to the thread pool (iosystem = %d). Adding the asynchronous operation failed", pio_get_vname_from_file(file, varid), pio_get_fname_from_file(file), ios->iosysid); + } + + return PIO_NOERR; +} + +void spio_iosys_async_op_hdf5_set_frame_free(void *pdata) +{ + if(pdata){ + delete(static_cast(pdata)); + } +} + +int spio_iosys_async_op_hdf5_set_frame(void *pdata) +{ + int ret = PIO_NOERR; + + Hdf5_set_frame_info *info = static_cast(pdata); + + assert(info && info->file && info->file->iosystem); + + ret = spio_hdf5_set_frame(info->file, info->varid, info->frame); + + info->file->npend_ops--; + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return ret; +} + +int spio_iosys_async_hdf5_set_frame_op_add(file_desc_t *file, int varid, int frame) +{ + int ret = PIO_NOERR; + + assert(file && file->iosystem && (varid >= 0) && (frame >= 0)); + + iosystem_desc_t *ios = file->iosystem; + + if(!ios->ioproc){ + return PIO_NOERR; + } + + Hdf5_set_frame_info *info = new Hdf5_set_frame_info{file, varid, frame}; + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_HDF5_SET_FRAME_OP, + static_cast(info), + spio_iosys_async_op_hdf5_set_frame, + pio_async_poke_func_unavail, + spio_iosys_async_op_hdf5_set_frame_free}; + + /* One more pending op using this iodesc & file */ + file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation (to set frame to %d for variable = %s in file = %s) to the thread pool (iosystem = %d). Adding the asynchronous operation failed", frame, pio_get_vname_from_file(file, varid), pio_get_fname_from_file(file), ios->iosysid); + } + + return PIO_NOERR; +} + +#endif // _HDF5 + +int spio_wait_all_hdf5_async_ops(int iosysid) +{ + unsigned long long int sleep_time = 0; + /* Sleep for 0.5 seconds */ + const int SLEEP_TIME_IN_MILLISECONDS = 500; + /* Warn every 2 seconds */ + const int WARN_TIME_IN_MILLISECONDS = 500; + while(SPIO_Util::GVars::npend_hdf5_async_ops > 0){ + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_IN_MILLISECONDS)); + sleep_time += SLEEP_TIME_IN_MILLISECONDS; + if((SPIO_Util::GVars::npend_hdf5_async_ops > 0) && (sleep_time % WARN_TIME_IN_MILLISECONDS == 0)){ + std::string warn_msg = std::string("Continuing to wait on all HDF5 async ops... ") + + "(Elapsed time = " + std::to_string(sleep_time) + " ms)"; + PIOc_warn(iosysid, PIO_DEFAULT, __FILE__, __LINE__, warn_msg.c_str()); + } + } + + return PIO_NOERR; +} + +int pio_iosys_async_op_hdf5_write(void *pdata) +{ +#ifdef _HDF5 + /* FIXME: Add futures */ + int ret = PIO_NOERR; + Hdf5_wcache *wcache = static_cast(pdata); + assert(wcache); + + file_desc_t *file = wcache->file; + int nvars = wcache->nvars; + int fndims = wcache->fndims; + io_desc_t *iodesc = wcache->iodesc; + + assert(file && (nvars > 0) && (fndims > 0) && iodesc); + assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); + + iosystem_desc_t *ios = file->iosystem; + var_desc_t *v1desc = file->varlist + wcache->varids[0]; + + assert(ios && v1desc && ios->ioproc); + + bool wr_fillbuf = wcache->wr_fillbuf; + + //int num_regions = (wr_fillbuf) ? iodesc->maxfillregions : iodesc->maxregions; + io_region *region = (wr_fillbuf) ? iodesc->fillregion : iodesc->firstregion; + PIO_Offset llen = (wr_fillbuf) ? iodesc->holegridsize : iodesc->llen; + void *iobuf = (wr_fillbuf) ? (wcache->fillbuf) : (wcache->iobuf); + std::size_t iobuf_sz = (wr_fillbuf) ? (wcache->fillbuf_sz) : (wcache->iobuf_sz); + + assert(region && (nvars * llen * iodesc->mpitype_size == static_cast(iobuf_sz))); + assert(((iobuf_sz == 0) && !iobuf) || ((iobuf_sz != 0) && iobuf)); + + /* Info on all regions */ + std::vector vreg_infos; + /* Total number of elements written out for all the regions. Since all variables write + * out the same regions - since they use the same I/O decomposition - the total number + * of elements written out for each variable will be the same (reg_nelems) + */ + hsize_t reg_nelems = 0; + + /* Collect region info, starts/counts etc, for all regions */ + while(region){ + std::size_t start[fndims], count[fndims]; + + ret = spio_find_start_count(iodesc->ndims, iodesc->dimlen, fndims, v1desc, region, start, count); + if(ret != PIO_NOERR){ + return pio_err(ios, file, ret, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) failed. Internal error finding start/count for the I/O regions written out from the I/O process", nvars, pio_get_fname_from_file(file), file->pio_ncid); + } + + std::size_t nelems = std::accumulate(count, count + fndims, 1, std::multiplies()); + + /* Note: The region info is tied to the first variable, we need to update the variable specific + * sections, start(:) based on the record/frame being written out, later + */ + if(nelems > 0){ + std::vector tmp_start(start, start + fndims); + std::vector tmp_count(count, count + fndims); + vreg_infos.push_back({tmp_start, tmp_count, nelems}); + reg_nelems += static_cast(nelems); + } + region = region->next; + } + + std::size_t num_regions = vreg_infos.size(); + + /* Write data - one variable at a time (all regions for a variable written out in a single call) */ + var_desc_t *vdesc = NULL; + void *bufptr = NULL; + for(int vidx = 0; vidx < nvars; vidx++){ + hid_t file_space_id = H5Dget_space(file->hdf5_vars[wcache->varids[vidx]].hdf5_dataset_id); + if(file_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to make a copy of the dataspace of the dataset associated with variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + hid_t mem_space_id = H5I_INVALID_HID; + if(reg_nelems > 0){ + /* Get the var info. */ + vdesc = file->varlist + wcache->varids[vidx]; + + /* Update the generic region info with variable-specific info */ + /* If this is a record (or quasi-record) var, set the start for + * the record dimension. */ + if((fndims > 1) && (vdesc->record > 0)){ + assert(static_cast(wcache->frame.size()) == nvars); + update_reg_infos_start_frame(vreg_infos, vdesc, wcache->frame[vidx]); + } + + + /* Create a hyperslab of all the regions written out for a variable */ + H5S_seloper_t op = H5S_SELECT_SET; + for(std::size_t ireg = 0; ireg < num_regions; ireg++){ + /* Union hyperslabs of all regions */ + if(H5Sselect_hyperslab(file_space_id, op, static_cast(vreg_infos[ireg].starts.data()), NULL, static_cast(vreg_infos[ireg].counts.data()), NULL) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to select a hyperslab region for a dataspace copied from the dataset associated with variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + op = H5S_SELECT_OR; + } + + /* Total number of elements across all regions = + * total number of elements written out with this hyperslab = reg_nelems + */ + mem_space_id = H5Screate_simple(1, ®_nelems, NULL); + if(mem_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new simple dataspace for variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + /* Get a pointer to the data. This buffer points to data from all the regions written out for this variable */ + bufptr = (void *)((char *)iobuf + vidx * iodesc->mpitype_size * llen); + } + else{ + /* No data, across all regions, to write on this IO task */ + if(H5Sselect_none(file_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to reset the selection region for a dataspace copied from the dataset associated with variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + mem_space_id = H5Screate(H5S_NULL); + if(mem_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new NULL dataspace for variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + bufptr = NULL; + } + + /* Collective write - for all regions in a single variable */ + hid_t mem_type_id = spio_nc_type_to_hdf5_type(iodesc->piotype); + if(mem_type_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unsupported memory type (type=%x) for variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, iodesc->piotype, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + hid_t file_var_type_id = H5Dget_type(file->hdf5_vars[wcache->varids[vidx]].hdf5_dataset_id); + if(file_var_type_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unable to query the type of variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + hid_t file_var_ntype_id = H5Tget_native_type(file_var_type_id, H5T_DIR_DEFAULT); + assert(file_var_ntype_id != H5I_INVALID_HID); + + /* When HDF5 filters (e.g. data compression) are enabled collective writes fail when datatype conversion is required for writing user data. + * So we manually perform the data conversion here before passing it to HDF5. When filters are not enabled the write might succeed but HDF5 + * might be switching off collective writes (hurts performance) when datatype conversion is required + * FIXME: Disable datatype conversion when filters are not enabled on the dataset + */ + void *wbuf = bufptr; + if((reg_nelems > 0) && !H5Tequal(mem_type_id, file_var_ntype_id)){ + assert(file->dt_converter); + wbuf = static_cast(file->dt_converter)->convert(iodesc->ioid, bufptr, iodesc->mpitype_size * reg_nelems, + iodesc->piotype, spio_hdf5_type_to_pio_type(file_var_ntype_id)); + if(wbuf == NULL){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unable to convert the type (from %d to %d) of variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, iodesc->piotype, + spio_hdf5_type_to_pio_type(file_var_ntype_id), pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + } + + if(H5Dwrite(file->hdf5_vars[wcache->varids[vidx]].hdf5_dataset_id, file_var_ntype_id, mem_space_id, file_space_id, + file->dxplid_coll, wbuf) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to write the dataset associated with variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + +#if SPIO_HDF5_FLUSH_AFTER_COLL_WR + if(H5Fflush(file->hdf5_file_id, H5F_SCOPE_LOCAL) < 0){ + H5Eprint2(H5E_DEFAULT, stderr); + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to flush the dataset associated with variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } +#endif + + if(H5Sclose(file_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a dataspace copied from the dataset associated with variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + + if(H5Sclose(mem_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a simple (or NULL) dataspace for variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, wcache->varids[vidx]), wcache->varids[vidx]); + } + } + + /* FIXME: Is this barrier needed ? */ + MPI_Barrier(ios->io_comm); + + iodesc->nasync_pend_ops--; + file->npend_ops--; + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return PIO_NOERR; +#else // _HDF5 + assert(0); + return PIO_EINTERNAL; +#endif // _HDF5 +} + +void pio_iosys_async_op_hdf5_write_free(void *pdata) +{ +#ifdef _HDF5 + Hdf5_wcache *wcache = static_cast(pdata); + assert(wcache); + + /* Using swap trick to free vectors + * - swap vector with an empty local/temp vector that gets deallocated when func exits + */ + //wcache->varids.clear(); + std::vector().swap(wcache->varids); + //wcache->frame.clear(); + std::vector().swap(wcache->frame); + + if(wcache->iobuf){ brel(wcache->iobuf); } + if(wcache->fillbuf){ brel(wcache->fillbuf); } + + free(wcache); +#else // _HDF5 + assert(0); +#endif // _HDF5 +} + +int pio_iosys_async_hdf5_write_op_add(file_desc_t *file, int nvars, int fndims, + const int *varids, io_desc_t *iodesc, int fill, const int *frame) +{ +#ifdef _HDF5 + int ret = PIO_NOERR; + + assert(file && (nvars > 0) && (fndims > 0) && varids && iodesc); + + iosystem_desc_t *ios = file->iosystem; + assert(ios); + + if(!ios->ioproc){ + return PIO_NOERR; + } + + std::vector vids(varids, varids + nvars); + std::vector frms; + if(frame){ + frms.resize(nvars); + std::copy(frame, frame + nvars, frms.begin()); + } + + Hdf5_wcache *wcache = static_cast(calloc(1, sizeof(Hdf5_wcache))); + *wcache = {file, nvars, fndims, vids, iodesc, frms, (fill) ? true : false, NULL, 0, NULL, 0}; + + /* We need to copy the iobuf/fillbuf since the mvcache gets reused for future writes */ + /* Copy iobuf/fillvalue */ + var_desc_t *v1desc = file->varlist + varids[0]; + //PIO_Offset llen = fill ? iodesc->holegridsize : iodesc->llen; + if(fill){ + std::size_t fillbuf_sz = iodesc->holegridsize * iodesc->mpitype_size; + /* On I/O processes with no data (after rearrangement) to write the fillbuf_sz will be 0 */ + if(fillbuf_sz > 0){ + wcache->fillbuf = bget(fillbuf_sz); + if(!wcache->fillbuf){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Queuing asynchronous op/task for writing fillvalue for variables (number of variables = %d) to file (%s, ncid=%d) using PIO_IOTYPE_HDF5x failed. Unable to allocate memory (%lld bytes) for caching variable fillvalue", nvars, pio_get_fname_from_file(file), file->pio_ncid, static_cast(fillbuf_sz)); + } + + std::memcpy(wcache->fillbuf, v1desc->fillbuf, fillbuf_sz); + wcache->fillbuf_sz = fillbuf_sz; + } + } + else{ + /* Copy buffer, with rearranged data, for nvars */ + std::size_t iobuf_sz = nvars * iodesc->llen * iodesc->mpitype_size; + /* On I/O processes with no data (after rearrangement) to write the iobuf_sz will be 0 */ + if(iobuf_sz > 0){ + wcache->iobuf = bget(iobuf_sz); + if(!wcache->iobuf){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Queuing asynchronous op/task for writing fillvalue for variables (number of variables = %d) to file (%s, ncid=%d) using PIO_IOTYPE_HDF5x failed. Unable to allocate memory (%lld bytes) for caching variable data for all the variables", nvars, pio_get_fname_from_file(file), file->pio_ncid, static_cast(iobuf_sz)); + } + + void *iobuf = spio_file_mvcache_get(file, iodesc->ioid); + assert(iobuf); + std::memcpy(wcache->iobuf, iobuf, iobuf_sz); + wcache->iobuf_sz = iobuf_sz; + } + } + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_HDF5_WRITE_OP, + static_cast(wcache), + pio_iosys_async_op_hdf5_write, + pio_async_poke_func_unavail, + pio_iosys_async_op_hdf5_write_free}; + + /* One more pending op using this iodesc & file */ + iodesc->nasync_pend_ops++; + file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation (to write %d variables in file = %s) to the thread pool (iosystem = %d). Adding the asynchronous operation failed", nvars, pio_get_fname_from_file(file), ios->iosysid); + } + + return PIO_NOERR; +#else // _HDF5 + assert(0); + return PIO_EINTERNAL; +#endif // _HDF5 +} + diff --git a/src/clib/core/iolib/hdf5/spio_async_hdf5_utils.hpp b/src/clib/core/iolib/hdf5/spio_async_hdf5_utils.hpp new file mode 100644 index 00000000000..f52368204ab --- /dev/null +++ b/src/clib/core/iolib/hdf5/spio_async_hdf5_utils.hpp @@ -0,0 +1,30 @@ +#ifndef __SPIO_ASYNC_HDF5_UTILS_HPP__ +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" + +namespace SPIO_Util{ + namespace GVars{ + extern std::atomic npend_hdf5_async_ops; + } // namespace GVars +} // namespace SPIO_Util + +int spio_iosys_async_hdf5_create_op_add(file_desc_t *file, const char *filename); +int spio_iosys_async_hdf5_def_var_op_add(file_desc_t *file, const char *name, + nc_type xtype, int ndims, const int *dimidsp, int varid); +int spio_iosys_async_hdf5_put_att_op_add(file_desc_t *file, int varid, + const char *aname, nc_type atype, PIO_Offset alen, const void *abuf); +int spio_iosys_async_hdf5_enddef_op_add(file_desc_t *file); +int spio_iosys_async_hdf5_put_var_op_add(file_desc_t *file, int varid, + const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, + nc_type xtype, const void *vbuf); + +int spio_wait_all_hdf5_async_ops(int iosysid); +int pio_iosys_async_op_hdf5_write(void *pdata); +void pio_iosys_async_op_hdf5_write_free(void *pdata); +int pio_iosys_async_hdf5_write_op_add(file_desc_t *file, int nvars, int fndims, + const int *varids, io_desc_t *iodesc, int fill, const int *frame); +int spio_iosys_async_hdf5_set_frame_op_add(file_desc_t *file, int varid, int frame); + +#define __SPIO_ASYNC_HDF5_UTILS_HPP__ +#endif // __SPIO_ASYNC_HDF5_UTILS_HPP__ diff --git a/src/clib/core/iolib/hdf5/spio_hdf5_utils.cpp b/src/clib/core/iolib/hdf5/spio_hdf5_utils.cpp new file mode 100644 index 00000000000..bdf37945daf --- /dev/null +++ b/src/clib/core/iolib/hdf5/spio_hdf5_utils.cpp @@ -0,0 +1,1541 @@ +/** @file + * Utility functions for using the HDF5 library + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "spio_io_summary.h" +#include "spio_file_mvcache.h" +#include "spio_dt_converter.hpp" +//#include "spio_hash.h" +//#include "pio_rearr_contig.hpp" +//#include "spio_decomp_logger.hpp" +#include "spio_async_utils.hpp" +#include "spio_async_tcomm.hpp" +#include +#include +#include +#include +#include + +#include "spio_hdf5_utils.hpp" +#include "pio_sdecomps_regex.hpp" +/* Include headers for HDF5 compression filters */ +#if PIO_USE_HDF5 +#include +#ifdef _SPIO_HAS_H5Z_ZFP +#include "H5Zzfp_lib.h" +#include "H5Zzfp_props.h" +#endif +#ifdef _SPIO_HAS_H5Z_BLOSC2 +#include "blosc2_filter.h" +#endif +#endif + +#ifdef _HDF5 +int spio_hdf5_create(iosystem_desc_t *ios, file_desc_t *file, const char *filename) +{ + int mpierr = MPI_SUCCESS, ret = PIO_NOERR; + + assert(ios && file && filename); + assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); + assert(ios->ioproc); + + if(file->mode & PIO_NOCLOBBER){ + /* No clobber : Check if file exists from all processes */ + struct stat sd; + if(0 == stat(filename, &sd)){ + return pio_err(ios, NULL, PIO_EEXIST, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The HDF5 file already exists and PIO_NOCLOBBER mode is specified", + filename); + } + } + else{ + /* Clobber mode : Delete HDF5 file (from root I/O proc) if it exists */ + if(ios->tcomm_info->get_io_comm_rank() == 0){ + struct stat sd; + if(0 == stat(filename, &sd)) { unlink(filename); } + } + + /* Wait for root process (that might delete an existing file) */ + if((mpierr = MPI_Barrier(ios->tcomm_info->get_io_comm()))){ + return check_mpi(ios, file, mpierr, __FILE__, __LINE__); + } + } + + MPI_Info *pinfo = ios->tcomm_info->create_mpi_info(); + if(!pinfo){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "Failed to create MPI Info object for the file", filename); + } + + hid_t fcpl_id = H5Pcreate(H5P_FILE_CREATE); + if(fcpl_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new file creation property list", + filename); + } + + if(H5Pset_link_creation_order(fcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set tracking and indexing of link creation order", + filename); + } + + /* H5DSattach_scale calls (even with MPI_Barrier) might fail or hang if attribute creation + * order is tracked or indexed. Before we have a better workaround, temporarily disable + * tracking and indexing of attribute creation order. */ +#if 0 + if(H5Pset_attr_creation_order(fcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set tracking and indexing of attribute creation order", + filename); + } +#endif + + hid_t fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if(fapl_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new file access property list", + filename); + } + + if(H5Pset_fapl_mpio(fapl_id, ios->tcomm_info->get_io_comm(), *pinfo) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to store the user-supplied MPI IO parameters", + filename); + } + + file->hdf5_file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl_id, fapl_id); + if(file->hdf5_file_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new HDF5 file", + filename); + } + +#if SPIO_DISABLE_HDF5_MPI_FILE_SYNC + /* This is essentially the same as setting env variable, HDF5_DO_MPI_FILE_SYNC=FALSE */ + if(H5Fset_mpi_atomicity(file->hdf5_file_id, false) < 0){ + std::string warn_msg = std::string("Unable to turn off MPI file syncing for HDF5 output") + + "( file = " + pio_get_fname_from_file(file) + ")"; + PIOc_warn(ios->iosysid, file->pio_ncid, __FILE__, __LINE__, warn_msg.c_str()); + } +#endif + + if(H5Pclose(fcpl_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a file creation property list", + filename); + } + + if(H5Pclose(fapl_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a file access property list", + filename); + } + + /* Set up collective dataset transfer property list */ + file->dxplid_coll = H5Pcreate(H5P_DATASET_XFER); + if(file->dxplid_coll == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new dataset transfer property list (collective data transfer)", + filename); + } + + if(H5Pset_dxpl_mpio(file->dxplid_coll, H5FD_MPIO_COLLECTIVE) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set data transfer mode to collective", + filename); + } + + /* Set up independent dataset transfer property list */ + file->dxplid_indep = H5Pcreate(H5P_DATASET_XFER); + if(file->dxplid_indep == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new dataset transfer property list (independent data transfer)", + filename); + } + + if(H5Pset_dxpl_mpio(file->dxplid_indep, H5FD_MPIO_COLLECTIVE) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set data transfer mode to collective", + filename); + } + + if(H5Pset_dxpl_mpio_collective_opt(file->dxplid_indep, H5FD_MPIO_INDIVIDUAL_IO) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set data transfer mode to individual I/O", + filename); + } + + /* Writing _NCProperties attribute */ + const char* attr_name = "_NCProperties"; + char nc_properties[PIO_MAX_NAME]; + unsigned int major, minor, release; + + if(H5get_libversion(&major, &minor, &release) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to retrieve the the version of the HDF5 library", + filename); + } + + snprintf(nc_properties, PIO_MAX_NAME, + "version=2,scorpio=%d.%d.%d,hdf5=%1u.%1u.%1u", + PIO_VERSION_MAJOR, PIO_VERSION_MINOR, PIO_VERSION_PATCH, + major, minor, release); + + hid_t attr_id; + hsize_t asize = strlen(nc_properties); + hid_t loc_id = file->hdf5_file_id; + + hid_t space_id = H5Screate(H5S_SCALAR); + if(space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new scalar dataspace", + filename); + } + + hid_t h5_string_type = H5Tcopy(H5T_C_S1); + if(h5_string_type == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to make a copy of the predefined string datatype in C", + filename); + } + + assert(asize > 0); + if(H5Tset_size(h5_string_type, asize) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set the total size (%ld bytes) for a derived C-style string datatype", + filename, asize); + } + + if(H5Tset_strpad(h5_string_type, H5T_STR_NULLTERM) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to define the type of padding (NULL-terminated) used for a derived C-style string datatype", + filename); + } + + if(H5Tset_cset(h5_string_type, H5T_CSET_ASCII) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set the character set (US ASCII) to be used in a derived C-style string datatype", + filename); + } + + /* H5Aexists() returns zero (false), a positive (true) or a negative (failure) value */ + htri_t att_exists = H5Aexists(loc_id, attr_name); + if(att_exists > 0) { assert(0); } + else if(att_exists == 0){ + attr_id = H5Acreate2(loc_id, attr_name, h5_string_type, space_id, H5P_DEFAULT, H5P_DEFAULT); + if(attr_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to the file", + filename, attr_name); + } + } + else{ + /* Error determining whether an attribute with a given name exists on an object */ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to determine whether an attribute (%s) exists on the file", + filename, attr_name); + } + + if(H5Awrite(attr_id, h5_string_type, nc_properties) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to the file", + filename, attr_name); + } + + if(H5Sclose(space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a scalar dataspace", + filename); + } + + if(H5Aclose(attr_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to the file", + filename, attr_name); + } + + if(H5Tclose(h5_string_type) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Creating file (%s) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a derived C-style string datatype " + "used by an attribute (%s) attached to the file", + filename, attr_name); + } + + return PIO_NOERR; +} + +/* Create HDF5 dataset property ID */ +static hid_t spio_create_hdf5_dataset_pid(iosystem_desc_t *ios, file_desc_t *file, const char *var_name, const std::vector &max_dim_sz, nc_type var_type) +{ + herr_t ret; + hid_t dpid = H5I_INVALID_HID; + + std::size_t var_ndims = max_dim_sz.size(); + + assert((ios != NULL) && (file != NULL) && (var_ndims >= 0)); + + bool var_has_more_than_four_dims = (max_dim_sz.size() > 4) ? true : false; + bool var_has_only_unlimited_dims = true; + bool var_is_scalar = true; + for(std::vector::const_iterator citer = max_dim_sz.cbegin(); citer != max_dim_sz.cend(); ++citer){ + if(*citer != H5S_UNLIMITED){ + var_has_only_unlimited_dims = false; + if(*citer != 1) { var_is_scalar = false; } + } + } + + /* Initialize the compression filter property list */ + dpid = H5Pcreate(H5P_DATASET_CREATE); + assert(dpid != H5I_INVALID_HID); + + /* We currently support compression for non-scalar data */ + if(file->iotype != PIO_IOTYPE_HDF5C) return dpid; + if((var_ndims < 1) || (var_type == NC_CHAR)){ + std::string msg("Disabling HDF5 compression for variable"); + msg += std::string("(name=") + std::string(var_name) + std::string(", file=") + std::string(pio_get_fname_from_file(file)) + std::string(")"); + msg += (var_ndims < 1) ? std::string(" since its a scalar variable") : std::string(" since its a string/char variable"); + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, msg.c_str()); + return dpid; + } + + /* Check if any variables have compression disabled by the user */ + /* FIXME: Variables written out in a chunk size different from the one defined can cause hangs + * e.g. E3SM variables : decomp_type, numlev, hwrt_prec, avgflag, fillvalue, + * meridional_complement, zonal_complement + */ +#ifndef SPIO_NO_CXX_REGEX + std::regex vname_override_rgx(SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX); + if(var_name && std::regex_match(std::string(var_name), vname_override_rgx)){ + std::string msg("Disabling HDF5 compression for variable"); + msg += std::string("(name=") + std::string(var_name) + std::string(", file=") + std::string(pio_get_fname_from_file(file)) + std::string(")"); + msg += std::string(" since it matches the user specified regex"); + msg += std::string(" (SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX=\"") + std::string(SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX) + std::string("\")"); + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, msg.c_str()); + return dpid; + } +#endif + +#ifdef _SPIO_HDF5_USE_COMPRESSION + +#ifdef _SPIO_HDF5_USE_LOSSY_COMPRESSION + +#ifdef _SPIO_HAS_H5Z_ZFP + /* Avoid ZFP compression for vars with only unlimited dims */ + if(var_has_only_unlimited_dims || var_is_scalar || var_has_more_than_four_dims){ + std::string msg("Disabling HDF5 ZFP compression for variable"); + msg += std::string("(name=") + std::string(var_name) + std::string(", file=") + std::string(pio_get_fname_from_file(file)) + std::string(")"); + msg += (var_has_only_unlimited_dims) ? std::string(" since it only has unlimited dims") : + ((var_is_scalar) ? std::string(" since it is essentially a scalar variable") : std::string(" since variable has more than 4 dimensions")); + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, msg.c_str()); + return dpid; + } + + if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_RATE"){ + /* Lossy compression : Fixed bit rate : Number of bits used for compressed values is fixed, e.g. 16 */ + ret = H5Pset_zfp_rate(dpid, SPIO_HDF5_ZFP_COMPRESSION_RATE); + if(ret < 0){ + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter compression rate failed (ignoring specific compression bit rate)"); + } + } + else if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_PRECISION"){ + /* Lossy compression : Fixed precision Variable bit rate : Number of bits used for original value is fixed. e.g. 16 */ + ret = H5Pset_zfp_precision(dpid, SPIO_HDF5_ZFP_PRECISION); + if(ret < 0){ + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter relative error bound failed (continuing with the default error bounds)"); + } + } + else if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_ACCURACY"){ + /* Lossy compression : Fixed accuracy Variable bit rate : Absolute error between original and compressed values is bound e.g. 0.001 */ + ret = H5Pset_zfp_accuracy(dpid, SPIO_HDF5_ZFP_ACCURACY); + if(ret < 0){ + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter absolute error bound failed (continuing with the default error bounds)"); + } + } + else if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_REVERSIBLE"){ + /* Lossless compression */ + ret = H5Pset_zfp_reversible(dpid); + if(ret < 0){ + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter for lossless compression failed (ignoring compression option)"); + } + } +#endif + +#else /* lossless compression, ifdef _SPIO_HDF5_USE_LOSSY_COMPRESSION */ + +#ifdef _SPIO_HAS_H5Z_BLOSC2 + /* Lossless compression : Default Blosc2 + ZSTD */ + unsigned int cd_values[7]; + cd_values[4] = SPIO_HDF5_BLOSC2_COMPRESSION_LEVEL; // compression level + cd_values[5] = SPIO_HDF5_BLOSC2_SHUFFLE_METHOD; // shuffle option + cd_values[6] = SPIO_HDF5_BLOSC2_COMPRESSION_LIBRARY; // Use ZSTD for compression + ret = H5Pset_filter(dpid, FILTER_BLOSC2, H5Z_FLAG_OPTIONAL, 7, cd_values); + if(ret < 0){ + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "User requested lossless compression, but setting HDF5 Blosc2 filter failed. Writing data without compression"); + } +#endif + +#endif /* ifdef _SPIO_HDF5_USE_LOSSY_COMPRESSION */ + +#endif /* ifdef _SPIO_HDF5_USE_COMPRESSION */ + + /* By default HDF5 does not track the order of creation of the attributes. So the attributes + * appear based on the alphanumeric order, of the attribute name, in the file. However, + * H5DSattach_scale calls (even with MPI_Barrier) might fail or hang if attribute creation + * order is tracked or indexed. Before we have a better workaround, temporarily disable + * tracking and indexing of attribute creation order. + */ +#if 0 + if(H5Pset_attr_creation_order(dcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set tracking and indexing of attribute creation order", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } +#endif + + return dpid; +} + +/* Get default chunk size (no of elems) - across each dimension - for variable data. The + * max chunk size (across all dimensions) is specified via PIO_CHUNK_SIZE (in bytes) + */ +static inline std::vector spio_get_dim_chunk_sz(iosystem_desc_t *ios, file_desc_t *file, + const std::string &vname, + const std::vector &dim_names, const std::vector &dim_sz, + const std::vector &default_dim_chunk_sz, nc_type xtype) +{ + const std::string fname(file->fname); + const std::string dim_chunk_info(SPIO_DIM_CHUNK_INFO); + bool user_provided_dim_chunk_info = !dim_chunk_info.empty(); + + std::size_t ndims = dim_sz.size(); + std::vector dim_chunk_sz = dim_sz; + + /* Unlimited dimensions have a chunk size of 1 */ + std::transform(dim_chunk_sz.begin(), dim_chunk_sz.end(), dim_chunk_sz.begin(), + [](hsize_t i) { return (i != H5S_UNLIMITED) ? i : 1; }); + + /* No chunking for scalars and 1D vars */ + if(ndims <= 1) { return dim_chunk_sz; } + + /* Search/Match using ioid is not required, passing -1 for ioid */ + if(!PIO_Util::spio_chunk_regex_match(-1, fname, vname)){ + std::string msg = std::string("Disabling chunking for variable. File name ") + + "(" + fname + ")" + " or variable name " + "(" + vname + + ") did not match chunking regex " + + "(SPIO_ENABLE_CHUNKING_REGEX = " + SPIO_ENABLE_CHUNKING_REGEX + ")"; + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, msg.c_str()); + return dim_chunk_sz; + } + + /* Number of elements corresponding to PIO_CHUNK_SIZE */ + double chunk_nelems = static_cast(PIO_CHUNK_SIZE)/static_cast(spio_get_nc_type_size(xtype)); + /* Assuming that elements are evenly distributed across all non-unlimited dimensions, + * Total (across all dimensions) chunked elements = (d * d * ...(n -1) times), where d is the size of each + * dimension + */ + hsize_t chunk_per_dim_nelems = static_cast(pow(chunk_nelems, 1.0/(ndims - 1) )); + + for(std::size_t i = 0; i < ndims; i++){ + if(!user_provided_dim_chunk_info){ + /* Chunk size across UNLIMITED dimension is 1 */ + dim_chunk_sz[i] = (dim_sz[i] != H5S_UNLIMITED) ? (std::min(chunk_per_dim_nelems, dim_sz[i])) : 1; + } + else{ + /* User specified dim chunk info, if default_dim_chunk_sz (parsed from user provided dim chunk info) + * is 0 no chunking for that dim. + */ + if(default_dim_chunk_sz[i] != 0){ + dim_chunk_sz[i] = default_dim_chunk_sz[i]; + std::string msg = std::string("Setting user specified chunk size for variable. File name ") + + "(" + fname + ")" + ", variable name " + "(" + vname + ")" + + ", dimension name " + "(" + dim_names[i] + ")" + + ", chunk size = " + std::to_string(static_cast(dim_chunk_sz[i])); + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, msg.c_str()); + } + } + } + + return dim_chunk_sz; +} + +/* Create an HDF5 string type - ASCII + null terminated */ +static inline hid_t spio_create_hdf5_str_type(void ) +{ + hid_t st = H5Tcopy(H5T_C_S1); + if(st != H5I_INVALID_HID){ + if(H5Tset_strpad(st, H5T_STR_NULLTERM) < 0){ + H5Tclose(st); + return H5I_INVALID_HID; + } + if(H5Tset_cset(st, H5T_CSET_ASCII) < 0){ + H5Tclose(st); + return H5I_INVALID_HID; + } + } + + return st; +} + +/* Write a hidden coordinates attribute (_Netcdf4Coordinates), which lists the dimids of the variable. */ +static inline int spio_add_nc_hidden_coord(iosystem_desc_t *ios, file_desc_t *file, int varid, + int ndims, const int *dimidsp) +{ + assert(ios && file && (varid >= 0) && (ndims >= 0)); + + /* No coordinate atttribute for scalars */ + if(ndims == 0) { return PIO_NOERR; } + + assert(dimidsp); + + /* Writing "_Netcdf4Coordinates" hidden attribute. This attribute stores the dimension ids of the + * variable dimensions in an integer array. + * This attribute is required for NetCDF to read HDF5 output + */ + const char* attr_name = "_Netcdf4Coordinates"; + + /* Sanity check : Ensure that "_Netcdf4Coordinates" attribute does not exist */ + htri_t attr_exists = H5Aexists(file->hdf5_vars[varid].hdf5_dataset_id, attr_name); + assert(attr_exists == 0); + + /* Create dataspace for the attribute i.e., space for integer array to store the dimension ids */ + std::array coords_len = {static_cast(ndims)}; + hid_t coords_space_id = H5Screate_simple(1, coords_len.data(), NULL); + if(coords_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new simple dataspace", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Create the hidden attribute */ + hid_t coords_att_id = H5Acreate2(file->hdf5_vars[varid].hdf5_dataset_id, attr_name, + H5T_NATIVE_INT, coords_space_id, H5P_DEFAULT, H5P_DEFAULT); + if(coords_att_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to the variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, attr_name); + } + + /* Write the dimension ids */ + if(H5Awrite(coords_att_id, H5T_NATIVE_INT, dimidsp) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to the variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, attr_name); + } + + if(H5Aclose(coords_att_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to the variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, attr_name); + } + + if(H5Sclose(coords_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a simple dataspace", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + return PIO_NOERR; +} + +int spio_hdf5_def_var(iosystem_desc_t *ios, file_desc_t *file, const char *name, + nc_type xtype, int ndims, const int *dimidsp, int varid) +{ + int ret = PIO_NOERR; + + assert(ios && file && name && ndims >= 0 && varid >= 0); + assert((ndims == 0) || dimidsp); + assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); + assert(ios->ioproc); + + /* Cache the dim sizes for HDF5 calls */ + std::vector dim_sz(ndims), max_dim_sz(ndims); + std::vector default_dim_chunk_sz(ndims); + std::vector dim_names(ndims); + for(int i = 0; i < ndims; i++){ + if(file->hdf5_dims[dimidsp[i]].len != PIO_UNLIMITED){ + dim_sz[i] = max_dim_sz[i] = file->hdf5_dims[dimidsp[i]].len; + } + else{ + dim_sz[i] = 1; + max_dim_sz[i] = H5S_UNLIMITED; + } + default_dim_chunk_sz[i] = file->hdf5_dims[dimidsp[i]].chunk_sz; + dim_names[i] = file->hdf5_dims[dimidsp[i]].name; + } + + /* Create HDF5 dataset (and optionally add filters as needed) */ + hid_t dcpl_id = spio_create_hdf5_dataset_pid(ios, file, name, max_dim_sz, xtype); + if(dcpl_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new dataset creation property list", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + file->hdf5_vars[varid].hdf5_type = (xtype == NC_CHAR) ? spio_create_hdf5_str_type() : spio_nc_type_to_hdf5_type(xtype); + assert(file->hdf5_vars[varid].hdf5_type != H5I_INVALID_HID); + + /* Set default chunk size for variable data */ + if(ndims > 0){ + if(H5Pset_chunk(dcpl_id, ndims, spio_get_dim_chunk_sz(ios, file, name, dim_names, max_dim_sz, default_dim_chunk_sz, xtype).data()) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set the size of the chunks used to store a chunked layout dataset", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + + /* Create a simple dataspace to define the global variable dimensions */ + hid_t sid = H5Screate_simple(ndims, dim_sz.data(), max_dim_sz.data()); + if(sid == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new simple dataspace", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Define the variable */ + const char* dataset_name = (file->hdf5_vars[varid].alt_name == NULL)? name : file->hdf5_vars[varid].alt_name; + file->hdf5_vars[varid].hdf5_dataset_id = H5Dcreate2(file->hdf5_file_id, dataset_name, file->hdf5_vars[varid].hdf5_type, + sid, H5P_DEFAULT, dcpl_id, H5P_DEFAULT); + if(file->hdf5_vars[varid].hdf5_dataset_id < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new dataset (%s) for the variable", + name, varid, pio_get_fname_from_file(file), file->pio_ncid, dataset_name); + } + + if(H5Sclose(sid) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a simple dataspace", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Pclose(dcpl_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a dataset creation property list", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Add a hidden attribute, "_Netcdf4Coordinates", to store var dimension ids so that NetCDF can read the var */ + if(spio_add_nc_hidden_coord(ios, file, varid, ndims, dimidsp) != PIO_NOERR){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "Adding NetCDF hidden coordinate attribute failed", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + return PIO_NOERR; +} + +static int spio_hdf5_sync_defs(iosystem_desc_t *ios, file_desc_t *file) +{ + int i = 0, ret = PIO_NOERR; + + assert(ios && file); + assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); + assert(ios->ioproc); + + /* Step 1 : Process dimensions with no coordinate variables associated with it */ + for(i = 0; i < file->hdf5_num_dims; i++){ + /* For dimensions without an associated coordinate var, define them here. However since the + * the user can call redef() multiple times define it only its not already defined/valid + */ + if(!file->hdf5_dims[i].has_coord_var && (file->hdf5_dims[i].hdf5_dataset_id == H5I_INVALID_HID)){ + hid_t space_id, dcpl_id = H5I_INVALID_HID, dimscale_id; + hsize_t dims[1], max_dims[1], chunk_dims[1] = {1}; + + dcpl_id = H5Pcreate(H5P_DATASET_CREATE); + if(dcpl_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new dataset creation property list", + pio_get_fname_from_file(file), file->pio_ncid); + } + + /* H5DSattach_scale calls (even with MPI_Barrier) might fail or hang if attribute creation + * order is tracked or indexed. Before we have a better workaround, temporarily disable + * tracking and indexing of attribute creation order. */ +#if 0 + if(H5Pset_attr_creation_order(dcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set tracking and indexing of attribute creation order", + pio_get_fname_from_file(file), file->pio_ncid); + } +#endif + + /* Set size of dataset to size of dimension. */ + max_dims[0] = dims[0] = file->hdf5_dims[i].len; + + /* If this dimension scale is unlimited, set up chunking with a chunksize of 1. */ + if(max_dims[0] == PIO_UNLIMITED){ + max_dims[0] = H5S_UNLIMITED; + + if(H5Pset_chunk(dcpl_id, 1, chunk_dims) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set the size of the chunks used to store a chunked layout dataset", + pio_get_fname_from_file(file), file->pio_ncid); + } + } + + space_id = H5Screate_simple(1, dims, max_dims); + if(space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new simple dataspace", + pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Create a dataset that will be converted to a dimension scale. */ + dimscale_id = H5Dcreate2(file->hdf5_file_id, file->hdf5_dims[i].name, H5T_IEEE_F32BE, + space_id, H5P_DEFAULT, dcpl_id, H5P_DEFAULT); + if(dimscale_id < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new dataset (%s) that will be converted to a dimension scale", + pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name); + } + + char dimscale_name[PIO_MAX_NAME]; + snprintf(dimscale_name, PIO_MAX_NAME, "%s%10d", "This is a netCDF dimension but not a netCDF variable.", (int)dims[0]); + + if(H5DSset_scale(dimscale_id, dimscale_name) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to convert a dataset (for dimension %s) to a dimension scale", + pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name); + } + + file->hdf5_dims[i].hdf5_dataset_id = dimscale_id; + + /* Write a special attribute (_Netcdf4Dimid) for the netCDF-4 dimension ID. */ + hid_t dimid_att_id; + htri_t attr_exists; + + hid_t dimid_space_id = H5Screate(H5S_SCALAR); + if(dimid_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new scalar dataspace", + pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Writing _Netcdf4Dimid attribute */ + const char* attr_name = "_Netcdf4Dimid"; + + /* H5Aexists() returns zero (false), a positive (true) or a negative (failure) value */ + attr_exists = H5Aexists(dimscale_id, attr_name); + if(attr_exists > 0) { assert(0); } + else if(attr_exists == 0){ + dimid_att_id = H5Acreate2(dimscale_id, attr_name, + H5T_NATIVE_INT, dimid_space_id, H5P_DEFAULT, H5P_DEFAULT); + if(dimid_att_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); + } + } + else{ + /* Error determining whether an attribute with a given name exists on an object */ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to determine whether an attribute (%s) exists on a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); + } + + if(H5Awrite(dimid_att_id, H5T_NATIVE_INT, &i) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); + } + + if(H5Sclose(dimid_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a scalar dataspace", + pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Aclose(dimid_att_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); + } + + if(H5Sclose(space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a simple dataspace", + pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Pclose(dcpl_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a dataset creation property list", + pio_get_fname_from_file(file), file->pio_ncid); + } + } + } + + /* Step 2 : Process coordinate variables and associated dimensions */ + for(i = 0; i < file->hdf5_num_vars; i++){ + /* Note: For async I/O operations enddef() calls queued before + * the HDF5 variable is defined will see partially initialized + * "files" (file->hdf5_vars[i] is partially inited) + */ + if(file->hdf5_vars[i].hdf5_dataset_id == H5I_INVALID_HID) { continue; } + /* Upgrade the dataset of a coordinate variable to a dimension scale */ + if(file->hdf5_vars[i].is_coord_var){ + /* Write a special attribute (_Netcdf4Dimid) for the netCDF-4 dimension ID. */ + hid_t dimscale_id = file->hdf5_vars[i].hdf5_dataset_id; + hid_t dimid_att_id; + htri_t attr_exists; + + /* Writing _Netcdf4Dimid attribute */ + const char* attr_name = "_Netcdf4Dimid"; + + attr_exists = H5Aexists(dimscale_id, attr_name); + if(attr_exists < 0){ + /* Error determining whether an attribute with a given name exists on an object */ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to determine whether an attribute (%s) exists on a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); + } + + if(attr_exists > 0){ + /* If redef/enddef is called, potentially multiple times, + * this attribute might already have been created + */ + continue; + } + + if(H5DSset_scale(file->hdf5_vars[i].hdf5_dataset_id, file->hdf5_vars[i].name) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to convert a dataset (for coordinate variable %s) to a dimension scale", + pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_vars[i].name); + } + + assert(file->hdf5_vars[i].ndims > 0); + int dimid = file->hdf5_vars[i].hdf5_dimids[0]; + file->hdf5_dims[dimid].hdf5_dataset_id = file->hdf5_vars[i].hdf5_dataset_id; + + hid_t dimid_space_id = H5Screate(H5S_SCALAR); + if(dimid_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new scalar dataspace", + pio_get_fname_from_file(file), file->pio_ncid); + } + + dimid_att_id = H5Acreate2(dimscale_id, attr_name, + H5T_NATIVE_INT, dimid_space_id, H5P_DEFAULT, H5P_DEFAULT); + if(dimid_att_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); + } + + if(H5Awrite(dimid_att_id, H5T_NATIVE_INT, &dimid) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); + } + + if(H5Aclose(dimid_att_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to a dimension scale (%s)", + pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); + } + + if(H5Sclose(dimid_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a scalar dataspace", + pio_get_fname_from_file(file), file->pio_ncid); + } + } + } + + /* Step 3 : Process rest of the variables (These are non-coordinate variables). The coordinate + * variables and associated dimensions need to be defined (Step 2) before we start processing + * all variables. + */ + for(i = 0; i < file->hdf5_num_vars; i++){ + if(!file->hdf5_vars[i].is_coord_var){ + /* Not a coordinate var */ + int ndims = file->hdf5_vars[i].ndims; + if(ndims > 0){ + int* dimids = file->hdf5_vars[i].hdf5_dimids; + for(int j = 0; j < ndims; j++){ + if(H5DSattach_scale(file->hdf5_vars[i].hdf5_dataset_id, file->hdf5_dims[dimids[j]].hdf5_dataset_id, j) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Syncing definitions of variables/attributes in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to attach a dimension scale (for dimension %s) to %dth dimension of a dataset (for variable %s)", + pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[dimids[j]].name, j, file->hdf5_vars[i].name); + } + + /* According to HDF5 developers, the H5DS routines are not parallel, so all the ranks are going to be + * doing the same operations. At some point, with enough iterations of the loop, HDF5 might get out of + * step between the ranks. + * Workaround: place a barrier to sync H5DSattach_scale calls. + */ + MPI_Barrier(ios->tcomm_info->get_io_comm()); + } + } + } + } + return PIO_NOERR; +} + +int spio_hdf5_enddef(iosystem_desc_t *ios, file_desc_t *file) +{ + return spio_hdf5_sync_defs(ios, file); +} + +int spio_hdf5_copy_att(iosystem_desc_t *ios, file_desc_t *ifile, int ivarid, const char *name, + file_desc_t *ofile, int ovarid) +{ + int ret = PIO_NOERR; + + assert(ios && ifile && ofile && name); + assert((ifile->iotype == PIO_IOTYPE_HDF5) || (ifile->iotype == PIO_IOTYPE_HDF5C)); + assert((ofile->iotype == PIO_IOTYPE_HDF5) || (ofile->iotype == PIO_IOTYPE_HDF5C)); + assert(ios->ioproc); + + /* Error message template */ + const std::string err_msg = std::string("Copying attribute (") + name + ") associated with variable (varid=" + std::to_string(ivarid) + " in input file (" + pio_get_fname_from_file(ifile) + ", ncid=" + std::to_string(ifile->pio_ncid) + ") to variable (varid=" + std::to_string(ovarid) + ") in file (" + pio_get_fname_from_file(ofile) + ", ncid=" + std::to_string(ofile->pio_ncid) + ") using HDF5 iotype failed."; + + /* Sync any pending defs (attributes) */ + ret = spio_hdf5_sync_defs(ios, ifile); + if(ret == PIO_NOERR){ ret = spio_hdf5_sync_defs(ios, ofile); } + + /* Read attribute from the input file */ + hid_t iloc_id = (ivarid == PIO_GLOBAL) ? ifile->hdf5_file_id : ifile->hdf5_vars[ivarid].hdf5_dataset_id; + + hid_t iattr_id = H5Aopen(iloc_id, name, H5P_DEFAULT); + if(iattr_id == H5I_INVALID_HID){ + const std::string lerr_msg = " The low level (HDF5) I/O library call failed to open attribute in input file"; + return pio_err(ios, ifile, PIO_EHDF5ERR, __FILE__, __LINE__, "%s", (err_msg + lerr_msg).c_str()); + } + + hsize_t asize = H5Aget_storage_size(iattr_id); + hid_t h5_space = H5Aget_space(iattr_id); + hid_t h5_xtype = H5Aget_type(iattr_id); + if((asize <= 0) || (h5_space == H5I_INVALID_HID) || (h5_xtype == H5I_INVALID_HID)){ + const std::string lerr_msg = std::string(" The low level (HDF5) I/O library call to ") + ((asize <= 0) ? " get storage size failed" : ((h5_space == H5I_INVALID_HID) ? " get dataspace of attribute failed" : " get type of attribute failed")); + if(h5_xtype != H5I_INVALID_HID) { H5Tclose(h5_xtype); } + if(h5_space != H5I_INVALID_HID) { H5Sclose(h5_space); } + H5Aclose(iattr_id); + return pio_err(ios, ifile, PIO_EHDF5ERR, __FILE__, __LINE__, "%s", (err_msg + lerr_msg).c_str()); + } + + void *abuf = malloc(static_cast(asize)); + if(!abuf){ + const std::string lerr_msg = std::string(" Allocating ") + std::to_string(static_cast(asize)) + " bytes for reading attribute failed"; + H5Tclose(h5_xtype); H5Sclose(h5_space); H5Aclose(iattr_id); + return pio_err(ios, ifile, PIO_ENOMEM, __FILE__, __LINE__, "%s", (err_msg + lerr_msg).c_str()); + } + + if(H5Aread(iattr_id, h5_xtype, abuf) < 0){ + const std::string lerr_msg = " Reading attribute from input file failed"; + free(abuf); H5Tclose(h5_xtype); H5Sclose(h5_space); H5Aclose(iattr_id); + return pio_err(ios, ifile, PIO_ENOMEM, __FILE__, __LINE__, "%s", (err_msg + lerr_msg).c_str()); + } + + /* Create attribute in output file and write to it */ + hid_t oloc_id = (ovarid == PIO_GLOBAL) ? ofile->hdf5_file_id : ofile->hdf5_vars[ovarid].hdf5_dataset_id; + + hid_t oattr_id = H5Acreate2(oloc_id, name, h5_xtype, h5_space, H5P_DEFAULT, H5P_DEFAULT); + if(oattr_id == H5I_INVALID_HID){ + const std::string lerr_msg = " Creating attribute in ouput file failed"; + free(abuf); H5Tclose(h5_xtype); H5Sclose(h5_space); H5Aclose(iattr_id); + return pio_err(ios, ofile, PIO_ENOMEM, __FILE__, __LINE__, "%s", (err_msg + lerr_msg).c_str()); + } + + if(H5Awrite(oattr_id, h5_xtype, abuf) < 0){ + const std::string lerr_msg = " Writing attribute to output file failed"; + H5Aclose(oattr_id); free(abuf); H5Tclose(h5_xtype); H5Sclose(h5_space); H5Aclose(iattr_id); + return pio_err(ios, ofile, PIO_ENOMEM, __FILE__, __LINE__, "%s", (err_msg + lerr_msg).c_str()); + } + + H5Aclose(oattr_id); free(abuf); H5Tclose(h5_xtype); H5Sclose(h5_space); H5Aclose(iattr_id); + + return PIO_NOERR; +} + +int spio_hdf5_put_att(iosystem_desc_t *ios, file_desc_t *file, int varid, const char *name, + nc_type atttype, PIO_Offset len, const void *op) +{ + int ret = PIO_NOERR; + hid_t attr_id; + hid_t space_id; + hsize_t asize = len; + htri_t att_exists; + hid_t loc_id; + hid_t h5_xtype; + + assert(ios && file && name && op); + assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); + assert(ios->ioproc); + + if(varid == PIO_GLOBAL){ + loc_id = file->hdf5_file_id; + } + else{ + loc_id = file->hdf5_vars[varid].hdf5_dataset_id; + } + + if(atttype == NC_CHAR){ + /* String type */ + if(asize == 0){ + space_id = H5Screate(H5S_NULL); + } + else{ + space_id = H5Screate(H5S_SCALAR); + } + + if(space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new scalar dataspace", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + h5_xtype = H5Tcopy(H5T_C_S1); + if(h5_xtype == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to make a copy of the predefined string datatype in C", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + /* For empty strings, asize is 0, while H5Tset_size() requires that size must be positive */ + if(H5Tset_size(h5_xtype, (asize == 0 ? 1 : asize)) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set the total size (%ld bytes) for a derived C-style string datatype", + name, varid, pio_get_fname_from_file(file), file->pio_ncid, (asize == 0 ? 1 : asize)); + } + + if(H5Tset_strpad(h5_xtype, H5T_STR_NULLTERM) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to define the type of padding (NULL-terminated) used for a derived C-style string datatype", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Tset_cset(h5_xtype, H5T_CSET_ASCII) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set the character set (US ASCII) to be used in a derived C-style string datatype", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + else{ + space_id = H5Screate_simple(1, &asize, &asize); + if(space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new simple dataspace", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + h5_xtype = spio_nc_type_to_hdf5_type(atttype); + if(h5_xtype == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unsupported variable type (type=%x)", + name, varid, pio_get_fname_from_file(file), file->pio_ncid, atttype); + } + } + + /* H5Aexists() returns zero (false), a positive (true) or a negative (failure) value */ + att_exists = H5Aexists(loc_id, name); + if(att_exists > 0){ + attr_id = H5Aopen(loc_id, name, H5P_DEFAULT); + if(attr_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) with HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to open this existing attribute", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + else if(att_exists == 0){ + attr_id = H5Acreate2(loc_id, name, h5_xtype, space_id, H5P_DEFAULT, H5P_DEFAULT); + if(attr_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new attribute", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + else{ + /* Error determining whether an attribute with a given name exists on an object */ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to determine whether this attribute exists", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Awrite(attr_id, h5_xtype, op) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to write this attribute", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Sclose(space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a simple dataspace", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Aclose(attr_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close this attribute", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + /* String attribute */ + if(atttype == NC_CHAR){ + if(H5Tclose(h5_xtype) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a derived C-style string datatype used by this attribute", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + + return PIO_NOERR; +} + +int spio_hdf5_put_var(iosystem_desc_t *ios, file_desc_t *file, int varid, + const PIO_Offset *start, const PIO_Offset *count, + const PIO_Offset *stride, nc_type xtype, const void *buf) +{ + int ret = PIO_NOERR; + hsize_t dims[H5S_MAX_RANK]; + hsize_t mdims[H5S_MAX_RANK]; + + assert(ios && file && varid >= 0 && buf); + assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); + assert(ios->ioproc); + + hid_t file_space_id = H5Dget_space(file->hdf5_vars[varid].hdf5_dataset_id); + if(file_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to make a copy of the dataspace of the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Sget_simple_extent_dims(file_space_id, dims, mdims) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to retrieve dimension size and maximum size of a dataspace copied from the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + int ndims = file->hdf5_vars[varid].ndims; + + /* Extend record dimension if needed */ + if(ndims > 0 && start != NULL && count != NULL && mdims[0] == H5S_UNLIMITED && dims[0] < (hsize_t)(start[0] + count[0])){ + dims[0] = (hsize_t) (start[0] + count[0]); + + if(H5Sclose(file_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a dataspace copied from the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Dextend(file->hdf5_vars[varid].hdf5_dataset_id, dims) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to extend the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + file_space_id = H5Dget_space(file->hdf5_vars[varid].hdf5_dataset_id); + if(file_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to make a copy of the dataspace of the dataset (extended) associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + + hsize_t hstart[(ndims > 0) ? ndims : 1]; + hsize_t hcount[(ndims > 0) ? ndims : 1]; + hsize_t hstride[(ndims > 0) ? ndims : 1]; + hsize_t nelems = 0; + + for(int i = 0; i < ndims; i++){ + if(start){ + hstart[i] = (hsize_t) start[i]; + } + else{ + hstart[i] = 0; + } + hcount[i] = 0; + hstride[i] = 1; + } + + /* Only the IO master does the IO */ + if(ios->iomaster == MPI_ROOT){ + if(count){ + for(int i = 0; i < ndims; i++){ + hcount[i] = (hsize_t)count[i]; + } + } + else{ + for(int i = 0; i < ndims; i++){ + hcount[i] = dims[i]; + } + } + + nelems = (ndims > 0) ? 1 : 0; + for(int i = 0; i < ndims; i++){ + nelems *= hcount[i]; + } + + if(stride){ + for(int i = 0; i < ndims; i++){ + hstride[i] = (stride[i] > 0) ? ((hsize_t)stride[i]) : 1 ; + } + } + } + + hid_t mem_space_id = H5Screate_simple(ndims, hcount, hcount); + if(mem_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to create a new simple dataspace", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(ndims > 0){ + if(H5Sselect_hyperslab(file_space_id, H5S_SELECT_SET, hstart, hstride, hcount, NULL) < 0){ + H5Eprint2(H5E_DEFAULT, stderr); + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to select a hyperslab region for a dataspace copied from the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + + hid_t mem_type_id; + if(xtype == NC_CHAR){ + /* String type */ + mem_type_id = H5Tcopy(H5T_C_S1); + if(mem_type_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to make a copy of the predefined string datatype in C", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Tset_strpad(mem_type_id, H5T_STR_NULLTERM) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to define the type of padding (NULL-terminated) used for a derived C-style string datatype", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Tset_cset(mem_type_id, H5T_CSET_ASCII) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to set the character set (US ASCII) to be used in a derived C-style string datatype", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + else{ + mem_type_id = spio_nc_type_to_hdf5_type(xtype); + if(mem_type_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unsupported memory type (type=%x)", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, xtype); + } + } + + hid_t file_var_type_id = H5Dget_type(file->hdf5_vars[varid].hdf5_dataset_id); + assert(file_var_type_id != H5I_INVALID_HID); + + hid_t file_var_ntype_id = H5Tget_native_type(file_var_type_id, H5T_DIR_DEFAULT); + assert(file_var_ntype_id != H5I_INVALID_HID); + + void *wbuf = NULL; + bool dt_conv_reqd = (nelems > 0) && !H5Tequal(mem_type_id, file_var_ntype_id); + if(dt_conv_reqd){ + assert(file->dt_converter); + wbuf = static_cast(file->dt_converter)->convert(buf, spio_get_nc_type_size(xtype) * nelems, xtype, spio_hdf5_type_to_pio_type(file_var_ntype_id)); + assert(wbuf != NULL); + if(wbuf == NULL){ + return pio_err(ios, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unable to convert buffer from %d (buffer type in memory) to %d (variable type in file)", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, static_cast(xtype), spio_hdf5_type_to_pio_type(file_var_ntype_id)); + } + } + + /* Independent write */ + if(H5Dwrite(file->hdf5_vars[varid].hdf5_dataset_id, file_var_ntype_id, mem_space_id, + file_space_id, file->dxplid_indep, (dt_conv_reqd) ? wbuf : buf) < 0){ + H5Eprint2(H5E_DEFAULT, stderr); + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to write the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(dt_conv_reqd){ + brel(wbuf); + } + + if(H5Sclose(mem_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a simple dataspace", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(xtype == NC_CHAR){ + if(H5Tclose(mem_type_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a derived C-style string datatype used by this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + + if(H5Sclose(file_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a dataspace copied from the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + return PIO_NOERR; +} + +int spio_hdf5_close(iosystem_desc_t *ios, file_desc_t *file) +{ + int i = 0; + int ierr = PIO_NOERR; + + assert(ios && file); + assert((file->iotype == PIO_IOTYPE_HDF5) || ((file->iotype == PIO_IOTYPE_HDF5C))); + assert(ios->ioproc); + + /* Handle pending defines here + * e.g. If user calls close() without an enddef() + */ + ierr = spio_hdf5_enddef(ios, file); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) using HDF5 iotype failed. " + "Processing pending defines (variables, dimensions) failed", + pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Since close is always an async op, when async thread is used, we do not need + * to explicitly wait for async ops to complete + * i.e., we don't need spio_wait_all_hdf5_async_ops() here + */ + if(H5Pclose(file->dxplid_coll) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a dataset transfer property list (collective data transfer)", + pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Pclose(file->dxplid_indep) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a dataset transfer property list (independent data transfer)", + pio_get_fname_from_file(file), file->pio_ncid); + } + + for(i = 0; i < file->hdf5_num_dims; i++){ + if(!file->hdf5_dims[i].has_coord_var){ + if(H5Dclose(file->hdf5_dims[i].hdf5_dataset_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a dataset created for a dimension (%s, dimid = %d)", + pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name, i); + } + } + } + + for(i = 0; i < file->hdf5_num_vars; i++){ + if(H5Dclose(file->hdf5_vars[i].hdf5_dataset_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to close a dataset created for a variable (%s, varid = %d)", + pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_vars[i].name, i); + } + + if(file->hdf5_vars[i].nc_type == NC_CHAR){ + if(H5Tclose(file->hdf5_vars[i].hdf5_type) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a derived C-style string datatype created for a variable (%s, varid = %d)", + pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name, i); + } + } + } + + if(H5Fclose(file->hdf5_file_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to terminate access to an HDF5 file", + pio_get_fname_from_file(file), file->pio_ncid); + } + + return PIO_NOERR; +} + +int spio_hdf5_set_frame(file_desc_t *file, int varid, int frame) +{ + assert(file && file->iosystem && (frame >= 0)); + iosystem_desc_t *ios = file->iosystem; + assert(ios->ioproc); + + hsize_t dims[H5S_MAX_RANK]; + hsize_t mdims[H5S_MAX_RANK]; + + /* Get current dimension size */ + hid_t file_space_id = H5Dget_space(file->hdf5_vars[varid].hdf5_dataset_id); + if(file_space_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to make a copy of the dataspace of the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + int ndim = H5Sget_simple_extent_dims(file_space_id, dims, mdims); + if(ndim < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to retrieve dimension size and maximum size of a dataspace copied from the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + if(H5Sclose(file_space_id) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to release a dataspace copied from the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Extend record dimension if needed */ + if(ndim > 0 && mdims[0] == H5S_UNLIMITED && dims[0] < (hsize_t)(frame + 1)){ + dims[0] = frame + 1; + if(H5Dextend(file->hdf5_vars[varid].hdf5_dataset_id, dims) < 0){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " + "The low level (HDF5) I/O library call failed to extend the dataset associated with this variable", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + + /* Cache the frame in the varlist (used by the next write call) */ + file->varlist[varid].record = frame; + + return PIO_NOERR; +} + +PIO_Offset spio_hdf5_get_dim_chunk_sz_from_chunk_info(const std::string &dim_name) +{ + const std::string dim_chunk_info(SPIO_DIM_CHUNK_INFO); + bool user_provided_dim_chunk_info = !dim_chunk_info.empty(); + + if(!user_provided_dim_chunk_info) { return 0; } + + /* FIXME: Parse it once - hdf5 init - instead of re-parsing when each dim is defined */ + /* FIXME: Since C++ regex support is limited for some compilers using streams for now */ + /* SPIO_DIM_CHUNK_INFO = "DIM1_NAME:DIM1_CHUNK_SZ;DIM2_NAME:DIM2_CHUNK_SZ;" */ + + std::istringstream istr(dim_chunk_info); + std::string dim_info_tok, dim_name_tok, dim_chunk_sz_tok; + const char dim_info_delim = ';'; + const char chunk_info_delim = ':'; + + while(std::getline(istr, dim_info_tok, dim_info_delim)){ + std::size_t pos = 0; + if((pos = dim_info_tok.find(chunk_info_delim, 0)) != std::string::npos){ + dim_name_tok = dim_info_tok.substr(0, pos); + if((dim_name_tok == dim_name) && (pos + 1 < dim_info_tok.size())){ + dim_chunk_sz_tok = dim_info_tok.substr(pos + 1); + return static_cast(std::stoi(dim_chunk_sz_tok)); + } + } + } + + return 0; +} +#endif diff --git a/src/clib/core/iolib/hdf5/spio_hdf5_utils.hpp b/src/clib/core/iolib/hdf5/spio_hdf5_utils.hpp new file mode 100644 index 00000000000..0fff055878c --- /dev/null +++ b/src/clib/core/iolib/hdf5/spio_hdf5_utils.hpp @@ -0,0 +1,83 @@ +#ifndef __SPIO_HDF5_UTILS_HPP__ +#define __SPIO_HDF5_UTILS_HPP__ + +#include +#include +#include +#if PIO_USE_HDF5 +#include +#endif // PIO_USE_HDF5 + +#ifdef _HDF5 + +/* Function declarations */ +int spio_hdf5_create(iosystem_desc_t *ios, file_desc_t *file, const char *filename); + +int spio_hdf5_def_var(iosystem_desc_t *ios, file_desc_t *file, const char *name, + nc_type xtype, int ndims, const int *dimidsp, int varid); + +int spio_hdf5_enddef(iosystem_desc_t *ios, file_desc_t *file); + +int spio_hdf5_copy_att(iosystem_desc_t *ios, file_desc_t *ifile, int ivarid, const char *name, + file_desc_t *ofile, int ovarid); + +int spio_hdf5_put_att(iosystem_desc_t *ios, file_desc_t *file, int varid, const char *name, + nc_type atttype, PIO_Offset len, const void *op); + +int spio_hdf5_put_var(iosystem_desc_t *ios, file_desc_t *file, int varid, + const PIO_Offset *start, const PIO_Offset *count, + const PIO_Offset *stride, nc_type xtype, const void *buf); + +int spio_hdf5_close(iosystem_desc_t *ios, file_desc_t *file); +int spio_hdf5_set_frame(file_desc_t *file, int varid, int frame); + +PIO_Offset spio_hdf5_get_dim_chunk_sz_from_chunk_info(const std::string &dim_name); + +/* Inline functions */ +inline hid_t spio_nc_type_to_hdf5_type(nc_type xtype) +{ + switch(xtype){ + case NC_BYTE: return H5T_NATIVE_UINT8; + case NC_UBYTE: return H5T_NATIVE_UCHAR; + case NC_CHAR: return H5T_NATIVE_CHAR; + case NC_SHORT: return H5T_NATIVE_SHORT; + case NC_USHORT: return H5T_NATIVE_USHORT; + case NC_INT: return H5T_NATIVE_INT; + case NC_UINT: return H5T_NATIVE_UINT; + case NC_FLOAT : return H5T_NATIVE_FLOAT; + case NC_DOUBLE: return H5T_NATIVE_DOUBLE; + case NC_INT64: return H5T_NATIVE_INT64; + case NC_UINT64: return H5T_NATIVE_UINT64; + default: return H5I_INVALID_HID; + } + + return H5I_INVALID_HID; +} + +inline nc_type spio_hdf5_type_to_pio_type(hid_t ntype) +{ + /* switch() does not work with HDF5 "types" since these types are macros + * (which include library initialization call, H5Open(), if needed) + */ + if(H5Tequal(ntype, H5T_NATIVE_UINT8)) { return PIO_BYTE; } + else if(H5Tequal(ntype, H5T_NATIVE_UCHAR)) { return PIO_UBYTE; } + else if(H5Tequal(ntype, H5T_NATIVE_CHAR)) { return PIO_CHAR; } + else if(H5Tequal(ntype, H5T_NATIVE_SHORT)) { return PIO_SHORT; } + else if(H5Tequal(ntype, H5T_NATIVE_USHORT)) { return PIO_USHORT; } + else if(H5Tequal(ntype, H5T_NATIVE_INT)) { return PIO_INT; } + else if(H5Tequal(ntype, H5T_NATIVE_UINT)) { return PIO_UINT; } + else if(H5Tequal(ntype, H5T_NATIVE_FLOAT)) { return PIO_FLOAT; } + else if(H5Tequal(ntype, H5T_NATIVE_DOUBLE)) { return PIO_DOUBLE; } + else if(H5Tequal(ntype, H5T_NATIVE_INT64)) { return PIO_INT64; } + else if(H5Tequal(ntype, H5T_NATIVE_UINT64)) { return PIO_UINT64; } + else{ + assert(0); + } + + return PIO_NAT; +} + + +#endif + +#endif // __SPIO_HDF5_UTILS_HPP__ diff --git a/src/clib/pio_darray.cpp b/src/clib/core/pio_darray.cpp similarity index 63% rename from src/clib/pio_darray.cpp rename to src/clib/core/pio_darray.cpp index 0e84dfcb48e..bd582f4e68b 100644 --- a/src/clib/pio_darray.cpp +++ b/src/clib/core/pio_darray.cpp @@ -6,7 +6,6 @@ * array. Only by combining the distributed arrays from all processor * can the full array be obtained. * - * @author Jim Edwards */ #include #include @@ -18,6 +17,14 @@ #include "spio_io_summary.h" #include "spio_file_mvcache.h" #include "spio_hash.h" +#include "spio_gptl_utils.hpp" +#include "spio_ltimer_utils.hpp" +#include "pio_rearr_contig.hpp" +#include "spio_decomp_map_info_pool.hpp" +#include "spio_decomp_logger.hpp" +#include "spio_dt_converter.hpp" +#include "spio_async_utils.hpp" +#include /* uint64_t definition */ #ifdef _ADIOS2 @@ -107,583 +114,463 @@ PIO_Offset PIOc_set_buffer_size_limit_impl(PIO_Offset limit) * @param flushtodisk non-zero to cause buffers to be flushed to disk. * @return 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Jim Edwards, Ed Hartnett */ int PIOc_write_darray_multi_impl(int ncid, const int *varids, int ioid, int nvars, PIO_Offset arraylen, const void *array, const int *frame, const void **fillvalue, bool flushtodisk) { - iosystem_desc_t *ios; /* Pointer to io system information. */ - file_desc_t *file; /* Pointer to file information. */ - io_desc_t *iodesc; /* Pointer to IO description information. */ - size_t rlen = 0; /* Total data buffer size. */ - var_desc_t *vdesc0; /* Array of var_desc structure for each var. */ - int fndims = 0; /* Number of dims in the var in the file. */ - int mpierr = MPI_SUCCESS; /* Return code from MPI function calls. */ - int ierr = PIO_NOERR; /* Return code. */ - - GPTLstart("PIO:PIOc_write_darray_multi"); - /* Get the file info. */ - if ((ierr = pio_get_file(ncid, &file))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, - "Writing multiple variables to file (ncid=%d) failed. Unable to query the internal file structure associated with the file. Invalid file id", ncid); - } - assert(file); - ios = file->iosystem; - assert(ios); + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + io_desc_t *iodesc; /* Pointer to IO description information. */ + size_t rlen = 0; /* Total data buffer size. */ + var_desc_t *vdesc0; /* Array of var_desc structure for each var. */ + int fndims = 0; /* Number of dims in the var in the file. */ + int mpierr = MPI_SUCCESS; /* Return code from MPI function calls. */ + int ierr = PIO_NOERR; /* Return code. */ + + SPIO_Util::GPTL_Util::GPTL_timer func_timer("PIO:PIOc_write_darray_multi"); + /* Get the file info. */ + if((ierr = pio_get_file(ncid, &file))){ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Writing multiple variables to file (ncid=%d) failed. Unable to query the internal file structure associated with the file. Invalid file id", ncid); + } + assert(file); + ios = file->iosystem; + assert(ios); + + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_wr_timer(ios->io_fstats->wr_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_tot_timer(ios->io_fstats->tot_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper file_fstats_wr_timer(file->io_fstats->wr_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper file_fstats_tot_timer(file->io_fstats->tot_timer_name); + + /* Check inputs. */ + if(nvars <= 0 || !varids){ + return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error, invalid arguments, nvars = %d (expected > 0), varids is %s (expected not NULL)", pio_get_fname_from_file(file), ncid, nvars, PIO_IS_NULL(varids)); + } + for(int v = 0; v < nvars; v++){ + if(varids[v] < 0 || varids[v] > PIO_MAX_VARS){ + return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error, invalid arguments, nvars = %d, varids[%d] = %d (expected >= 0 && <= PIO_MAX_VARS=%d)", pio_get_fname_from_file(file), ncid, nvars, v, varids[v], PIO_MAX_VARS); + } + } + + LOG((1, "PIOc_write_darray_multi ncid = %d ioid = %d nvars = %d arraylen = %ld " + "flushtodisk = %d", + ncid, ioid, nvars, arraylen, flushtodisk)); + + /* Check that we can write to this file. */ + if(!(file->mode & PIO_WRITE)){ + return pio_err(ios, file, PIO_EPERM, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Trying to write to a read only file, try reopening the file in write mode (use the PIO_WRITE flag)", pio_get_fname_from_file(file), ncid); + } + + /* Get iodesc. */ + if(!(iodesc = pio_get_iodesc_from_id(ioid))){ + return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Invalid arguments, invalid PIO decomposition id (%d) provided", pio_get_fname_from_file(file), ncid, ioid); + } + pioassert(iodesc->rearranger == PIO_REARR_BOX || iodesc->rearranger == PIO_REARR_SUBSET || iodesc->rearranger == PIO_REARR_CONTIG, + "unknown rearranger", __FILE__, __LINE__); + + /* Get a pointer to the variable info for the first variable. */ + vdesc0 = &file->varlist[varids[0]]; + + /* Run these on all tasks if async is not in use, but only on + * non-IO tasks if async is in use. */ + if(!ios->async || !ios->ioproc){ + /* Get the number of dims for this var. */ + LOG((3, "about to call PIOc_inq_varndims varids[0] = %d", varids[0])); + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + ierr = PIOc_inq_varndims_impl(file->pio_ncid, varids[0], &fndims); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Inquiring number of dimensions in the first variable (%s, varid=%d) in the list failed", pio_get_fname_from_file(file), ncid, pio_get_vname_from_file(file, varids[0]), varids[0]); + } spio_ltimer_start(ios->io_fstats->wr_timer_name); spio_ltimer_start(ios->io_fstats->tot_timer_name); spio_ltimer_start(file->io_fstats->wr_timer_name); spio_ltimer_start(file->io_fstats->tot_timer_name); + LOG((3, "called PIOc_inq_varndims varids[0] = %d fndims = %d", varids[0], fndims)); + } - /* Check inputs. */ - if (nvars <= 0 || !varids) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Internal error, invalid arguments, nvars = %d (expected > 0), varids is %s (expected not NULL)", pio_get_fname_from_file(file), ncid, nvars, PIO_IS_NULL(varids)); - } - for (int v = 0; v < nvars; v++) - if (varids[v] < 0 || varids[v] > PIO_MAX_VARS) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Internal error, invalid arguments, nvars = %d, varids[%d] = %d (expected >= 0 && <= PIO_MAX_VARS=%d)", pio_get_fname_from_file(file), ncid, nvars, v, varids[v], PIO_MAX_VARS); - } - - LOG((1, "PIOc_write_darray_multi ncid = %d ioid = %d nvars = %d arraylen = %ld " - "flushtodisk = %d", - ncid, ioid, nvars, arraylen, flushtodisk)); - - /* Check that we can write to this file. */ - if (!(file->mode & PIO_WRITE)) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EPERM, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Trying to write to a read only file, try reopening the file in write mode (use the PIO_WRITE flag)", pio_get_fname_from_file(file), ncid); - } - - /* Get iodesc. */ - if (!(iodesc = pio_get_iodesc_from_id(ioid))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Invalid arguments, invalid PIO decomposition id (%d) provided", pio_get_fname_from_file(file), ncid, ioid); - } - pioassert(iodesc->rearranger == PIO_REARR_BOX || iodesc->rearranger == PIO_REARR_SUBSET, - "unknown rearranger", __FILE__, __LINE__); + /* If async is in use, and this is not an IO task, bcast the parameters. */ + if(ios->async){ + int msg = PIO_MSG_WRITEDARRAYMULTI; + char frame_present = frame ? true : false; /* Is frame non-NULL? */ + char fillvalue_present = fillvalue ? true : false; /* Is fillvalue non-NULL? */ + int flushtodisk_int = flushtodisk; /* Need this to be int not boolean. */ - /* Get a pointer to the variable info for the first variable. */ - vdesc0 = &file->varlist[varids[0]]; + int *amsg_frame = NULL; + void *amsg_fillvalue = fillvalue; - /* Run these on all tasks if async is not in use, but only on - * non-IO tasks if async is in use. */ - if (!ios->async || !ios->ioproc) - { - /* Get the number of dims for this var. */ - LOG((3, "about to call PIOc_inq_varndims varids[0] = %d", varids[0])); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - ierr = PIOc_inq_varndims_impl(file->pio_ncid, varids[0], &fndims); - if(ierr != PIO_NOERR){ - GPTLstop("PIO:PIOc_write_darray_multi"); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Inquiring number of dimensions in the first variable (%s, varid=%d) in the list failed", pio_get_fname_from_file(file), ncid, pio_get_vname_from_file(file, varids[0]), varids[0]); - } - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - LOG((3, "called PIOc_inq_varndims varids[0] = %d fndims = %d", varids[0], fndims)); + if(!frame_present){ + amsg_frame = (int *)calloc(nvars, sizeof(int)); + } + if(!fillvalue_present){ + amsg_fillvalue = (char *) calloc(nvars * iodesc->piotype_size, sizeof(char )); } - /* If async is in use, and this is not an IO task, bcast the parameters. */ - if (ios->async) - { - int msg = PIO_MSG_WRITEDARRAYMULTI; - char frame_present = frame ? true : false; /* Is frame non-NULL? */ - char fillvalue_present = fillvalue ? true : false; /* Is fillvalue non-NULL? */ - int flushtodisk_int = flushtodisk; /* Need this to be int not boolean. */ - - int *amsg_frame = NULL; - void *amsg_fillvalue = fillvalue; - - if(!frame_present) - { - amsg_frame = (int *)calloc(nvars, sizeof(int)); - } - if(!fillvalue_present) - { - amsg_fillvalue = (char *) calloc(nvars * iodesc->piotype_size, sizeof(char )); - } - - PIO_SEND_ASYNC_MSG(ios, msg, &ierr, - ncid, nvars, nvars, varids, ioid, arraylen, - arraylen * iodesc->piotype_size, array, frame_present, - nvars, - (frame_present) ? frame : amsg_frame, fillvalue_present, - nvars * iodesc->piotype_size, amsg_fillvalue, flushtodisk_int); + PIO_SEND_ASYNC_MSG(ios, msg, &ierr, + ncid, nvars, nvars, varids, ioid, arraylen, + arraylen * iodesc->piotype_size, array, frame_present, + nvars, + (frame_present) ? frame : amsg_frame, fillvalue_present, + nvars * iodesc->piotype_size, amsg_fillvalue, flushtodisk_int); - if(!frame_present) - { - free(amsg_frame); - } - if(!fillvalue_present) - { - free(amsg_fillvalue); - } + if(!frame_present){ + free(amsg_frame); + } + if(!fillvalue_present){ + free(amsg_fillvalue); + } - /* Share results known only on computation tasks with IO tasks. */ - if ((mpierr = MPI_Bcast(&fndims, 1, MPI_INT, ios->comproot, ios->my_comm))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); - } - LOG((3, "shared fndims = %d", fndims)); + /* Share results known only on computation tasks with IO tasks. */ + if((mpierr = MPI_Bcast(&fndims, 1, MPI_INT, ios->comproot, ios->my_comm))){ + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); } + LOG((3, "shared fndims = %d", fndims)); + } - /* if the buffer is already in use in pnetcdf we need to flush first */ - if (file->iotype == PIO_IOTYPE_PNETCDF && spio_file_mvcache_get(file, ioid)) - { - ierr = flush_output_buffer(file, true, 0); - if (ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Flushing data to disk (PIO_IOTYPE_PNETCDF) failed", pio_get_fname_from_file(file), ncid); - } + /* if the buffer is already in use in pnetcdf we need to flush first */ + if(file->iotype == PIO_IOTYPE_PNETCDF && spio_file_mvcache_get(file, ioid)){ + ierr = flush_output_buffer(file, true, 0); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Flushing data to disk (PIO_IOTYPE_PNETCDF) failed", pio_get_fname_from_file(file), ncid); } + } - pioassert(!spio_file_mvcache_get(file, ioid), "buffer overwrite",__FILE__, __LINE__); + pioassert(!spio_file_mvcache_get(file, ioid), "buffer overwrite",__FILE__, __LINE__); - /* Determine total size of aggregated data (all vars/records). - * For netcdf serial writes we collect the data on io nodes and - * then move that data one node at a time to the io master node - * and write. The buffer size on io task 0 must be as large as - * the largest used to accommodate this serial io method (use - * iodesc->maxiobuflen to calculate it). */ - if ((file->iotype == PIO_IOTYPE_NETCDF || file->iotype == PIO_IOTYPE_NETCDF4C) && ios->iomaster == MPI_ROOT) - rlen = iodesc->maxiobuflen * nvars; - else - rlen = iodesc->llen * nvars; + /* Determine total size of aggregated data (all vars/records). + * For netcdf serial writes we collect the data on io nodes and + * then move that data one node at a time to the io master node + * and write. The buffer size on io task 0 must be as large as + * the largest used to accommodate this serial io method (use + * iodesc->maxiobuflen to calculate it). */ + if((file->iotype == PIO_IOTYPE_NETCDF || file->iotype == PIO_IOTYPE_NETCDF4C) && ios->iomaster == MPI_ROOT){ + rlen = iodesc->maxiobuflen * nvars; + } + else{ + rlen = iodesc->llen * nvars; + } #ifdef PIO_MICRO_TIMING - bool var_mtimer_was_running[nvars]; - /* Use the timer on the first variable to capture the total - *time to rearrange data for all variables - */ - ierr = mtimer_start(file->varlist[varids[0]].wr_rearr_mtimer); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Starting a micro timer failed", pio_get_fname_from_file(file), ncid); - } - /* Stop any write timers that are running, these timers will - *be updated later with the avg rearrange time - * (wr_rearr_mtimer) - */ - for(int i=0; ivarlist[varids[i]].wr_mtimer)); - ierr = mtimer_pause(file->varlist[varids[i]].wr_mtimer, - &(var_mtimer_was_running[i])); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Pausing a micro timer failed", pio_get_fname_from_file(file), ncid); - } - } + bool var_mtimer_was_running[nvars]; + /* Use the timer on the first variable to capture the total + *time to rearrange data for all variables + */ + ierr = mtimer_start(file->varlist[varids[0]].wr_rearr_mtimer); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Starting a micro timer failed", pio_get_fname_from_file(file), ncid); + } + /* Stop any write timers that are running, these timers will + *be updated later with the avg rearrange time + * (wr_rearr_mtimer) + */ + for(int i=0; ivarlist[varids[i]].wr_mtimer)); + ierr = mtimer_pause(file->varlist[varids[i]].wr_mtimer, &(var_mtimer_was_running[i])); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Pausing a micro timer failed", pio_get_fname_from_file(file), ncid); + } + } #endif - /* Allocate iobuf. */ - void *mv_iobuf = NULL; - if (rlen > 0) - { - /* Allocate memory for the buffer for all vars/records. */ - mv_iobuf = spio_file_mvcache_alloc(file, ioid, iodesc->mpitype_size * rlen); - if (!mv_iobuf) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Out of memory (Trying to allocate %lld bytes for rearranged data for multiple variables with the same decomposition)", pio_get_fname_from_file(file), ncid, (unsigned long long)(iodesc->mpitype_size * rlen)); - } - LOG((3, "allocated %lld bytes for variable buffer", rlen * iodesc->mpitype_size)); - - /* If fill values are desired, and we're using the BOX - * rearranger, insert fill values. */ - if (iodesc->needsfill && iodesc->rearranger == PIO_REARR_BOX) - { - PIO_Offset localiobuflen = rlen / nvars; - LOG((3, "inserting fill values iodesc->maxiobuflen = %lld, localiobuflen = %lld", iodesc->maxiobuflen, localiobuflen)); - for (int nv = 0; nv < nvars; nv++) - for (PIO_Offset i = 0; i < localiobuflen; i++) - memcpy(&((char *)mv_iobuf)[iodesc->mpitype_size * (i + nv * localiobuflen)], - &((char *)fillvalue)[nv * iodesc->mpitype_size], iodesc->mpitype_size); - } - } - else if (file->iotype == PIO_IOTYPE_PNETCDF && ios->ioproc) - { - /* this assures that iobuf is allocated on all iotasks thus - assuring that the flush_output_buffer call above is called - collectively (from all iotasks) */ - mv_iobuf = spio_file_mvcache_alloc(file, ioid, 1); - if (!mv_iobuf) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Out of memory (Trying to allocate 1 byte)", pio_get_fname_from_file(file), ncid); + /* Allocate iobuf. */ + void *mv_iobuf = NULL; + if(rlen > 0){ + /* Allocate memory for the buffer for all vars/records. */ + mv_iobuf = spio_file_mvcache_alloc(file, ioid, iodesc->mpitype_size * rlen); + if(!mv_iobuf){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Out of memory (Trying to allocate %lld bytes for rearranged data for multiple variables with the same decomposition)", pio_get_fname_from_file(file), ncid, (unsigned long long)(iodesc->mpitype_size * rlen)); + } + LOG((3, "allocated %lld bytes for variable buffer", rlen * iodesc->mpitype_size)); + + /* If fill values are desired, and we're using the BOX + * rearranger, insert fill values. */ + if(iodesc->needsfill && ((iodesc->rearranger == PIO_REARR_BOX) || (iodesc->rearranger == PIO_REARR_CONTIG))){ + PIO_Offset localiobuflen = rlen / nvars; + LOG((3, "inserting fill values iodesc->maxiobuflen = %lld, localiobuflen = %lld", iodesc->maxiobuflen, localiobuflen)); + for(int nv = 0; nv < nvars; nv++){ + for(PIO_Offset i = 0; i < localiobuflen; i++){ + memcpy(&((char *)mv_iobuf)[iodesc->mpitype_size * (i + nv * localiobuflen)], + &((char *)fillvalue)[nv * iodesc->mpitype_size], iodesc->mpitype_size); } - LOG((3, "allocated token for variable buffer")); - } - - /* Move data from compute to IO tasks. */ - if ((ierr = rearrange_comp2io(ios, iodesc, array, mv_iobuf, nvars))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Error rearranging and moving data from compute tasks to I/O tasks", pio_get_fname_from_file(file), ncid); + } } + } + else if(file->iotype == PIO_IOTYPE_PNETCDF && ios->ioproc){ + /* this assures that iobuf is allocated on all iotasks thus + assuring that the flush_output_buffer call above is called + collectively (from all iotasks) */ + mv_iobuf = spio_file_mvcache_alloc(file, ioid, 1); + if(!mv_iobuf){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Out of memory (Trying to allocate 1 byte)", pio_get_fname_from_file(file), ncid); + } + LOG((3, "allocated token for variable buffer")); + } + + /* Move data from compute to IO tasks. */ + if(iodesc->rearranger == PIO_REARR_CONTIG){ + ierr = iodesc->rearr->rearrange_comp2io(array, arraylen * nvars * iodesc->mpitype_size, mv_iobuf, rlen * iodesc->mpitype_size, nvars); + } + else{ + ierr = rearrange_comp2io(ios, iodesc, file, array, mv_iobuf, nvars); + } + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Error rearranging and moving data from compute tasks to I/O tasks(rearranger = %d)", + pio_get_fname_from_file(file), ncid, iodesc->rearranger); + } #ifdef PIO_MICRO_TIMING - double rearr_time = 0; - /* Use the timer on the first variable to capture the total - *time to rearrange data for all variables - */ - ierr = mtimer_pause(file->varlist[varids[0]].wr_rearr_mtimer, NULL); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Pausing a micro timer (to measure rearrange time) failed", pio_get_fname_from_file(file), ncid); - } - - ierr = mtimer_get_wtime(file->varlist[varids[0]].wr_rearr_mtimer, - &rearr_time); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); + double rearr_time = 0; + /* Use the timer on the first variable to capture the total + *time to rearrange data for all variables + */ + ierr = mtimer_pause(file->varlist[varids[0]].wr_rearr_mtimer, NULL); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Pausing a micro timer (to measure rearrange time) failed", pio_get_fname_from_file(file), ncid); + } + + ierr = mtimer_get_wtime(file->varlist[varids[0]].wr_rearr_mtimer, &rearr_time); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Retrieving wallclock time from a micro timer (rearrange time) failed", pio_get_fname_from_file(file), ncid); + } + + /* Calculate the average rearrange time for a variable */ + rearr_time /= nvars; + for(int i=0; ivarlist[varids[i]].wr_rearr_mtimer); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Resetting micro timer (to measure rearrange time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); + } + + /* Update the rearrange timer with avg rearrange time for a var */ + ierr = mtimer_update(file->varlist[varids[i]].wr_rearr_mtimer, rearr_time); + if(ierr != PIO_NOERR){ + LOG((1, "ERROR: Unable to update wr rearr timer")); + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Updating micro timer (to measure rearrange time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); + } + ierr = mtimer_flush(file->varlist[varids[i]].wr_rearr_mtimer, + get_var_desc_str(file->pio_ncid, varids[i], NULL)); + if(ierr != PIO_NOERR){ + LOG((1, "ERROR: Unable to flush wr rearr timer")); + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Flushing micro timer (to measure rearrange time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); + } + /* Update the write timer with avg rearrange time for a var + * i.e, the write timer includes the rearrange time + */ + ierr = mtimer_update(file->varlist[varids[i]].wr_mtimer, rearr_time); + if(ierr != PIO_NOERR){ + LOG((1, "ERROR: Unable to update wr timer")); + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Updating micro timer (to measure write time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); + } + + /* If the write timer was already running, resume it */ + if(var_mtimer_was_running[i]){ + ierr = mtimer_resume(file->varlist[varids[i]].wr_mtimer); + if(ierr != PIO_NOERR){ + LOG((1, "ERROR: Unable to resume wr timer")); return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Retrieving wallclock time from a micro timer (rearrange time) failed", pio_get_fname_from_file(file), ncid); - } - - /* Calculate the average rearrange time for a variable */ - rearr_time /= nvars; - for(int i=0; ivarlist[varids[i]].wr_rearr_mtimer); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Resetting micro timer (to measure rearrange time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); - } - - /* Update the rearrange timer with avg rearrange time for a var */ - ierr = mtimer_update(file->varlist[varids[i]].wr_rearr_mtimer, - rearr_time); - if(ierr != PIO_NOERR) - { - LOG((1, "ERROR: Unable to update wr rearr timer")); - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Updating micro timer (to measure rearrange time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); - } - ierr = mtimer_flush(file->varlist[varids[i]].wr_rearr_mtimer, - get_var_desc_str(file->pio_ncid, varids[i], NULL)); - if(ierr != PIO_NOERR) - { - LOG((1, "ERROR: Unable to flush wr rearr timer")); - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Flushing micro timer (to measure rearrange time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); - } - /* Update the write timer with avg rearrange time for a var - * i.e, the write timer includes the rearrange time - */ - ierr = mtimer_update(file->varlist[varids[i]].wr_mtimer, - rearr_time); - if(ierr != PIO_NOERR) - { - LOG((1, "ERROR: Unable to update wr timer")); - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Updating micro timer (to measure write time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); - } - - /* If the write timer was already running, resume it */ - if(var_mtimer_was_running[i]) - { - ierr = mtimer_resume(file->varlist[varids[i]].wr_mtimer); - if(ierr != PIO_NOERR) - { - LOG((1, "ERROR: Unable to resume wr timer")); - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Updating micro timer (to measure write time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); - } - } + "Writing multiple variables to file (%s, ncid=%d) failed. Updating micro timer (to measure write time) for variable %d failed", pio_get_fname_from_file(file), ncid, i); + } } + } #endif - /* Write the darray based on the iotype. */ - LOG((2, "about to write darray for iotype = %d", file->iotype)); - switch (file->iotype) - { + /* Write the darray based on the iotype. */ + LOG((2, "about to write darray for iotype = %d", file->iotype)); + switch(file->iotype){ case PIO_IOTYPE_NETCDF4P: case PIO_IOTYPE_NETCDF4P_NCZARR: case PIO_IOTYPE_PNETCDF: + if((ierr = write_darray_multi_par(file, nvars, fndims, varids, iodesc, + DARRAY_DATA, frame))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable data in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + } + break; case PIO_IOTYPE_HDF5: case PIO_IOTYPE_HDF5C: - if ((ierr = write_darray_multi_par(file, nvars, fndims, varids, iodesc, - DARRAY_DATA, frame))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable data in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); +#if PIO_USE_ASYNC_WR_THREAD + ierr = pio_iosys_async_hdf5_write_op_add(file, nvars, fndims, varids, iodesc, + DARRAY_DATA, frame); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error queing async task to write variable data in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); } +#else + if((ierr = write_darray_multi_par(file, nvars, fndims, varids, iodesc, + DARRAY_DATA, frame))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable data in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + } +#endif break; case PIO_IOTYPE_NETCDF4C: case PIO_IOTYPE_NETCDF: - if ((ierr = write_darray_multi_serial(file, nvars, fndims, varids, iodesc, - DARRAY_DATA, frame))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable data serially (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + if((ierr = write_darray_multi_serial(file, nvars, fndims, varids, iodesc, + DARRAY_DATA, frame))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable data serially (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); } break; default: - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); return pio_err(NULL, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, "Writing multiple variables to file (%s, ncid=%d) failed. Invalid iotype (%d) provided", pio_get_fname_from_file(file), ncid, file->iotype); - } + } - /* For PNETCDF the iobuf is freed in flush_output_buffer() */ - if (file->iotype != PIO_IOTYPE_PNETCDF) - { - /* Release resources. */ - if (mv_iobuf) - { - LOG((3,"freeing variable buffer in pio_darray")); - spio_file_mvcache_free(file, ioid); - } + /* For PNETCDF the iobuf is freed in flush_output_buffer() */ + if(file->iotype != PIO_IOTYPE_PNETCDF){ + /* Release resources. */ + if(mv_iobuf){ + LOG((3,"freeing variable buffer in pio_darray")); + spio_file_mvcache_free(file, ioid); } - - /* The box rearranger will always have data (it could be fill - * data) to fill the entire array - that is the aggregate start - * and count values will completely describe one unlimited - * dimension unit of the array. For the subset method this is not - * necessarily the case, areas of missing data may never be - * written. In order to make sure that these areas are given the - * missing value a 'holegrid' is used to describe the missing - * points. This is generally faster than the netcdf method of - * filling the entire array with missing values before overwriting - * those values later. */ - if (iodesc->rearranger == PIO_REARR_SUBSET && iodesc->needsfill) - { - LOG((2, "nvars = %d holegridsize = %ld iodesc->needsfill = %d\n", nvars, - iodesc->holegridsize, iodesc->needsfill)); - - pioassert(!vdesc0->fillbuf, "buffer overwrite",__FILE__, __LINE__); - - /* Get a buffer. */ - if (ios->io_rank == 0) - vdesc0->fillbuf = bget(iodesc->maxholegridsize * iodesc->mpitype_size * nvars); - else if (iodesc->holegridsize > 0) - vdesc0->fillbuf = bget(iodesc->holegridsize * iodesc->mpitype_size * nvars); - - /* copying the fill value into the data buffer for the box - * rearranger. This will be overwritten with data where - * provided. */ - for (int nv = 0; nv < nvars; nv++) - for (int i = 0; i < iodesc->holegridsize; i++) - memcpy(&((char *)vdesc0->fillbuf)[iodesc->mpitype_size * (i + nv * iodesc->holegridsize)], - &((char *)fillvalue)[iodesc->mpitype_size * nv], iodesc->mpitype_size); - - /* Write the darray based on the iotype. */ - switch (file->iotype) - { - case PIO_IOTYPE_PNETCDF: - case PIO_IOTYPE_NETCDF4P: - case PIO_IOTYPE_NETCDF4P_NCZARR: - if ((ierr = write_darray_multi_par(file, nvars, fndims, varids, iodesc, - DARRAY_FILL, frame))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable fillvalues in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); - } - break; - case PIO_IOTYPE_NETCDF4C: - case PIO_IOTYPE_NETCDF: - if ((ierr = write_darray_multi_serial(file, nvars, fndims, varids, iodesc, - DARRAY_FILL, frame))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable fillvalues serially (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); - } - break; - default: - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EBADIOTYPE, __FILE__, __LINE__, - "Writing fillvalues for multiple variables to file (%s, ncid=%d) failed. Unsupported iotype (%s) provided", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); - } - - /* For PNETCDF fillbuf is freed in flush_output_buffer() */ - if (file->iotype != PIO_IOTYPE_PNETCDF) - { - /* Free resources. */ - if (vdesc0->fillbuf) - { - brel(vdesc0->fillbuf); - vdesc0->fillbuf = NULL; - } - } +#if !PIO_USE_ASYNC_WR_THREAD + if((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)){ + assert(file->dt_converter); + static_cast(file->dt_converter)->free(ioid); + } +#endif + } + + /* The box rearranger will always have data (it could be fill + * data) to fill the entire array - that is the aggregate start + * and count values will completely describe one unlimited + * dimension unit of the array. For the subset method this is not + * necessarily the case, areas of missing data may never be + * written. In order to make sure that these areas are given the + * missing value a 'holegrid' is used to describe the missing + * points. This is generally faster than the netcdf method of + * filling the entire array with missing values before overwriting + * those values later. */ + if(iodesc->rearranger == PIO_REARR_SUBSET && iodesc->needsfill){ + LOG((2, "nvars = %d holegridsize = %ld iodesc->needsfill = %d\n", nvars, + iodesc->holegridsize, iodesc->needsfill)); + + pioassert(!vdesc0->fillbuf, "buffer overwrite",__FILE__, __LINE__); + + /* Get a buffer. */ + if(ios->io_rank == 0){ + vdesc0->fillbuf = bget(iodesc->maxholegridsize * iodesc->mpitype_size * nvars); + } + else if(iodesc->holegridsize > 0){ + vdesc0->fillbuf = bget(iodesc->holegridsize * iodesc->mpitype_size * nvars); + } + + /* copying the fill value into the data buffer for the box + * rearranger. This will be overwritten with data where + * provided. */ + for(int nv = 0; nv < nvars; nv++){ + for(int i = 0; i < iodesc->holegridsize; i++){ + memcpy(&((char *)vdesc0->fillbuf)[iodesc->mpitype_size * (i + nv * iodesc->holegridsize)], + &((char *)fillvalue)[iodesc->mpitype_size * nv], iodesc->mpitype_size); + } } - /* Only PNETCDF does non-blocking buffered writes, and hence - * needs an explicit flush/wait to make sure data is written - * to disk (if the buffer is full) - */ - if (ios->ioproc && file->iotype == PIO_IOTYPE_PNETCDF) - { - /* Flush data to disk for pnetcdf. */ - if ((ierr = flush_output_buffer(file, flushtodisk, 0))) - { - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); + /* Write the darray based on the iotype. */ + switch (file->iotype){ + case PIO_IOTYPE_PNETCDF: + case PIO_IOTYPE_NETCDF4P: + case PIO_IOTYPE_NETCDF4P_NCZARR: + if((ierr = write_darray_multi_par(file, nvars, fndims, varids, iodesc, + DARRAY_FILL, frame))){ return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing multiple variables to file (%s, ncid=%d) failed. Flushing data to disk (PIO_IOTYPE_PNETCDF) failed", pio_get_fname_from_file(file), ncid); - } - } - else - { - for(int i=0; ivarlist[varids[i]].wb_pend = 0; + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable fillvalues in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + } + break; + /* FIXME: Handle fillvalues for HDF5 */ + case PIO_IOTYPE_HDF5: + case PIO_IOTYPE_HDF5C: + #if PIO_USE_ASYNC_WR_THREAD + ierr = pio_iosys_async_hdf5_write_op_add(file, nvars, fndims, varids, iodesc, + DARRAY_FILL, frame); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error queing async task to write variable fillvalues in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + } + #else + if((ierr = write_darray_multi_par(file, nvars, fndims, varids, iodesc, + DARRAY_FILL, frame))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable fillvalues in parallel (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + } + #endif + break; + case PIO_IOTYPE_NETCDF4C: + case PIO_IOTYPE_NETCDF: + if((ierr = write_darray_multi_serial(file, nvars, fndims, varids, iodesc, + DARRAY_FILL, frame))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Internal error writing variable fillvalues serially (iotype = %s)", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + } + break; + default: + return pio_err(ios, file, PIO_EBADIOTYPE, __FILE__, __LINE__, + "Writing fillvalues for multiple variables to file (%s, ncid=%d) failed. Unsupported iotype (%s) provided", pio_get_fname_from_file(file), ncid, pio_iotype_to_string(file->iotype)); + } + + /* For PNETCDF fillbuf is freed in flush_output_buffer() */ + if(file->iotype != PIO_IOTYPE_PNETCDF){ + /* Free resources. */ + if(vdesc0->fillbuf){ + brel(vdesc0->fillbuf); + vdesc0->fillbuf = NULL; + } + #if !PIO_USE_ASYNC_WR_THREAD + if((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)){ + assert(file->dt_converter); + static_cast(file->dt_converter)->free(ioid); + } + #endif + } + } + + /* Only PNETCDF does non-blocking buffered writes, and hence + * needs an explicit flush/wait to make sure data is written + * to disk (if the buffer is full) + */ + if(ios->ioproc && file->iotype == PIO_IOTYPE_PNETCDF){ + /* Flush data to disk for pnetcdf. */ + if((ierr = flush_output_buffer(file, flushtodisk, 0))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing multiple variables to file (%s, ncid=%d) failed. Flushing data to disk (PIO_IOTYPE_PNETCDF) failed", pio_get_fname_from_file(file), ncid); + } + } + else{ + /* FIXME: Fix logic here to handle async HDF5 writes */ + for(int i=0; ivarlist[varids[i]].wb_pend = 0; #ifdef PIO_MICRO_TIMING - /* No more async events pending (all buffered data is written out) */ - mtimer_async_event_in_progress(file->varlist[varids[i]].wr_mtimer, false); - mtimer_flush(file->varlist[varids[i]].wr_mtimer, get_var_desc_str(file->pio_ncid, varids[i], NULL)); + /* No more async events pending (all buffered data is written out) */ + mtimer_async_event_in_progress(file->varlist[varids[i]].wr_mtimer, false); + mtimer_flush(file->varlist[varids[i]].wr_mtimer, get_var_desc_str(file->pio_ncid, varids[i], NULL)); #endif - } - file->wb_pend = 0; } + file->wb_pend = 0; + } - GPTLstop("PIO:PIOc_write_darray_multi"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return PIO_NOERR; + return PIO_NOERR; } /** @@ -694,7 +581,6 @@ int PIOc_write_darray_multi_impl(int ncid, const int *varids, int ioid, int nvar * @param vdesc pointer to var_desc_t info for this var. * @returns 0 for success, non-zero error code for failure. * @ingroup PIO_write_darray - * @author Ed Hartnett */ int find_var_fillvalue(file_desc_t *file, int varid, var_desc_t *vdesc) { @@ -2097,602 +1983,506 @@ static int PIOc_write_darray_adios(file_desc_t *file, int varid, int ioid, * data. * @returns 0 for success, non-zero error code for failure. * @ingroup PIO_write_darray - * @author Jim Edwards, Ed Hartnett */ int PIOc_write_darray_impl(int ncid, int varid, int ioid, PIO_Offset arraylen, const void *array, const void *fillvalue) { - iosystem_desc_t *ios; /* Pointer to io system information. */ - file_desc_t *file; /* Info about file we are writing to. */ - io_desc_t *iodesc; /* The IO description. */ - var_desc_t *vdesc; /* Info about the var being written. */ - void *bufptr; /* A data buffer. */ - MPI_Datatype vtype; /* The MPI type of the variable. */ - wmulti_buffer *wmb; /* The write multi buffer for one or more vars. */ - int recordvar; /* Non-zero if this is a record variable. */ - int needsflush = 0; /* True if we need to flush buffer. */ + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Info about file we are writing to. */ + io_desc_t *iodesc; /* The IO description. */ + var_desc_t *vdesc; /* Info about the var being written. */ + void *bufptr; /* A data buffer. */ + MPI_Datatype vtype; /* The MPI type of the variable. */ + wmulti_buffer *wmb; /* The write multi buffer for one or more vars. */ + int recordvar; /* Non-zero if this is a record variable. */ + int needsflush = 0; /* True if we need to flush buffer. */ #if PIO_LIMIT_CACHED_IO_REGIONS - PIO_Offset decomp_max_regions; /* Max non-contiguous regions in the IO decomposition */ - PIO_Offset io_max_regions; /* Max non-contiguous regions cached in a single IO process */ + PIO_Offset decomp_max_regions; /* Max non-contiguous regions in the IO decomposition */ + PIO_Offset io_max_regions; /* Max non-contiguous regions cached in a single IO process */ #endif - int mpierr = MPI_SUCCESS; /* Return code from MPI functions. */ - int ierr = PIO_NOERR; /* Return code. */ - - GPTLstart("PIO:PIOc_write_darray"); - GPTLstart("PIO:write_total"); - LOG((1, "PIOc_write_darray ncid = %d varid = %d ioid = %d arraylen = %d", - ncid, varid, ioid, arraylen)); - - /* Get the file info. */ - if ((ierr = pio_get_file(ncid, &file))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, - "Writing variable (varid=%d) failed on file. Invalid file id (ncid=%d) provided", varid, ncid); - } - assert(file); - spio_ltimer_start(file->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - ios = file->iosystem; - assert(ios); - - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstart("PIO:PIOc_write_darray_adios"); - GPTLstart("PIO:write_total_adios"); - } - - LOG((1, "PIOc_write_darray ncid=%d varid=%d wb_pend=%llu file_wb_pend=%llu", - ncid, varid, - (unsigned long long int) file->varlist[varid].wb_pend, - (unsigned long long int) file->wb_pend - )); - - /* Can we write to this file? */ - if (!(file->mode & PIO_WRITE)) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_write_darray_adios"); - GPTLstop("PIO:write_total_adios"); - } - return pio_err(ios, file, PIO_EPERM, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. The file was not opened for writing, try reopening the file in write mode (use the PIO_WRITE flag)", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } + int mpierr = MPI_SUCCESS; /* Return code from MPI functions. */ + int ierr = PIO_NOERR; /* Return code. */ + + SPIO_Util::GPTL_Util::GPTL_timer func_timer2("PIO:write_total"); + LOG((1, "PIOc_write_darray ncid = %d varid = %d ioid = %d arraylen = %d", + ncid, varid, ioid, arraylen)); + + /* Get the file info. */ + if((ierr = pio_get_file(ncid, &file))){ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Writing variable (varid=%d) failed on file. Invalid file id (ncid=%d) provided", varid, ncid); + } + assert(file); + ios = file->iosystem; + assert(ios); + + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper file_fstats_wr_timer(file->io_fstats->wr_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper file_fstats_tot_timer(file->io_fstats->tot_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_wr_timer(ios->io_fstats->wr_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_tot_timer(ios->io_fstats->tot_timer_name); + + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + GPTLstart("PIO:write_total_adios"); + } + + LOG((1, "PIOc_write_darray ncid=%d varid=%d wb_pend=%llu file_wb_pend=%llu", + ncid, varid, + (unsigned long long int) file->varlist[varid].wb_pend, + (unsigned long long int) file->wb_pend + )); + + /* Can we write to this file? */ + if(!(file->mode & PIO_WRITE)){ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + GPTLstop("PIO:write_total_adios"); + } + return pio_err(ios, file, PIO_EPERM, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. The file was not opened for writing, try reopening the file in write mode (use the PIO_WRITE flag)", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Get decomposition information. */ + if(!(iodesc = pio_get_iodesc_from_id(ioid))){ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + GPTLstop("PIO:write_total_adios"); + } + return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Invalid I/O descriptor id (ioid=%d) provided", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, ioid); + } + + /* Check that the local size of the variable passed in matches the + * size expected by the io descriptor. Fail if arraylen is too + * small, just put a warning in the log and truncate arraylen + * if it is too big (the excess values will be ignored.) */ + if(arraylen < iodesc->ndof){ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + GPTLstop("PIO:write_total_adios"); + } + return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. The local array size (arraylen=%lld) is smaller than expected, the I/O decomposition (ioid=%d) requires a local array of size = %lld", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (long long int) arraylen, ioid, (long long int) iodesc->ndof); + } + LOG((2, "%s arraylen = %d iodesc->ndof = %d", + (arraylen > iodesc->ndof) ? "WARNING: arraylen > iodesc->ndof" : "", + arraylen, iodesc->ndof)); #ifdef _ADIOS2 - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - /* ADIOS type does not support open to append mode */ - if (file->is_reopened) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - GPTLstop("PIO:PIOc_write_darray_adios"); - GPTLstop("PIO:write_total_adios"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using ADIOS iotype failed. " - "Open to append mode is not supported yet", - pio_get_vname_from_file(file, varid), varid, - pio_get_fname_from_file(file), ncid); - } - } + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + /* ADIOS type does not support open to append mode */ + if(file->is_reopened){ + GPTLstop("PIO:PIOc_write_darray"); + GPTLstop("PIO:write_total"); + GPTLstop("PIO:PIOc_write_darray_adios"); + GPTLstop("PIO:write_total_adios"); + return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using ADIOS iotype failed. " + "Open to append mode is not supported yet", + pio_get_vname_from_file(file, varid), varid, + pio_get_fname_from_file(file), ncid); + } + } #endif - /* Get decomposition information. */ - if (!(iodesc = pio_get_iodesc_from_id(ioid))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_write_darray_adios"); - GPTLstop("PIO:write_total_adios"); - } - return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Invalid I/O descriptor id (ioid=%d) provided", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, ioid); - } - - /* Check that the local size of the variable passed in matches the - * size expected by the io descriptor. Fail if arraylen is too - * small, just put a warning in the log and truncate arraylen - * if it is too big (the excess values will be ignored.) */ - if (arraylen < iodesc->ndof) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_write_darray_adios"); - GPTLstop("PIO:write_total_adios"); - } - return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. The local array size (arraylen=%lld) is smaller than expected, the I/O decomposition (ioid=%d) requires a local array of size = %lld", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (long long int) arraylen, ioid, (long long int) iodesc->ndof); - } - LOG((2, "%s arraylen = %d iodesc->ndof = %d", - (arraylen > iodesc->ndof) ? "WARNING: arraylen > iodesc->ndof" : "", - arraylen, iodesc->ndof)); - if (arraylen > iodesc->ndof) - arraylen = iodesc->ndof; + /* Get decomposition information. */ + if(!(iodesc = pio_get_iodesc_from_id(ioid))){ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + GPTLstop("PIO:PIOc_write_darray_adios"); + GPTLstop("PIO:write_total_adios"); + } + return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Invalid I/O descriptor id (ioid=%d) provided", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, ioid); + } + + /* Check that the local size of the variable passed in matches the + * size expected by the io descriptor. Fail if arraylen is too + * small, just put a warning in the log and truncate arraylen + * if it is too big (the excess values will be ignored.) */ + if(arraylen < iodesc->ndof){ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + GPTLstop("PIO:PIOc_write_darray_adios"); + GPTLstop("PIO:write_total_adios"); + } + return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. The local array size (arraylen=%lld) is smaller than expected, the I/O decomposition (ioid=%d) requires a local array of size = %lld", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (long long int) arraylen, ioid, (long long int) iodesc->ndof); + } + LOG((2, "%s arraylen = %d iodesc->ndof = %d", + (arraylen > iodesc->ndof) ? "WARNING: arraylen > iodesc->ndof" : "", + arraylen, iodesc->ndof)); + if(arraylen > iodesc->ndof){ + arraylen = iodesc->ndof; + } #ifdef PIO_MICRO_TIMING - mtimer_start(file->varlist[varid].wr_mtimer); + mtimer_start(file->varlist[varid].wr_mtimer); #endif #if PIO_SAVE_DECOMPS - if(!(iodesc->is_saved) && - pio_save_decomps_regex_match(ioid, file->fname, file->varlist[varid].vname)) - { - char filename[PIO_MAX_NAME]; - ierr = pio_create_uniq_str(ios, iodesc, filename, PIO_MAX_NAME, "piodecomp", ".dat"); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_write_darray_adios"); - GPTLstop("PIO:write_total_adios"); - } - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Saving I/O decomposition (ioid=%d) failed. Unable to create a unique file name for saving the I/O decomposition", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, ioid); - } - LOG((2, "Saving decomp map (write) to %s", filename)); - PIOc_writemap_impl(filename, ioid, iodesc->ndims, iodesc->dimlen, iodesc->maplen, iodesc->map, ios->my_comm); - iodesc->is_saved = true; - } + if(!(iodesc->is_saved) && + pio_save_decomps_regex_match(ioid, file->fname, file->varlist[varid].vname)){ + std::string filename; + pio_create_uniq_str(ios, iodesc, filename, "piodecomp", ".dat"); + + LOG((2, "Saving decomp map (write) to %s", filename.c_str())); + PIOc_writemap_impl(filename.c_str(), ioid, iodesc->ndims, iodesc->dimlen, iodesc->maplen, iodesc->map, ios->my_comm); + + std::string log_fname; + pio_create_uniq_str(ios, iodesc, log_fname, "piodecomp", ".nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + (*logger).write_only().open().put(iodesc).close(); + delete logger; + + iodesc->is_saved = true; + SPIO_Util::Decomp_Util::gdpool_mgr.get_decomp_map_info_pool()->add_decomp_map_info(ioid, filename.c_str()); + } + SPIO_Util::Decomp_Util::gdpool_mgr.get_decomp_map_info_pool()->add_var_info(ioid, file->pio_ncid, file->fname, varid, file->varlist[varid].vname); #endif - /* Get var description. */ - vdesc = &(file->varlist[varid]); - LOG((2, "vdesc record %d nreqs %d", vdesc->record, vdesc->nreqs)); - - /* Run these on all tasks if async is not in use, but only on - * non-IO tasks if async is in use. */ - if (!ios->async || !ios->ioproc) - { - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - - /* Find out PIO data type of var. */ - if (vdesc->pio_type == PIO_NAT) - { - if ((ierr = PIOc_inq_vartype_impl(ncid, varid, &vdesc->pio_type))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Inquiring variable data type failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } + /* Get var description. */ + vdesc = &(file->varlist[varid]); + LOG((2, "vdesc record %d nreqs %d", vdesc->record, vdesc->nreqs)); - assert(vdesc->pio_type != PIO_NAT); + /* Run these on all tasks if async is not in use, but only on + * non-IO tasks if async is in use. */ + if(!ios->async || !ios->ioproc){ + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); - /* Find out length of type. */ - if (vdesc->type_size == 0) - { - if ((ierr = PIOc_inq_type_impl(ncid, vdesc->pio_type, NULL, &vdesc->type_size))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Inquiring variable data type length failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } + /* Find out PIO data type of var. */ + if(vdesc->pio_type == PIO_NAT){ + if((ierr = PIOc_inq_vartype_impl(ncid, varid, &vdesc->pio_type))){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Inquiring variable data type failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } - assert(vdesc->type_size > 0); + assert(vdesc->pio_type != PIO_NAT); - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); + /* Find out length of type. */ + if(vdesc->type_size == 0){ + if((ierr = PIOc_inq_type_impl(ncid, vdesc->pio_type, NULL, &vdesc->type_size))){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Inquiring variable data type length failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } } - ios->io_fstats->wb += vdesc->type_size * iodesc->llen; - file->io_fstats->wb += vdesc->type_size * iodesc->llen; + assert(vdesc->type_size > 0); - /* If we don't know the fill value for this var, get it. */ -/* TODO handle fill values for HDF5 type later */ -if ((file->iotype != PIO_IOTYPE_HDF5) && (file->iotype != PIO_IOTYPE_HDF5C)) -{ - if (!vdesc->fillvalue) - { - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - if ((ierr = find_var_fillvalue(file, varid, vdesc))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_write_darray_adios"); - GPTLstop("PIO:write_total_adios"); - } - return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Finding fillvalue associated with the variable failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + spio_ltimer_start(ios->io_fstats->wr_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->wr_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + } + + ios->io_fstats->wb += vdesc->type_size * iodesc->llen; + file->io_fstats->wb += vdesc->type_size * iodesc->llen; + + /* If we don't know the fill value for this var, get it. */ + /* TODO handle fill values for HDF5 type later */ + if((file->iotype != PIO_IOTYPE_HDF5) && (file->iotype != PIO_IOTYPE_HDF5C)){ + if(!vdesc->fillvalue){ + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + if((ierr = find_var_fillvalue(file, varid, vdesc))){ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + GPTLstop("PIO:write_total_adios"); } - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Finding fillvalue associated with the variable failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + spio_ltimer_start(ios->io_fstats->wr_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->wr_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); } -} + } - /* Is this a record variable? The user must set the vdesc->record - * value by calling PIOc_setframe() before calling this - * function. */ - recordvar = vdesc->record >= 0 ? 1 : 0; - LOG((3, "recordvar = %d looking for multibuffer", recordvar)); + /* Is this a record variable? The user must set the vdesc->record + * value by calling PIOc_setframe() before calling this + * function. */ + recordvar = vdesc->record >= 0 ? 1 : 0; + LOG((3, "recordvar = %d looking for multibuffer", recordvar)); - /* Move to end of list or the entry that matches this ioid. */ - for (wmb = &file->buffer; wmb->next; wmb = wmb->next) - if (wmb->ioid == ioid && wmb->recordvar == recordvar) - break; - LOG((3, "wmb->ioid = %d wmb->recordvar = %d", wmb->ioid, wmb->recordvar)); + /* Move to end of list or the entry that matches this ioid. */ + for(wmb = &file->buffer; wmb->next; wmb = wmb->next){ + if(wmb->ioid == ioid && wmb->recordvar == recordvar){ + break; + } + } + LOG((3, "wmb->ioid = %d wmb->recordvar = %d", wmb->ioid, wmb->recordvar)); #ifdef _ADIOS2 - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - ierr = PIO_NOERR; - ierr = PIOc_write_darray_adios(file, varid, ioid, iodesc, arraylen, array, fillvalue); - GPTLstop("PIO:PIOc_write_darray_adios"); - GPTLstop("PIO:write_total_adios"); - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return ierr; - } + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + ierr = PIO_NOERR; + ierr = PIOc_write_darray_adios(file, varid, ioid, iodesc, arraylen, array, fillvalue); + GPTLstop("PIO:write_total_adios"); + return ierr; + } #endif - /* If we did not find an existing wmb entry, create a new wmb. */ - if (wmb->ioid != ioid || wmb->recordvar != recordvar) - { - /* Allocate a buffer. */ - LOG((3, "allocating multi-buffer")); - if (!(wmb->next = (wmulti_buffer *) calloc(1, sizeof(wmulti_buffer)))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating %lld bytes for a write multi buffer to cache user data", pio_get_fname_from_file(file), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long) sizeof(wmulti_buffer)); - } - LOG((3, "allocated multi-buffer")); - - /* Set pointer to newly allocated buffer and initialize.*/ - wmb = wmb->next; - wmb->recordvar = recordvar; - wmb->next = NULL; - wmb->ioid = ioid; - wmb->num_arrays = 0; - wmb->arraylen = arraylen; - wmb->vid = NULL; - wmb->data = NULL; - wmb->frame = NULL; - wmb->fillvalue = NULL; - } - LOG((2, "wmb->num_arrays = %d arraylen = %d iodesc->mpitype_size = %d\n", - wmb->num_arrays, arraylen, iodesc->mpitype_size)); - - needsflush = PIO_wmb_needs_flush(wmb, arraylen, iodesc); - assert(needsflush >= 0); + /* If we did not find an existing wmb entry, create a new wmb. */ + if(wmb->ioid != ioid || wmb->recordvar != recordvar){ + /* Allocate a buffer. */ + LOG((3, "allocating multi-buffer")); + if(!(wmb->next = (wmulti_buffer *) calloc(1, sizeof(wmulti_buffer)))){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating %lld bytes for a write multi buffer to cache user data", pio_get_fname_from_file(file), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long) sizeof(wmulti_buffer)); + } + LOG((3, "allocated multi-buffer")); + + /* Set pointer to newly allocated buffer and initialize.*/ + wmb = wmb->next; + wmb->recordvar = recordvar; + wmb->next = NULL; + wmb->ioid = ioid; + wmb->num_arrays = 0; + wmb->arraylen = arraylen; + wmb->vid = NULL; + wmb->data = NULL; + wmb->frame = NULL; + wmb->fillvalue = NULL; + } + LOG((2, "wmb->num_arrays = %d arraylen = %d iodesc->mpitype_size = %d\n", + wmb->num_arrays, arraylen, iodesc->mpitype_size)); + + needsflush = PIO_wmb_needs_flush(wmb, arraylen, iodesc); + assert(needsflush >= 0); #if PIO_LIMIT_CACHED_IO_REGIONS - /* When using PIO with PnetCDF + SUBSET rearranger the number - of non-contiguous regions cached in a single IO process can - grow to a large number. PnetCDF is not efficient at handling - very large number of regions (sub-array requests) in the - data written out. We typically run out of memory or the - write is very slow. - - We need to set a limit on the potential (after rearrangement) - maximum number of non-contiguous regions in an IO process and - forcefully flush out user data cached by a compute process - when that limit has been reached. - - Latest PnetCDF (version 1.11.0 and later) is more efficient at - handling very large number of regions, so we have turned off - PIO_LIMIT_CACHED_IO_REGIONS option by default. */ - decomp_max_regions = (iodesc->maxregions >= iodesc->maxfillregions)? iodesc->maxregions : iodesc->maxfillregions; - io_max_regions = (1 + wmb->num_arrays) * decomp_max_regions; - if (io_max_regions > PIO_MAX_CACHED_IO_REGIONS) - needsflush = 2; + /* When using PIO with PnetCDF + SUBSET rearranger the number + of non-contiguous regions cached in a single IO process can + grow to a large number. PnetCDF is not efficient at handling + very large number of regions (sub-array requests) in the + data written out. We typically run out of memory or the + write is very slow. + + We need to set a limit on the potential (after rearrangement) + maximum number of non-contiguous regions in an IO process and + forcefully flush out user data cached by a compute process + when that limit has been reached. + + Latest PnetCDF (version 1.11.0 and later) is more efficient at + handling very large number of regions, so we have turned off + PIO_LIMIT_CACHED_IO_REGIONS option by default. */ + decomp_max_regions = (iodesc->maxregions >= iodesc->maxfillregions)? iodesc->maxregions : iodesc->maxfillregions; + io_max_regions = (1 + wmb->num_arrays) * decomp_max_regions; + if(io_max_regions > PIO_MAX_CACHED_IO_REGIONS){ + needsflush = 2; + } #endif - /* Tell all tasks on the computation communicator whether we need - * to flush data. */ - if ((mpierr = MPI_Allreduce(MPI_IN_PLACE, &needsflush, 1, MPI_INT, MPI_MAX, - ios->comp_comm))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + /* Tell all tasks on the computation communicator whether we need + * to flush data. */ + if((mpierr = MPI_Allreduce(MPI_IN_PLACE, &needsflush, 1, MPI_INT, MPI_MAX, ios->comp_comm))){ + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + } + LOG((2, "needsflush = %d", needsflush)); + + if(!ios->async || !ios->ioproc){ + if(file->varlist[varid].vrsize == 0){ + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + ierr = calc_var_rec_sz(ncid, varid); + if(ierr != PIO_NOERR){ + LOG((1, "Unable to calculate the variable record size")); + } + spio_ltimer_start(ios->io_fstats->wr_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->wr_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); } - LOG((2, "needsflush = %d", needsflush)); + } - if(!ios->async || !ios->ioproc) - { - if(file->varlist[varid].vrsize == 0) - { - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - ierr = calc_var_rec_sz(ncid, varid); - if(ierr != PIO_NOERR) - { - LOG((1, "Unable to calculate the variable record size")); - } - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - } - } - /* Flush data if needed. */ - if (needsflush > 0) - { + /* Flush data if needed. */ + if(needsflush > 0){ #if !PIO_USE_MALLOC #ifdef PIO_ENABLE_LOGGING - /* Collect a debug report about buffer. */ - cn_buffer_report(ios, true); + /* Collect a debug report about buffer. */ + cn_buffer_report(ios, true); #endif /* PIO_ENABLE_LOGGING */ #endif /* !PIO_USE_MALLOC */ - /* Flush buffer to I/O processes - rearrange data and - * start writing data from the I/O processes - * Note : Setting the last flag in flush_buffer to - * true will force flush the buffer to disk for all - * iotypes (wait for write to complete for PnetCDF) - */ - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - if ((ierr = flush_buffer(ncid, wmb, (needsflush == 2)))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Flushing data (multiple cached variables with the same decomposition) from compute processes to I/O processes %s failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (needsflush == 2) ? "and to disk" : ""); - } - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - } - - /* One record size (sum across all procs) of data is buffered */ - file->varlist[varid].wb_pend += file->varlist[varid].vrsize; - file->wb_pend += file->varlist[varid].vrsize; - LOG((1, "Current pending bytes for ncid=%d, varid=%d var_wb_pend= %llu, file_wb_pend=%llu", - ncid, varid, - (unsigned long long int) file->varlist[varid].wb_pend, - (unsigned long long int) file->wb_pend - )); - /* Buffering data is considered an async event (to indicate - *that the event is not yet complete) - */ + /* Flush buffer to I/O processes - rearrange data and + * start writing data from the I/O processes + * Note : Setting the last flag in flush_buffer to + * true will force flush the buffer to disk for all + * iotypes (wait for write to complete for PnetCDF) + */ + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + if((ierr = flush_buffer(ncid, wmb, (needsflush == 2)))){ + GPTLstop("PIO:write_total"); + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Flushing data (multiple cached variables with the same decomposition) from compute processes to I/O processes %s failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (needsflush == 2) ? "and to disk" : ""); + } + spio_ltimer_start(ios->io_fstats->wr_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->wr_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + } + + /* One record size (sum across all procs) of data is buffered */ + file->varlist[varid].wb_pend += file->varlist[varid].vrsize; + file->wb_pend += file->varlist[varid].vrsize; + LOG((1, "Current pending bytes for ncid=%d, varid=%d var_wb_pend= %llu, file_wb_pend=%llu", + ncid, varid, + (unsigned long long int) file->varlist[varid].wb_pend, + (unsigned long long int) file->wb_pend + )); + /* Buffering data is considered an async event (to indicate + *that the event is not yet complete) + */ #ifdef PIO_MICRO_TIMING - mtimer_async_event_in_progress(file->varlist[varid].wr_mtimer, true); + mtimer_async_event_in_progress(file->varlist[varid].wr_mtimer, true); #endif - /* Get memory for data. */ - if (arraylen > 0) - { - if (!(wmb->data = bgetr(wmb->data, (1 + wmb->num_arrays) * arraylen * iodesc->mpitype_size))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (long long int )((1 + wmb->num_arrays) * arraylen * iodesc->mpitype_size)); - } - LOG((2, "got %ld bytes for data", (1 + wmb->num_arrays) * arraylen * iodesc->mpitype_size)); - } - - /* vid is an array of variable ids in the wmb list, grow the list - * and add the new entry. */ - if (!(wmb->vid = (int *) realloc(wmb->vid, sizeof(int) * (1 + wmb->num_arrays)))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) for array of variable ids in write multi buffer to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long)(sizeof(int) * (1 + wmb->num_arrays))); - } - - /* wmb->frame is the record number, we assume that the variables - * in the wmb list may not all have the same unlimited dimension - * value although they usually do. */ - if (vdesc->record >= 0) - if (!(wmb->frame = (int *) realloc(wmb->frame, sizeof(int) * (1 + wmb->num_arrays)))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) for array of frame numbers in write multi buffer to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long)(sizeof(int) * (1 + wmb->num_arrays))); - } - - /* If we need a fill value, get it. If we are using the subset - * rearranger and not using the netcdf fill mode then we need to - * do an extra write to fill in the holes with the fill value. */ - if (iodesc->needsfill) - { - /* Get memory to hold fill value. */ - if (!(wmb->fillvalue = bgetr(wmb->fillvalue, iodesc->mpitype_size * (1 + wmb->num_arrays)))) - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) for variable fillvalues in write multi buffer to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long)(iodesc->mpitype_size * (1 + wmb->num_arrays))); - } - - /* If the user passed a fill value, use that, otherwise use - * the default fill value of the netCDF type. Copy the fill - * value to the buffer. */ - if (fillvalue) - { - memcpy((char *)wmb->fillvalue + iodesc->mpitype_size * wmb->num_arrays, - fillvalue, iodesc->mpitype_size); - LOG((3, "copied user-provided fill value iodesc->mpitype_size = %d", - iodesc->mpitype_size)); - } - else - { - void *fill; - signed char byte_fill = PIO_FILL_BYTE; - char char_fill = PIO_FILL_CHAR; - short short_fill = PIO_FILL_SHORT; - int int_fill = PIO_FILL_INT; - float float_fill = PIO_FILL_FLOAT; - double double_fill = PIO_FILL_DOUBLE; + /* Get memory for data. */ + if(arraylen > 0){ + if(!(wmb->data = bgetr(wmb->data, (1 + wmb->num_arrays) * arraylen * iodesc->mpitype_size))){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (long long int )((1 + wmb->num_arrays) * arraylen * iodesc->mpitype_size)); + } + LOG((2, "got %ld bytes for data", (1 + wmb->num_arrays) * arraylen * iodesc->mpitype_size)); + } + + /* vid is an array of variable ids in the wmb list, grow the list + * and add the new entry. */ + if(!(wmb->vid = (int *) realloc(wmb->vid, sizeof(int) * (1 + wmb->num_arrays)))){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) for array of variable ids in write multi buffer to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long)(sizeof(int) * (1 + wmb->num_arrays))); + } + + /* wmb->frame is the record number, we assume that the variables + * in the wmb list may not all have the same unlimited dimension + * value although they usually do. */ + if(vdesc->record >= 0){ + if(!(wmb->frame = (int *) realloc(wmb->frame, sizeof(int) * (1 + wmb->num_arrays)))){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) for array of frame numbers in write multi buffer to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long)(sizeof(int) * (1 + wmb->num_arrays))); + } + } + + /* If we need a fill value, get it. If we are using the subset + * rearranger and not using the netcdf fill mode then we need to + * do an extra write to fill in the holes with the fill value. */ + if(iodesc->needsfill){ + /* Get memory to hold fill value. */ + if(!(wmb->fillvalue = bgetr(wmb->fillvalue, iodesc->mpitype_size * (1 + wmb->num_arrays)))){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory allocating space (realloc %lld bytes) for variable fillvalues in write multi buffer to cache user data", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long)(iodesc->mpitype_size * (1 + wmb->num_arrays))); + } + + /* If the user passed a fill value, use that, otherwise use + * the default fill value of the netCDF type. Copy the fill + * value to the buffer. */ + if(fillvalue){ + memcpy((char *)wmb->fillvalue + iodesc->mpitype_size * wmb->num_arrays, + fillvalue, iodesc->mpitype_size); + LOG((3, "copied user-provided fill value iodesc->mpitype_size = %d", iodesc->mpitype_size)); + } + else{ + void *fill; + signed char byte_fill = PIO_FILL_BYTE; + char char_fill = PIO_FILL_CHAR; + short short_fill = PIO_FILL_SHORT; + int int_fill = PIO_FILL_INT; + float float_fill = PIO_FILL_FLOAT; + double double_fill = PIO_FILL_DOUBLE; #ifdef _NETCDF4 - unsigned char ubyte_fill = PIO_FILL_UBYTE; - unsigned short ushort_fill = PIO_FILL_USHORT; - unsigned int uint_fill = PIO_FILL_UINT; - long long int64_fill = PIO_FILL_INT64; - long long uint64_fill = PIO_FILL_UINT64; + unsigned char ubyte_fill = PIO_FILL_UBYTE; + unsigned short ushort_fill = PIO_FILL_USHORT; + unsigned int uint_fill = PIO_FILL_UINT; + long long int64_fill = PIO_FILL_INT64; + long long uint64_fill = PIO_FILL_UINT64; #endif /* _NETCDF4 */ - vtype = (MPI_Datatype)iodesc->mpitype; - LOG((3, "caller did not provide fill value vtype = %d", vtype)); - - /* This must be done with an if statement, not a case, or - * openmpi will not build. */ - if (vtype == MPI_BYTE) - fill = &byte_fill; - else if (vtype == MPI_CHAR) - fill = &char_fill; - else if (vtype == MPI_SHORT) - fill = &short_fill; - else if (vtype == MPI_INT) - fill = &int_fill; - else if (vtype == MPI_FLOAT) - fill = &float_fill; - else if (vtype == MPI_DOUBLE) - fill = &double_fill; + vtype = (MPI_Datatype)iodesc->mpitype; + LOG((3, "caller did not provide fill value vtype = %d", vtype)); + + /* This must be done with an if statement, not a case, or + * openmpi will not build. */ + if(vtype == MPI_BYTE){ + fill = &byte_fill; + } + else if(vtype == MPI_CHAR){ + fill = &char_fill; + } + else if(vtype == MPI_SHORT){ + fill = &short_fill; + } + else if(vtype == MPI_INT){ + fill = &int_fill; + } + else if(vtype == MPI_FLOAT){ + fill = &float_fill; + } + else if(vtype == MPI_DOUBLE){ + fill = &double_fill; + } #ifdef _NETCDF4 - else if (vtype == MPI_UNSIGNED_CHAR) - fill = &ubyte_fill; - else if (vtype == MPI_UNSIGNED_SHORT) - fill = &ushort_fill; - else if (vtype == MPI_UNSIGNED) - fill = &uint_fill; - else if (vtype == MPI_LONG_LONG) - fill = &int64_fill; - else if (vtype == MPI_UNSIGNED_LONG_LONG) - fill = &uint64_fill; + else if(vtype == MPI_UNSIGNED_CHAR){ + fill = &ubyte_fill; + } + else if(vtype == MPI_UNSIGNED_SHORT){ + fill = &ushort_fill; + } + else if(vtype == MPI_UNSIGNED){ + fill = &uint_fill; + } + else if(vtype == MPI_LONG_LONG){ + fill = &int64_fill; + } + else if(vtype == MPI_UNSIGNED_LONG_LONG){ + fill = &uint64_fill; + } #endif /* _NETCDF4 */ - else - { - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Unable to find a default fillvalue for variable, unsupported variable type", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - memcpy((char *)wmb->fillvalue + iodesc->mpitype_size * wmb->num_arrays, - fill, iodesc->mpitype_size); - LOG((3, "copied fill value")); - } - } - - /* Tell the buffer about the data it is getting. */ - wmb->arraylen = arraylen; - wmb->vid[wmb->num_arrays] = varid; - LOG((3, "wmb->num_arrays = %d wmb->vid[wmb->num_arrays] = %d", wmb->num_arrays, - wmb->vid[wmb->num_arrays])); - - /* Copy the user-provided data to the buffer. */ - bufptr = (void *)((char *)wmb->data + arraylen * iodesc->mpitype_size * wmb->num_arrays); - if (arraylen > 0) - { - memcpy(bufptr, array, arraylen * iodesc->mpitype_size); - LOG((3, "copied %ld bytes of user data", arraylen * iodesc->mpitype_size)); - } - - /* Add the unlimited dimension value of this variable to the frame - * array in wmb. */ - if (wmb->frame) - wmb->frame[wmb->num_arrays] = vdesc->record; - wmb->num_arrays++; - - LOG((2, "wmb->num_arrays = %d iodesc->maxbytes / iodesc->mpitype_size = %d " - "iodesc->ndof = %d iodesc->llen = %d", wmb->num_arrays, - iodesc->maxbytes / iodesc->mpitype_size, iodesc->ndof, iodesc->llen)); + else{ + return pio_err(ios, file, PIO_EBADTYPE, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Unable to find a default fillvalue for variable, unsupported variable type", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } - LOG((1, "Write darray end : pending bytes for ncid=%d, varid=%d var_wb_pend=%llu file_wb_pend=%llu", - ncid, varid, - (unsigned long long int) file->varlist[varid].wb_pend, - (unsigned long long int) file->wb_pend - )); + memcpy((char *)wmb->fillvalue + iodesc->mpitype_size * wmb->num_arrays, + fill, iodesc->mpitype_size); + LOG((3, "copied fill value")); + } + } + + /* Tell the buffer about the data it is getting. */ + wmb->arraylen = arraylen; + wmb->vid[wmb->num_arrays] = varid; + LOG((3, "wmb->num_arrays = %d wmb->vid[wmb->num_arrays] = %d", wmb->num_arrays, + wmb->vid[wmb->num_arrays])); + + /* Copy the user-provided data to the buffer. */ + bufptr = (void *)((char *)wmb->data + arraylen * iodesc->mpitype_size * wmb->num_arrays); + if(arraylen > 0){ + memcpy(bufptr, array, arraylen * iodesc->mpitype_size); + LOG((3, "copied %ld bytes of user data", arraylen * iodesc->mpitype_size)); + } + + /* Add the unlimited dimension value of this variable to the frame + * array in wmb. */ + if(wmb->frame){ + wmb->frame[wmb->num_arrays] = vdesc->record; + } + wmb->num_arrays++; + + LOG((2, "wmb->num_arrays = %d iodesc->maxbytes / iodesc->mpitype_size = %d " + "iodesc->ndof = %d iodesc->llen = %d", wmb->num_arrays, + iodesc->maxbytes / iodesc->mpitype_size, iodesc->ndof, iodesc->llen)); + + LOG((1, "Write darray end : pending bytes for ncid=%d, varid=%d var_wb_pend=%llu file_wb_pend=%llu", + ncid, varid, + (unsigned long long int) file->varlist[varid].wb_pend, + (unsigned long long int) file->wb_pend + )); #ifdef PIO_MICRO_TIMING - mtimer_stop(file->varlist[varid].wr_mtimer, get_var_desc_str(ncid, varid, NULL)); + mtimer_stop(file->varlist[varid].wr_mtimer, get_var_desc_str(ncid, varid, NULL)); #endif - GPTLstop("PIO:PIOc_write_darray"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return PIO_NOERR; + return PIO_NOERR; } #ifdef _ADIOS2 @@ -3668,371 +3458,282 @@ static int PIOc_read_darray_adios(file_desc_t *file, int fndims, io_desc_t *iode * processor. * @return 0 for success, error code otherwise. * @ingroup PIO_read_darray - * @author Jim Edwards, Ed Hartnett */ int PIOc_read_darray_impl(int ncid, int varid, int ioid, PIO_Offset arraylen, void *array) { - iosystem_desc_t *ios; /* Pointer to io system information. */ - file_desc_t *file; /* Pointer to file information. */ - io_desc_t *iodesc; /* Pointer to IO description information. */ - var_desc_t *vdesc; /* Info about the var being read. */ - void *iobuf = NULL; /* holds the data as read on the io node. */ - size_t rlen = 0; /* the length of data in iobuf. */ - int ierr = PIO_NOERR, mpierr = MPI_SUCCESS; /* Return code. */ - int fndims = 0; + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + io_desc_t *iodesc; /* Pointer to IO description information. */ + var_desc_t *vdesc; /* Info about the var being read. */ + void *iobuf = NULL; /* holds the data as read on the io node. */ + size_t rlen = 0; /* the length of data in iobuf. */ + int ierr = PIO_NOERR, mpierr = MPI_SUCCESS; /* Return code. */ + int fndims = 0; #ifdef _ADIOS2 - adios_var_desc_t *vdesc_adios2; /* Info about the variable from the adios structure */ + adios_var_desc_t *vdesc_adios2; /* Info about the variable from the adios structure */ #endif - GPTLstart("PIO:PIOc_read_darray"); - /* Get the file info. */ - if ((ierr = pio_get_file(ncid, &file))) - { - GPTLstop("PIO:PIOc_read_darray"); - return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, - "Reading variable (varid=%d) failed. Invalid arguments provided, file id (ncid=%d) is invalid", varid, ncid); - } - assert(file); - spio_ltimer_start(file->io_fstats->rd_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - ios = file->iosystem; - assert(ios); - - spio_ltimer_start(ios->io_fstats->rd_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - LOG((1, "PIOc_read_darray (ncid=%d (%s), varid=%d (%s)", ncid, pio_get_fname_from_file(file), varid, pio_get_vname_from_file(file, varid))); - - /* Get the iodesc. */ - if (!(iodesc = pio_get_iodesc_from_id(ioid))) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d)failed. Invalid arguments provided, I/O descriptor id (ioid=%d) is invalid", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, ioid); - } - pioassert(iodesc->rearranger == PIO_REARR_BOX || iodesc->rearranger == PIO_REARR_SUBSET, - "unknown rearranger", __FILE__, __LINE__); - - /* Get var description. */ - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { + /* Get the file info. */ + if((ierr = pio_get_file(ncid, &file))){ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Reading variable (varid=%d) failed. Invalid arguments provided, file id (ncid=%d) is invalid", varid, ncid); + } + assert(file); + ios = file->iosystem; + assert(ios); + + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_rd_timer(ios->io_fstats->rd_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_tot_timer(ios->io_fstats->tot_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper file_fstats_rd_timer(file->io_fstats->rd_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper file_fstats_tot_timer(file->io_fstats->tot_timer_name); + + LOG((1, "PIOc_read_darray (ncid=%d (%s), varid=%d (%s)", ncid, pio_get_fname_from_file(file), varid, pio_get_vname_from_file(file, varid))); + + /* Get the iodesc. */ + if(!(iodesc = pio_get_iodesc_from_id(ioid))){ + return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d)failed. Invalid arguments provided, I/O descriptor id (ioid=%d) is invalid", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, ioid); + } + pioassert(iodesc->rearranger == PIO_REARR_BOX || iodesc->rearranger == PIO_REARR_SUBSET || iodesc->rearranger == PIO_REARR_CONTIG, + "unknown rearranger", __FILE__, __LINE__); + + /* Get var description. */ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ #ifdef _ADIOS2 - vdesc_adios2 = &(file->adios_vars[varid]); + vdesc_adios2 = &(file->adios_vars[varid]); #endif - } - else - vdesc = &(file->varlist[varid]); + } + else{ + vdesc = &(file->varlist[varid]); + } - /* Run these on all tasks if async is not in use, but only on - * non-IO tasks if async is in use. */ - if (!ios->async || !ios->ioproc) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); + /* Run these on all tasks if async is not in use, but only on + * non-IO tasks if async is in use. */ + if(!ios->async || !ios->ioproc){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); - /* Find out PIO data type of var. */ - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { + /* Find out PIO data type of var. */ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ #ifdef _ADIOS2 - assert(vdesc_adios2->nc_type != PIO_NAT); - vdesc_adios2->adios_type = spio_get_adios_type(vdesc_adios2->nc_type); - assert(vdesc_adios2->adios_type != adios2_type_unknown); + assert(vdesc_adios2->nc_type != PIO_NAT); + vdesc_adios2->adios_type = spio_get_adios_type(vdesc_adios2->nc_type); + assert(vdesc_adios2->adios_type != adios2_type_unknown); #endif + } + else{ + if(vdesc->pio_type == PIO_NAT){ + if((ierr = PIOc_inq_vartype_impl(ncid, varid, &vdesc->pio_type))){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed. Inquiring variable data type failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); } - else - { - if (vdesc->pio_type == PIO_NAT) - { - if ((ierr = PIOc_inq_vartype_impl(ncid, varid, &vdesc->pio_type))) - { - GPTLstop("PIO:PIOc_read_darray"); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed. Inquiring variable data type failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - assert(vdesc->pio_type != PIO_NAT); - } + } + assert(vdesc->pio_type != PIO_NAT); + } - /* Find out length of type. */ - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { + /* Find out length of type. */ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ #ifdef _ADIOS2 - if (vdesc_adios2->adios_type_size == 0) - vdesc_adios2->adios_type_size = get_adios2_type_size(vdesc_adios2->adios_type, NULL); - assert(vdesc_adios2->adios_type_size > 0); + if(vdesc_adios2->adios_type_size == 0){ + vdesc_adios2->adios_type_size = get_adios2_type_size(vdesc_adios2->adios_type, NULL); + } + assert(vdesc_adios2->adios_type_size > 0); #endif + } + else{ + if(vdesc->type_size == 0){ + if((ierr = PIOc_inq_type_impl(ncid, vdesc->pio_type, NULL, &vdesc->type_size))){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed. Inquiring variable data type length failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); } - else - { - if (vdesc->type_size == 0) - { - if ((ierr = PIOc_inq_type_impl(ncid, vdesc->pio_type, NULL, &vdesc->type_size))) - { - GPTLstop("PIO:PIOc_read_darray"); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed. Inquiring variable data type length failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - - assert(vdesc->type_size > 0); - } - - spio_ltimer_start(ios->io_fstats->rd_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->rd_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); + } + assert(vdesc->type_size > 0); } - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { + spio_ltimer_start(ios->io_fstats->rd_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->rd_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + } + + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ #ifdef _ADIOS2 - ios->io_fstats->rb += vdesc_adios2->adios_type_size * iodesc->llen; - file->io_fstats->rb += vdesc_adios2->adios_type_size * iodesc->llen; + ios->io_fstats->rb += vdesc_adios2->adios_type_size * iodesc->llen; + file->io_fstats->rb += vdesc_adios2->adios_type_size * iodesc->llen; #endif - } - else - { - ios->io_fstats->rb += vdesc->type_size * iodesc->llen; - file->io_fstats->rb += vdesc->type_size * iodesc->llen; - } + } + else{ + ios->io_fstats->rb += vdesc->type_size * iodesc->llen; + file->io_fstats->rb += vdesc->type_size * iodesc->llen; + } #ifdef PIO_MICRO_TIMING - mtimer_start(file->varlist[varid].rd_mtimer); + mtimer_start(file->varlist[varid].rd_mtimer); #endif - /* Run these on all tasks if async is not in use, but only on - * non-IO tasks if async is in use. */ - if (!ios->async || !ios->ioproc) - { - /* Get the number of dims for this var. */ - LOG((3, "about to call PIOc_inq_varndims varid = %d", varid)); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - ierr = PIOc_inq_varndims_impl(file->pio_ncid, varid, &fndims); - if(ierr != PIO_NOERR){ - GPTLstop("PIO:PIOc_read_darray"); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Inquiring number of variable dimensions failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - spio_ltimer_start(ios->io_fstats->rd_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->rd_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - LOG((3, "called PIOc_inq_varndims varid = %d fndims = %d", varid, fndims)); - } - /* For netcdf serial reads we read some data to the io master - * node then send that data to a corresponding io node. The - * buffer size on io task 0 must be as large as the largest - * used to accommodate this serial io method (use iodesc-> - * maxiobuflen to set it). */ - if ((file->iotype == PIO_IOTYPE_NETCDF || file->iotype == PIO_IOTYPE_NETCDF4C) && ios->iomaster == MPI_ROOT) - rlen = iodesc->maxiobuflen; - else - rlen = iodesc->llen; - - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - rlen = 0; - - if(!ios->async || !ios->ioproc) - { - if(file->varlist[varid].vrsize == 0) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - ierr = calc_var_rec_sz(ncid, varid); - if(ierr != PIO_NOERR) - { - LOG((1, "Unable to calculate the variable record size")); - } - spio_ltimer_start(ios->io_fstats->rd_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->rd_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - } + /* Run these on all tasks if async is not in use, but only on + * non-IO tasks if async is in use. */ + if(!ios->async || !ios->ioproc){ + /* Get the number of dims for this var. */ + LOG((3, "about to call PIOc_inq_varndims varid = %d", varid)); + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + ierr = PIOc_inq_varndims_impl(file->pio_ncid, varid, &fndims); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Inquiring number of variable dimensions failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + spio_ltimer_start(ios->io_fstats->rd_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->rd_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + LOG((3, "called PIOc_inq_varndims varid = %d fndims = %d", varid, fndims)); + } + + /* For netcdf serial reads we read some data to the io master + * node then send that data to a corresponding io node. The + * buffer size on io task 0 must be as large as the largest + * used to accommodate this serial io method (use iodesc-> + * maxiobuflen to set it). */ + if((file->iotype == PIO_IOTYPE_NETCDF || file->iotype == PIO_IOTYPE_NETCDF4C) && ios->iomaster == MPI_ROOT){ + rlen = iodesc->maxiobuflen; + } + else{ + rlen = iodesc->llen; + } + + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + rlen = 0; + } + + if(!ios->async || !ios->ioproc){ + if(file->varlist[varid].vrsize == 0){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + ierr = calc_var_rec_sz(ncid, varid); + if(ierr != PIO_NOERR){ + LOG((1, "Unable to calculate the variable record size")); + } + spio_ltimer_start(ios->io_fstats->rd_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->rd_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); } + } - file->varlist[varid].rb_pend += file->varlist[varid].vrsize; - file->rb_pend += file->varlist[varid].vrsize; + file->varlist[varid].rb_pend += file->varlist[varid].vrsize; + file->rb_pend += file->varlist[varid].vrsize; - /* Allocate a buffer for one record. */ - if (ios->ioproc && rlen > 0) - if (!(iobuf = bget(iodesc->mpitype_size * rlen))) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Out of memory allocating space (%lld bytes) in I/O processes to read data from file (before rearrangement)", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (long long int) (iodesc->mpitype_size * rlen)); - } + /* Allocate a buffer for one record. */ + if(ios->ioproc && rlen > 0){ + if(!(iobuf = bget(iodesc->mpitype_size * rlen))){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Out of memory allocating space (%lld bytes) in I/O processes to read data from file (before rearrangement)", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, (long long int) (iodesc->mpitype_size * rlen)); + } + } - if(ios->async) - { - /* Send relevant args from compute procs to I/O procs */ - int msg = PIO_MSG_READDARRAY; - - PIO_SEND_ASYNC_MSG(ios, msg, &ierr, ncid, varid, ioid); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Sending async message, PIO_MSG_READDARRAY, failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } + if(ios->async){ + /* Send relevant args from compute procs to I/O procs */ + int msg = PIO_MSG_READDARRAY; + + PIO_SEND_ASYNC_MSG(ios, msg, &ierr, ncid, varid, ioid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Sending async message, PIO_MSG_READDARRAY, failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } - /* Share results known only on computation tasks with IO tasks. */ - mpierr = MPI_Bcast(&fndims, 1, MPI_INT, ios->comproot, ios->my_comm); - if(mpierr != MPI_SUCCESS) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); - } - LOG((3, "shared fndims = %d", fndims)); + /* Share results known only on computation tasks with IO tasks. */ + mpierr = MPI_Bcast(&fndims, 1, MPI_INT, ios->comproot, ios->my_comm); + if(mpierr != MPI_SUCCESS){ + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); } + LOG((3, "shared fndims = %d", fndims)); + } #if PIO_SAVE_DECOMPS - if(!(iodesc->is_saved) && - pio_save_decomps_regex_match(ioid, file->fname, file->varlist[varid].vname)) - { - char filename[PIO_MAX_NAME]; - ierr = pio_create_uniq_str(ios, iodesc, filename, PIO_MAX_NAME, "piodecomp", ".dat"); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Saving the I/O decomposition (ioid=%d) failed, unable to create a unique file name for saving the decomposition", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, ioid); - } - LOG((2, "Saving decomp map (read) to %s", filename)); - PIOc_writemap_impl(filename, ioid, iodesc->ndims, iodesc->dimlen, iodesc->maplen, iodesc->map, ios->my_comm); - iodesc->is_saved = true; - } + if(!(iodesc->is_saved) && + pio_save_decomps_regex_match(ioid, file->fname, file->varlist[varid].vname)){ + std::string filename; + pio_create_uniq_str(ios, iodesc, filename, "piodecomp", ".dat"); + + LOG((2, "Saving decomp map (read) to %s", filename.c_str())); + PIOc_writemap_impl(filename.c_str(), ioid, iodesc->ndims, iodesc->dimlen, iodesc->maplen, iodesc->map, ios->my_comm); + iodesc->is_saved = true; + } #endif - /* Call the correct darray read function based on iotype. */ - if(!ios->async || ios->ioproc) - { - switch (file->iotype) - { + + /* Call the correct darray read function based on iotype. */ + if(!ios->async || ios->ioproc){ + switch (file->iotype){ case PIO_IOTYPE_NETCDF: case PIO_IOTYPE_NETCDF4C: - if ((ierr = pio_read_darray_nc_serial(file, fndims, iodesc, varid, iobuf))) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Reading variable in serial (iotype=%s) failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, pio_iotype_to_string(file->iotype)); + if((ierr = pio_read_darray_nc_serial(file, fndims, iodesc, varid, iobuf))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Reading variable in serial (iotype=%s) failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, pio_iotype_to_string(file->iotype)); } break; case PIO_IOTYPE_PNETCDF: case PIO_IOTYPE_NETCDF4P: case PIO_IOTYPE_NETCDF4P_NCZARR: - if ((ierr = pio_read_darray_nc(file, fndims, iodesc, varid, iobuf))) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Reading variable in parallel (iotype=%s) failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, pio_iotype_to_string(file->iotype)); + if((ierr = pio_read_darray_nc(file, fndims, iodesc, varid, iobuf))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Reading variable in parallel (iotype=%s) failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, pio_iotype_to_string(file->iotype)); } break; -#ifdef _ADIOS2 + #ifdef _ADIOS2 case PIO_IOTYPE_ADIOS: case PIO_IOTYPE_ADIOSC: - GPTLstart("PIO:PIOc_read_darray_adios"); - if ((ierr = PIOc_read_darray_adios(file, fndims, iodesc, varid, array))) - { - GPTLstop("PIO:PIOc_read_darray_adios"); - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) using ADIOS iotype failed. " - "Reading variable in parallel failed", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), ncid); + if((ierr = PIOc_read_darray_adios(file, fndims, iodesc, varid, array))){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) using ADIOS iotype failed. " + "Reading variable in parallel failed", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), ncid); } - - GPTLstop("PIO:PIOc_read_darray_adios"); - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return PIO_NOERR; -#endif - default: - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); + #endif + default: return pio_err(NULL, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Invalid iotype (%d) provided", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, file->iotype); - } } + } #ifdef PIO_MICRO_TIMING - mtimer_start(file->varlist[varid].rd_rearr_mtimer); + mtimer_start(file->varlist[varid].rd_rearr_mtimer); #endif - /* Rearrange the data. */ - if ((ierr = rearrange_io2comp(ios, iodesc, iobuf, array))) - { - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Rearranging data read in the I/O processes to compute processes failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } + /* Rearrange the data. */ + if(iodesc->rearranger == PIO_REARR_CONTIG){ + ierr = iodesc->rearr->rearrange_io2comp(iobuf, rlen * iodesc->mpitype_size, array, iodesc->ndof * iodesc->mpitype_size, 1); + } + else{ + ierr = rearrange_io2comp(ios, iodesc, iobuf, array); + } + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed . Rearranging data read in the I/O processes to compute processes failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } #ifdef PIO_MICRO_TIMING - mtimer_stop(file->varlist[varid].rd_rearr_mtimer, get_var_desc_str(ncid, varid, NULL)); + mtimer_stop(file->varlist[varid].rd_rearr_mtimer, get_var_desc_str(ncid, varid, NULL)); #endif - /* We don't use non-blocking reads */ - file->varlist[varid].rb_pend = 0; - file->rb_pend = 0; + /* We don't use non-blocking reads */ + file->varlist[varid].rb_pend = 0; + file->rb_pend = 0; - /* Free the buffer. */ - if (rlen > 0) - brel(iobuf); + /* Free the buffer. */ + if(rlen > 0){ + brel(iobuf); + } #ifdef PIO_MICRO_TIMING - mtimer_stop(file->varlist[varid].rd_mtimer, get_var_desc_str(ncid, varid, NULL)); + mtimer_stop(file->varlist[varid].rd_mtimer, get_var_desc_str(ncid, varid, NULL)); #endif - GPTLstop("PIO:PIOc_read_darray"); - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return PIO_NOERR; + return PIO_NOERR; } diff --git a/src/clib/pio_darray_int.cpp b/src/clib/core/pio_darray_int.cpp similarity index 96% rename from src/clib/pio_darray_int.cpp rename to src/clib/core/pio_darray_int.cpp index fd57f4be689..4649566c5a1 100644 --- a/src/clib/pio_darray_int.cpp +++ b/src/clib/core/pio_darray_int.cpp @@ -6,19 +6,21 @@ * array. Only by combining the distributed arrays from all processor * can the full array be obtained. * - * @author Jim Edwards */ #include #include #include #include -#include +//#include #ifdef PIO_MICRO_TIMING #include "pio_timer.h" #endif #include "spio_io_summary.h" #include "spio_file_mvcache.h" +#include "spio_dbg_utils.hpp" +#include "spio_dt_converter.hpp" +#include "spio_hdf5_utils.hpp" /* 10MB default limit. */ extern PIO_Offset pio_buffer_size_limit; @@ -52,7 +54,6 @@ void *bpool_alloc(bufsize sz) * @param ios pointer to the iosystem descriptor which will use the * new buffer. * @returns 0 for success, error code otherwise. - * @author Jim Edwards */ int compute_buffer_init(iosystem_desc_t *ios) { @@ -86,9 +87,8 @@ int compute_buffer_init(iosystem_desc_t *ios) * values. * @return 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Ed Hartnett */ -int find_start_count(int ndims, const int *dimlen, int fndims, var_desc_t *vdesc, +int spio_find_start_count(int ndims, const int *dimlen, int fndims, var_desc_t *vdesc, io_region *region, size_t *start, size_t *count) { assert((ndims > 0) && dimlen && (fndims > 0) && vdesc && @@ -164,7 +164,6 @@ int find_start_count(int ndims, const int *dimlen, int fndims, var_desc_t *vdesc * in iobuf. NULL if this iodesc contains non-record vars. * @return 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Jim Edwards, Ed Hartnett */ int write_darray_multi_par(file_desc_t *file, int nvars, int fndims, const int *varids, io_desc_t *iodesc, int fill, const int *frame) @@ -234,7 +233,7 @@ int write_darray_multi_par(file_desc_t *file, int nvars, int fndims, const int * for (int regioncnt = 0; regioncnt < num_regions; regioncnt++) { /* Fill the start/count arrays. */ - if ((ierr = find_start_count(iodesc->ndims, iodesc->dimlen, fndims, vdesc, region, start, count))) + if ((ierr = spio_find_start_count(iodesc->ndims, iodesc->dimlen, fndims, vdesc, region, start, count))) { ierr = pio_err(ios, file, ierr, __FILE__, __LINE__, "Writing variables (number of variables = %d) to file (%s, ncid=%d) failed. Internal error, finding start/count for the I/O regions written out from the I/O process failed", nvars, pio_get_fname_from_file(file), file->pio_ncid); @@ -412,6 +411,7 @@ int write_darray_multi_par(file_desc_t *file, int nvars, int fndims, const int * bufptr, llen, iodesc->mpitype, vdesc->request + vdesc->nreqs); if (ierr != PIO_NOERR) { + LOG((3, "ERROR: ncmpi_iput_varn() FAILED: iodesc = %s", SPIO_Util::Dbg_Util::get_iodesc_info(iodesc).c_str())); ierr = pio_err(ios, file, ierr, __FILE__, __LINE__, "Writing variables (number of variables = %d) to file (%s, ncid=%d) using PIO_IOTYPE_PNETCDF iotype failed. Non blocking write for variable (%s, varid=%d) failed (Number of subarray requests/regions=%d, Size of data local to this process = %lld)", nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, varids[nv]), varids[nv], rrcnt, (long long int )llen); break; @@ -576,8 +576,38 @@ int write_darray_multi_par(file_desc_t *file, int nvars, int fndims, const int * nvars, pio_get_fname_from_file(file), file->pio_ncid, iodesc->piotype, pio_get_vname_from_file(file, varids[nv]), varids[nv]); } - if (H5Dwrite(file->hdf5_vars[varids[nv]].hdf5_dataset_id, mem_type_id, mem_space_id, file_space_id, - file->dxplid_coll, bufptr) < 0) + hid_t file_var_type_id = H5Dget_type(file->hdf5_vars[varids[nv]].hdf5_dataset_id); + if(file_var_type_id == H5I_INVALID_HID){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unable to query the type of variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, pio_get_vname_from_file(file, varids[nv]), varids[nv]); + } + + hid_t file_var_ntype_id = H5Tget_native_type(file_var_type_id, H5T_DIR_DEFAULT); + assert(file_var_ntype_id != H5I_INVALID_HID); + + /* When HDF5 filters (e.g. data compression) are enabled collective writes fail when datatype conversion is required for writing user data. + * So we manually perform the data conversion here before passing it to HDF5. When filters are not enabled the write might succeed but HDF5 + * might be switching off collective writes (hurts performance) when datatype conversion is required + * FIXME: Disable datatype conversion when filters are not enabled on the dataset + */ + void *wbuf = bufptr; + if((dsize_all > 0) && !H5Tequal(mem_type_id, file_var_ntype_id)){ + assert(file->dt_converter); + wbuf = static_cast(file->dt_converter)->convert(iodesc->ioid, bufptr, iodesc->mpitype_size * dsize_all, + iodesc->piotype, spio_hdf5_type_to_pio_type(file_var_ntype_id)); + if(wbuf == NULL){ + return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, + "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Unable to convert the type (from %d to %d) of variable (%s, varid=%d)", + nvars, pio_get_fname_from_file(file), file->pio_ncid, iodesc->piotype, + spio_hdf5_type_to_pio_type(file_var_ntype_id), pio_get_vname_from_file(file, varids[nv]), varids[nv]); + } + } + + if (H5Dwrite(file->hdf5_vars[varids[nv]].hdf5_dataset_id, file_var_ntype_id, mem_space_id, file_space_id, + file->dxplid_coll, wbuf) < 0) { return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, "Writing variables (number of variables = %d) to file (%s, ncid=%d) using HDF5 iotype failed. " @@ -674,7 +704,6 @@ int write_darray_multi_par(file_desc_t *file, int nvars, int fndims, const int * * regions. * @returns 0 for success, error code otherwise. * @ingroup PIO_read_darray - * @author Jim Edwards, Ed Hartnett **/ int find_all_start_count(io_region *region, int maxregions, int fndims, int iodesc_ndims, const int *dimlen, var_desc_t *vdesc, @@ -751,7 +780,6 @@ int find_all_start_count(io_region *region, int maxregions, int fndims, * * @return 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Jim Edwards, Ed Hartnett */ int send_all_start_count(iosystem_desc_t *ios, io_desc_t *iodesc, PIO_Offset llen, int maxregions, int nvars, int fndims, size_t *tmp_start, @@ -830,7 +858,6 @@ int send_all_start_count(iosystem_desc_t *ios, io_desc_t *iodesc, PIO_Offset lle * iobuf. * @return 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Jim Edwards, Ed Hartnett */ int recv_and_write_data(file_desc_t *file, const int *varids, const int *frame, io_desc_t *iodesc, PIO_Offset llen, int maxregions, int nvars, @@ -1034,7 +1061,6 @@ int recv_and_write_data(file_desc_t *file, const int *varids, const int *frame, * in iobuf. NULL if this iodesc contains non-record vars. * @return 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Jim Edwards, Ed Hartnett */ int write_darray_multi_serial(file_desc_t *file, int nvars, int fndims, const int *varids, io_desc_t *iodesc, int fill, const int *frame) @@ -1144,7 +1170,6 @@ int write_darray_multi_serial(file_desc_t *file, int nvars, int fndims, const in * iobuf. * @return 0 on success, error code otherwise. * @ingroup PIO_read_darray - * @author Jim Edwards, Ed Hartnett */ int pio_read_darray_nc(file_desc_t *file, int fndims, io_desc_t *iodesc, int vid, void *iobuf) { @@ -1400,7 +1425,6 @@ int pio_read_darray_nc(file_desc_t *file, int fndims, io_desc_t *iodesc, int vid * iobuf. * @returns 0 for success, error code otherwise. * @ingroup PIO_read_darray - * @author Jim Edwards, Ed Hartnett */ int pio_read_darray_nc_serial(file_desc_t *file, int fndims, io_desc_t *iodesc, int vid, void *iobuf) @@ -1787,7 +1811,6 @@ int set_file_req_block_size_limit(file_desc_t *file, PIO_Offset sz) * @param nreq_blocks Pointer to integer that will contain the * number of block ranges * - * @author Jayesh Krishna */ int get_file_req_blocks(file_desc_t *file, @@ -2034,7 +2057,6 @@ int get_file_req_blocks(file_desc_t *file, * @param addsize additional size to add to buffer (in bytes) * @return 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Jim Edwards, Jayesh Krishna, Ed Hartnett */ int flush_output_buffer(file_desc_t *file, bool force, PIO_Offset addsize) { @@ -2290,6 +2312,12 @@ int flush_output_buffer(file_desc_t *file, bool force, PIO_Offset addsize) /* Release resources. */ spio_file_mvcache_clear(file); + +#ifdef _HDF5 + assert(file->dt_converter); + static_cast(file->dt_converter)->clear(); +#endif + for (int i = 0; i < PIO_MAX_VARS; i++) { vdesc = file->varlist + i; @@ -2326,7 +2354,6 @@ int flush_output_buffer(file_desc_t *file, bool force, PIO_Offset addsize) * @param ios pointer to the IO system structure * @param collective true if collective report is desired * @ingroup PIO_write_darray - * @author Jim Edwards */ void cn_buffer_report(iosystem_desc_t *ios, bool collective) { @@ -2334,11 +2361,18 @@ void cn_buffer_report(iosystem_desc_t *ios, bool collective) LOG((2, "cn_buffer_report ios->iossysid = %d collective = %d", ios->iosysid, collective)); - long bget_stats[5]; - long bget_mins[5]; - long bget_maxs[5]; + bufsize curalloc = 0, totfree = 0, maxfree = 0; + long nget = 0, nrel = 0; + + bstats(&curalloc, &totfree, &maxfree, &nget, &nrel); + + long bget_stats[5] = {static_cast(curalloc), + static_cast(totfree), + static_cast(maxfree), + nget, nrel}; + long bget_mins[5] = {0}; + long bget_maxs[5] = {0}; - bstats(bget_stats, bget_stats+1,bget_stats+2,bget_stats+3,bget_stats+4); if (collective) { LOG((3, "cn_buffer_report calling MPI_Reduce ios->comp_comm = %d", ios->comp_comm)); @@ -2374,7 +2408,6 @@ void cn_buffer_report(iosystem_desc_t *ios, bool collective) * @param flushtodisk if true, then flush data to disk. * @returns 0 for success, error code otherwise. * @ingroup PIO_write_darray - * @author Jim Edwards, Ed Hartnett */ int flush_buffer(int ncid, wmulti_buffer *wmb, bool flushtodisk) { @@ -2484,7 +2517,6 @@ int flush_buffer(int ncid, wmulti_buffer *wmb, bool flushtodisk) * @param ios pointer to the IO system structure. * @param iodesc a pointer to decomposition description. * @returns 0 for success, error code otherwise. - * @author Jim Edwards */ int compute_maxaggregate_bytes(iosystem_desc_t *ios, io_desc_t *iodesc) { @@ -2508,7 +2540,7 @@ int compute_maxaggregate_bytes(iosystem_desc_t *ios, io_desc_t *iodesc) maxbytesoncomputetask = pio_cnbuffer_limit / iodesc->ndof; /* Take the min of the max IO and max comp bytes. */ - maxbytes = min(maxbytesoniotask, maxbytesoncomputetask); + maxbytes = std::min(maxbytesoniotask, maxbytesoncomputetask); LOG((2, "compute_maxaggregate_bytes maxbytesoniotask = %d maxbytesoncomputetask = %d", maxbytesoniotask, maxbytesoncomputetask)); diff --git a/src/clib/pio_file.cpp b/src/clib/core/pio_file.cpp similarity index 73% rename from src/clib/pio_file.cpp rename to src/clib/core/pio_file.cpp index f71f42d60cf..8e9cb48e895 100644 --- a/src/clib/pio_file.cpp +++ b/src/clib/core/pio_file.cpp @@ -8,11 +8,19 @@ #include "spio_io_summary.h" #include #include +#include +#include +#include +#include +#include "spio_hdf5_utils.hpp" +#include "spio_async_tcomm.hpp" #ifdef _ADIOS2 #include "../../tools/adios2pio-nm/adios2pio-nm-lib-c.h" #endif +#include "spio_async_utils.hpp" + /** * Open an existing file using PIO library. * @@ -29,7 +37,6 @@ * @param mode : The netcdf mode for the open operation * @return 0 for success, error code otherwise. * @ingroup PIO_openfile - * @author Jim Edwards, Ed Hartnett */ int PIOc_openfile_impl(int iosysid, int *ncidp, int *iotype, const char *filename, int mode) @@ -54,7 +61,6 @@ int PIOc_openfile_impl(int iosysid, int *ncidp, int *iotype, const char *filenam * @param mode : The netcdf mode for the open operation * @return 0 for success, error code otherwise. * @ingroup PIO_openfile - * @author Ed Hartnett */ int PIOc_openfile2_impl(int iosysid, int *ncidp, int *iotype, const char *filename, int mode) @@ -73,7 +79,6 @@ int PIOc_openfile2_impl(int iosysid, int *ncidp, int *iotype, const char *filena * @param ncidp pointer to int where ncid will go * @return 0 for success, error code otherwise. * @ingroup PIO_openfile - * @author Ed Hartnett */ int PIOc_open_impl(int iosysid, const char *path, int mode, int *ncidp) { @@ -128,7 +133,6 @@ int PIOc_open_impl(int iosysid, const char *path, int mode, int *ncidp) * @param mode The netcdf mode for the create operation. * @returns 0 for success, error code otherwise. * @ingroup PIO_createfile - * @author Jim Edwards, Ed Hartnett */ int PIOc_createfile_impl(int iosysid, int *ncidp, const int *iotype, const char *filename, int mode) @@ -136,22 +140,18 @@ int PIOc_createfile_impl(int iosysid, int *ncidp, const int *iotype, const char iosystem_desc_t *ios; /* Pointer to io system information. */ int ret; /* Return code from function calls. */ - GPTLstart("PIO:PIOc_createfile"); GPTLstart("PIO:write_total"); if ((*iotype == PIO_IOTYPE_ADIOS) || (*iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstart("PIO:PIOc_createfile_adios"); GPTLstart("PIO:write_total_adios"); } /* Get the IO system info from the id. */ if (!(ios = pio_get_iosystem_from_id(iosysid))) { - GPTLstop("PIO:PIOc_createfile"); GPTLstop("PIO:write_total"); if ((*iotype == PIO_IOTYPE_ADIOS) || (*iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:PIOc_createfile_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, @@ -164,13 +164,11 @@ int PIOc_createfile_impl(int iosysid, int *ncidp, const int *iotype, const char /* Create the file. */ if ((ret = spio_createfile_int(iosysid, ncidp, iotype, filename, mode))) { - GPTLstop("PIO:PIOc_createfile"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); if ((*iotype == PIO_IOTYPE_ADIOS) || (*iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:PIOc_createfile_adios"); GPTLstop("PIO:write_total_adios"); } @@ -189,11 +187,9 @@ int PIOc_createfile_impl(int iosysid, int *ncidp, const int *iotype, const char spio_ltimer_stop(ios->io_fstats->tot_timer_name); if ((ret = PIOc_set_fill_impl(*ncidp, NC_NOFILL, NULL))) { - GPTLstop("PIO:PIOc_createfile"); GPTLstop("PIO:write_total"); if ((*iotype == PIO_IOTYPE_ADIOS) || (*iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:PIOc_createfile_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(ios, NULL, ret, __FILE__, __LINE__, @@ -203,13 +199,11 @@ int PIOc_createfile_impl(int iosysid, int *ncidp, const int *iotype, const char spio_ltimer_start(ios->io_fstats->tot_timer_name); } - GPTLstop("PIO:PIOc_createfile"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); if ((*iotype == PIO_IOTYPE_ADIOS) || (*iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:PIOc_createfile_adios"); GPTLstop("PIO:write_total_adios"); } @@ -227,7 +221,6 @@ int PIOc_createfile_impl(int iosysid, int *ncidp, const int *iotype, const char * @param ncidp : A pio file descriptor (output) * @return 0 for success, error code otherwise. * @ingroup PIO_create - * @author Ed Hartnett */ int PIOc_create_impl(int iosysid, const char *filename, int cmode, int *ncidp) { @@ -445,136 +438,72 @@ static int sync_file(int ncid) return PIO_NOERR; } -/** - * Close a file previously opened with PIO. - * - * @param ncid: the file pointer - * @returns PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett +int spio_wait_on_hard_close(iosystem_desc_t *ios, file_desc_t *file) +{ + /* FIXME: Make the max time configurable */ + const int MAX_SLEEP_TIME_IN_MILLISECONDS = SPIO_ASYNC_OP_WAIT_TIMEOUT; + const int SLEEP_TIME_IN_MILLISECONDS = 500; + + assert(ios && file); + /* For read-only files, we don't have any async ops. This check is useful for + * applications that do not close - due to a bug in the application - read-only + * files + */ + if(!(file->mode & PIO_WRITE)) { return PIO_NOERR; } + + /* For files that will never be closed, due to user error for ex, we cannot wait + * indefenitely. On the other hand we do want to have enough time to finish async ops + */ + const int max_tries = (MAX_SLEEP_TIME_IN_MILLISECONDS > SLEEP_TIME_IN_MILLISECONDS) ? + (MAX_SLEEP_TIME_IN_MILLISECONDS/SLEEP_TIME_IN_MILLISECONDS) : 1; + int i = 0; + while(!file->is_hard_closed && (i++ < max_tries)){ + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_IN_MILLISECONDS)); + } + + if(!file->is_hard_closed && (i > max_tries)){ + std::string msg = std::string("Waiting on a close on file aborted due to exceeding max timelimit") + + std::string(" (") + + std::string("ncid = ") + std::to_string(file->pio_ncid) + + std::string(", file = ") + std::string(file->fname) + + std::string(", max timeout limit(milli secs) = ") + std::to_string(MAX_SLEEP_TIME_IN_MILLISECONDS) + + std::string(")"); + PIOc_warn(ios->iosysid, file->pio_ncid, __FILE__, __LINE__, msg.c_str()); + } + + /* Sync compute procs with I/O procs */ + MPI_Barrier(ios->tcomm_info->get_union_comm()); + + return PIO_NOERR; +} + +/* Close the file ("hard close") + * @param ios: Pointer to the iosystem_desc + * @param file: Pointer to the file_desc for the file + * @returns PIO_NOERR for success, a pio error code otherwise */ -int PIOc_closefile_impl(int ncid) +int spio_hard_closefile(iosystem_desc_t *ios, file_desc_t *file, + bool sync_with_ioprocs) { - iosystem_desc_t *ios; /* Pointer to io system information. */ - file_desc_t *file = NULL; /* Pointer to file information. */ - int ierr = PIO_NOERR; /* Return code from function calls. */ + int ierr = PIO_NOERR; #ifdef _ADIOS2 char outfilename[PIO_MAX_NAME + 1]; size_t len = 0; #endif - LOG((1, "PIOc_closefile ncid = %d", ncid)); + assert(ios && file); - /* Find the info about this file. */ - if ((ierr = pio_get_file(ncid, &file))) - { - return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, - "Closing file failed. Invalid file id (ncid=%d) provided", ncid); - } - assert(file); - ios = file->iosystem; - assert(ios); + /* Get the lock before proceeding */ + std::lock_guard lg(*(file->pmtx)); - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstart("PIO:PIOc_closefile_adios"); + int ncid = file->pio_ncid; - if (file->mode & PIO_WRITE) - { - GPTLstart("PIO:write_total_adios"); -#ifndef _ADIOS_BP2NC_TEST - GPTLstart("PIO:write_total"); -#endif - } + if(sync_with_ioprocs){ + //if(ios->ioproc) { while(file->npend_ops){} } + MPI_Barrier(ios->union_comm); } - else - { - GPTLstart("PIO:PIOc_closefile"); - - if (file->mode & PIO_WRITE) - { - GPTLstart("PIO:PIOc_closefile_write_mode"); - GPTLstart("PIO:write_total"); - } - } - - /* Sync changes before closing on all tasks if async is not in - * use, but only on non-IO tasks if async is in use. */ - if (!ios->async || !ios->ioproc) - if (file->mode & PIO_WRITE) - { - sync_file(ncid); - } - - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { -#ifndef _ADIOS_BP2NC_TEST - if (file->mode & PIO_WRITE) - { - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->wr_timer_name); - } - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); -#endif - } - else - { - if (file->mode & PIO_WRITE) - { - spio_ltimer_start(ios->io_fstats->wr_timer_name); - spio_ltimer_start(file->io_fstats->wr_timer_name); - } - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - } - - /* If async is in use and this is a comp tasks, then the compmaster - * sends a msg to the pio_msg_handler running on the IO master and - * waiting for a message. Then broadcast the ncid over the intercomm - * to the IO tasks. */ - if (ios->async) - { - int msg = PIO_MSG_CLOSE_FILE; - - PIO_SEND_ASYNC_MSG(ios, msg, &ierr, ncid); - if(ierr != PIO_NOERR) - { - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_closefile_adios"); - - if (file->mode & PIO_WRITE) - GPTLstop("PIO:write_total_adios"); - -#ifndef _ADIOS_BP2NC_TEST - if (file->mode & PIO_WRITE) - { - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); -#endif - } - else - { - GPTLstop("PIO:PIOc_closefile"); - if (file->mode & PIO_WRITE) - { - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - } - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Closing file (%s, ncid=%d) failed. Error sending async msg PIO_MSG_CLOSE_FILE", pio_get_fname_from_file(file), ncid); - } - } + if(file->is_hard_closed) { return PIO_NOERR; } /* ADIOS: assume all procs are also IO tasks */ #ifdef _ADIOS2 @@ -591,28 +520,6 @@ int PIOc_closefile_impl(int ncid) ierr = begin_adios2_step(file, NULL); if (ierr != PIO_NOERR) { - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_closefile_adios"); - GPTLstop("PIO:write_total_adios"); -#ifndef _ADIOS_BP2NC_TEST - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); -#endif - } - else - { - GPTLstop("PIO:PIOc_closefile"); - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - } return pio_err(NULL, file, ierr, __FILE__, __LINE__, "adios2_begin_step failed for file (%s)", pio_get_fname_from_file(file)); } @@ -625,29 +532,6 @@ int PIOc_closefile_impl(int ncid) attributeH = adios2_define_attribute(file->ioH, "/__pio__/fillmode", adios2_type_int32_t, &file->fillmode); if (attributeH == NULL) { - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_closefile_adios"); - GPTLstop("PIO:write_total_adios"); -#ifndef _ADIOS_BP2NC_TEST - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); -#endif - } - else - { - GPTLstop("PIO:PIOc_closefile"); - - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - } return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) attribute (name=/__pio__/fillmode) failed for file (%s, ncid=%d)", pio_get_fname_from_file(file), file->pio_ncid); @@ -686,28 +570,6 @@ int PIOc_closefile_impl(int ncid) GPTLstop("end_adios2_step_PIOc_closefile"); if (ierr != PIO_NOERR) { - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_closefile_adios"); - GPTLstop("PIO:write_total_adios"); -#ifndef _ADIOS_BP2NC_TEST - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); -#endif - } - else - { - GPTLstop("PIO:PIOc_closefile"); - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - } return pio_err(NULL, file, ierr, __FILE__, __LINE__, "adios2_end_step failed for file (%s)", pio_get_fname_from_file(file)); } @@ -715,29 +577,6 @@ int PIOc_closefile_impl(int ncid) adiosErr = adios2_close(file->engineH); if (adiosErr != adios2_error_none) { - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_closefile_adios"); - GPTLstop("PIO:write_total_adios"); - -#ifndef _ADIOS_BP2NC_TEST - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); -#endif - } - else - { - GPTLstop("PIO:PIOc_closefile"); - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - } return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Closing (ADIOS) file (%s, ncid=%d) failed (adios2_error=%s)", pio_get_fname_from_file(file), file->pio_ncid, convert_adios2_error_to_string(adiosErr)); } @@ -750,9 +589,6 @@ int PIOc_closefile_impl(int ncid) adiosErr = adios2_close(file->engineH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_closefile_adios"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Closing file (%s, ncid=%d) using ADIOS iotype failed. " "The low level (ADIOS) I/O library call failed to close all transports in adios2_Engine (adios2_error=%s)", @@ -767,9 +603,6 @@ int PIOc_closefile_impl(int ncid) adiosErr = adios2_remove_io(&result, ios->adios_readerH, file->io_name_reader); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_closefile_adios"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Closing file (%s, ncid=%d) using ADIOS iotype failed. " "The low level (ADIOS) I/O library call failed to remove an io created with adios2_declare_io (adios2_error=%s)", @@ -778,9 +611,6 @@ int PIOc_closefile_impl(int ncid) if (result == adios2_false) { - GPTLstop("PIO:PIOc_closefile_adios"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Closing file (%s, ncid=%d) using ADIOS iotype failed. " "adios2_remove_io: io not found and not removed", @@ -917,25 +747,6 @@ int PIOc_closefile_impl(int ncid) LOG((1, "DONE CONVERTING: %s", file->filename)); if (ierr != PIO_NOERR) { - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_closefile_adios"); - GPTLstop("PIO:write_total_adios"); - } - else - { - GPTLstop("PIO:PIOc_closefile"); - - if (file->mode & PIO_WRITE) - { - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - } return pio_err(ios, file, ierr, __FILE__, __LINE__, "C_API_ConvertBPToNC(infile = %s, outfile = %s, piotype = %s) failed", file->filename, outfilename, conv_iotype); } @@ -947,44 +758,6 @@ int PIOc_closefile_impl(int ncid) file->filename = NULL; } - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - GPTLstop("PIO:PIOc_closefile_adios"); - - if (file->mode & PIO_WRITE) - GPTLstop("PIO:write_total_adios"); - -#ifndef _ADIOS_BP2NC_TEST - if (file->mode & PIO_WRITE) - { - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); -#endif - } - else - { - GPTLstop("PIO:PIOc_closefile"); - - if (file->mode & PIO_WRITE) - { - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - } - - spio_write_file_io_summary(file); - - /* Delete file from our list of open files. */ - pio_delete_file_from_list(ncid); - return PIO_NOERR; } #endif @@ -1024,16 +797,6 @@ int PIOc_closefile_impl(int ncid) break; #endif default: - GPTLstop("PIO:PIOc_closefile"); - if (file->mode & PIO_WRITE) - { - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); return pio_err(ios, file, PIO_EBADIOTYPE, __FILE__, __LINE__, "Closing file (%s, ncid=%d) failed. Unsupported iotype (%d) specified", pio_get_fname_from_file(file), file->pio_ncid, file->iotype); } @@ -1042,16 +805,6 @@ int PIOc_closefile_impl(int ncid) ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); if(ierr != PIO_NOERR){ LOG((1, "nc*_close failed, ierr = %d", ierr)); - GPTLstop("PIO:PIOc_closefile"); - if (file->mode & PIO_WRITE) - { - GPTLstop("PIO:PIOc_closefile_write_mode"); - GPTLstop("PIO:write_total"); - spio_ltimer_stop(ios->io_fstats->wr_timer_name); - spio_ltimer_stop(file->io_fstats->wr_timer_name); - } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); return pio_err(NULL, file, ierr, __FILE__, __LINE__, "Closing file (%s, ncid=%d) failed. Underlying I/O library (iotype=%s) call failed", pio_get_fname_from_file(file), file->pio_ncid, pio_iotype_to_string(file->iotype)); } @@ -1095,22 +848,195 @@ int PIOc_closefile_impl(int ncid) } #endif - if (file->mode & PIO_WRITE) - { + file->is_hard_closed = true; + + return PIO_NOERR; +} + +/* "Soft close" the file + * The function assumes that only writes are pending on this file + * @param ios: Pointer to the iosystem_desc + * @param file: Pointer to the file_desc for the file + * @returns PIO_NOERR for success, a pio error code otherwise + */ +int spio_soft_closefile(iosystem_desc_t *ios, file_desc_t *file) +{ + assert(ios && file && ios->ioproc); + + return pio_iosys_async_file_close_op_add(file); +} + +/** + * Close a file previously opened with PIO. + * + * @param ncid: the file pointer + * @returns PIO_NOERR for success, error code otherwise. + */ +int PIOc_closefile_impl(int ncid) +{ + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file = NULL; /* Pointer to file information. */ + int ierr = PIO_NOERR; /* Return code from function calls. */ + + LOG((1, "PIOc_closefile ncid = %d", ncid)); + + /* Find the info about this file. */ + if((ierr = pio_get_file(ncid, &file))){ + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, + "Closing file failed. Invalid file id (ncid=%d) provided", ncid); + } + assert(file); + ios = file->iosystem; + assert(ios); + + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + if(file->mode & PIO_WRITE){ + GPTLstart("PIO:write_total_adios"); +#ifndef _ADIOS_BP2NC_TEST + GPTLstart("PIO:write_total"); +#endif + } + } + else{ + if(file->mode & PIO_WRITE){ + GPTLstart("PIO:PIOc_closefile_write_mode"); + GPTLstart("PIO:write_total"); + } + } + + /* Sync changes before closing on all tasks if async is not in + * use, but only on non-IO tasks if async is in use. */ + if(!ios->async || !ios->ioproc){ + if(file->mode & PIO_WRITE){ + sync_file(ncid); + } + } + + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ +#ifndef _ADIOS_BP2NC_TEST + if(file->mode & PIO_WRITE){ + spio_ltimer_start(ios->io_fstats->wr_timer_name); + spio_ltimer_start(file->io_fstats->wr_timer_name); + } + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); +#endif + } + else{ + if(file->mode & PIO_WRITE){ + spio_ltimer_start(ios->io_fstats->wr_timer_name); + spio_ltimer_start(file->io_fstats->wr_timer_name); + } + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + } + + /* If async is in use and this is a comp tasks, then the compmaster + * sends a msg to the pio_msg_handler running on the IO master and + * waiting for a message. Then broadcast the ncid over the intercomm + * to the IO tasks. */ + if(ios->async){ + int msg = PIO_MSG_CLOSE_FILE; + + PIO_SEND_ASYNC_MSG(ios, msg, &ierr, ncid); + if(ierr != PIO_NOERR){ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + if(file->mode & PIO_WRITE){ GPTLstop("PIO:write_total_adios"); } + +#ifndef _ADIOS_BP2NC_TEST + if(file->mode & PIO_WRITE){ + GPTLstop("PIO:write_total"); + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + } + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); +#endif + } + else{ + if(file->mode & PIO_WRITE){ + GPTLstop("PIO:PIOc_closefile_write_mode"); + GPTLstop("PIO:write_total"); + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + } + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + } + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) failed. Error sending async msg PIO_MSG_CLOSE_FILE", pio_get_fname_from_file(file), ncid); + } + } + + bool soft_close = false; +#if PIO_USE_ASYNC_WR_THREAD + if((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)){ + if(ios->ioproc){ + soft_close = true; + } + else{ + /* Wait on all hdf5 async ops before a "hard close" */ + /* + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) failed. Error sending async msg PIO_MSG_CLOSE_FILE", pio_get_fname_from_file(file), ncid); + } + */ + } + } + else{ + /* Wait on all hdf5 async ops before a "hard close" */ + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) failed. Error sending async msg PIO_MSG_CLOSE_FILE", pio_get_fname_from_file(file), ncid); + } + } +#endif + + if(soft_close){ + ierr = spio_soft_closefile(ios, file); + } + else{ + ierr = spio_hard_closefile(ios, file, false); + } + + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + if (file->mode & PIO_WRITE) { GPTLstop("PIO:write_total_adios"); } + + #ifndef _ADIOS_BP2NC_TEST + if(file->mode & PIO_WRITE){ + GPTLstop("PIO:write_total"); + spio_ltimer_stop(ios->io_fstats->wr_timer_name); + spio_ltimer_stop(file->io_fstats->wr_timer_name); + } + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + #endif + } + else{ + if(file->mode & PIO_WRITE) { GPTLstop("PIO:PIOc_closefile_write_mode"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); + } + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); + /* FIXME: How to account for async case ? */ spio_write_file_io_summary(file); - /* Delete file from our list of open files. */ +#if PIO_USE_ASYNC_WR_THREAD + if((file->iotype != PIO_IOTYPE_HDF5) && (file->iotype != PIO_IOTYPE_HDF5C)){ + pio_delete_file_from_list(ncid); + } +#else pio_delete_file_from_list(ncid); +#endif - GPTLstop("PIO:PIOc_closefile"); return ierr; } @@ -1120,7 +1046,6 @@ int PIOc_closefile_impl(int ncid) * @param iosysid a pio system handle. * @param filename a filename. * @returns PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_deletefile_impl(int iosysid, const char *filename) { @@ -1130,13 +1055,11 @@ int PIOc_deletefile_impl(int iosysid, const char *filename) int msg = PIO_MSG_DELETE_FILE; size_t len; - GPTLstart("PIO:PIOc_deletefile"); LOG((1, "PIOc_deletefile iosysid = %d filename = %s", iosysid, filename)); /* Get the IO system info from the id. */ if (!(ios = pio_get_iosystem_from_id(iosysid))) { - GPTLstop("PIO:PIOc_deletefile"); return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, "Deleting file (%s) failed. Invalid I/O system id (iosysid=%d) specified.", (filename) ? filename : "NULL", iosysid); } @@ -1152,13 +1075,20 @@ int PIOc_deletefile_impl(int iosysid, const char *filename) PIO_SEND_ASYNC_MSG(ios, msg, &ierr, len, filename); if(ierr != PIO_NOERR) { - GPTLstop("PIO:PIOc_deletefile"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, ierr, __FILE__, __LINE__, "Deleting file (%s) failed. Sending async message, PIO_MSG_DELETE_FILE, failed", (filename) ? filename : "NULL"); } } +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_close_soft_closed_file(filename); + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Deleting file (%s) failed. Error closing previous soft closed file", filename); + } +#endif + /* If this is an IO task, then call the netCDF function. The * barriers are needed to assure that no task is trying to operate * on the file while it is being deleted. IOTYPE is not known, but @@ -1176,7 +1106,6 @@ int PIOc_deletefile_impl(int iosysid, const char *filename) char *adios_bp_filename = (char *) calloc(adios_bp_filename_len, sizeof(char)); if (adios_bp_filename == NULL) { - GPTLstop("PIO:PIOc_deletefile"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Deleting file (%s) failed. Allocating memory for adios filename failed", (filename) ? filename : "NULL"); @@ -1212,13 +1141,11 @@ int PIOc_deletefile_impl(int iosysid, const char *filename) ierr = check_netcdf(ios, NULL, ierr, __FILE__, __LINE__); if(ierr != PIO_NOERR){ - GPTLstop("PIO:PIOc_deletefile"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, ierr, __FILE__, __LINE__, "Deleting file (%s) failed. Internal I/O library call failed.", (filename) ? filename : "NULL"); } - GPTLstop("PIO:PIOc_deletefile"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return ierr; } @@ -1233,20 +1160,17 @@ int PIOc_deletefile_impl(int iosysid, const char *filename) * * @param ncid the ncid of the file to sync. * @returns PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_sync_impl(int ncid) { file_desc_t *file = NULL; /* Pointer to file information. */ int ierr = PIO_NOERR; /* Return code from function calls. */ - GPTLstart("PIO:PIOc_sync"); LOG((1, "PIOc_sync ncid = %d", ncid)); /* Get the file info from the ncid. */ if ((ierr = pio_get_file(ncid, &file))) { - GPTLstop("PIO:PIOc_sync"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Syncing file (ncid=%d) failed. Invalid file id. Unable to find internal structure associated with the file id", ncid); } @@ -1268,7 +1192,5 @@ int PIOc_sync_impl(int ncid) GPTLstop("PIO:write_total_adios"); } - GPTLstop("PIO:PIOc_sync"); - return ierr; } diff --git a/src/clib/pio_get_nc.cpp b/src/clib/core/pio_get_nc.cpp similarity index 96% rename from src/clib/pio_get_nc.cpp rename to src/clib/core/pio_get_nc.cpp index 854a08532c2..6de4e073d21 100644 --- a/src/clib/pio_get_nc.cpp +++ b/src/clib/core/pio_get_nc.cpp @@ -2,10 +2,7 @@ * @file * PIO functions to get data (excluding varm functions). * - * @author Ed Hartnett - * @date 2016 * - * @see http://code.google.com/p/parallelio/ */ #include @@ -31,7 +28,6 @@ * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_text_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, char *buf) @@ -58,7 +54,6 @@ int PIOc_get_vars_text_impl(int ncid, int varid, const PIO_Offset *start, const * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_uchar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, unsigned char *buf) @@ -85,7 +80,6 @@ int PIOc_get_vars_uchar_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_schar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, signed char *buf) @@ -113,7 +107,6 @@ int PIOc_get_vars_schar_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_ushort_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, unsigned short *buf) @@ -140,7 +133,6 @@ int PIOc_get_vars_ushort_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_short_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, short *buf) @@ -167,7 +159,6 @@ int PIOc_get_vars_short_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_uint_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, unsigned int *buf) @@ -194,7 +185,6 @@ int PIOc_get_vars_uint_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_int_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, int *buf) @@ -221,7 +211,6 @@ int PIOc_get_vars_int_impl(int ncid, int varid, const PIO_Offset *start, const P * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_long_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, long *buf) @@ -248,7 +237,6 @@ int PIOc_get_vars_long_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_float_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, float *buf) @@ -276,7 +264,6 @@ int PIOc_get_vars_float_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_double_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, double *buf) @@ -304,7 +291,6 @@ int PIOc_get_vars_double_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, @@ -332,7 +318,6 @@ int PIOc_get_vars_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_longlong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, long long *buf) @@ -356,7 +341,6 @@ int PIOc_get_vars_longlong_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_text_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, char *buf) @@ -380,7 +364,6 @@ int PIOc_get_vara_text_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_uchar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned char *buf) @@ -404,7 +387,6 @@ int PIOc_get_vara_uchar_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_schar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, signed char *buf) @@ -429,7 +411,6 @@ int PIOc_get_vara_schar_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_ushort_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned short *buf) @@ -453,7 +434,6 @@ int PIOc_get_vara_ushort_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_short_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, short *buf) @@ -477,7 +457,6 @@ int PIOc_get_vara_short_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_long_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, long *buf) @@ -501,7 +480,6 @@ int PIOc_get_vara_long_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_uint_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned int *buf) @@ -525,7 +503,6 @@ int PIOc_get_vara_uint_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_int_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, int *buf) @@ -549,7 +526,6 @@ int PIOc_get_vara_int_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_float_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, float *buf) @@ -573,7 +549,6 @@ int PIOc_get_vara_float_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_double_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, double *buf) @@ -598,7 +573,6 @@ int PIOc_get_vara_double_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, unsigned long long *buf) @@ -622,7 +596,6 @@ int PIOc_get_vara_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_longlong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, long long *buf) @@ -640,7 +613,6 @@ int PIOc_get_vara_longlong_impl(int ncid, int varid, const PIO_Offset *start, * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_text_impl(int ncid, int varid, char *buf) { @@ -657,7 +629,6 @@ int PIOc_get_var_text_impl(int ncid, int varid, char *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_uchar_impl(int ncid, int varid, unsigned char *buf) { @@ -674,7 +645,6 @@ int PIOc_get_var_uchar_impl(int ncid, int varid, unsigned char *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_schar_impl(int ncid, int varid, signed char *buf) { @@ -691,7 +661,6 @@ int PIOc_get_var_schar_impl(int ncid, int varid, signed char *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_ushort_impl(int ncid, int varid, unsigned short *buf) { @@ -708,7 +677,6 @@ int PIOc_get_var_ushort_impl(int ncid, int varid, unsigned short *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_short_impl(int ncid, int varid, short *buf) { @@ -725,7 +693,6 @@ int PIOc_get_var_short_impl(int ncid, int varid, short *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_uint_impl(int ncid, int varid, unsigned int *buf) { @@ -742,7 +709,6 @@ int PIOc_get_var_uint_impl(int ncid, int varid, unsigned int *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_int_impl(int ncid, int varid, int *buf) { @@ -759,7 +725,6 @@ int PIOc_get_var_int_impl(int ncid, int varid, int *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_long_impl(int ncid, int varid, long *buf) { @@ -776,7 +741,6 @@ int PIOc_get_var_long_impl(int ncid, int varid, long *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_float_impl(int ncid, int varid, float *buf) { @@ -793,7 +757,6 @@ int PIOc_get_var_float_impl(int ncid, int varid, float *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_double_impl(int ncid, int varid, double *buf) { @@ -810,7 +773,6 @@ int PIOc_get_var_double_impl(int ncid, int varid, double *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_ulonglong_impl(int ncid, int varid, unsigned long long *buf) { @@ -827,7 +789,6 @@ int PIOc_get_var_ulonglong_impl(int ncid, int varid, unsigned long long *buf) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_longlong_impl(int ncid, int varid, long long *buf) { @@ -847,7 +808,6 @@ int PIOc_get_var_longlong_impl(int ncid, int varid, long long *buf) * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_text_impl(int ncid, int varid, const PIO_Offset *index, char *buf) { @@ -867,7 +827,6 @@ int PIOc_get_var1_text_impl(int ncid, int varid, const PIO_Offset *index, char * * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_uchar_impl(int ncid, int varid, const PIO_Offset *index, unsigned char *buf) { @@ -887,7 +846,6 @@ int PIOc_get_var1_uchar_impl(int ncid, int varid, const PIO_Offset *index, unsig * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_schar_impl(int ncid, int varid, const PIO_Offset *index, signed char *buf) { @@ -907,7 +865,6 @@ int PIOc_get_var1_schar_impl(int ncid, int varid, const PIO_Offset *index, signe * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_ushort_impl(int ncid, int varid, const PIO_Offset *index, unsigned short *buf) { @@ -927,7 +884,6 @@ int PIOc_get_var1_ushort_impl(int ncid, int varid, const PIO_Offset *index, unsi * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_short_impl(int ncid, int varid, const PIO_Offset *index, short *buf) { @@ -949,7 +905,6 @@ int PIOc_get_var1_short_impl(int ncid, int varid, const PIO_Offset *index, short * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_uint_impl(int ncid, int varid, const PIO_Offset *index, unsigned int *buf) { @@ -969,7 +924,6 @@ int PIOc_get_var1_uint_impl(int ncid, int varid, const PIO_Offset *index, unsign * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_long_impl(int ncid, int varid, const PIO_Offset *index, long *buf) { @@ -989,7 +943,6 @@ int PIOc_get_var1_long_impl(int ncid, int varid, const PIO_Offset *index, long * * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_int_impl(int ncid, int varid, const PIO_Offset *index, int *buf) { @@ -1009,7 +962,6 @@ int PIOc_get_var1_int_impl(int ncid, int varid, const PIO_Offset *index, int *bu * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_float_impl(int ncid, int varid, const PIO_Offset *index, float *buf) { @@ -1029,7 +981,6 @@ int PIOc_get_var1_float_impl(int ncid, int varid, const PIO_Offset *index, float * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_double_impl(int ncid, int varid, const PIO_Offset *index, double *buf) { @@ -1049,7 +1000,6 @@ int PIOc_get_var1_double_impl(int ncid, int varid, const PIO_Offset *index, doub * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_ulonglong_impl(int ncid, int varid, const PIO_Offset *index, unsigned long long *buf) @@ -1070,7 +1020,6 @@ int PIOc_get_var1_ulonglong_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_longlong_impl(int ncid, int varid, const PIO_Offset *index, long long *buf) @@ -1089,7 +1038,6 @@ int PIOc_get_var1_longlong_impl(int ncid, int varid, const PIO_Offset *index, * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var_impl(int ncid, int varid, void *buf) { @@ -1110,7 +1058,6 @@ int PIOc_get_var_impl(int ncid, int varid, void *buf) * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_var1_impl(int ncid, int varid, const PIO_Offset *index, void *buf) { @@ -1134,7 +1081,6 @@ int PIOc_get_var1_impl(int ncid, int varid, const PIO_Offset *index, void *buf) * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vara_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, void *buf) @@ -1162,7 +1108,6 @@ int PIOc_get_vara_impl(int ncid, int varid, const PIO_Offset *start, const PIO_O * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_get_vars_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, void *buf) diff --git a/src/clib/pio_getput_int.cpp b/src/clib/core/pio_getput_int.cpp similarity index 95% rename from src/clib/pio_getput_int.cpp rename to src/clib/core/pio_getput_int.cpp index 1dc0643ce29..43a9686db5c 100644 --- a/src/clib/pio_getput_int.cpp +++ b/src/clib/core/pio_getput_int.cpp @@ -3,10 +3,7 @@ * Internal PIO functions to get and put attributes and data * (excluding varm functions). * - * @author Ed Hartnett - * @date 2016 * - * @see http://code.google.com/p/parallelio/ */ #include @@ -14,6 +11,8 @@ #include #include "spio_io_summary.h" #include "spio_hash.h" +#include "spio_hdf5_utils.hpp" +#include "spio_async_hdf5_utils.hpp" /** * Write a netCDF attribute of any type, converting to any type. @@ -29,7 +28,6 @@ * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Ed Hartnett */ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, PIO_Offset len, nc_type memtype, const void *op) @@ -41,12 +39,10 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, int mpierr = MPI_SUCCESS; /* Return code from MPI function codes. */ int ierr = PIO_NOERR; /* Return code from function calls. */ - GPTLstart("PIO:spio_put_att_tc"); GPTLstart("PIO:write_total"); /* Find the info about this file. */ if ((ierr = pio_get_file(ncid, &file))) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Writing variable (varid=%d) attribute (%s) to file failed. Invalid file id (ncid=%d) provided", varid, (name) ? name : "NULL", ncid); @@ -60,6 +56,21 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, spio_ltimer_start(ios->io_fstats->wr_timer_name); spio_ltimer_start(ios->io_fstats->tot_timer_name); +#if PIO_USE_ASYNC_WR_THREAD + /* FIXME: Relax this wait */ + /* + if((file->iotype != PIO_IOTYPE_HDF5) && (file->iotype != PIO_IOTYPE_HDF5C)){ + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " + "Error waiting on all pending asynchronous HDF5 ops", + name, varid, pio_get_fname_from_file(file), file->pio_ncid); + } + } + */ +#endif + #ifdef _ADIOS2 if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { @@ -83,14 +94,12 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstart("PIO:spio_put_att_tc_adios"); GPTLstart("PIO:write_total_adios"); } /* User must provide some valid parameters. */ if (!name || !op || strlen(name) > PIO_MAX_NAME || len < 0) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -98,7 +107,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, @@ -120,11 +128,9 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, ierr = PIOc_inq_type_impl(ncid, atttype, NULL, &atttype_len); if(ierr != PIO_NOERR){ LOG((1, "PIOc_inq_type failed, ierr = %d", ierr)); - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); } return ierr; @@ -137,11 +143,9 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, { ierr = PIOc_inq_type_impl(ncid, memtype, NULL, &memtype_len); if(ierr != PIO_NOERR){ - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); } LOG((1, "PIOc_inq_type failed, ierr = %d", ierr)); @@ -166,7 +170,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, len * memtype_len, op); if(ierr != PIO_NOERR) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -174,7 +177,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(ios, NULL, ierr, __FILE__, __LINE__, @@ -184,7 +186,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, /* Broadcast values currently only known on computation tasks to IO tasks. */ if ((mpierr = MPI_Bcast(&atttype_len, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -192,14 +193,12 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); } return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); } if ((mpierr = MPI_Bcast(&memtype_len, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -207,7 +206,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); } return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); @@ -225,13 +223,11 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, ierr = begin_adios2_step(file, ios); if (ierr != PIO_NOERR) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(NULL, file, ierr, __FILE__, __LINE__, "adios2_begin_step failed for file (%s)", pio_get_fname_from_file(file)); @@ -256,13 +252,11 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, int num_attrs = file->num_attrs; if (num_attrs >= PIO_MAX_ATTRS) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EMAXATTS, __FILE__, __LINE__, "Writing variable (%s, varid=%d) attribute (%s) to file (%s, ncid=%d) using ADIOS iotype failed. " @@ -273,13 +267,11 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, file->adios_attrs[num_attrs].att_name = spio_strdup(name); if (file->adios_attrs[num_attrs].att_name == NULL) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, "Writing variable (%s, varid=%d) attribute (%s) to file (%s, ncid=%d) using ADIOS iotype failed. " @@ -320,13 +312,11 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, if (attributeH == NULL) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) attribute (name=%s) failed for file (%s, ncid=%d)", @@ -342,13 +332,11 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, } } - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_att_tc_adios"); GPTLstop("PIO:write_total_adios"); return PIO_NOERR; @@ -361,7 +349,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, int num_attrs = file->hdf5_num_attrs; if (num_attrs >= PIO_MAX_ATTRS) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -376,7 +363,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, file->hdf5_attrs[num_attrs].att_name = spio_strdup(name); if (file->hdf5_attrs[num_attrs].att_name == NULL) { - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -449,7 +435,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, ierr = ncmpi_put_att_ulonglong(file->fh, varid, name, atttype, len, (const unsigned long long int *) op); break; default: - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -470,7 +455,11 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, file->io_fstats->wb += len * atttype_len; } +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_iosys_async_hdf5_put_att_op_add(file, varid, name, atttype, len, op); +#else ierr = spio_hdf5_put_att(ios, file, varid, name, atttype, len, op); +#endif } #endif /* _HDF5 */ @@ -529,7 +518,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, /* break; */ #endif /* _NETCDF */ default: - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -544,7 +532,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); if(ierr != PIO_NOERR){ LOG((1, "nc*_put_att_* failed, ierr = %d", ierr)); - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -554,7 +541,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, "Writing variable (%s, varid=%d) attribute (%s) to file (%s, ncid=%d) failed. Internal I/O library (%s) call failed", pio_get_vname_from_file(file, varid), varid, name, pio_get_fname_from_file(file), file->pio_ncid, pio_iotype_to_string(file->iotype)); } - GPTLstop("PIO:spio_put_att_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -579,7 +565,6 @@ int spio_put_att_tc(int ncid, int varid, const char *name, nc_type atttype, * of type memtype. * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Ed Hartnett */ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void *ip) { @@ -592,11 +577,9 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void int mpierr = MPI_SUCCESS; /* Return code from MPI function calls. */ int ierr = PIO_NOERR; /* Return code from function calls. */ - GPTLstart("PIO:spio_get_att_tc"); /* Find the info about this file. */ if ((ierr = pio_get_file(ncid, &file))) { - GPTLstop("PIO:spio_get_att_tc"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Reading attribute (%s) from file failed. Invalid file id (ncid=%d) provided", (name) ? name : "NULL", ncid); } @@ -612,7 +595,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void /* User must provide a name and destination pointer. */ if (!name || !ip || strlen(name) > PIO_MAX_NAME) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -636,7 +618,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void ierr = PIOc_inq_att_impl(ncid, varid, name, &atttype, &attlen); if(ierr != PIO_NOERR){ LOG((1, "PIOc_inq_att failed, ierr = %d", ierr)); - GPTLstop("PIO:spio_get_att_tc"); return ierr; } LOG((2, "atttype = %d attlen = %d", atttype, attlen)); @@ -645,7 +626,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void ierr = PIOc_inq_type_impl(ncid, atttype, NULL, &atttype_len); if(ierr != PIO_NOERR){ LOG((1, "PIOc_inq_type failed, ierr=%d", ierr)); - GPTLstop("PIO:spio_get_att_tc"); return ierr; } @@ -658,7 +638,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void ierr = PIOc_inq_type_impl(ncid, memtype, NULL, &memtype_len); if(ierr != PIO_NOERR){ LOG((1, "PIOc_inq_type failed, ierr = %d", ierr)); - GPTLstop("PIO:spio_get_att_tc"); return ierr; } } @@ -680,7 +659,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void if(ierr != PIO_NOERR) { LOG((1, "Error sending async msg for PIO_MSG_GET_ATT")); - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -693,7 +671,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void LOG((2, "spio_get_att_tc bcast from comproot = %d attlen = %d atttype_len = %d", ios->comproot, attlen, atttype_len)); if ((mpierr = MPI_Bcast(&attlen, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -702,7 +679,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void } if ((mpierr = MPI_Bcast(&atttype_len, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -711,7 +687,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void } if ((mpierr = MPI_Bcast(&memtype_len, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -739,7 +714,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void full_name = (char *) calloc(full_name_len, 1); if (full_name == NULL) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -760,7 +734,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void full_name = (char *) calloc(full_name_len, 1); if (full_name == NULL) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -801,7 +774,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void adiosErr = adios2_attribute_data(ip, &size_attr, attr); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -817,7 +789,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void break; } default: - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -830,7 +801,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void free(full_name); - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -886,7 +856,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void ierr = ncmpi_get_att_ulonglong(file->fh, varid, name, (unsigned long long int *) ip); break; default: - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -944,7 +913,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void /* break; */ #endif /* _NETCDF */ default: - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -958,7 +926,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); if(ierr != PIO_NOERR){ LOG((1, "nc*_get_att_* failed, ierr = %d", ierr)); - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -972,7 +939,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void if ((mpierr = MPI_Bcast(ip, (int)attlen * memtype_len, MPI_BYTE, ios->ioroot, ios->my_comm))) { - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -981,7 +947,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void } LOG((2, "get_att_tc data bcast complete")); - GPTLstop("PIO:spio_get_att_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1022,7 +987,6 @@ int spio_get_att_tc(int ncid, int varid, const char *name, nc_type memtype, void * will be used. Use special PIO_LONG_INTERNAL for _long() functions. * @param buf pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, nc_type xtype, void *buf) @@ -1039,7 +1003,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off int mpierr = MPI_SUCCESS; /* Return code from MPI function codes. */ int ierr = PIO_NOERR; /* Return code. */ - GPTLstart("PIO:spio_get_vars_tc"); LOG((1, "spio_get_vars_tc ncid = %d varid = %d xtype = %d start_present = %d " "count_present = %d stride_present = %d", ncid, varid, xtype, start_present, count_present, stride_present)); @@ -1047,7 +1010,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Find the info about this file. */ if ((ierr = pio_get_file(ncid, &file))) { - GPTLstop("PIO:spio_get_vars_tc"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Reading variable (varid=%d) from file failed. Invalid file id (ncid=%d) provided", varid, ncid); } @@ -1063,7 +1025,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* User must provide a place to put some data. */ if (!buf) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1083,7 +1044,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off spio_ltimer_stop(file->io_fstats->tot_timer_name); ierr = PIOc_inq_vartype_impl(ncid, varid, &vartype); if(ierr != PIO_NOERR){ - GPTLstop("PIO:spio_get_vars_tc"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed. Inquiring the variable type failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), ncid); } @@ -1099,7 +1059,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off { ierr = PIOc_inq_type_impl(ncid, xtype, NULL, &typelen); if(ierr != PIO_NOERR){ - GPTLstop("PIO:spio_get_vars_tc"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed. Inquiring the variable type length failed", pio_get_vname_from_file(file, varid), varid, pio_get_vname_from_file(file, varid), ncid); } @@ -1108,7 +1067,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Get the number of dims for this var. */ ierr = PIOc_inq_varndims_impl(ncid, varid, &ndims); if(ierr != PIO_NOERR){ - GPTLstop("PIO:spio_get_vars_tc"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Reading variable (%s, varid=%d) from file (%s, ncid=%d) failed. Inquiring the number of variable dimensions failed", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), ncid); } @@ -1163,7 +1121,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off xtype, num_elem, typelen); if(ierr != PIO_NOERR) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1188,7 +1145,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Broadcast values currently only known on computation tasks to IO tasks. */ if ((mpierr = MPI_Bcast(&ndims, 1, MPI_INT, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1197,7 +1153,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off } if ((mpierr = MPI_Bcast(&num_elem, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1206,7 +1161,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off } if ((mpierr = MPI_Bcast(&typelen, 1, MPI_OFFSET, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1215,7 +1169,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off } if ((mpierr = MPI_Bcast(&xtype, 1, MPI_INT, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1236,7 +1189,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Turn on independent access for pnetcdf file. */ if ((ierr = ncmpi_begin_indep_data(file->fh))) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1288,7 +1240,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ierr = ncmpi_get_vars_ulonglong(file->fh, varid, start, count, stride, (unsigned long long int *) buf); break; default: - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1301,7 +1252,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Turn off independent access for pnetcdf file. */ if ((ierr = ncmpi_end_indep_data(file->fh))) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1323,7 +1273,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off cnt = (PIO_Offset*) calloc(ndims, sizeof(PIO_Offset)); if (cnt == NULL) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1374,7 +1323,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ierr = ncmpi_get_vars_ulonglong_all(file->fh, varid, start, count, stride, (unsigned long long int *) buf); break; default: - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1448,7 +1396,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* break; */ #endif /* _NETCDF */ default: - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1463,7 +1410,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off { if (varid < 0 || varid >= file->num_vars) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1477,7 +1423,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Check put_var tag */ if (strncmp(file->adios_vars[varid].nc_op_tag, "put_var", 7) != 0) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1504,7 +1449,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_current_step(¤t_adios_step, file->engineH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1524,7 +1468,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_end_step(file->engineH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1541,7 +1484,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_close(file->engineH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1555,7 +1497,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off file->engineH = adios2_open(file->ioH, file->fname, adios2_mode_read); if (file->engineH == NULL) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1578,7 +1519,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_end_step(file->engineH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1603,7 +1543,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_end_step(file->engineH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1619,7 +1558,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_begin_step(file->engineH, adios2_step_mode_read, -1.0, &status); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1704,7 +1642,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off av->adios_varid = adios2_inquire_variable(file->ioH, vname); if (av->adios_varid == NULL) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1729,7 +1666,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off mem_buffer = (char *) calloc(av->adios_type_size, 1); if (mem_buffer == NULL) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1743,7 +1679,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_get(file->engineH, av->adios_varid, mem_buffer, adios2_mode_sync); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1770,7 +1705,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* This is not a scalar var. */ if (stride_present) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1833,7 +1767,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off av->adios_varid = adios2_inquire_variable(file->ioH, vname); if (av->adios_varid == NULL) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1851,7 +1784,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adios2_varinfo *data_blocks = adios2_inquire_blockinfo(file->engineH, av->adios_varid, required_adios_step); if (data_blocks == NULL) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1879,7 +1811,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_variable_type(&read_type, av->adios_varid); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1910,7 +1841,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off mem_buffer_size = (char *) calloc(1, sizeof(size_t)); if (mem_buffer_size == NULL) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1924,7 +1854,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_set_block_selection(av->adios_varid, block_id); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1938,7 +1867,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_selection_size(&var_size, av->adios_varid); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1966,7 +1894,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off mem_buffer = (char *) calloc(var_size, av->adios_type_size); if (mem_buffer == NULL) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -1980,7 +1907,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_get(file->engineH, av->adios_varid, mem_buffer, adios2_mode_sync); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -2225,7 +2151,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off } else { - GPTLstop("PIO:PIOc_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -2246,7 +2171,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); if(ierr != PIO_NOERR){ LOG((1, "nc*_get_vars_* failed, ierr = %d", ierr)); - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -2260,7 +2184,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off typelen, ios->ioroot)); if ((mpierr = MPI_Bcast(buf, num_elem * typelen, MPI_BYTE, ios->ioroot, ios->my_comm))) { - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -2272,7 +2195,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ios->io_fstats->rb += num_elem * typelen; file->io_fstats->rb += num_elem * typelen; - GPTLstop("PIO:spio_get_vars_tc"); spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); @@ -2295,7 +2217,6 @@ int spio_get_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off * @param xtype the netcdf type of the variable. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int spio_get_var1_tc(int ncid, int varid, const PIO_Offset *index, nc_type xtype, void *buf) @@ -2342,7 +2263,6 @@ int spio_get_var1_tc(int ncid, int varid, const PIO_Offset *index, nc_type xtype * @param xtype the netcdf type of the variable. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int spio_get_var_tc(int ncid, int varid, nc_type xtype, void *buf) { @@ -2443,7 +2363,6 @@ int spio_get_var_tc(int ncid, int varid, nc_type xtype, void *buf) * @param buf pointer to the data to be written. * * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, nc_type xtype, const void *buf) @@ -2463,7 +2382,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off int mpierr = MPI_SUCCESS; /* Return code from MPI function codes. */ int ierr = PIO_NOERR; /* Return code from function calls. */ - GPTLstart("PIO:spio_put_vars_tc"); GPTLstart("PIO:write_total"); LOG((1, "spio_put_vars_tc ncid = %d varid = %d start_present = %d " "count_present = %d stride_present = %d xtype = %d", ncid, varid, @@ -2472,7 +2390,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Get file info. */ if ((ierr = pio_get_file(ncid, &file))) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, "Writing variable (varid=%d) to file failed. Invalid file id (ncid=%d) provided", varid, ncid); @@ -2509,14 +2426,12 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstart("PIO:spio_put_vars_tc_adios"); GPTLstart("PIO:write_total_adios"); } /* User must provide a place to put some data. */ if (!buf) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -2524,7 +2439,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(ios, file, PIO_EINVAL, __FILE__, __LINE__, @@ -2542,11 +2456,9 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off spio_ltimer_stop(file->io_fstats->tot_timer_name); ierr = PIOc_inq_vartype_impl(ncid, varid, &vartype); if(ierr != PIO_NOERR){ - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, @@ -2560,11 +2472,9 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* Get the number of dims for this var. */ ierr = PIOc_inq_varndims_impl(ncid, varid, &ndims); if(ierr != PIO_NOERR){ - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, @@ -2578,11 +2488,9 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off { ierr = PIOc_inq_type_impl(ncid, xtype, NULL, &typelen); if(ierr != PIO_NOERR){ - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, @@ -2636,7 +2544,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off num_elem * typelen, buf); if(ierr != PIO_NOERR) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -2644,7 +2551,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); } return pio_err(ios, NULL, ierr, __FILE__, __LINE__, @@ -2668,7 +2574,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off LOG((2, "spio_put_vars_tc bcast from comproot")); if ((mpierr = MPI_Bcast(&ndims, 1, MPI_INT, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -2676,14 +2581,12 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); } return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); } if ((mpierr = MPI_Bcast(&xtype, 1, MPI_INT, ios->comproot, ios->my_comm))) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -2691,7 +2594,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off spio_ltimer_stop(file->io_fstats->tot_timer_name); if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); } return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); @@ -2699,19 +2601,28 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off LOG((2, "spio_put_vars_tc complete bcast from comproot ndims = %d", ndims)); } + /* FIXME: Relax this wait */ + /* + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. " + "Error waiting on all pending asynchronous HDF5 ops", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); + } + */ + /* ADIOS: assume all procs are also IO tasks */ #ifdef _ADIOS2 if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) { if (varid < 0 || varid >= file->num_vars) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EBADID, __FILE__, __LINE__, "Writing variable to file (%s, ncid=%d) failed. Invalid variable id (varid=%d, expected >=0 and < number of variables in the file, %d) provided", @@ -2721,13 +2632,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ierr = begin_adios2_step(file, ios); if (ierr != PIO_NOERR) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(NULL, file, ierr, __FILE__, __LINE__, "adios2_begin_step failed for file (%s)", pio_get_fname_from_file(file)); @@ -2805,13 +2714,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adios2_constant_dims_false); if (av->adios_varid == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(NULL, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) variable (name=%s) failed for file (%s, ncid=%d)", @@ -2822,13 +2729,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_put(file->engineH, av->adios_varid, buf, adios2_mode_sync); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Putting (ADIOS) variable (name=%s) failed (adios2_error=%s) for file (%s, ncid=%d)", @@ -2846,13 +2751,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off LOG((2, "ADIOS does not support striding %s:%s\n" "Variable %s will be corrupted in the output" , __FILE__, __func__, av->name)); - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "ADIOS does not support striding. Variable %s file (%s, ncid=%d)", @@ -2930,13 +2833,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adios2_constant_dims_false); if (av->adios_varid == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) variable (name=%s) failed for file (%s, ncid=%d)", @@ -2948,13 +2849,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off adiosErr = adios2_set_selection(av->adios_varid, 1, NULL, &av_size); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Setting (ADIOS) selection to variable (name=%s) failed (adios2_error=%s) for file (%s, ncid=%d)", @@ -2981,13 +2880,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off char *mem_buffer = (char*)calloc(av_size, sizeof(char)); if (mem_buffer == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, "Writing variable (%s, varid=%d) to file (%s, ncid=%d) failed. Out of memory, allocating memory (%lld bytes) for putting ADIOS variable (name = %s)", @@ -3004,13 +2901,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off mem_buffer = NULL; if (adiosErr != adios2_error_none) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Putting (ADIOS) variable (name=%s) failed (adios2_error=%s) for file (%s, ncid=%d)", @@ -3035,13 +2930,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off attributeH = adios2_define_attribute_array(file->ioH, att_name, adios2_type_string, dimnames, av->ndims); if (attributeH == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) attribute array (name=%s, size=%d) failed for file (%s, ncid=%d)", @@ -3064,13 +2957,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off attributeH = adios2_define_attribute(file->ioH, att_name, adios2_type_int32_t, &av->ndims); if (attributeH == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) attribute (name=%s) failed for file (%s, ncid=%d)", @@ -3086,13 +2977,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off attributeH = adios2_define_attribute(file->ioH, att_name, adios2_type_int32_t, &av->nc_type); if (attributeH == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) attribute (name=%s) failed for file (%s, ncid=%d)", @@ -3110,13 +2999,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off attributeH = adios2_define_attribute(file->ioH, att_name, adios2_type_int32_t, &save_adios_type); if (attributeH == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) attribute (name=%s) failed for file (%s, ncid=%d)", @@ -3132,13 +3019,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off attributeH = adios2_define_attribute(file->ioH, att_name, adios2_type_string, "put_var"); if (attributeH == NULL) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Defining (ADIOS) attribute (name=%s) failed for file (%s, ncid=%d)", @@ -3148,13 +3033,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off file->num_written_blocks += 4; } - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); - GPTLstop("PIO:spio_put_vars_tc_adios"); GPTLstop("PIO:write_total_adios"); return PIO_NOERR; @@ -3174,7 +3057,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off if (!(vdesc->request = (int *) realloc(vdesc->request, sizeof(int) * (vdesc->nreqs + PIO_REQUEST_ALLOC_CHUNK)))) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3190,7 +3072,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off PIO_REQUEST_ALLOC_CHUNK)); if(!(vdesc->request_sz)) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3282,7 +3163,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ierr = ncmpi_bput_var_ulonglong(file->fh, varid, (const unsigned long long int *) buf, request); break; default: - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3317,7 +3197,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off LOG((2, "stride not present")); if (!(fake_stride = (PIO_Offset *) malloc(ndims * sizeof(PIO_Offset)))) { - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3376,7 +3255,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ierr = ncmpi_bput_vars_ulonglong(file->fh, varid, start, count, fake_stride, (const unsigned long long int *) buf, request); break; default: - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3417,7 +3295,11 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off file->io_fstats->wb += num_elem * typelen; } +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_iosys_async_hdf5_put_var_op_add(file, varid, start, count, stride, xtype, buf); +#else ierr = spio_hdf5_put_var(ios, file, varid, start, count, stride, xtype, buf); +#endif } #endif /* _HDF5 */ @@ -3486,7 +3368,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off /* break; */ #endif /* _NETCDF */ default: - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3502,7 +3383,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); if(ierr != PIO_NOERR){ LOG((1, "nc*_put_vars_* failed, ierr = %d", ierr)); - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3515,7 +3395,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off LOG((2, "spio_put_vars_tc bcast netcdf return code %d complete", ierr)); - GPTLstop("PIO:spio_put_vars_tc"); GPTLstop("PIO:write_total"); spio_ltimer_stop(ios->io_fstats->wr_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -3551,7 +3430,6 @@ int spio_put_vars_tc(int ncid, int varid, const PIO_Offset *start, const PIO_Off * @param op pointer to the data to be written. * * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int spio_put_var1_tc(int ncid, int varid, const PIO_Offset *index, nc_type xtype, const void *op) @@ -3608,7 +3486,6 @@ int spio_put_var1_tc(int ncid, int varid, const PIO_Offset *index, nc_type xtype * @param op pointer to the data to be written. * * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int spio_put_var_tc(int ncid, int varid, nc_type xtype, const void *op) { diff --git a/src/clib/pio_msg.cpp b/src/clib/core/pio_msg.cpp similarity index 99% rename from src/clib/pio_msg.cpp rename to src/clib/core/pio_msg.cpp index 648da01c5c3..8337f62e1f3 100644 --- a/src/clib/pio_msg.cpp +++ b/src/clib/core/pio_msg.cpp @@ -5,7 +5,6 @@ * messages from the computation nodes, and responds to messages by * running the appropriate netCDF function. * - * @author Ed Hartnett */ #include @@ -1076,7 +1075,6 @@ int recv_async_msg(iosystem_desc_t *ios, int msg, ...) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_type_handler(iosystem_desc_t *ios) { @@ -1126,7 +1124,6 @@ int inq_type_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_format_handler(iosystem_desc_t *ios) { @@ -1172,7 +1169,6 @@ int inq_format_handler(iosystem_desc_t *ios) * @param ios pointer to the iosystem info. * @returns 0 for success, error code otherwise. * @internal - * @author Ed Hartnett */ int set_fill_handler(iosystem_desc_t *ios) { @@ -1220,7 +1216,6 @@ int set_fill_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int create_file_handler(iosystem_desc_t *ios) { @@ -1261,7 +1256,6 @@ int create_file_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int close_file_handler(iosystem_desc_t *ios) { @@ -1301,7 +1295,6 @@ int close_file_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_handler(iosystem_desc_t *ios) { @@ -1357,7 +1350,6 @@ int inq_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_unlimdims_handler(iosystem_desc_t *ios) { @@ -1409,7 +1401,6 @@ int inq_unlimdims_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_dim_handler(iosystem_desc_t *ios, int msg) { @@ -1460,7 +1451,6 @@ int inq_dim_handler(iosystem_desc_t *ios, int msg) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_dimid_handler(iosystem_desc_t *ios) { @@ -1507,7 +1497,6 @@ int inq_dimid_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_att_handler(iosystem_desc_t *ios) { @@ -1557,7 +1546,6 @@ int inq_att_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_attname_handler(iosystem_desc_t *ios) { @@ -1605,7 +1593,6 @@ int inq_attname_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_attid_handler(iosystem_desc_t *ios) { @@ -1652,7 +1639,6 @@ int inq_attid_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int att_put_handler(iosystem_desc_t *ios) { @@ -1709,7 +1695,6 @@ int att_put_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Jayesh Krishna */ int att_copy_handler(iosystem_desc_t *ios) { @@ -1751,7 +1736,6 @@ int att_copy_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int att_get_handler(iosystem_desc_t *ios) { @@ -1813,7 +1797,6 @@ int att_get_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int put_vars_handler(iosystem_desc_t *ios) { @@ -1933,7 +1916,6 @@ int put_vars_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int get_vars_handler(iosystem_desc_t *ios) { @@ -2328,7 +2310,6 @@ int inq_var_deflate_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int inq_varid_handler(iosystem_desc_t *ios) { @@ -2366,7 +2347,6 @@ int inq_varid_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int sync_file_handler(iosystem_desc_t *ios) { @@ -2405,7 +2385,6 @@ int sync_file_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int setframe_handler(iosystem_desc_t *ios) { @@ -2447,7 +2426,6 @@ int setframe_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int advanceframe_handler(iosystem_desc_t *ios) { @@ -2487,7 +2465,6 @@ int advanceframe_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int change_def_file_handler(iosystem_desc_t *ios, int msg) { @@ -2530,7 +2507,6 @@ int change_def_file_handler(iosystem_desc_t *ios, int msg) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int def_var_handler(iosystem_desc_t *ios) { @@ -2805,7 +2781,6 @@ int set_var_chunk_cache_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int def_dim_handler(iosystem_desc_t *ios) { @@ -2848,7 +2823,6 @@ int def_dim_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int rename_dim_handler(iosystem_desc_t *ios) { @@ -2892,7 +2866,6 @@ int rename_dim_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int rename_var_handler(iosystem_desc_t *ios) { @@ -2936,7 +2909,6 @@ int rename_var_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int rename_att_handler(iosystem_desc_t *ios) { @@ -2980,7 +2952,6 @@ int rename_att_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int delete_att_handler(iosystem_desc_t *ios) { @@ -3024,7 +2995,6 @@ int delete_att_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int open_file_handler(iosystem_desc_t *ios) { @@ -3070,7 +3040,6 @@ int open_file_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int delete_file_handler(iosystem_desc_t *ios) { @@ -3191,7 +3160,6 @@ int initdecomp_dof_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int write_darray_multi_handler(iosystem_desc_t *ios) { @@ -3297,7 +3265,6 @@ int write_darray_multi_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int readdarray_handler(iosystem_desc_t *ios) { @@ -3336,7 +3303,6 @@ int readdarray_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int seterrorhandling_handler(iosystem_desc_t *ios) { @@ -3382,7 +3348,6 @@ int seterrorhandling_handler(iosystem_desc_t *ios) * @param ios pointer to the iosystem_desc_t data. * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. - * @author Ed Hartnett */ int set_chunk_cache_handler(iosystem_desc_t *ios) { @@ -3426,7 +3391,6 @@ int set_chunk_cache_handler(iosystem_desc_t *ios) * @param ios pointer to the iosystem_desc_t data. * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. - * @author Ed Hartnett */ int get_chunk_cache_handler(iosystem_desc_t *ios) { @@ -3480,7 +3444,6 @@ int get_chunk_cache_handler(iosystem_desc_t *ios) * @param ios pointer to the iosystem_desc_t data. * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. - * @author Ed Hartnett */ int get_var_chunk_cache_handler(iosystem_desc_t *ios) { @@ -3533,7 +3496,6 @@ int get_var_chunk_cache_handler(iosystem_desc_t *ios) * @param ios pointer to the iosystem_desc_t data. * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. - * @author Ed Hartnett */ int freedecomp_handler(iosystem_desc_t *ios) { @@ -3574,7 +3536,6 @@ int freedecomp_handler(iosystem_desc_t *ios) * @returns 0 for success, PIO_EIO for MPI Bcast errors, or error code * from netCDF base function. * @internal - * @author Ed Hartnett */ int finalize_handler(iosystem_desc_t *ios, int index) { @@ -3616,7 +3577,6 @@ int finalize_handler(iosystem_desc_t *ios, int index) * @param iosys pointer to pointer to iosystem info * @param io_comm MPI communicator for IO * @returns 0 for success, error code otherwise. - * @author Ed Hartnett */ int pio_msg_handler2(int io_rank, int component_count, iosystem_desc_t **iosys, MPI_Comm io_comm) diff --git a/src/clib/pio_nc.cpp b/src/clib/core/pio_nc.cpp similarity index 92% rename from src/clib/pio_nc.cpp rename to src/clib/core/pio_nc.cpp index d3683671af5..8b7fe03d7e5 100644 --- a/src/clib/pio_nc.cpp +++ b/src/clib/core/pio_nc.cpp @@ -11,8 +11,6 @@ * tasks (io_comm). Each routine must be called collectively from * union_comm. * - * @author Jim Edwards (jedwards@ucar.edu), Ed Hartnett - * @date Feburary 2014, April 2016 */ #include #include @@ -21,6 +19,8 @@ #include "pio_timer.h" #endif #include "spio_io_summary.h" +#include "spio_hdf5_utils.hpp" +#include "spio_async_hdf5_utils.hpp" const char spio_nc_fillvalue_aname[] = "_FillValue"; @@ -84,7 +84,6 @@ int get_adios2_type_size(adios2_type type, const void *var) * * @return PIO_NOERR for success, error code otherwise. See * PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_impl(int ncid, int *ndimsp, int *nvarsp, int *ngattsp, int *unlimdimidp) { @@ -294,7 +293,6 @@ int PIOc_inq_impl(int ncid, int *ndimsp, int *nvarsp, int *ngattsp, int *unlimdi * @param ncid the ncid of the open file. * @param ndimsp a pointer that will get the number of dimensions. * @returns 0 for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_ndims_impl(int ncid, int *ndimsp) { @@ -309,7 +307,6 @@ int PIOc_inq_ndims_impl(int ncid, int *ndimsp) * @param ncid the ncid of the open file. * @param nvarsp a pointer that will get the number of variables. * @returns 0 for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_nvars_impl(int ncid, int *nvarsp) { @@ -323,7 +320,6 @@ int PIOc_inq_nvars_impl(int ncid, int *nvarsp) * @param ncid the ncid of the open file. * @param nattsp a pointer that will get the number of attributes. * @returns 0 for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_natts_impl(int ncid, int *ngattsp) { @@ -338,7 +334,6 @@ int PIOc_inq_natts_impl(int ncid, int *ngattsp) * @param unlimdimidp a pointer that will the ID of the unlimited * dimension, or -1 if there is no unlimited dimension. * @returns 0 for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_unlimdim_impl(int ncid, int *unlimdimidp) { @@ -357,7 +352,6 @@ int PIOc_inq_unlimdim_impl(int ncid, int *unlimdimidp) * dimension IDs. * @returns 0 for success, error code otherwise. * @ingroup PIO_inq_unlimdim - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_unlimdims_impl(int ncid, int *nunlimdimsp, int *unlimdimidsp) { @@ -525,7 +519,6 @@ int PIOc_inq_unlimdims_impl(int ncid, int *nunlimdimsp, int *unlimdimidsp) * @param name pointer that will get the name of the type. * @param sizep pointer that will get the size of the type in bytes. * @returns 0 for success, error code otherwise. - * @author Ed Hartnett */ int PIOc_inq_type_impl(int ncid, nc_type xtype, char *name, PIO_Offset *sizep) { @@ -682,7 +675,6 @@ int PIOc_inq_type_impl(int ncid, nc_type xtype, char *name, PIO_Offset *sizep) * @param ncid the ncid of an open file. * @param formatp a pointer that will get the format. * @returns 0 for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_format_impl(int ncid, int *formatp) { @@ -783,7 +775,6 @@ int PIOc_inq_format_impl(int ncid, int *formatp) * PIOc_openfile() or PIOc_createfile(). * @param lenp a pointer that will get the number of values * @return PIO_NOERR for success, error code otherwise. See PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_dim_impl(int ncid, int dimid, char *name, PIO_Offset *lenp) { @@ -983,7 +974,6 @@ int PIOc_inq_dim_impl(int ncid, int dimid, char *name, PIO_Offset *lenp) * @param name a pointer that gets the name of the dimension. Igorned * if NULL. * @returns 0 for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_dimname_impl(int ncid, int dimid, char *name) { @@ -1000,7 +990,6 @@ int PIOc_inq_dimname_impl(int ncid, int dimid, char *name) * @param lenp a pointer that gets the length of the dimension. Igorned * if NULL. * @returns 0 for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_dimlen_impl(int ncid, int dimid, PIO_Offset *lenp) { @@ -1020,7 +1009,6 @@ int PIOc_inq_dimlen_impl(int ncid, int dimid, PIO_Offset *lenp) * PIOc_openfile() or PIOc_createfile(). * @param idp a pointer that will get the id of the variable or attribute. * @return PIO_NOERR for success, error code otherwise. See PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_dimid_impl(int ncid, const char *name, int *idp) { @@ -1202,7 +1190,6 @@ int PIOc_inq_dimid_impl(int ncid, const char *name, int *idp) * @param xtypep a pointer that will get the type of the attribute. * @param nattsp a pointer that will get the number of attributes * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_var_impl(int ncid, int varid, char *name, int namelen, nc_type *xtypep, int *ndimsp, int *dimidsp, int *nattsp) @@ -1478,9 +1465,9 @@ int PIOc_inq_var_impl(int ncid, int varid, char *name, int namelen, nc_type *xty snprintf(timer_log_fname, PIO_MAX_NAME, "piorwinfo%010dwrank.dat", ios->ioroot); if(!mtimer_is_valid(file->varlist[varid].rd_mtimer)) { - char tmp_timer_name[PIO_MAX_NAME]; - snprintf(tmp_timer_name, PIO_MAX_NAME, "%s_%s", "rd", file->varlist[varid].vname); - file->varlist[varid].rd_mtimer = mtimer_create(tmp_timer_name, ios->my_comm, timer_log_fname); + std::string tmp_timer_name; + tmp_timer_name = std::string("rd_") + std::string(file->varlist[varid].vname); + file->varlist[varid].rd_mtimer = mtimer_create(tmp_timer_name.c_str(), ios->my_comm, timer_log_fname); if(!mtimer_is_valid(file->varlist[varid].rd_mtimer)) { spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -1489,8 +1476,8 @@ int PIOc_inq_var_impl(int ncid, int varid, char *name, int namelen, nc_type *xty "Inquiring information of variable %s (varid=%d) failed on file %s (ncid=%d) failed. Error creating micro timer (read) for variable", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), ncid); } assert(!mtimer_is_valid(file->varlist[varid].rd_rearr_mtimer)); - snprintf(tmp_timer_name, PIO_MAX_NAME, "%s_%s", "rd_rearr", file->varlist[varid].vname); - file->varlist[varid].rd_rearr_mtimer = mtimer_create(tmp_timer_name, ios->my_comm, timer_log_fname); + tmp_timer_name = std::string("rd_rearr_") + std::string(file->varlist[varid].vname); + file->varlist[varid].rd_rearr_mtimer = mtimer_create(tmp_timer_name.c_str(), ios->my_comm, timer_log_fname); if(!mtimer_is_valid(file->varlist[varid].rd_rearr_mtimer)) { spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -1498,8 +1485,8 @@ int PIOc_inq_var_impl(int ncid, int varid, char *name, int namelen, nc_type *xty return pio_err(ios, file, PIO_EINTERNAL, __FILE__, __LINE__, "Inquiring information of variable %s (varid=%d) failed on file %s (ncid=%d) failed. Error creating micro timer (read rearrange) for variable", pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), ncid); } - snprintf(tmp_timer_name, PIO_MAX_NAME, "%s_%s", "wr", file->varlist[varid].vname); - file->varlist[varid].wr_mtimer = mtimer_create(tmp_timer_name, ios->my_comm, timer_log_fname); + tmp_timer_name = std::string("wr_") + std::string(file->varlist[varid].vname); + file->varlist[varid].wr_mtimer = mtimer_create(tmp_timer_name.c_str(), ios->my_comm, timer_log_fname); if(!mtimer_is_valid(file->varlist[varid].wr_mtimer)) { spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -1508,8 +1495,8 @@ int PIOc_inq_var_impl(int ncid, int varid, char *name, int namelen, nc_type *xty "Inquiring information of variable %s (varid=%d) failed on file %s (ncid=%d) failed. Error creating micro timer (write) for variable", pio_get_fname_from_file(file), varid, pio_get_fname_from_file(file), ncid); } assert(!mtimer_is_valid(file->varlist[varid].wr_rearr_mtimer)); - snprintf(tmp_timer_name, PIO_MAX_NAME, "%s_%s", "wr_rearr", file->varlist[varid].vname); - file->varlist[varid].wr_rearr_mtimer = mtimer_create(tmp_timer_name, ios->my_comm, timer_log_fname); + tmp_timer_name = std::string("wr_rearr_") + std::string(file->varlist[varid].vname); + file->varlist[varid].wr_rearr_mtimer = mtimer_create(tmp_timer_name.c_str(), ios->my_comm, timer_log_fname); if(!mtimer_is_valid(file->varlist[varid].wr_rearr_mtimer)) { spio_ltimer_stop(ios->io_fstats->tot_timer_name); @@ -1591,7 +1578,6 @@ int PIOc_inq_var_impl(int ncid, int varid, char *name, int namelen, nc_type *xty * @param name a pointer that will get the variable name. * @param namelen the size of the user buffer pointed by name. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_varname_impl(int ncid, int varid, char *name, int namelen) { @@ -1607,7 +1593,6 @@ int PIOc_inq_varname_impl(int ncid, int varid, char *name, int namelen) * @param xtypep a pointer that will get the type of the * attribute. Ignored if NULL. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_vartype_impl(int ncid, int varid, nc_type *xtypep) { @@ -1623,7 +1608,6 @@ int PIOc_inq_vartype_impl(int ncid, int varid, nc_type *xtypep) * @param ndimsp a pointer that will get the number of * dimensions. Ignored if NULL. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_varndims_impl(int ncid, int varid, int *ndimsp) { @@ -1639,7 +1623,6 @@ int PIOc_inq_varndims_impl(int ncid, int varid, int *ndimsp) * @param dimidsp a pointer that will get an array of dimids. Ignored * if NULL. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_vardimid_impl(int ncid, int varid, int *dimidsp) { @@ -1655,7 +1638,6 @@ int PIOc_inq_vardimid_impl(int ncid, int varid, int *dimidsp) * @param nattsp a pointer that will get the number of attriburtes. Ignored * if NULL. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_varnatts_impl(int ncid, int varid, int *nattsp) { @@ -1676,7 +1658,6 @@ int PIOc_inq_varnatts_impl(int ncid, int varid, int *nattsp) * @param varid the variable ID. * @param varidp a pointer that will get the variable id * @return PIO_NOERR for success, error code otherwise. See PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_varid_impl(int ncid, const char *name, int *varidp) { @@ -1893,7 +1874,6 @@ int PIOc_inq_varid_impl(int ncid, const char *name, int *varidp) * @param xtypep a pointer that will get the type of the attribute. * @param lenp a pointer that will get the number of values * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_att_impl(int ncid, int varid, const char *name, nc_type *xtypep, PIO_Offset *lenp) @@ -2082,7 +2062,6 @@ int PIOc_inq_att_impl(int ncid, int varid, const char *name, nc_type *xtypep, * @param lenp a pointer that gets the lenght of the attribute * array. Ignored if NULL. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_attlen_impl(int ncid, int varid, const char *name, PIO_Offset *lenp) { @@ -2099,7 +2078,6 @@ int PIOc_inq_attlen_impl(int ncid, int varid, const char *name, PIO_Offset *lenp * @param xtypep a pointer that gets the type of the * attribute. Ignored if NULL. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_atttype_impl(int ncid, int varid, const char *name, nc_type *xtypep) { @@ -2120,7 +2098,6 @@ int PIOc_inq_atttype_impl(int ncid, int varid, const char *name, nc_type *xtypep * @param varid the variable ID. * @param attnum the attribute ID. * @return PIO_NOERR for success, error code otherwise. See PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_attname_impl(int ncid, int varid, int attnum, char *name) { @@ -2293,7 +2270,6 @@ int PIOc_inq_attname_impl(int ncid, int varid, int attnum, char *name) * @param varid the variable ID. * @param idp a pointer that will get the id of the variable or attribute. * @return PIO_NOERR for success, error code otherwise. See PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_attid_impl(int ncid, int varid, const char *name, int *idp) { @@ -2463,7 +2439,6 @@ int PIOc_inq_attid_impl(int ncid, int varid, const char *name, int *idp) * @param ncid the ncid of the open file, obtained from * PIOc_openfile() or PIOc_createfile(). * @return PIO_NOERR for success, error code otherwise. See PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_rename_dim_impl(int ncid, int dimid, const char *name) { @@ -2564,7 +2539,6 @@ int PIOc_rename_dim_impl(int ncid, int dimid, const char *name) * PIOc_openfile() or PIOc_createfile(). * @param varid the variable ID. * @return PIO_NOERR for success, error code otherwise. See PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_rename_var_impl(int ncid, int varid, const char *name) { @@ -2666,7 +2640,6 @@ int PIOc_rename_var_impl(int ncid, int varid, const char *name) * @param varid the variable ID. * @return PIO_NOERR for success, error code otherwise. See * PIOc_Set_File_Error_Handling - * @author Jim Edwards, Ed Hartnett */ int PIOc_rename_att_impl(int ncid, int varid, const char *name, const char *newname) @@ -2777,7 +2750,6 @@ int PIOc_rename_att_impl(int ncid, int varid, const char *name, * @param varid the variable ID. * @param name of the attribute to delete. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_del_att_impl(int ncid, int varid, const char *name) { @@ -2878,7 +2850,6 @@ int PIOc_del_att_impl(int ncid, int varid, const char *name) * @param old_modep a pointer to an int that gets the old setting. * @return PIO_NOERR for success, error code otherwise. * @ingroup PIO_set_fill - * @author Jim Edwards, Ed Hartnett */ int PIOc_set_fill_impl(int ncid, int fillmode, int *old_modep) { @@ -2999,11 +2970,208 @@ int PIOc_set_fill_impl(int ncid, int fillmode, int *old_modep) * @param ncid the ncid of the open file, obtained from * PIOc_openfile() or PIOc_createfile(). * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_enddef_impl(int ncid) { - return spio_change_def(ncid, 1); + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int ierr = PIO_NOERR; /* Return code from function calls. */ + + LOG((2, "PIOc_enddef_impl ncid = %d", ncid)); + + /* Find the info about this file. When I check the return code + * here, some tests fail. ???*/ + if((ierr = pio_get_file(ncid, &file))){ + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, + "Ending the define mode for file (ncid = %d) failed. Invalid file id", ncid); + } + assert(file); + ios = file->iosystem; + assert(ios); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + + /* If async is in use, and this is not an IO task, bcast the parameters. */ + if(ios->async){ + int msg = PIO_MSG_ENDDEF; + + PIO_SEND_ASYNC_MSG(ios, msg, &ierr, ncid); + if(ierr != PIO_NOERR){ + LOG((1, "Error sending async msg for PIO_MSG_ENDDEF")); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Ending the define mode for file (%s) failed. Error sending async msg, PIO_MSG_ENDDEF, on iosystem (iosysid=%d)", pio_get_fname_from_file(file), ios->iosysid); + } + } + +#if PIO_USE_ASYNC_WR_THREAD + /* FIXME: Relax this wait */ + /* + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " + "Error waiting on all pending asynchronous HDF5 ops", + pio_get_fname_from_file(file), file->pio_ncid); + } + */ +#endif + + /* If this is an IO task, then call the netCDF function. */ + LOG((3, "PIOc_enddef_impl ios->ioproc = %d", ios->ioproc)); + if(ios->ioproc){ + LOG((3, "PIOc_enddef_impl calling netcdf function file->fh = %d file->do_io = %d iotype = %d", + file->fh, file->do_io, file->iotype)); + + /* NetCDF and PnetCDF by default do not reserve any extra space in + * the NetCDF file headers (compactness etc). However this can be a + * problem (performance related) when renaming variables etc since + * this information is stored in the NetCDF header. Adding more + * information into the header requires the libraries to resize the + * file, extend the header space and copy the contents of the file + * (similar to realloc). + * + * The solution for the performance issue is to reserve some extra + * space in the header when creating NetCDF files. The current calls + * to nc_enddef()/ncmpi_enddef() need to be replaced with nc__enddef + * ()/ncmpi__enddef() (note the double underscore). + * + * This reservation should be made only once: after the header section + * is expanded, a new reservation may involve moving (shifting) data, + * which can be very expensive if the data sections are huge. + */ +#ifdef _PNETCDF + if(file->iotype == PIO_IOTYPE_PNETCDF){ + /* ncmpi__enddef has been available since PnetCDF 1.5.0 (PNETCDF_MIN_VER_REQD is currently 1.8.1): + int ncmpi__enddef(int ncid, + MPI_Offset h_minfree, + MPI_Offset v_align, + MPI_Offset v_minfree, + MPI_Offset r_align); + + The usual call of ncmpi_enddef(ncid) is equivalent to ncmpi__enddef(ncid, 0, 0, 0, 0). + + Call ncmpi__enddef with appropriate arguments instead of ncmpi_enddef: + - To reserve a sufficiently large space for the file header if it is expected + to expand (set h_minfree > 0 explicitly). + See https://github.com/E3SM-Project/scorpio/issues/436 + + - To prevent PnetCDF (versions prior to 1.13.0) from implicitly selecting the + file striping size to align the header extent (set v_align > 0 explicitly). + See https://github.com/E3SM-Project/scorpio/issues/566 + */ + + /* Sets the pad at the end of the "header" section. */ + MPI_Offset h_minfree = 0; /* Unless extra header space is requested, this can be left as default (0) */ + + /* Controls the alignment of the beginning of the data section for fixed-size/record variables. */ + const MPI_Offset v_align = 4; /* For fixed-size variables, needs to be left as the default (4 bytes) */ + const MPI_Offset r_align = 4; /* For record variables, needs to be left as the default (4 bytes) */ + + /* Sets the pad at the end of the data section for fixed-size variables. */ + const MPI_Offset v_minfree = 0; /* This can be left as default (0) */ + + if(file->reserve_extra_header_space){ + /* The recommended size by Charlie Zender (NCO developer) is 10 KB */ + assert(PIO_RESERVED_FILE_HEADER_SIZE >= 0); + h_minfree = PIO_RESERVED_FILE_HEADER_SIZE; + + file->reserve_extra_header_space = false; + } + + ierr = ncmpi__enddef(file->fh, h_minfree, v_align, v_minfree, r_align); + } +#endif /* _PNETCDF */ +#ifdef _NETCDF + if(((file->iotype == PIO_IOTYPE_NETCDF) || (file->iotype == PIO_IOTYPE_NETCDF4C) || + (file->iotype == PIO_IOTYPE_NETCDF4P) || (file->iotype == PIO_IOTYPE_NETCDF4P_NCZARR)) && + file->do_io){ + LOG((3, "PIOc_enddef_impl calling nc_enddef file->fh = %d", file->fh)); + + /* We only reserve header space for CLASSIC NetCDF files */ + bool reserve_extra_header_space = (file->iotype == PIO_IOTYPE_NETCDF) && + file->reserve_extra_header_space; + bool enddef_complete = false; + + /* CAUTION: nc__enddef may not be available on future NetCDF implementations. */ +#ifdef NETCDF_C_NC__ENDDEF_EXISTS + if(reserve_extra_header_space){ + /* Sets the pad at the end of the "header" section. + * The recommended size by Charlie Zender (NCO developer) is 10 KB */ + assert(PIO_RESERVED_FILE_HEADER_SIZE >= 0); + const MPI_Offset h_minfree = PIO_RESERVED_FILE_HEADER_SIZE; + + /* Controls the alignment of the beginning of the data section for fixed-size/record variables. */ + const size_t v_align = 4; /* For fixed-size variables, needs to be left as the default (4 bytes) */ + const size_t r_align = 4; /* For record variables, needs to be left as the default (4 bytes) */ + + /* Sets the pad at the end of the data section for fixed-size variables. */ + const size_t v_minfree = 0; /* This can be left as default (0) */ + + /* nc__enddef has been available since NetCDF 3.x (NETCDF_C_MIN_VER_REQD is currently 4.3.3) */ + ierr = nc__enddef(file->fh, h_minfree, v_align, v_minfree, r_align); + + file->reserve_extra_header_space = false; + + enddef_complete = true; + } +#endif + + if(!enddef_complete){ + /* The default method for enddef for all NetCDF4 I/O types (and also for PIO_IOTYPE_NETCDF when we + * don't require header expansion OR when nc__enddef() is unavailable to expand the header + * According to NCO user guide, nc__enddef will improve speed of future metadata expansion with + * classic and 64bit NetCDF files, though not necessarily with NetCDF4 files. + */ + ierr = nc_enddef(file->fh); + } + } +#endif /* _NETCDF */ + + /* For NetCDF I/O types whether "this file is in define mode before enddef() is called" is + * handled by the low-level NetCDF enddef() calls. For HDF5 and ADIOS we handle it explicitly + */ +#ifdef _HDF5 + if((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)){ + if(file->in_def_mode){ +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_iosys_async_hdf5_enddef_op_add(file); +#else + ierr = spio_hdf5_enddef(ios, file); +#endif /* PIO_USE_ASYNC_WR_THREAD */ + } + else{ + ierr = pio_err(ios, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Ending the define mode for file (%s) failed. The file is not in define mode.", pio_get_fname_from_file(file)); + } + } +#endif /* _HDF5 */ + + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + /* Do nothing - no "enddef" - for ADIOS I/O types. Just a sanity check here */ + if(!file->in_def_mode){ + ierr = pio_err(ios, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Ending the define mode for file (%s) failed. The file is not in define mode.", pio_get_fname_from_file(file)); + } + } + } + + ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); + if(ierr != PIO_NOERR){ + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Ending the define mode for file (%s) failed. Low-level I/O library API failed", pio_get_fname_from_file(file)); + } + + /* File no longer in "define mode" */ + file->in_def_mode = false; + LOG((3, "PIOc_enddef_impl succeeded")); + + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return ierr; } /** @@ -3018,11 +3186,98 @@ int PIOc_enddef_impl(int ncid) * @param ncid the ncid of the open file, obtained from * PIOc_openfile() or PIOc_createfile(). * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_redef_impl(int ncid) { - return spio_change_def(ncid, 0); + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int ierr = PIO_NOERR; /* Return code from function calls. */ + + LOG((2, "PIOc_redef_impl ncid = %d ", ncid)); + + /* Find the info about this file. When I check the return code + * here, some tests fail. ???*/ + if((ierr = pio_get_file(ncid, &file))){ + return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, + "Changing the define mode (redef) for file (ncid = %d) failed. Invalid file id", ncid); + } + assert(file); + ios = file->iosystem; + assert(ios); + spio_ltimer_start(ios->io_fstats->tot_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + + /* If async is in use, and this is not an IO task, bcast the parameters. */ + if(ios->async){ + int msg = PIO_MSG_REDEF; + + PIO_SEND_ASYNC_MSG(ios, msg, &ierr, ncid); + if(ierr != PIO_NOERR){ + LOG((1, "Error sending async msg for PIO_MSG_REDEF")); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Changing the define mode (redef) for file (%s) failed. Error sending async msg, PIO_MSG_REDEF, on iosystem (iosysid=%d)", pio_get_fname_from_file(file), ios->iosysid); + } + } + +#if PIO_USE_ASYNC_WR_THREAD + /* FIXME: Relax this wait */ + /* + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " + "Error waiting on all pending asynchronous HDF5 ops", + pio_get_fname_from_file(file), file->pio_ncid); + } + */ +#endif + + /* If this is an IO task, then call the netCDF function. */ + LOG((3, "PIOc_redef_impl ios->ioproc = %d", ios->ioproc)); + if(ios->ioproc){ + LOG((3, "PIOc_redef calling netcdf function file->fh = %d file->do_io = %d iotype = %d", + file->fh, file->do_io, file->iotype)); + switch(file->iotype){ +#ifdef _PNETCDF + case PIO_IOTYPE_PNETCDF: ierr = ncmpi_redef(file->fh); break; +#endif +#ifdef _NETCDF + case PIO_IOTYPE_NETCDF: + case PIO_IOTYPE_NETCDF4C: + case PIO_IOTYPE_NETCDF4P: + case PIO_IOTYPE_NETCDF4P_NCZARR: + if(file->do_io) { ierr = nc_redef(file->fh); } + /* Unlike PnetCDF ncmpi_redef() call, NetCDF does not fail for multiple redef() calls + * So perform the sanity checks in the "default:" case for NetCDF I/O types */ + /* break; */ +#endif /* _NETCDF */ + default: + /* Do nothing : Just some sanity checks */ + if(file->in_def_mode){ + ierr = pio_err(ios, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Changing the define mode (redef) for file (%s) failed. The file is already in define mode.", pio_get_fname_from_file(file)); + } + break; + } + } + + ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); + if(ierr != PIO_NOERR){ + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Changing the define mode (redef) for file (%s) failed. Low-level I/O library API failed", pio_get_fname_from_file(file)); + } + + file->in_def_mode = true; + + LOG((3, "PIOc_redef_impl succeeded")); + + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return ierr; } /** @@ -3038,7 +3293,6 @@ int PIOc_redef_impl(int ncid) * PIOc_openfile() or PIOc_createfile(). * @param idp a pointer that will get the id of the variable or attribute. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_def_dim_impl(int ncid, const char *name, PIO_Offset len, int *idp) { @@ -3203,7 +3457,9 @@ int PIOc_def_dim_impl(int ncid, const char *name, PIO_Offset len, int *idp) } file->hdf5_dims[file->hdf5_num_dims].len = len; + file->hdf5_dims[file->hdf5_num_dims].chunk_sz = spio_hdf5_get_dim_chunk_sz_from_chunk_info(name); file->hdf5_dims[file->hdf5_num_dims].has_coord_var = false; + file->hdf5_dims[file->hdf5_num_dims].hdf5_dataset_id = H5I_INVALID_HID; *idp = file->hdf5_num_dims; file->hdf5_num_dims++; } @@ -3279,7 +3535,6 @@ int PIOc_def_dim_impl(int ncid, const char *name, PIO_Offset len, int *idp) * @param varidp a pointer that will get the variable id * @return PIO_NOERR for success, error code otherwise. * @ingroup PIO_def_var - * @author Jim Edwards, Ed Hartnett */ int PIOc_def_var_impl(int ncid, const char *name, nc_type xtype, int ndims, const int *dimidsp, int *varidp) @@ -3382,6 +3637,17 @@ int PIOc_def_var_impl(int ncid, const char *name, nc_type xtype, int ndims, return PIO_EINVAL; } + /* FIXME: Relax this wait */ + /* + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Defining variable (%s) in file (%s, ncid=%d) using HDF5 iotype failed. " + "Error waiting on all pending asynchronous HDF5 ops", + name, pio_get_fname_from_file(file), file->pio_ncid); + } + */ + /* ADIOS: assume all procs are also IO tasks */ #ifdef _ADIOS2 if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) @@ -3548,6 +3814,7 @@ int PIOc_def_var_impl(int ncid, const char *name, nc_type xtype, int ndims, file->hdf5_vars[file->hdf5_num_vars].nc_type = xtype; file->hdf5_vars[file->hdf5_num_vars].ndims = ndims; file->hdf5_vars[file->hdf5_num_vars].is_coord_var = false; + file->hdf5_vars[file->hdf5_num_vars].hdf5_dataset_id = H5I_INVALID_HID; file->hdf5_vars[file->hdf5_num_vars].hdf5_dimids = (int*)malloc(ndims * sizeof(int)); if (file->hdf5_vars[file->hdf5_num_vars].hdf5_dimids == NULL) @@ -3673,7 +3940,11 @@ int PIOc_def_var_impl(int ncid, const char *name, nc_type xtype, int ndims, #ifdef _HDF5 if ((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)) { +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_iosys_async_hdf5_def_var_op_add(file, name, xtype, ndims, dimidsp, *varidp); +#else ierr = spio_hdf5_def_var(ios, file, name, xtype, ndims, dimidsp, *varidp); +#endif if (ierr != PIO_NOERR) { char errmsg[PIO_MAX_NAME]; @@ -3804,7 +4075,6 @@ int PIOc_def_var_impl(int ncid, const char *name, nc_type xtype, int ndims, * @param fill_value pointer to the fill value to be used if fill_mode is set to NC_FILL. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_def_var - * @author Jim Edwards, Ed Hartnett */ int PIOc_def_var_fill_impl(int ncid, int varid, int fill_mode, const void *fill_valuep) { @@ -3971,7 +4241,6 @@ int PIOc_def_var_fill_impl(int ncid, int varid, int fill_mode, const void *fill_ * this variable. Ignored if NULL. * @return PIO_NOERR for success, error code otherwise. * @ingroup PIO_inq_var_fill - * @author Jim Edwards, Ed Hartnett */ int PIOc_inq_var_fill_impl(int ncid, int varid, int *no_fill, void *fill_valuep) { @@ -4179,7 +4448,6 @@ int PIOc_inq_var_fill_impl(int ncid, int varid, int *no_fill, void *fill_valuep) * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. * @ingroup PIO_get_att - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_impl(int ncid, int varid, const char *name, void *ip) { @@ -4237,7 +4505,6 @@ int PIOc_get_att_impl(int ncid, int varid, const char *name, void *ip) * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const void *op) @@ -4258,7 +4525,6 @@ int PIOc_put_att_impl(int ncid, int varid, const char *name, nc_type xtype, * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_double_impl(int ncid, int varid, const char *name, double *ip) { @@ -4278,7 +4544,6 @@ int PIOc_get_att_double_impl(int ncid, int varid, const char *name, double *ip) * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_uchar_impl(int ncid, int varid, const char *name, unsigned char *ip) { @@ -4298,7 +4563,6 @@ int PIOc_get_att_uchar_impl(int ncid, int varid, const char *name, unsigned char * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_ushort_impl(int ncid, int varid, const char *name, unsigned short *ip) { @@ -4318,7 +4582,6 @@ int PIOc_get_att_ushort_impl(int ncid, int varid, const char *name, unsigned sho * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. * @ingroup PIO_get_att - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_uint_impl(int ncid, int varid, const char *name, unsigned int *ip) { @@ -4338,7 +4601,6 @@ int PIOc_get_att_uint_impl(int ncid, int varid, const char *name, unsigned int * * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_long_impl(int ncid, int varid, const char *name, long *ip) { @@ -4360,7 +4622,6 @@ int PIOc_get_att_long_impl(int ncid, int varid, const char *name, long *ip) * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. * @ingroup PIO_get_att - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_text_impl(int ncid, int varid, const char *name, char *ip) { @@ -4380,7 +4641,6 @@ int PIOc_get_att_text_impl(int ncid, int varid, const char *name, char *ip) * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_schar_impl(int ncid, int varid, const char *name, signed char *ip) { @@ -4400,7 +4660,6 @@ int PIOc_get_att_schar_impl(int ncid, int varid, const char *name, signed char * * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_ulonglong_impl(int ncid, int varid, const char *name, unsigned long long *ip) { @@ -4420,7 +4679,6 @@ int PIOc_get_att_ulonglong_impl(int ncid, int varid, const char *name, unsigned * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_short_impl(int ncid, int varid, const char *name, short *ip) { @@ -4440,7 +4698,6 @@ int PIOc_get_att_short_impl(int ncid, int varid, const char *name, short *ip) * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_int_impl(int ncid, int varid, const char *name, int *ip) { @@ -4460,7 +4717,6 @@ int PIOc_get_att_int_impl(int ncid, int varid, const char *name, int *ip) * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_longlong_impl(int ncid, int varid, const char *name, long long *ip) { @@ -4480,7 +4736,6 @@ int PIOc_get_att_longlong_impl(int ncid, int varid, const char *name, long long * @param name the name of the attribute to get * @param ip a pointer that will get the attribute value. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_get_att_float_impl(int ncid, int varid, const char *name, float *ip) { @@ -4502,7 +4757,6 @@ int PIOc_get_att_float_impl(int ncid, int varid, const char *name, float *ip) * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_schar_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const signed char *op) @@ -4525,7 +4779,6 @@ int PIOc_put_att_schar_impl(int ncid, int varid, const char *name, nc_type xtype * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_long_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const long *op) @@ -4548,7 +4801,6 @@ int PIOc_put_att_long_impl(int ncid, int varid, const char *name, nc_type xtype, * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_int_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const int *op) @@ -4571,7 +4823,6 @@ int PIOc_put_att_int_impl(int ncid, int varid, const char *name, nc_type xtype, * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_uchar_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned char *op) @@ -4594,7 +4845,6 @@ int PIOc_put_att_uchar_impl(int ncid, int varid, const char *name, nc_type xtype * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_longlong_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const long long *op) @@ -4617,7 +4867,6 @@ int PIOc_put_att_longlong_impl(int ncid, int varid, const char *name, nc_type xt * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_uint_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned int *op) @@ -4640,7 +4889,6 @@ int PIOc_put_att_uint_impl(int ncid, int varid, const char *name, nc_type xtype, * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_float_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const float *op) @@ -4663,7 +4911,6 @@ int PIOc_put_att_float_impl(int ncid, int varid, const char *name, nc_type xtype * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_ulonglong_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned long long *op) @@ -4686,7 +4933,6 @@ int PIOc_put_att_ulonglong_impl(int ncid, int varid, const char *name, nc_type x * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_ushort_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const unsigned short *op) @@ -4709,7 +4955,6 @@ int PIOc_put_att_ushort_impl(int ncid, int varid, const char *name, nc_type xtyp * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_text_impl(int ncid, int varid, const char *name, PIO_Offset len, const char *op) @@ -4732,7 +4977,6 @@ int PIOc_put_att_text_impl(int ncid, int varid, const char *name, * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_short_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const short *op) @@ -4755,7 +4999,6 @@ int PIOc_put_att_short_impl(int ncid, int varid, const char *name, nc_type xtype * @param len the length of the attribute array. * @param op a pointer with the attribute data. * @return PIO_NOERR for success, error code otherwise. - * @author Jim Edwards, Ed Hartnett */ int PIOc_put_att_double_impl(int ncid, int varid, const char *name, nc_type xtype, PIO_Offset len, const double *op) @@ -4780,7 +5023,6 @@ int PIOc_put_att_double_impl(int ncid, int varid, const char *name, nc_type xtyp * @param ovarid the ID of the variable to copy the attribute * to in the output file. * @return PIO_NOERR for success, error code otherwise. - * @author Jayesh Krishna */ int PIOc_copy_att_impl(int incid, int ivarid, const char *name, int oncid, int ovarid) @@ -4871,6 +5113,15 @@ int PIOc_copy_att_impl(int incid, int ivarid, const char *name, } ierr = check_netcdf(ios, ifile, ierr, __FILE__, __LINE__); break; +#endif +#ifdef _HDF5 + case PIO_IOTYPE_HDF5: + case PIO_IOTYPE_HDF5C: + if(ios->ioproc){ + ierr = spio_hdf5_copy_att(ifile->iosystem, ifile, ivarid, name, ofile, ovarid); + } + ierr = check_netcdf(ios, ifile, ierr, __FILE__, __LINE__); + break; #endif default: { @@ -4881,32 +5132,42 @@ int PIOc_copy_att_impl(int incid, int ivarid, const char *name, PIO_Offset att_len = 0, type_sz = 0; spio_ltimer_stop(ios->io_fstats->tot_timer_name); GPTLstop(ifile->io_fstats->tot_timer_name); - ierr = PIOc_inq_att_impl(ifile->fh, ivarid, name, &att_type, &att_len); + ierr = PIOc_inq_att_impl(incid, ivarid, name, &att_type, &att_len); if(ierr != PIO_NOERR){ + spio_ltimer_start(ios->io_fstats->tot_timer_name); + GPTLstart(ifile->io_fstats->tot_timer_name); ierr = pio_err(ios, ifile, ierr, __FILE__, __LINE__, "Copying attribute, %s, associated with variable %s (varid=%d) in file %s (ncid=%d) to file %s (ncid=%d) failed. Inquiring attribute type and length in file %s (ncid=%d) failed", name, pio_get_vname_from_file(ifile, ivarid), ivarid, pio_get_fname_from_file(ifile), incid, pio_get_fname_from_file(ofile), oncid, pio_get_fname_from_file(ifile), incid); break; } - ierr = PIOc_inq_type_impl(ifile->fh, att_type, NULL, &type_sz); + ierr = PIOc_inq_type_impl(incid, att_type, NULL, &type_sz); if(ierr != PIO_NOERR){ + spio_ltimer_start(ios->io_fstats->tot_timer_name); + GPTLstart(ifile->io_fstats->tot_timer_name); ierr = pio_err(ios, ifile, ierr, __FILE__, __LINE__, "Copying attribute, %s, associated with variable %s (varid=%d) in file %s (ncid=%d) to file %s (ncid=%d) failed. Inquiring attribute type size (attribute type = %x) failed", name, pio_get_vname_from_file(ifile, ivarid), ivarid, pio_get_fname_from_file(ifile), incid, pio_get_fname_from_file(ofile), oncid, att_type); break; } void *pbuf = malloc(type_sz * att_len); if(!pbuf){ + spio_ltimer_start(ios->io_fstats->tot_timer_name); + GPTLstart(ifile->io_fstats->tot_timer_name); ierr = pio_err(ios, ifile, PIO_ENOMEM, __FILE__, __LINE__, "Copying attribute, %s, associated with variable %s (varid=%d) in file %s (ncid=%d) to file %s (ncid=%d) failed. Unable to allocate %lld bytes to temporarily cache the attribute for copying", name, pio_get_vname_from_file(ifile, ivarid), ivarid, pio_get_fname_from_file(ifile), incid, pio_get_fname_from_file(ofile), oncid, (long long int) (type_sz * att_len)); break; } - ierr = PIOc_get_att_impl(ifile->fh, ivarid, name, pbuf); + ierr = PIOc_get_att_impl(incid, ivarid, name, pbuf); if(ierr != PIO_NOERR){ + spio_ltimer_start(ios->io_fstats->tot_timer_name); + GPTLstart(ifile->io_fstats->tot_timer_name); ierr = pio_err(ios, ifile, ierr, __FILE__, __LINE__, "Copying attribute, %s, associated with variable %s (varid=%d) in file %s (ncid=%d) to file %s (ncid=%d) failed. Getting attribute from file %s (ncid=%d) failed", name, pio_get_vname_from_file(ifile, ivarid), ivarid, pio_get_fname_from_file(ifile), incid, pio_get_fname_from_file(ofile), oncid, pio_get_fname_from_file(ifile), incid); break; } - ierr = PIOc_put_att_impl(ofile->fh, ovarid, name, att_type, att_len, pbuf); + ierr = PIOc_put_att_impl(oncid, ovarid, name, att_type, att_len, pbuf); if(ierr != PIO_NOERR){ + spio_ltimer_start(ios->io_fstats->tot_timer_name); + GPTLstart(ifile->io_fstats->tot_timer_name); ierr = pio_err(ios, ifile, ierr, __FILE__, __LINE__, "Copying attribute, %s, associated with variable %s (varid=%d) in file %s (ncid=%d) to file %s (ncid=%d) failed. Putting/Writing attribute to file %s (ncid=%d, varid=%d) failed", name, pio_get_vname_from_file(ifile, ivarid), ivarid, pio_get_fname_from_file(ifile), incid, pio_get_fname_from_file(ofile), oncid, pio_get_fname_from_file(ofile), oncid, ovarid); break; diff --git a/src/clib/pio_nc4.cpp b/src/clib/core/pio_nc4.cpp similarity index 99% rename from src/clib/pio_nc4.cpp rename to src/clib/core/pio_nc4.cpp index 56ca7ec7888..cd2d1f35b8e 100644 --- a/src/clib/pio_nc4.cpp +++ b/src/clib/core/pio_nc4.cpp @@ -2,7 +2,6 @@ * * Functions to wrap netCDF-4 functions for PIO. * - * @author Ed Hartnett */ #include #include @@ -30,7 +29,6 @@ * compressed. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_def_var - * @author Ed Hartnett */ int PIOc_def_var_deflate_impl(int ncid, int varid, int shuffle, int deflate, int deflate_level) @@ -129,7 +127,6 @@ int PIOc_def_var_deflate_impl(int ncid, int varid, int shuffle, int deflate, * if NULL. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_inq_var - * @author Ed Hartnett */ int PIOc_inq_var_deflate_impl(int ncid, int varid, int *shufflep, int *deflatep, int *deflate_levelp) @@ -251,7 +248,6 @@ int PIOc_inq_var_deflate_impl(int ncid, int varid, int *shufflep, int *deflatep, * @param chunksizep an array of chunksizes. Must have a chunksize for * every variable dimension. * @return PIO_NOERR for success, otherwise an error code. - * @author Ed Hartnett */ int PIOc_def_var_chunking_impl(int ncid, int varid, int storage, const PIO_Offset *chunksizesp) { @@ -394,7 +390,6 @@ int PIOc_def_var_chunking_impl(int ncid, int varid, int storage, const PIO_Offse * dimensions. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_inq_var - * @author Ed Hartnett */ int PIOc_inq_var_chunking_impl(int ncid, int varid, int *storagep, PIO_Offset *chunksizesp) { @@ -551,7 +546,6 @@ int PIOc_inq_var_chunking_impl(int ncid, int varid, int *storagep, PIO_Offset *c * every variable dimension. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_def_var - * @author Ed Hartnett */ int PIOc_def_var_endian_impl(int ncid, int varid, int endian) { @@ -632,7 +626,6 @@ int PIOc_def_var_endian_impl(int ncid, int varid, int endian) * endianness. Ignored if NULL. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_inq_var - * @author Ed Hartnett */ int PIOc_inq_var_endian_impl(int ncid, int varid, int *endianp) { @@ -734,7 +727,6 @@ int PIOc_inq_var_endian_impl(int ncid, int varid, int *endianp) * @param preemption preemption setting for file cache. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_def_var - * @author Ed Hartnett */ int PIOc_set_chunk_cache_impl(int iosysid, int iotype, PIO_Offset size, PIO_Offset nelems, float preemption) @@ -831,7 +823,6 @@ int PIOc_set_chunk_cache_impl(int iosysid, int iotype, PIO_Offset size, PIO_Offs * @param preemptionp gets the preemption setting for file cache. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_def_var - * @author Ed Hartnett */ int PIOc_get_chunk_cache_impl(int iosysid, int iotype, PIO_Offset *sizep, PIO_Offset *nelemsp, float *preemptionp) @@ -953,7 +944,6 @@ int PIOc_get_chunk_cache_impl(int iosysid, int iotype, PIO_Offset *sizep, PIO_Of * every variable dimension. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_def_var - * @author Ed Hartnett */ int PIOc_set_var_chunk_cache_impl(int ncid, int varid, PIO_Offset size, PIO_Offset nelems, float preemption) @@ -1040,7 +1030,6 @@ int PIOc_set_var_chunk_cache_impl(int ncid, int varid, PIO_Offset size, PIO_Offs * @param preemptionp will get the cache preemption value. Ignored if NULL. * @return PIO_NOERR for success, otherwise an error code. * @ingroup PIO_inq_var - * @author Ed Hartnett */ int PIOc_get_var_chunk_cache_impl(int ncid, int varid, PIO_Offset *sizep, PIO_Offset *nelemsp, float *preemptionp) diff --git a/src/clib/pio_put_nc.cpp b/src/clib/core/pio_put_nc.cpp similarity index 97% rename from src/clib/pio_put_nc.cpp rename to src/clib/core/pio_put_nc.cpp index a68e558ad65..0624612c257 100644 --- a/src/clib/pio_put_nc.cpp +++ b/src/clib/core/pio_put_nc.cpp @@ -2,9 +2,6 @@ * @file * PIO functions to write data. * - * @author Ed Hartnett - * @date 2016 - * @see http://code.google.com/p/parallelio/ */ #include @@ -30,7 +27,6 @@ * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_text_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const char *op) @@ -57,7 +53,6 @@ int PIOc_put_vars_text_impl(int ncid, int varid, const PIO_Offset *start, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_uchar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, @@ -85,7 +80,6 @@ int PIOc_put_vars_uchar_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_schar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const signed char *op) @@ -113,7 +107,6 @@ int PIOc_put_vars_schar_impl(int ncid, int varid, const PIO_Offset *start, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_ushort_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const unsigned short *op) @@ -140,7 +133,6 @@ int PIOc_put_vars_ushort_impl(int ncid, int varid, const PIO_Offset *start, cons * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_short_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const short *op) @@ -168,7 +160,6 @@ int PIOc_put_vars_short_impl(int ncid, int varid, const PIO_Offset *start, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_uint_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const unsigned int *op) @@ -195,7 +186,6 @@ int PIOc_put_vars_uint_impl(int ncid, int varid, const PIO_Offset *start, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_int_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const int *op) @@ -222,7 +212,6 @@ int PIOc_put_vars_int_impl(int ncid, int varid, const PIO_Offset *start, const P * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_long_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const long *op) @@ -249,7 +238,6 @@ int PIOc_put_vars_long_impl(int ncid, int varid, const PIO_Offset *start, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_float_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const float *op) @@ -277,7 +265,6 @@ int PIOc_put_vars_float_impl(int ncid, int varid, const PIO_Offset *start, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_longlong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const long long *op) @@ -305,7 +292,6 @@ int PIOc_put_vars_longlong_impl(int ncid, int varid, const PIO_Offset *start, co * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_double_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const double *op) @@ -333,7 +319,6 @@ int PIOc_put_vars_double_impl(int ncid, int varid, const PIO_Offset *start, cons * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const unsigned long long *op) @@ -354,7 +339,6 @@ int PIOc_put_vars_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, c * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_text_impl(int ncid, int varid, const PIO_Offset *index, const char *op) { @@ -374,7 +358,6 @@ int PIOc_put_var1_text_impl(int ncid, int varid, const PIO_Offset *index, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_uchar_impl(int ncid, int varid, const PIO_Offset *index, const unsigned char *op) @@ -395,7 +378,6 @@ int PIOc_put_var1_uchar_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_schar_impl(int ncid, int varid, const PIO_Offset *index, const signed char *op) @@ -416,7 +398,6 @@ int PIOc_put_var1_schar_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_ushort_impl(int ncid, int varid, const PIO_Offset *index, const unsigned short *op) @@ -437,7 +418,6 @@ int PIOc_put_var1_ushort_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_short_impl(int ncid, int varid, const PIO_Offset *index, const short *op) @@ -458,7 +438,6 @@ int PIOc_put_var1_short_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_uint_impl(int ncid, int varid, const PIO_Offset *index, const unsigned int *op) @@ -479,7 +458,6 @@ int PIOc_put_var1_uint_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_int_impl(int ncid, int varid, const PIO_Offset *index, const int *op) { @@ -499,7 +477,6 @@ int PIOc_put_var1_int_impl(int ncid, int varid, const PIO_Offset *index, const i * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_float_impl(int ncid, int varid, const PIO_Offset *index, const float *op) { @@ -519,7 +496,6 @@ int PIOc_put_var1_float_impl(int ncid, int varid, const PIO_Offset *index, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_long_impl(int ncid, int varid, const PIO_Offset *index, const long *op) { @@ -539,7 +515,6 @@ int PIOc_put_var1_long_impl(int ncid, int varid, const PIO_Offset *index, const * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_double_impl(int ncid, int varid, const PIO_Offset *index, const double *op) @@ -560,7 +535,6 @@ int PIOc_put_var1_double_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_ulonglong_impl(int ncid, int varid, const PIO_Offset *index, const unsigned long long *op) @@ -581,7 +555,6 @@ int PIOc_put_var1_ulonglong_impl(int ncid, int varid, const PIO_Offset *index, * used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_longlong_impl(int ncid, int varid, const PIO_Offset *index, const long long *op) @@ -605,7 +578,6 @@ int PIOc_put_var1_longlong_impl(int ncid, int varid, const PIO_Offset *index, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_text_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const char *op) @@ -629,7 +601,6 @@ int PIOc_put_vara_text_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_uchar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned char *op) @@ -653,7 +624,6 @@ int PIOc_put_vara_uchar_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_schar_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const signed char *op) @@ -677,7 +647,6 @@ int PIOc_put_vara_schar_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_ushort_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned short *op) @@ -701,7 +670,6 @@ int PIOc_put_vara_ushort_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_short_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const short *op) @@ -725,7 +693,6 @@ int PIOc_put_vara_short_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_uint_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned int *op) @@ -749,7 +716,6 @@ int PIOc_put_vara_uint_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_int_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const int *op) @@ -773,7 +739,6 @@ int PIOc_put_vara_int_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_long_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const long *op) @@ -797,7 +762,6 @@ int PIOc_put_vara_long_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_float_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const float *op) @@ -821,7 +785,6 @@ int PIOc_put_vara_float_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const unsigned long long *op) @@ -845,7 +808,6 @@ int PIOc_put_vara_ulonglong_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_longlong_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const long long *op) @@ -869,7 +831,6 @@ int PIOc_put_vara_longlong_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_double_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const double *op) @@ -893,7 +854,6 @@ int PIOc_put_vara_double_impl(int ncid, int varid, const PIO_Offset *start, * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_text_impl(int ncid, int varid, const char *op) { @@ -916,7 +876,6 @@ int PIOc_put_var_text_impl(int ncid, int varid, const char *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_uchar_impl(int ncid, int varid, const unsigned char *op) { @@ -939,7 +898,6 @@ int PIOc_put_var_uchar_impl(int ncid, int varid, const unsigned char *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_schar_impl(int ncid, int varid, const signed char *op) { @@ -962,7 +920,6 @@ int PIOc_put_var_schar_impl(int ncid, int varid, const signed char *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_ushort_impl(int ncid, int varid, const unsigned short *op) { @@ -985,7 +942,6 @@ int PIOc_put_var_ushort_impl(int ncid, int varid, const unsigned short *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_short_impl(int ncid, int varid, const short *op) { @@ -1008,7 +964,6 @@ int PIOc_put_var_short_impl(int ncid, int varid, const short *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_uint_impl(int ncid, int varid, const unsigned int *op) { @@ -1031,7 +986,6 @@ int PIOc_put_var_uint_impl(int ncid, int varid, const unsigned int *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_int_impl(int ncid, int varid, const int *op) { @@ -1054,7 +1008,6 @@ int PIOc_put_var_int_impl(int ncid, int varid, const int *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_long_impl(int ncid, int varid, const long *op) { @@ -1077,7 +1030,6 @@ int PIOc_put_var_long_impl(int ncid, int varid, const long *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_float_impl(int ncid, int varid, const float *op) { @@ -1100,7 +1052,6 @@ int PIOc_put_var_float_impl(int ncid, int varid, const float *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_ulonglong_impl(int ncid, int varid, const unsigned long long *op) { @@ -1123,7 +1074,6 @@ int PIOc_put_var_ulonglong_impl(int ncid, int varid, const unsigned long long *o * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_longlong_impl(int ncid, int varid, const long long *op) { @@ -1146,7 +1096,6 @@ int PIOc_put_var_longlong_impl(int ncid, int varid, const long long *op) * the variable will be used. * @param op pointer to the data to be written. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_double_impl(int ncid, int varid, const double *op) { @@ -1163,7 +1112,6 @@ int PIOc_put_var_double_impl(int ncid, int varid, const double *op) * @param varid the variable ID number * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var_impl(int ncid, int varid, const void *op) { @@ -1183,7 +1131,6 @@ int PIOc_put_var_impl(int ncid, int varid, const void *op) * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_var1_impl(int ncid, int varid, const PIO_Offset *index, const void *op) { @@ -1206,7 +1153,6 @@ int PIOc_put_var1_impl(int ncid, int varid, const PIO_Offset *index, const void * the variable will be used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vara_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const void *op) @@ -1233,7 +1179,6 @@ int PIOc_put_vara_impl(int ncid, int varid, const PIO_Offset *start, const PIO_O * used. * @param buf pointer that will get the data. * @return PIO_NOERR on success, error code otherwise. - * @author Ed Hartnett */ int PIOc_put_vars_impl(int ncid, int varid, const PIO_Offset *start, const PIO_Offset *count, const PIO_Offset *stride, const void *op) diff --git a/src/clib/pio_spmd.cpp b/src/clib/core/pio_spmd.cpp similarity index 88% rename from src/clib/pio_spmd.cpp rename to src/clib/core/pio_spmd.cpp index 4ef329410bf..77b3babfb4d 100644 --- a/src/clib/pio_spmd.cpp +++ b/src/clib/core/pio_spmd.cpp @@ -4,8 +4,6 @@ * Atmosphere Model; C translation. This includes MPI_Gather, * MPI_Gatherv, and MPI_Alltoallw with flow control options. * - * @author Jim Edwards - * @date 2014 */ #include #include @@ -19,7 +17,6 @@ * @param i input number * @returns the smallest power of 2 greater than * or equal to i. - * @author Jim Edwards */ int ceil2(int i) { @@ -38,7 +35,6 @@ int ceil2(int i) * @param p integer between 0 and np - 1. * @param k integer between 0 and np - 1. * @returns (p + 1) ^ k else -1. - * @author Jim Edwards */ int pair(int np, int p, int k) { @@ -72,7 +68,6 @@ int pair(int np, int p, int k) * @param comm MPI communicator for the MPI_Alltoallw call. * @param fc pointer to the struct that provided flow control options. * @returns 0 for success, error code otherwise. - * @author Jim Edwards */ int pio_swapm(const void *sendbuf, const int *sendcounts, const int *sdispls, const MPI_Datatype *sendtypes, void *recvbuf, const int *recvcounts, const int *rdispls, const MPI_Datatype *recvtypes, @@ -465,6 +460,96 @@ int pio_swapm(const void *sendbuf, const int *sendcounts, const int *sdispls, co return PIO_NOERR; } +/** + * Non-blocking wait on swapm request + * @param p User swapm request (the request passed to pio_swapm() call) + * @param flag bool that holds the status of the user request. + * The flag is false if some requests are pending and true if all the requests are completed + * @returns PIO_NOERR on success, a pio error code otherwise + * - Similar to MPI_Testall(), makes progress on all requests + */ +int pio_swapm_iwait(void *p, bool &flag) +{ + pio_swapm_req *ureq = (pio_swapm_req *)p; + int status = 0; + int mpierr; + + assert(ureq != NULL); + if(ureq->nrcvids != 0){ + mpierr = MPI_Testall(ureq->nrcvids, ureq->rcvids, &status, MPI_STATUSES_IGNORE); + if(mpierr != MPI_SUCCESS){ return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); } + + /* status == 1 (true), implies that all the request have completed */ + if(status == 1){ + free(ureq->rcvids); + ureq->nrcvids = 0; + } + } + if(ureq->nsndids != 0){ + mpierr = MPI_Testall(ureq->nsndids, ureq->sndids, &status, MPI_STATUSES_IGNORE); + if(mpierr != MPI_SUCCESS){ return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); } + + /* status == 1 (true), implies that all the request have completed */ + if(status == 1){ + free(ureq->sndids); + ureq->nsndids = 0; + } + } + + flag = (status == 1); + return PIO_NOERR; +} + +/** + * Blocking wait on user swapm request + * @param p User swapm request (the request passed to pio_swapm() call) + * @returns PIO_NOERR on success, a pio error code otherwise + * - Similar to MPI_Waitall(), waits for the request to complete + */ +int pio_swapm_wait(void *p) +{ + pio_swapm_req *ureq = (pio_swapm_req *)p; + int mpierr; + + assert(ureq != NULL); + if(ureq->nrcvids != 0) + { + mpierr = MPI_Waitall(ureq->nrcvids, ureq->rcvids, MPI_STATUSES_IGNORE); + if(mpierr != MPI_SUCCESS) + { + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + ureq->nrcvids = 0; + } + if(ureq->nsndids != 0) + { + mpierr = MPI_Waitall(ureq->nsndids, ureq->sndids, MPI_STATUSES_IGNORE); + if(mpierr != MPI_SUCCESS) + { + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + ureq->nsndids = 0; + } + return PIO_NOERR; +} + +/* + * Free a pio swapm user request + * @param p pointer to the pio swapm user request + */ +void pio_swapm_req_free(void *p) +{ + pio_swapm_req *ureq = (pio_swapm_req *)p; + if(ureq != NULL) + { + free(ureq->rcvids); + free(ureq->sndids); + free(ureq); + } +} + /** * Provides the functionality of MPI_Gatherv with flow control * options. This function is not currently used, but we hope it will @@ -486,7 +571,6 @@ int pio_swapm(const void *sendbuf, const int *sendcounts, const int *sdispls, co * @param comm communicator. * @param flow_cntl if non-zero, flow control will be used. * @returns 0 for success, error code otherwise. - * @author Jim Edwards */ int pio_fc_gatherv(const void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, const int *recvcnts, const int *displs, diff --git a/src/clib/pio_varm.cpp b/src/clib/core/pio_varm.cpp similarity index 100% rename from src/clib/pio_varm.cpp rename to src/clib/core/pio_varm.cpp diff --git a/src/clib/pioc.cpp b/src/clib/core/pioc.cpp similarity index 79% rename from src/clib/pioc.cpp rename to src/clib/core/pioc.cpp index 62431c26291..195567931ae 100644 --- a/src/clib/pioc.cpp +++ b/src/clib/core/pioc.cpp @@ -1,10 +1,7 @@ /** * @file * Some initialization and support functions. - * @author Jim Edwards - * @date 2014 * - * @see http://code.google.com/p/parallelio/ */ #include @@ -17,6 +14,16 @@ #include "pio_sdecomps_regex.h" #include "spio_io_summary.h" #include "spio_rearrange_any.h" +#include "spio_ltimer.h" +#include "spio_ltimer_utils.hpp" +#include "spio_gptl_utils.hpp" +#include "pio_rearr_contig.hpp" +#include "spio_dbg_utils.hpp" +#include "spio_decomp_map_info_pool.hpp" +#include "spio_decomp_logger.hpp" +#include "spio_hdf5_utils.hpp" +#include "spio_async_tcomm.hpp" +#include "spio_async_hdf5_utils.hpp" /* Include headers for HDF5 compression filters */ #if PIO_USE_HDF5 @@ -29,6 +36,9 @@ #include "blosc2_filter.h" #endif #endif +#include +#include +#include bool fortran_order = false; @@ -46,7 +56,6 @@ extern int blocksize; * @param active pointer that gets true if IO system is active, false * otherwise. * @returns 0 on success, error code otherwise - * @author Jim Edwards */ int PIOc_iosystem_is_active_impl(int iosysid, bool *active) { @@ -73,7 +82,6 @@ int PIOc_iosystem_is_active_impl(int iosysid, bool *active) * * @param ncid the ncid of an open file * @returns 1 if file is open, 0 otherwise. - * @author Jim Edwards */ int PIOc_File_is_Open_impl(int ncid) { @@ -140,7 +148,6 @@ static const char *PIO_error_handler_to_string(int eh) * @param method the error handling method * @returns old error handler * @ingroup PIO_error_method - * @author Jim Edwards */ int PIOc_Set_File_Error_Handling_impl(int ncid, int method) { @@ -173,7 +180,6 @@ int PIOc_Set_File_Error_Handling_impl(int ncid, int method) * @param ncid the ncid of the open file * @param varid the variable ID * @returns 0 on success, error code otherwise - * @author Jim Edwards, Ed Hartnett */ int PIOc_advanceframe_impl(int ncid, int varid) { @@ -227,7 +233,6 @@ int PIOc_advanceframe_impl(int ncid, int varid) * first record, 1 for the second * @return PIO_NOERR for no error, or error code. * @ingroup PIO_setframe - * @author Jim Edwards, Ed Hartnett */ int PIOc_setframe_impl(int ncid, int varid, int frame) { @@ -340,56 +345,19 @@ int PIOc_setframe_impl(int ncid, int varid, int frame) #endif #ifdef _HDF5 - if ((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)) - { - if (frame >= 0) - { - if (ios->ioproc) - { - hsize_t dims[H5S_MAX_RANK]; - hsize_t mdims[H5S_MAX_RANK]; - - /* Get current dimension size */ - hid_t file_space_id = H5Dget_space(file->hdf5_vars[varid].hdf5_dataset_id); - if (file_space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to make a copy of the dataspace of the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - int ndim = H5Sget_simple_extent_dims(file_space_id, dims, mdims); - if (ndim < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to retrieve dimension size and maximum size of a dataspace copied from the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Sclose(file_space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a dataspace copied from the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - /* Extend record dimension if needed */ - if (ndim > 0 && mdims[0] == H5S_UNLIMITED && dims[0] < (hsize_t)(frame + 1)) - { - dims[0] = frame + 1; - if (H5Dextend(file->hdf5_vars[varid].hdf5_dataset_id, dims) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to extend the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - } + if((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)){ + if((frame >= 0) && (ios->ioproc)){ +#if PIO_USE_ASYNC_WR_THREAD + ret = spio_iosys_async_hdf5_set_frame_op_add(file, varid, frame); +#else + ret = spio_hdf5_set_frame(file, varid, frame); +#endif + if(ret != PIO_NOERR){ + return pio_err(ios, file, ret, __FILE__, __LINE__, + "Setting frame to variable (%s, varid=%d) in file (%s, ncid=%d) using HDF5 iotype failed", + pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); } + } } #endif @@ -403,7 +371,6 @@ int PIOc_setframe_impl(int ncid, int varid, int frame) * @param numiotasks a pointer taht gets the number of IO * tasks. Ignored if NULL. * @returns 0 on success, error code otherwise - * @author Ed Hartnett */ int PIOc_get_numiotasks_impl(int iosysid, int *numiotasks) { @@ -429,7 +396,6 @@ int PIOc_get_numiotasks_impl(int iosysid, int *numiotasks) * @param ioid IO description ID of an IO decomposition that represents the * variable layout. * @returns the local size (size on the local process) of the variable. - * @author Jim Edwards */ int PIOc_get_local_array_size_impl(int ioid) { @@ -453,7 +419,6 @@ int PIOc_get_local_array_size_impl(int ioid) * @param method the error handling method * @returns old error handler * @ingroup PIO_error_method - * @author Jim Edwards */ int PIOc_Set_IOSystem_Error_Handling_impl(int iosysid, int method) { @@ -485,7 +450,6 @@ int PIOc_Set_IOSystem_Error_Handling_impl(int iosysid, int method) * if NULL. * @returns 0 for success, error code otherwise. * @ingroup PIO_error_method - * @author Jim Edwards, Ed Hartnett */ int PIOc_set_iosystem_error_handling_impl(int iosysid, int method, int *old_method) { @@ -540,102 +504,199 @@ int PIOc_set_iosystem_error_handling_impl(int iosysid, int method, int *old_meth } /* Create a unique string/name using information provided by the user */ -int pio_create_uniq_str(iosystem_desc_t *ios, io_desc_t *iodesc, char *str, int len, const char *prefix, const char *suffix) +void pio_create_uniq_str(iosystem_desc_t *ios, io_desc_t *iodesc, std::string &str, const std::string &prefix, const std::string &suffix) { - static int counter = 0; - const char *DEFAULT_PREFIX = "pio"; - const char *DEFAULT_SUFFIX = ".dat"; - const int HUNDRED = 100; - const char *INT_FMT_LT_HUNDRED = "%2.2d"; - const int TEN_THOUSAND = 1000; - const char *INT_FMT_LT_TEN_THOUSAND = "%4.4d"; - const int MILLION = 1000000; - const char *INT_FMT_LT_MILLION = "%6.6d"; - - assert(str && (len > 0)); - - if(!prefix) - { - prefix = DEFAULT_PREFIX; - } - if(!suffix) - { - suffix = DEFAULT_SUFFIX; - } - - int rem_len = len; - char *sptr = str; - - /* Add prefix */ - int prefix_len = strlen(prefix); - assert(rem_len > prefix_len); - snprintf(sptr, rem_len, "%s", prefix); - rem_len -= prefix_len; - sptr += prefix_len; - - if(ios) - { - /* Add ios specific info into the str */ - assert(ios->iosysid < MILLION); - assert(ios->num_comptasks < MILLION); - assert(rem_len > 0); - const char *iosysid_fmt = (ios->iosysid < HUNDRED) ? (INT_FMT_LT_HUNDRED) : ((ios->iosysid < TEN_THOUSAND) ? (INT_FMT_LT_TEN_THOUSAND): INT_FMT_LT_MILLION); - const char *num_comptasks_fmt = (ios->num_comptasks < HUNDRED) ? (INT_FMT_LT_HUNDRED) : ((ios->num_comptasks < TEN_THOUSAND) ? (INT_FMT_LT_TEN_THOUSAND): INT_FMT_LT_MILLION); - const char *num_iotasks_fmt = num_comptasks_fmt; - - snprintf(sptr, rem_len, iosysid_fmt, ios->iosysid); - rem_len = len - strlen(str); - sptr = str + strlen(str); - snprintf(sptr, rem_len, "%s", "id"); - rem_len = len - strlen(str); - sptr = str + strlen(str); - - snprintf(sptr, rem_len, num_comptasks_fmt, ios->num_comptasks); - rem_len = len - strlen(str); - sptr = str + strlen(str); - snprintf(sptr, rem_len, "%s", "tasks"); - rem_len = len - strlen(str); - sptr = str + strlen(str); - - snprintf(sptr, rem_len, num_iotasks_fmt, ios->num_iotasks); - rem_len = len - strlen(str); - sptr = str + strlen(str); - snprintf(sptr, rem_len, "%s", "io"); - rem_len = len - strlen(str); - sptr = str + strlen(str); - } - - if(iodesc) - { - /* Add iodesc specific info into the str */ - assert(iodesc->ndims < MILLION); - assert(rem_len > 0); - const char *ndims_fmt = (iodesc->ndims < HUNDRED) ? (INT_FMT_LT_HUNDRED) : ((iodesc->ndims < TEN_THOUSAND) ? (INT_FMT_LT_TEN_THOUSAND): INT_FMT_LT_MILLION); - snprintf(sptr, rem_len, ndims_fmt, iodesc->ndims); - rem_len = len - strlen(str); - sptr = str + strlen(str); - snprintf(sptr, rem_len, "%s", "dims"); - rem_len = len - strlen(str); - sptr = str + strlen(str); - } - - /* Add counter - to make the str unique */ - assert(counter < MILLION); - const char *counter_fmt = (counter < HUNDRED) ? (INT_FMT_LT_HUNDRED) : ((counter < TEN_THOUSAND) ? (INT_FMT_LT_TEN_THOUSAND): INT_FMT_LT_MILLION); - snprintf(sptr, rem_len, counter_fmt, counter); - rem_len = len - strlen(str); - sptr = str + strlen(str); - - counter++; - - /* Add suffix */ - int suffix_len = strlen(suffix); - assert(rem_len > suffix_len); - snprintf(sptr, rem_len, "%s", suffix); - rem_len -= suffix_len; - sptr += suffix_len; + static std::atomic counter(0); + const std::string DEFAULT_PREFIX("pio"); + const std::string DEFAULT_SUFFIX(".dat"); + + /* Add prefix */ + str += (!prefix.empty()) ? prefix : DEFAULT_PREFIX; + if(ios){ + /* Add ios specific info into the str */ + str += std::to_string(ios->iosysid) + std::string("id"); + str += std::to_string(ios->num_comptasks) + std::string("tasks"); + str += std::to_string(ios->num_iotasks) + std::string("io"); + } + if(iodesc){ + /* Add iodesc specific info into the str */ + str += std::to_string(iodesc->ndims) + std::string("dims"); + } - return PIO_NOERR; + /* Add counter - to make the str unique */ + str += std::to_string(counter++); + + /* Add suffix */ + str += (!suffix.empty()) ? suffix : DEFAULT_SUFFIX; +} + +static int subset_rearranger_init(iosystem_desc_t *ios, io_desc_t *iodesc, const PIO_Offset *iostart, const PIO_Offset *iocount) +{ + int ret = PIO_NOERR; + + assert(ios && iodesc); + assert(iodesc->map && iodesc->dimlen); + + iodesc->num_aiotasks = ios->num_iotasks; + LOG((2, "creating subset rearranger iodesc->num_aiotasks = %d", iodesc->num_aiotasks)); + if((ret = subset_rearrange_create(ios, iodesc->maplen, (PIO_Offset *)(iodesc->map), + iodesc->dimlen, iodesc->ndims, iodesc))){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Error creating the SUBSET rearranger"); + } + + return PIO_NOERR; +} + +static int box_rearranger_init(iosystem_desc_t *ios, io_desc_t *iodesc, const PIO_Offset *iostart, const PIO_Offset *iocount) +{ + int ret = PIO_NOERR; + assert(ios && iodesc); + assert(iodesc->map && iodesc->dimlen && iodesc->firstregion); + + if(ios->ioproc){ + /* Unless the user specifies the start and count for each + * IO task compute it. */ + if(iostart && iocount){ + LOG((3, "iostart and iocount provided")); + for(int i = 0; i < iodesc->ndims; i++){ + iodesc->firstregion->start[i] = iostart[i]; + iodesc->firstregion->count[i] = iocount[i]; + } + iodesc->num_aiotasks = ios->num_iotasks; + } + else{ + /* Compute start and count values for each io task. */ + LOG((2, "about to call CalcStartandCount piotype = %d ndims = %d", iodesc->piotype, iodesc->ndims)); + if((ret = CalcStartandCount(iodesc->piotype, iodesc->ndims, iodesc->dimlen, ios->num_iotasks, + ios->io_rank, iodesc->firstregion->start, + iodesc->firstregion->count, &iodesc->num_aiotasks))){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Internal error calculating start/count for the decomposition"); + } + } + + /* Compute the max io buffer size needed for an iodesc. */ + if((ret = compute_maxIObuffersize(ios->io_comm, iodesc))){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Internal error computing max io buffer size needed for the decomposition"); + } + LOG((3, "compute_maxIObuffersize called iodesc->maxiobuflen = %d", iodesc->maxiobuflen)); + } + + /* Depending on array size and io-blocksize the actual number + * of io tasks used may vary. */ + if((ret = MPI_Bcast(&(iodesc->num_aiotasks), 1, MPI_INT, ios->ioroot, ios->my_comm))){ + return check_mpi(ios, NULL, ret, __FILE__, __LINE__); + } + LOG((3, "iodesc->num_aiotasks = %d", iodesc->num_aiotasks)); + + /* Compute the communications pattern for this decomposition. */ + if((ret = box_rearrange_create(ios, iodesc->maplen, iodesc->map, iodesc->dimlen, iodesc->ndims, iodesc))){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Error initializing the PIO decomposition. Error creating the BOX rearranger"); + } + + return PIO_NOERR; +} + +static void init_iodesc_contig_rearr_fields(iosystem_desc_t *ios, io_desc_t *iodesc) +{ + assert(ios && iodesc && iodesc->rearr && iodesc->rearr->is_init()); + + iodesc->ndof = iodesc->rearr->get_decomp_map_lsz(); + if(ios->ioproc){ + iodesc->num_aiotasks = ios->num_iotasks; + } + + /* The first region is pre-allocated when iodesc is allocated */ + iodesc->maxregions = 0; + /* FIXME: We only need fillvalues when the total decomp map size is less than var size */ + iodesc->needsfill = true; + /* FIXME: Remove the field since we no longer use maxbytes */ + iodesc->maxbytes = 0; + iodesc->llen = iodesc->rearr->get_rearrange_buf_sz(); + iodesc->maxiobuflen = iodesc->rearr->get_decomp_gsz(); + + if(ios->ioproc){ + assert(iodesc->firstregion && iodesc->firstregion->start && iodesc->firstregion->count); + /* Get (start, count) pairs of contiguous decomp ranges for this I/O process */ + std::vector > off_ranges = iodesc->rearr->get_rearr_decomp_map_contig_ranges(); + io_region *cur_region = iodesc->firstregion; + io_region *prev_region = cur_region; + int loff = 0; + + LOG((1, "Contig rearranger : iodesc {ioid=%d, llen=%lld, maxiobuflen=%lld, ndims=%d, dimlen=%s, maxregions=%lld", iodesc->ioid, static_cast(iodesc->llen), static_cast(iodesc->maxiobuflen), iodesc->ndims, SPIO_Util::Dbg_Util::vec1d_to_string(iodesc->dimlen, iodesc->dimlen + iodesc->ndims).c_str(), static_cast(off_ranges.size()))); + // FIXME : Move to C++ lists for region list + for(std::size_t i = 0; i < off_ranges.size(); i++){ + if(cur_region == NULL){ + alloc_region2(ios, iodesc->ndims, &cur_region); + prev_region->next = cur_region; + } + cur_region->loffset = loff; + loff += static_cast(off_ranges[i].second); + /* Convert the range (start, count) offsets to dim indices & store in the region info */ + iodesc->rearr->off_range_to_dim_range(off_ranges[i].first, off_ranges[i].second, + cur_region->start, cur_region->count); + + LOG((1, "Contig range = [%lld, %lld), start=%s, count=%s", static_cast(off_ranges[i].first), static_cast(off_ranges[i].second), SPIO_Util::Dbg_Util::vec1d_to_string(cur_region->start, cur_region->start + iodesc->ndims).c_str(), SPIO_Util::Dbg_Util::vec1d_to_string(cur_region->count, cur_region->count + iodesc->ndims).c_str())); + prev_region = cur_region; + cur_region = cur_region->next; + } + iodesc->maxregions = iodesc->rearr->get_rearr_decomp_map_contig_max_nranges(); + } +} + +static int contig_rearranger_init(iosystem_desc_t *ios, io_desc_t *iodesc, const PIO_Offset *iostart, const PIO_Offset *iocount) +{ + int ret = PIO_NOERR; + + assert(ios && iodesc); + assert(iodesc->map && iodesc->dimlen); + + if(iostart && iocount){ + /* FIXME: We should at least convert iostart/iocount arrays to decomp maps */ + } + + iodesc->rearr = new SPIO::DataRearr::Contig_rearr(ios); + ret = iodesc->rearr->init(iodesc->piotype, iodesc->map, iodesc->maplen, iodesc->dimlen, iodesc->ndims, iodesc); + if(ret != PIO_NOERR){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Error initializing the I/O decomposition. Error initializing PIO_REARR_CONTIG rearranger"); + } + + init_iodesc_contig_rearr_fields(ios, iodesc); + + return PIO_NOERR; +} + +static inline void dbg_log_iodesc(iosystem_desc_t *ios, io_desc_t *iodesc) +{ + assert(ios && iodesc); + + /* Log results. */ + LOG((2, "iodesc ioid = %d nrecvs = %d ndof = %d ndims = %d num_aiotasks = %d " + "rearranger = %d maxregions = %d needsfill = %d llen = %d maxiobuflen = %d", + iodesc->ioid, iodesc->nrecvs, iodesc->ndof, iodesc->ndims, iodesc->num_aiotasks, + iodesc->rearranger, iodesc->maxregions, iodesc->needsfill, iodesc->llen, + iodesc->maxiobuflen)); + if(ios->ioproc){ + if(iodesc->rearranger == PIO_REARR_SUBSET){ + for(int j = 0; j < iodesc->llen; j++){ + LOG((3, "rindex[%d] = %lld", j, iodesc->rindex[j])); + } + } + else{ + int totalrecv = 0; + for(int j = 0; j < iodesc->nrecvs; j++){ + totalrecv += iodesc->rcount[j]; + } + + for(int j = 0; j < totalrecv; j++){ + LOG((3, "rindex[%d] = %lld", j, iodesc->rindex[j])); + } + } + } } /** @@ -679,303 +740,248 @@ int pio_create_uniq_str(iosystem_desc_t *ios, io_desc_t *iodesc, char *str, int * iostarts are generated. * @returns 0 on success, error code otherwise * @ingroup PIO_initdecomp - * @author Jim Edwards, Ed Hartnett */ -int PIOc_InitDecomp_impl(int iosysid, int pio_type, int ndims, const int *gdimlen, int maplen, - const PIO_Offset *compmap, int *ioidp, const int *rearranger, - const PIO_Offset *iostart, const PIO_Offset *iocount) +static int initdecomp(int iosysid, int pio_type, int ndims, const int *gdimlen, int maplen, + const PIO_Offset *compmap, int *ioidp, const int *rearranger, + const PIO_Offset *iostart, const PIO_Offset *iocount, bool map_zero_based=false) { - iosystem_desc_t *ios; /* Pointer to io system information. */ - io_desc_t *iodesc; /* The IO description. */ - int mpierr = MPI_SUCCESS; /* Return code from MPI function calls. */ - int ierr; /* Return code. */ - - GPTLstart("PIO:PIOc_initdecomp"); - LOG((1, "PIOc_InitDecomp iosysid = %d pio_type = %d ndims = %d maplen = %d", - iosysid, pio_type, ndims, maplen)); + iosystem_desc_t *ios; /* Pointer to io system information. */ + io_desc_t *iodesc; /* The IO description. */ + int mpierr = MPI_SUCCESS; /* Return code from MPI function calls. */ + int ierr; /* Return code. */ + + LOG((1, "PIOc_InitDecomp iosysid = %d pio_type = %d ndims = %d maplen = %d", + iosysid, pio_type, ndims, maplen)); + + /* Get IO system info. */ + if(!(ios = pio_get_iosystem_from_id(iosysid))){ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Invalid io system id (%d) provided. Could not find an iosystem associated with the id", iosysid); + } + assert(ios); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_tot_timer(ios->io_fstats->tot_timer_name); - /* Get IO system info. */ - if (!(ios = pio_get_iosystem_from_id(iosysid))) - { - GPTLstop("PIO:PIOc_initdecomp"); - return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Invalid io system id (%d) provided. Could not find an iosystem associated with the id", iosysid); - } - assert(ios); - spio_ltimer_start(ios->io_fstats->tot_timer_name); + /* Caller must provide these. */ + if(!gdimlen || !compmap || !ioidp){ + return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Invalid pointers (NULL) to gdimlen(%s) or compmap(%s) or ioidp (%s) provided", (gdimlen) ? "not NULL" : "NULL", (compmap) ? "not NULL" : "NULL", (ioidp) ? "not NULL" : "NULL"); + } - /* Caller must provide these. */ - if (!gdimlen || !compmap || !ioidp) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Invalid pointers (NULL) to gdimlen(%s) or compmap(%s) or ioidp (%s) provided", (gdimlen) ? "not NULL" : "NULL", (compmap) ? "not NULL" : "NULL", (ioidp) ? "not NULL" : "NULL"); + /* Check the dim lengths. */ + for(int i = 0; i < ndims; i++){ + if(gdimlen[i] <= 0){ + return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Invalid value for global dimension lengths provided. The global length of dimension %d is provided as %d (expected > 0)", i, gdimlen[i]); } + } - /* Check the dim lengths. */ - for (int i = 0; i < ndims; i++) - if (gdimlen[i] <= 0) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Invalid value for global dimension lengths provided. The global length of dimension %d is provided as %d (expected > 0)", i, gdimlen[i]); - } - - /* If async is in use, and this is not an IO task, bcast the parameters. */ - if (ios->async) - { - int msg = PIO_MSG_INITDECOMP_DOF; /* Message for async notification. */ - char rearranger_present = rearranger ? true : false; - int amsg_rearranger = (rearranger) ? (*rearranger) : 0; - char iostart_present = iostart ? true : false; - char iocount_present = iocount ? true : false; - PIO_Offset *amsg_iostart = NULL, *amsg_iocount = NULL; + /* If async is in use, and this is not an IO task, bcast the parameters. */ + if(ios->async){ + int msg = PIO_MSG_INITDECOMP_DOF; /* Message for async notification. */ + char rearranger_present = rearranger ? true : false; + int amsg_rearranger = (rearranger) ? (*rearranger) : 0; + char iostart_present = iostart ? true : false; + char iocount_present = iocount ? true : false; + PIO_Offset *amsg_iostart = NULL, *amsg_iocount = NULL; - if(!iostart_present) - { - amsg_iostart = (PIO_Offset *) calloc(ndims, sizeof(PIO_Offset)); - } - if(!iocount_present) - { - amsg_iocount = (PIO_Offset *) calloc(ndims, sizeof(PIO_Offset)); - } - if(!amsg_iostart || !amsg_iocount) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Out of memory allocating %lld bytes for start array and %lld bytes for count array for sending asynchronous message, PIO_MSG_INITDECOMP_DOF, on iosystem (iosysid=%d)", (unsigned long long) (ndims * sizeof(PIO_Offset)), (unsigned long long) (ndims * sizeof(PIO_Offset)), ios->iosysid); - } + if(!iostart_present){ + amsg_iostart = (PIO_Offset *) calloc(ndims, sizeof(PIO_Offset)); + } + if(!iocount_present){ + amsg_iocount = (PIO_Offset *) calloc(ndims, sizeof(PIO_Offset)); + } + if(!amsg_iostart || !amsg_iocount){ + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Out of memory allocating %lld bytes for start array and %lld bytes for count array for sending asynchronous message, PIO_MSG_INITDECOMP_DOF, on iosystem (iosysid=%d)", (unsigned long long) (ndims * sizeof(PIO_Offset)), (unsigned long long) (ndims * sizeof(PIO_Offset)), ios->iosysid); + } - PIO_SEND_ASYNC_MSG(ios, msg, &ierr, iosysid, pio_type, ndims, - gdimlen, maplen, compmap, rearranger_present, amsg_rearranger, - iostart_present, ndims, - (iostart_present) ? iostart : amsg_iostart, - iocount_present, ndims, - (iocount_present) ? iocount : amsg_iocount); + PIO_SEND_ASYNC_MSG(ios, msg, &ierr, iosysid, pio_type, ndims, + gdimlen, maplen, compmap, rearranger_present, amsg_rearranger, + iostart_present, ndims, + (iostart_present) ? iostart : amsg_iostart, + iocount_present, ndims, + (iocount_present) ? iocount : amsg_iocount); - if(!iostart_present) - { - free(amsg_iostart); - } - if(!iocount_present) - { - free(amsg_iocount); - } + if(!iostart_present){ + free(amsg_iostart); } - - /* Allocate space for the iodesc info. This also allocates the - * first region and copies the rearranger opts into this - * iodesc. */ - if ((ierr = malloc_iodesc(ios, pio_type, ndims, &iodesc))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Out of memory allocating memory for I/O descriptor"); + if(!iocount_present){ + free(amsg_iocount); } + } - /* Remember the maplen. */ - iodesc->maplen = maplen; + /* Allocate space for the iodesc info. This also allocates the + * first region and copies the rearranger opts into this + * iodesc. */ + if((ierr = malloc_iodesc(ios, pio_type, ndims, maplen, &iodesc))){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Out of memory allocating memory for I/O descriptor (ndims = %d, maplen = %d)", ndims, maplen); + } - /* Remember the map. */ - if (!(iodesc->map = (PIO_Offset *) malloc(sizeof(PIO_Offset) * maplen))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Out of memory allocating %lld bytes to store I/O decomposition map", (unsigned long long) (sizeof(PIO_Offset) * maplen)); - } - for (int m = 0; m < maplen; m++) - iodesc->map[m] = compmap[m]; + /* Set the rearranger. */ + if(!rearranger){ + iodesc->rearranger = ios->default_rearranger; + } + else{ + iodesc->rearranger = *rearranger; + } + LOG((2, "iodesc->rearranger = %d", iodesc->rearranger)); + + /* In scenarios involving ultra-high resolution E3SM/SCREAM cases, + * the default BOX rearranger may encounter significant performance + * bottlenecks due to excessively large local decomposition maps. + * This elongated map length can dramatically extend the execution + * time of the box_rearrange_create() function, thereby increasing + * the overall runtime of the case. + * + * To mitigate this issue and ensure efficient runtime, Impose a + * maximum limit on the local map length for BOX rearranger. Once + * this limit is surpassed, switch to SUBSET rearranger. + * + * CAUTION: This transition is most effective for ADIOS type, as it + * does not involve data rearrangement. However, for PnetCDF type, + * utilizing SUBSET rearranger may significantly increase write time, + * potentially negating the benefits of reduced initialization time + * associated with this approach. + */ + if(iodesc->rearranger == PIO_REARR_ANY){ + iodesc->rearranger = spio_get_opt_pio_rearr(ios, maplen); + } - /* Remember the dim sizes. */ - if (!(iodesc->dimlen = (int *)malloc(sizeof(int) * ndims))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Out of memory allocating %lld bytes for dimension sizes in the I/O decomposition map", (unsigned long long) (sizeof(int) * ndims)); + /* Cache the local decomposition map */ + if(map_zero_based){ + /* BOX and SUBSET rearrangers expect map to the 1-based */ + if((iodesc->rearranger == PIO_REARR_BOX) || (iodesc->rearranger == PIO_REARR_SUBSET)){ + std::transform(compmap, compmap + maplen, iodesc->map, + [](PIO_Offset off) { return off + 1; }); } - for (int d = 0; d < ndims; d++) - iodesc->dimlen[d] = gdimlen[d]; - - /* Set the rearranger. */ - if (!rearranger) - iodesc->rearranger = ios->default_rearranger; - else - iodesc->rearranger = *rearranger; - LOG((2, "iodesc->rearranger = %d", iodesc->rearranger)); - - /* In scenarios involving ultra-high resolution E3SM/SCREAM cases, - * the default BOX rearranger may encounter significant performance - * bottlenecks due to excessively large local decomposition maps. - * This elongated map length can dramatically extend the execution - * time of the box_rearrange_create() function, thereby increasing - * the overall runtime of the case. - * - * To mitigate this issue and ensure efficient runtime, Impose a - * maximum limit on the local map length for BOX rearranger. Once - * this limit is surpassed, switch to SUBSET rearranger. - * - * CAUTION: This transition is most effective for ADIOS type, as it - * does not involve data rearrangement. However, for PnetCDF type, - * utilizing SUBSET rearranger may significantly increase write time, - * potentially negating the benefits of reduced initialization time - * associated with this approach. - */ - if (iodesc->rearranger == PIO_REARR_ANY) - { - iodesc->rearranger = spio_get_opt_pio_rearr(ios, maplen); + else{ + std::copy(compmap, compmap + maplen, iodesc->map); } - - /* Is this the subset rearranger? */ - if (iodesc->rearranger == PIO_REARR_SUBSET) - { - iodesc->num_aiotasks = ios->num_iotasks; - LOG((2, "creating subset rearranger iodesc->num_aiotasks = %d", - iodesc->num_aiotasks)); - if ((ierr = subset_rearrange_create(ios, maplen, (PIO_Offset *)compmap, gdimlen, - ndims, iodesc))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Error creating the SUBSET rearranger"); - } + } + else{ + /* The decomposition map is 1-based */ + if(iodesc->rearranger == PIO_REARR_CONTIG){ + /* CONTIG rearranger expects map to be 0-based */ + std::transform(compmap, compmap + maplen, iodesc->map, + [](PIO_Offset off) { return off - 1; }); } - else /* box rearranger */ - { - if (ios->ioproc) - { - /* Unless the user specifies the start and count for each - * IO task compute it. */ - if (iostart && iocount) - { - LOG((3, "iostart and iocount provided")); - for (int i = 0; i < ndims; i++) - { - iodesc->firstregion->start[i] = iostart[i]; - iodesc->firstregion->count[i] = iocount[i]; - } - iodesc->num_aiotasks = ios->num_iotasks; - } - else - { - /* Compute start and count values for each io task. */ - LOG((2, "about to call CalcStartandCount pio_type = %d ndims = %d", pio_type, ndims)); - if ((ierr = CalcStartandCount(pio_type, ndims, gdimlen, ios->num_iotasks, - ios->io_rank, iodesc->firstregion->start, - iodesc->firstregion->count, &iodesc->num_aiotasks))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Internal error calculating start/count for the decomposition"); - } - } + else{ + std::copy(compmap, compmap + maplen, iodesc->map); + } + } - /* Compute the max io buffer size needed for an iodesc. */ - if ((ierr = compute_maxIObuffersize(ios->io_comm, iodesc))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Internal error computing max io buffer size needed for the decomposition"); - } - LOG((3, "compute_maxIObuffersize called iodesc->maxiobuflen = %d", - iodesc->maxiobuflen)); - } + /* Cache the dimension lengths */ + std::copy(gdimlen, gdimlen + ndims, iodesc->dimlen); - /* Depending on array size and io-blocksize the actual number - * of io tasks used may vary. */ - if ((mpierr = MPI_Bcast(&(iodesc->num_aiotasks), 1, MPI_INT, ios->ioroot, - ios->my_comm))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); - } - LOG((3, "iodesc->num_aiotasks = %d", iodesc->num_aiotasks)); + /* Initialize the rearranger */ + if(iodesc->rearranger == PIO_REARR_SUBSET){ + ierr = subset_rearranger_init(ios, iodesc, iostart, iocount); + } + else if(iodesc->rearranger == PIO_REARR_BOX){ + ierr = box_rearranger_init(ios, iodesc, iostart, iocount); + } + else if(iodesc->rearranger == PIO_REARR_CONTIG){ + ierr = contig_rearranger_init(ios, iodesc, iostart, iocount); + } + else{ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Error initializing rearranger, invalid rearranger (ndims = %d, maplen = %d, rearranger=%d)", ndims, maplen, iodesc->rearranger); + } - /* Compute the communications pattern for this decomposition. */ - if (iodesc->rearranger == PIO_REARR_BOX) - if ((ierr = box_rearrange_create(ios, maplen, compmap, gdimlen, ndims, iodesc))) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Error initializing the PIO decomposition. Error creating the BOX rearranger"); - } - } + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Initializing the PIO decomposition failed. Error initializing rearranger (ndims = %d, maplen = %d, rearranger=%d)", ndims, maplen, iodesc->rearranger); + } - /* Add this IO description to the list. */ - MPI_Comm comm = MPI_COMM_NULL; + /* Add this IO description to the list. */ + MPI_Comm comm = MPI_COMM_NULL; #ifdef _ADIOS2 - comm = ios->union_comm; + comm = ios->union_comm; #endif - if(ios->async) - { - /* For asynchronous I/O service, the iodescs (iodesc ids) need to - * be unique across the union_comm (union of I/O and compute comms) - */ - comm = ios->union_comm; - } - *ioidp = pio_add_to_iodesc_list(iodesc, comm); + if(ios->async){ + /* For asynchronous I/O service, the iodescs (iodesc ids) need to + * be unique across the union_comm (union of I/O and compute comms) + */ + comm = ios->union_comm; + } + *ioidp = pio_add_to_iodesc_list(iodesc, comm); #if PIO_SAVE_DECOMPS - if(pio_save_decomps_regex_match(*ioidp, NULL, NULL)) - { - char filename[PIO_MAX_NAME]; - ierr = pio_create_uniq_str(ios, iodesc, filename, PIO_MAX_NAME, "piodecomp", ".dat"); - if(ierr != PIO_NOERR) - { - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Initializing the PIO decomposition failed. Creating a unique file name for saving the decomposition failed"); - } - LOG((2, "Saving decomp map to %s", filename)); - PIOc_writemap_impl(filename, *ioidp, ndims, gdimlen, maplen, (PIO_Offset *)compmap, ios->my_comm); - iodesc->is_saved = true; - } + if(pio_save_decomps_regex_match(*ioidp, NULL, NULL)){ + std::string filename; + pio_create_uniq_str(ios, iodesc, filename, "piodecomp", ".dat"); + + LOG((2, "Saving decomp map to %s", filename.c_str())); + PIOc_writemap_impl(filename.c_str(), *ioidp, ndims, gdimlen, maplen, (PIO_Offset *)compmap, ios->my_comm); + + std::string log_fname; + pio_create_uniq_str(ios, iodesc, log_fname, "piodecomp", ".nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + (*logger).write_only().open().put(iodesc).close(); + delete logger; + + iodesc->is_saved = true; + + SPIO_Util::Decomp_Util::gdpool_mgr.get_decomp_map_info_pool()->add_decomp_map_info(*ioidp, filename.c_str()); + } #endif #if PIO_ENABLE_LOGGING - /* Log results. */ - LOG((2, "iodesc ioid = %d nrecvs = %d ndof = %d ndims = %d num_aiotasks = %d " - "rearranger = %d maxregions = %d needsfill = %d llen = %d maxiobuflen = %d", - iodesc->ioid, iodesc->nrecvs, iodesc->ndof, iodesc->ndims, iodesc->num_aiotasks, - iodesc->rearranger, iodesc->maxregions, iodesc->needsfill, iodesc->llen, - iodesc->maxiobuflen)); - if (ios->ioproc) - { - if (iodesc->rearranger == PIO_REARR_SUBSET) - { - for (int j = 0; j < iodesc->llen; j++) - LOG((3, "rindex[%d] = %lld", j, iodesc->rindex[j])); - } - else - { - int totalrecv = 0; - for (int j = 0; j < iodesc->nrecvs; j++) - totalrecv += iodesc->rcount[j]; - - for (int j = 0; j < totalrecv; j++) - LOG((3, "rindex[%d] = %lld", j, iodesc->rindex[j])); - } - } + dbg_log_iodesc(ios, iodesc); #endif /* PIO_ENABLE_LOGGING */ - /* This function only does something if pre-processor macro - * PERFTUNE is set. */ - performance_tune_rearranger(ios, iodesc); + return PIO_NOERR; +} - GPTLstop("PIO:PIOc_initdecomp"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return PIO_NOERR; +/** + * Initialize the decomposition used with distributed arrays. The + * decomposition describes how the data will be distributed between + * tasks. + * + * Internally, this function will: + *
    + *
  • Allocate and initialize an iodesc struct for this + * decomposition. (This also allocates an io_region struct for the + * first region.) + *
  • (Box rearranger only) If iostart or iocount are NULL, call + * CalcStartandCount() to determine starts/counts. Then call + * compute_maxIObuffersize() to compute the max IO buffer size needed. + *
  • Create the rearranger. + *
  • Assign an ioid and add this decomposition to the list of open + * decompositions. + *
+ * + * @param iosysid the IO system ID. + * @param pio_type the basic PIO data type used. + * @param ndims the number of dimensions in the variable, not + * including the unlimited dimension. + * @param gdimlen an array length ndims with the sizes of the global + * dimensions. + * @param maplen the local length of the compmap array. + * @param compmap a 1 based array of offsets into the array record on + * file. A 0 in this array indicates a value which should not be + * transfered. + * @param ioidp pointer that will get the io description ID. + * @param rearranger pointer to the rearranger to be used for this + * decomp or NULL to use the default. + * @param iostart An array of start values for block cyclic + * decompositions for the SUBSET rearranger. Ignored if block + * rearranger is used. If NULL and SUBSET rearranger is used, the + * iostarts are generated. + * @param iocount An array of count values for block cyclic + * decompositions for the SUBSET rearranger. Ignored if block + * rearranger is used. If NULL and SUBSET rearranger is used, the + * iostarts are generated. + * @returns 0 on success, error code otherwise + * @ingroup PIO_initdecomp + */ +int PIOc_InitDecomp_impl(int iosysid, int pio_type, int ndims, const int *gdimlen, int maplen, + const PIO_Offset *compmap, int *ioidp, const int *rearranger, + const PIO_Offset *iostart, const PIO_Offset *iocount) +{ + return initdecomp(iosysid, pio_type, ndims, gdimlen, maplen, + compmap, ioidp, rearranger, iostart, iocount); } /** @@ -1003,43 +1009,14 @@ int PIOc_InitDecomp_impl(int iosysid, int pio_type, int ndims, const int *gdimle * decompositions. If NULL ??? * @returns 0 on success, error code otherwise * @ingroup PIO_initdecomp - * @author Jim Edwards, Ed Hartnett */ int PIOc_init_decomp_impl(int iosysid, int pio_type, int ndims, const int *gdimlen, int maplen, const PIO_Offset *compmap, int *ioidp, int rearranger, const PIO_Offset *iostart, const PIO_Offset *iocount) { - PIO_Offset* compmap_1_based = (PIO_Offset*)calloc(maplen, sizeof(PIO_Offset)); - if (compmap_1_based == NULL) - { - return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, - "Initializing the decomposition used with distributed arrays failed. " - "Out of memory allocating %lld bytes for one-based compmap", - (unsigned long long) (maplen * sizeof(PIO_Offset))); - } - - int *rearrangerp = NULL; - - LOG((1, "PIOc_init_decomp iosysid = %d pio_type = %d ndims = %d maplen = %d", - iosysid, pio_type, ndims, maplen)); - - /* If the user specified a non-default rearranger, use it. */ - if (rearranger) - rearrangerp = &rearranger; - - /* Add 1 to all elements in compmap. */ - for (int e = 0; e < maplen; e++) - { - LOG((3, "zero-based compmap[%d] = %d", e, compmap[e])); - compmap_1_based[e] = compmap[e] + 1; - } - - /* Call the legacy version of the function. */ - int ret = PIOc_InitDecomp_impl(iosysid, pio_type, ndims, gdimlen, maplen, compmap_1_based, - ioidp, rearrangerp, iostart, iocount); - - free(compmap_1_based); - return ret; + return initdecomp(iosysid, pio_type, ndims, gdimlen, maplen, + compmap, ioidp, (rearranger) ? &rearranger : NULL, + iostart, iocount, true); } /** @@ -1057,7 +1034,6 @@ int PIOc_init_decomp_impl(int iosysid, int pio_type, int ndims, const int *gdiml * @param pointer that gets the IO ID. * @returns 0 for success, error code otherwise * @ingroup PIO_initdecomp - * @author Jim Edwards */ int PIOc_InitDecomp_bc_impl(int iosysid, int pio_type, int ndims, const int *gdimlen, const long int *start, const long int *count, int *ioidp) @@ -1271,7 +1247,6 @@ static int init_adios_comm(iosystem_desc_t *ios) * @param iosysidp index of the defined system descriptor. * @return 0 on success, otherwise a PIO error code. * @ingroup PIO_init - * @author Jim Edwards, Ed Hartnett */ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, int base, int rearr, int *iosysidp) @@ -1285,12 +1260,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* TIMING_INTERNAL implies that the timing statistics are gathered/ * displayed by pio */ -#ifdef TIMING -#ifdef TIMING_INTERNAL - pio_init_gptl(); -#endif -#endif - GPTLstart("PIO:PIOc_Init_Intracomm"); /* Turn on the logging system. */ pio_init_logging(); @@ -1302,7 +1271,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in ret = mtimer_init(PIO_MICRO_MPI_WTIME_ROOT); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, "PIO Init failed, initializing PIO micro timers failed (ret=%d)", ret); } @@ -1311,7 +1279,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* Find the number of computation tasks. */ if ((mpierr = MPI_Comm_size(comp_comm, &num_comptasks))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); } @@ -1322,7 +1289,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in base < 0 || base >= num_comptasks || stride * (num_iotasks - 1) >= num_comptasks) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__, "PIO Init failed. Invalid arguments provided. Pointer to iosysid is %s (expected not NULL), num_iotasks=%d (expected >= 1 && <= num_comptasks, %d), stride = %d (expected >= 1), base = %d (expected >= 0 && < num_comptasks, %d), stride * (num_iotasks - 1) = %d (expected < num_comptasks, %d)", (iosysidp) ? "not NULL" : "NULL", num_iotasks, num_comptasks, stride, base, num_comptasks, stride * (num_iotasks - 1), num_comptasks); } @@ -1333,7 +1299,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* Allocate memory for the iosystem info. */ if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init failed. Out of memory allocating %lld bytes for I/O system descriptor", (unsigned long long) sizeof(iosystem_desc_t)); } @@ -1348,6 +1313,7 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in ios->sname[0] = '\0'; ios->io_comm = MPI_COMM_NULL; ios->intercomm = MPI_COMM_NULL; + ios->node_comm = MPI_COMM_NULL; ios->error_handler = default_error_handler; ios->default_rearranger = rearr; ios->num_iotasks = num_iotasks; @@ -1362,7 +1328,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* Copy the computation communicator into union_comm. */ if ((mpierr = MPI_Comm_dup(comp_comm, &ios->union_comm))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } @@ -1373,7 +1338,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in ret = init_adios_comm(ios); if (ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, "Initializing ADIOS failed"); } @@ -1382,7 +1346,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in ios->adiosH = adios2_init_mpi(ios->adios_comm); if (ios->adiosH == NULL) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, "Initializing ADIOS failed"); } @@ -1451,7 +1414,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in ios->adios_readerH = adios2_init_mpi(ios->union_comm); if (ios->adios_readerH == NULL) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, "Initializing ADIOS (for read) failed"); } #endif @@ -1496,7 +1458,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* Copy the computation communicator into comp_comm. */ if ((mpierr = MPI_Comm_dup(comp_comm, &ios->comp_comm))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } LOG((2, "union_comm = %d comp_comm = %d", ios->union_comm, ios->comp_comm)); @@ -1507,7 +1468,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* Find MPI rank in comp_comm communicator. */ if ((mpierr = MPI_Comm_rank(ios->comp_comm, &ios->comp_rank))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } @@ -1518,7 +1478,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in * for computation. */ if (!(ios->compranks = (int *)calloc(ios->num_comptasks, sizeof(int)))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init failed. Out of memory allocating %lld bytes for array of compute process ranks in the I/O descriptor", (unsigned long long) (ios->num_comptasks * sizeof(int))); } @@ -1534,7 +1493,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in * for IO. */ if (!(ios->ioranks = (int *)calloc(ios->num_iotasks, sizeof(int)))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init failed. Out of memory allocating %lld bytes for array of I/O process ranks in the I/O descriptor", (unsigned long long) (ios->num_iotasks * sizeof(int))); } @@ -1557,7 +1515,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* Create a group for the computation tasks. */ if ((mpierr = MPI_Comm_group(ios->comp_comm, &ios->compgroup))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } @@ -1565,14 +1522,12 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in if ((mpierr = MPI_Group_incl(ios->compgroup, ios->num_iotasks, ios->ioranks, &ios->iogroup))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } /* Create an MPI communicator for the IO tasks. */ if ((mpierr = MPI_Comm_create(ios->comp_comm, ios->iogroup, &ios->io_comm))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } @@ -1583,7 +1538,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in { if ((mpierr = MPI_Comm_rank(ios->io_comm, &ios->io_rank))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } } @@ -1591,9 +1545,20 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in ios->io_rank = -1; LOG((3, "ios->io_comm = %d ios->io_rank = %d", ios->io_comm, ios->io_rank)); + /* Create the node local comm - all procs in comp_comm that are local to + * this compute node (share memory) */ + mpierr = MPI_Comm_split_type(ios->comp_comm, MPI_COMM_TYPE_SHARED, 0, + ios->info, &(ios->node_comm)); + if(mpierr != MPI_SUCCESS){ + return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); + } + /* Rank in the union comm is the same as rank in the comp comm. */ ios->union_rank = ios->comp_rank; + /* FIXME: Catch exceptions and return error */ + ios->tcomm_info = new SPIO_Util::TComm_info(ios->union_comm, ios->union_rank, ios->ioroot, ios->comproot, ios->io_comm, ios->io_rank, (ios->iomaster) ? true : false, ios->comp_comm, ios->comp_rank, (ios->compmaster) ? true : false, ios->intercomm, ios->my_comm, ios->node_comm); + /* Async I/O service message info - not used here */ ios->async_ios_msg_info.seq_num = PIO_MSG_START_SEQ_NUM; ios->async_ios_msg_info.prev_msg = PIO_MSG_INVALID; @@ -1604,26 +1569,22 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in /* Allocate buffer space for compute nodes. */ if ((ret = compute_buffer_init(ios))) { - GPTLstop("PIO:PIOc_Init_Intracomm"); return pio_err(ios, NULL, ret, __FILE__, __LINE__, "PIO Init failed. Internal error allocating buffer space on compute processes to cache user data"); } - ret = pio_create_uniq_str(ios, NULL, ios->sname, PIO_MAX_NAME, "UNKNOWN:SPIO_COMP_", "_tmp_name"); - if(ret != PIO_NOERR) - { - /* Not a fatal error */ - LOG((0, "Creating a unique name for the iosystem (iosysid=%d) failed, ret = %d", *iosysidp, ret)); - } + std::string sname; + pio_create_uniq_str(ios, NULL, sname, "UNKNOWN:SPIO_COMP_", "_tmp_name"); + + snprintf(ios->sname, PIO_MAX_NAME, "%s", sname.c_str()); /* Set the timer names for this iosystem */ - snprintf(ios->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "PIO:wr_%s", ios->sname); - snprintf(ios->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "PIO:rd_%s", ios->sname); - snprintf(ios->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "PIO:tot_%s", ios->sname); + snprintf(ios->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:wr_") + sname).c_str()); + snprintf(ios->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:rd_") + sname).c_str()); + snprintf(ios->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:tot_") + sname).c_str()); LOG((2, "Init_Intracomm complete iosysid = %d", *iosysidp)); - GPTLstop("PIO:PIOc_Init_Intracomm"); return PIO_NOERR; } @@ -1638,7 +1599,6 @@ int PIOc_Init_Intracomm_impl(MPI_Comm comp_comm, int num_iotasks, int stride, in * @param rearr_opts the rearranger options * @param iosysidp a pointer that gets the IO system ID * @returns 0 for success, error code otherwise - * @author Jim Edwards */ int PIOc_Init_Intracomm_from_F90_impl(int f90_comp_comm, const int num_iotasks, const int stride, @@ -1683,7 +1643,6 @@ int PIOc_Init_Intracomm_from_F90_impl(int f90_comp_comm, * @param hint the hint for MPI * @param hintval the value of the hint * @returns 0 for success, or PIO_BADID if iosysid can't be found. - * @author Jim Edwards, Ed Hartnett */ int PIOc_set_hint_impl(int iosysid, const char *hint, const char *hintval) { @@ -1697,12 +1656,11 @@ int PIOc_set_hint_impl(int iosysid, const char *hint, const char *hintval) "Setting PIO hints failed. Invalid io system id (%d) provided", iosysid); } assert(ios); - spio_ltimer_start(ios->io_fstats->tot_timer_name); + SPIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer_wrapper ios_fstats_tot_timer(ios->io_fstats->tot_timer_name); /* User must provide these. */ if (!hint || !hintval) { - spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__, "Setting PIO hints failed. Invalid pointers (NULL) to hint (%s) or hintval (%s) provided", (hint) ? "not NULL" : "NULL", (hintval) ? "not NULL" : "NULL"); } @@ -1714,7 +1672,6 @@ int PIOc_set_hint_impl(int iosysid, const char *hint, const char *hintval) if ((mpierr = MPI_Info_create(&ios->info))) { LOG((1, "ERROR: Setting PIO hints failed. Creating MPI Info object failed (mpierr = %d)", mpierr)); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } @@ -1723,11 +1680,9 @@ int PIOc_set_hint_impl(int iosysid, const char *hint, const char *hintval) if ((mpierr = MPI_Info_set(ios->info, hint, hintval))) { LOG((1, "ERROR: Setting PIO hints failed. Settnig MPI hints using info object failed (mpierr = %d)", mpierr)); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } - spio_ltimer_stop(ios->io_fstats->tot_timer_name); return PIO_NOERR; } @@ -1738,7 +1693,6 @@ int PIOc_set_hint_impl(int iosysid, const char *hint, const char *hintval) * @param iosysid: the io system ID provided by PIOc_Init_Intracomm(). * @returns 0 for success or non-zero for error. * @ingroup PIO_finalize - * @author Jim Edwards, Ed Hartnett */ int PIOc_finalize_impl(int iosysid) { @@ -1750,14 +1704,12 @@ int PIOc_finalize_impl(int iosysid) char gptl_log_fname[PIO_MAX_NAME]; #endif - GPTLstart("PIO:PIOc_finalize"); LOG((1, "PIOc_finalize iosysid = %d MPI_COMM_NULL = %d", iosysid, MPI_COMM_NULL)); /* Find the IO system information. */ if (!(ios = pio_get_iosystem_from_id(iosysid))) { - GPTLstop("PIO:PIOc_finalize"); return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, "PIO Finalize failed. Invalid iosystem id (%d) provided", iosysid); } @@ -1777,13 +1729,23 @@ int PIOc_finalize_impl(int iosysid) PIO_SEND_ASYNC_MSG(ios, msg, &ierr, iosysid); if(ierr != PIO_NOERR) { - GPTLstop("PIO:PIOc_finalize"); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, ierr, __FILE__, __LINE__, "PIO Finalize failed on iosytem (%d). Error sending async msg for PIO_MSG_FINALIZE", iosysid); } } +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_close_all_files_and_delete_from_list(iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "PIO Finalize failed on iosytem (%d). Error closing files on this I/O system", iosysid); + } +#endif + +#if PIO_SAVE_DECOMPS + SPIO_Util::Decomp_Util::serialize_decomp_map_info_pool(ios); +#endif + ierr = spio_write_io_summary(ios); if(ierr != PIO_NOERR) { @@ -1802,6 +1764,24 @@ int PIOc_finalize_impl(int iosysid) #endif /* ifdef _HDF5 */ + /* Find the number of open IO systems. */ + if ((ierr = pio_num_iosystem(&niosysid))) + { + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "PIO Finalize failed on iosystem (%d). Unable to get the number of open I/O systems", iosysid); + } + +#if PIO_USE_ASYNC_WR_THREAD + if(niosysid == 1){ + ierr = pio_delete_all_iodescs(iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "PIO Finalize failed on iosytem (%d). Error deleting I/O decomps on this I/O system", iosysid); + } + } +#endif + + LOG((2, "%d iosystems are still open.", niosysid)); free(ios->io_fstats); /* Free this memory that was allocated in init_intracomm. */ @@ -1812,15 +1792,6 @@ int PIOc_finalize_impl(int iosysid) free(ios->compranks); LOG((3, "Freed compranks.")); - /* Learn the number of open IO systems. */ - if ((ierr = pio_num_iosystem(&niosysid))) - { - GPTLstop("PIO:PIOc_finalize"); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "PIO Finalize failed on iosystem (%d). Unable to get the number of open I/O systems", iosysid); - } - - LOG((2, "%d iosystems are still open.", niosysid)); /* Free the MPI groups. */ if (ios->compgroup != MPI_GROUP_NULL) @@ -1859,7 +1830,6 @@ int PIOc_finalize_impl(int iosysid) adios2_error adiosErr = adios2_finalize(ios->adiosH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_finalize"); return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, "Finalizing ADIOS failed (adios2_error=%s) on iosystem (%d)", convert_adios2_error_to_string(adiosErr), iosysid); } @@ -1871,7 +1841,6 @@ int PIOc_finalize_impl(int iosysid) adios2_error adiosErr = adios2_finalize(ios->adios_readerH); if (adiosErr != adios2_error_none) { - GPTLstop("PIO:PIOc_finalize"); return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, "Finalizing ADIOS (for read) failed (adios2_error=%s) on iosystem (%d)", convert_adios2_error_to_string(adiosErr), iosysid); } @@ -1895,7 +1864,6 @@ int PIOc_finalize_impl(int iosysid) pio_finalize_logging(); LOG((2, "PIOc_finalize completed successfully")); - GPTLstop("PIO:PIOc_finalize"); #ifdef TIMING #ifdef TIMING_INTERNAL if(spio_gptl_was_init_in_lib()){ @@ -1919,6 +1887,9 @@ int PIOc_finalize_impl(int iosysid) MPI_Comm_free(&ios->union_comm); if (ios->io_comm != MPI_COMM_NULL) MPI_Comm_free(&ios->io_comm); + if (ios->node_comm != MPI_COMM_NULL) + MPI_Comm_free(&ios->node_comm); + if(ios->tcomm_info) { delete(ios->tcomm_info); } /* Delete the iosystem_desc_t data associated with this id. */ LOG((2, "About to delete iosysid %d.", iosysid)); @@ -1938,7 +1909,6 @@ int PIOc_finalize_impl(int iosysid) * @param ioproc a pointer that gets 1 if task is an IO task, 0 * otherwise. Ignored if NULL. * @returns 0 for success, or PIO_BADID if iosysid can't be found. - * @author Jim Edwards */ int PIOc_iam_iotask_impl(int iosysid, bool *ioproc) { @@ -1964,7 +1934,6 @@ int PIOc_iam_iotask_impl(int iosysid, bool *ioproc) * @param iorank a pointer that gets the io rank, or -1 if task is not * in the IO communicator. Ignored if NULL. * @returns 0 for success, or PIO_BADID if iosysid can't be found. - * @author Jim Edwards */ int PIOc_iotask_rank_impl(int iosysid, int *iorank) { @@ -1987,7 +1956,6 @@ int PIOc_iotask_rank_impl(int iosysid, int *iorank) * * @param iotype the io type to check * @returns 1 if iotype is in build, 0 if not. - * @author Jim Edwards */ int PIOc_iotype_available_impl(int iotype) { @@ -2090,7 +2058,6 @@ int PIOc_iotype_available_impl(int iotype) * * @return PIO_NOERR on success, error code otherwise. * @ingroup PIO_init - * @author Ed Hartnett */ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_list, int component_count, const int *num_procs_per_comp, const int **proc_list, @@ -2108,18 +2075,10 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li /* TIMING_INTERNAL implies that the timing statistics are gathered/ * displayed by pio */ -#ifdef TIMING -#ifdef TIMING_INTERNAL - pio_init_gptl(); -#endif -#endif - GPTLstart("PIO:PIOc_init_async"); - /* Check input parameters. */ if (num_io_procs < 1 || component_count < 1 || !num_procs_per_comp || !iosysidp || (rearranger != PIO_REARR_BOX && rearranger != PIO_REARR_SUBSET)) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__, "PIO Init (async) failed. Invalid arguments provided, num_io_procs=%d (expected >= 1), component_count=%d (expected >= 1), num_procs_per_comp is %s (expected not NULL), iosysidp is %s (expected not NULL), rearranger=%s (expected PIO_REARR_BOX or PIO_REARR_SUBSET)", num_io_procs, component_count, (num_procs_per_comp) ? "not NULL" : "NULL", (iosysidp) ? "not NULL" : "NULL", (rearranger == PIO_REARR_BOX) ? "PIO_REARR_BOX" : ((rearranger == PIO_REARR_SUBSET) ? "PIO_REARR_SUBSET" : "UNKNOWN REARRANGER")); } @@ -2127,7 +2086,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li /* Temporarily limit to one computational component. */ if (component_count > 1) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__, "PIO Init (async) failed. Currently only one computational component is supported, and %d computation components were specified", component_count); } @@ -2145,7 +2103,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li ret = mtimer_init(PIO_MICRO_MPI_WTIME_ROOT); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, "PIO Init (async) failed. Initializing micro timers failed"); } @@ -2157,7 +2114,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li LOG((3, "calculating processors for IO component")); if (!(io_proc_list_buf = (int *) malloc(num_io_procs * sizeof(int)))) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory."); } @@ -2180,7 +2136,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li /* Allocate space for array of arrays. */ if (!(proc_list_buf = (int **)malloc((component_count) * sizeof(int *)))) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory"); } @@ -2193,7 +2148,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li /* Allocate space for each array. */ if (!(proc_list_buf[cmp] = (int *)malloc(num_procs_per_comp[cmp] * sizeof(int)))) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory"); } @@ -2214,7 +2168,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li /* Get rank of this task in world. */ if ((ret = MPI_Comm_rank(world, &my_rank))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2232,7 +2185,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li { if (!(iosys[cmp1] = (iosystem_desc_t *)calloc(1, sizeof(iosystem_desc_t)))) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory"); } @@ -2249,7 +2201,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if ((ret = MPI_Comm_group(world, &world_group))) { LOG((1, "ERROR: PIO Init (async failed). Getting MPI group associated with world failed")); - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } LOG((3, "world group created\n")); @@ -2271,7 +2222,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if ((ret = MPI_Group_incl(world_group, num_io_procs, my_io_proc_list, &io_group))) { LOG((1, "ERROR: PIO Init (async) failed. Creating MPI group for IO component failed")); - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } LOG((3, "created IO group - io_group = %d MPI_GROUP_EMPTY = %d", io_group, MPI_GROUP_EMPTY)); @@ -2280,7 +2230,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if ((ret = MPI_Comm_create(world, io_group, &io_comm))) { LOG((1, "ERROR: PIO Init (async) failed. Creating shared MPI Comm for IO component failed")); - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } LOG((3, "created io comm io_comm = %d", io_comm)); @@ -2292,7 +2241,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if (in_io) if ((mpierr = MPI_Comm_dup(io_comm, user_io_comm))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); } } @@ -2304,7 +2252,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li LOG((3, "about to get io rank")); if ((ret = MPI_Comm_rank(io_comm, &io_rank))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } iomaster = !io_rank ? MPI_ROOT : MPI_PROC_NULL; @@ -2334,6 +2281,7 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li my_iosys->union_comm = MPI_COMM_NULL; my_iosys->intercomm = MPI_COMM_NULL; my_iosys->my_comm = MPI_COMM_NULL; + my_iosys->node_comm = MPI_COMM_NULL; my_iosys->async = 1; my_iosys->error_handler = default_error_handler; my_iosys->num_comptasks = num_procs_per_comp[cmp]; @@ -2357,7 +2305,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if ((ret = MPI_Group_incl(world_group, num_procs_per_comp[cmp], my_proc_list[cmp], &group[cmp]))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } LOG((3, "created component MPI group - group[%d] = %d", cmp, group[cmp])); @@ -2384,7 +2331,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li /* Allocate space for computation task ranks. */ if (!(my_iosys->compranks = (int *)calloc(my_iosys->num_comptasks, sizeof(int)))) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory"); } @@ -2397,7 +2343,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if ((ret = MPI_Group_incl(world_group, nprocs_union, proc_list_union, &union_group[cmp]))) { LOG((1, "ERROR: PIO Init (async) failed. Creating union group failed")); - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } LOG((3, "created union MPI_group - union_group[%d] = %d with %d procs", cmp, @@ -2425,7 +2370,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li LOG((3, "creating intracomm cmp = %d from group[%d] = %d", cmp, cmp, group[cmp])); if ((ret = MPI_Comm_create(world, group[cmp], &my_iosys->comp_comm))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2435,14 +2379,12 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if (user_comp_comm) if ((mpierr = MPI_Comm_dup(my_iosys->comp_comm, &user_comp_comm[cmp]))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); } /* Get the rank in this comp comm. */ if ((ret = MPI_Comm_rank(my_iosys->comp_comm, &my_iosys->comp_rank))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2463,7 +2405,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li LOG((3, "making a dup of io_comm = %d io_rank = %d", io_comm, io_rank)); if ((ret = MPI_Comm_dup(io_comm, &my_iosys->io_comm))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } LOG((3, "dup of io_comm = %d io_rank = %d", my_iosys->io_comm, io_rank)); @@ -2477,7 +2418,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li * for IO. */ if (!(my_iosys->ioranks = (int *) calloc(my_iosys->num_iotasks, sizeof(int)))) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory"); } @@ -2494,13 +2434,11 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li * and one of the computation components. */ if ((ret = MPI_Comm_create(world, union_group[cmp], &my_iosys->union_comm))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } if ((ret = MPI_Comm_rank(my_iosys->union_comm, &my_iosys->union_rank))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2518,7 +2456,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if ((ret = MPI_Intercomm_create(my_iosys->io_comm, 0, my_iosys->union_comm, my_proc_list[cmp][0], 0, &my_iosys->intercomm))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } } @@ -2530,13 +2467,23 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if ((ret = MPI_Intercomm_create(my_iosys->comp_comm, 0, my_iosys->union_comm, my_io_proc_list[0], 0, &my_iosys->intercomm))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } + + /* Create the node local comm - all procs in comp_comm that are local to + * this compute node (share memory) */ + mpierr = MPI_Comm_split_type(my_iosys->comp_comm, MPI_COMM_TYPE_SHARED, 0, + my_iosys->info, &(my_iosys->node_comm)); + if(mpierr != MPI_SUCCESS){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } } LOG((3, "intercomm created for cmp = %d", cmp)); } + /* FIXME: Catch exceptions and return error */ + my_iosys->tcomm_info = new SPIO_Util::TComm_info(my_iosys->union_comm, my_iosys->union_rank, my_iosys->ioroot, my_iosys->comproot, my_iosys->io_comm, my_iosys->io_rank, (my_iosys->iomaster) ? true : false, my_iosys->comp_comm, my_iosys->comp_rank, (my_iosys->compmaster) ? true : false, my_iosys->intercomm, my_iosys->my_comm, my_iosys->node_comm); + /* Async I/O service message info */ my_iosys->async_ios_msg_info.seq_num = PIO_MSG_START_SEQ_NUM; my_iosys->async_ios_msg_info.prev_msg = PIO_MSG_INVALID; @@ -2545,23 +2492,21 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li iosysidp[cmp] = pio_add_to_iosystem_list(my_iosys, MPI_COMM_NULL); LOG((2, "new iosys ID added to iosystem_list iosysid = %d", iosysidp[cmp])); - ret = pio_create_uniq_str(my_iosys, NULL, my_iosys->sname, PIO_MAX_NAME, "UNKNOWN:SPIO_COMP_", "_tmp_name"); - if(ret != PIO_NOERR) - { - /* Not Fatal error */ - LOG((0, "Creating a unique name for the iosystem (iosysid=%d) failed,ret = %d", my_iosys->iosysid, ret)); - } + std::string sname; + pio_create_uniq_str(my_iosys, NULL, sname, "UNKNOWN:SPIO_COMP_", "_tmp_name"); + + snprintf(my_iosys->sname, PIO_MAX_NAME, "%s", sname.c_str()); + /* Set the timer names for this iosystem */ - snprintf(my_iosys->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "PIO:wr_%s", my_iosys->sname); - snprintf(my_iosys->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "PIO:rd_%s", my_iosys->sname); - snprintf(my_iosys->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "PIO:tot_%s", my_iosys->sname); + snprintf(my_iosys->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:wr_") + sname).c_str()); + snprintf(my_iosys->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:rd_") + sname).c_str()); + snprintf(my_iosys->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:tot_") + sname).c_str()); } /* next computational component */ /* Initialize async message signatures */ ret = init_async_msgs_sign(); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, ret, __FILE__, __LINE__, "PIO Init (async) failed. Initializing async message signatures failed"); } @@ -2575,7 +2520,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li io_rank, component_count)); if ((ret = pio_msg_handler2(io_rank, component_count, iosys, io_comm))) { - GPTLstop("PIO:PIOc_init_async"); return pio_err(NULL, NULL, ret, __FILE__, __LINE__, "Error processing I/O message"); } @@ -2589,7 +2533,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li if (in_io) if ((mpierr = MPI_Comm_free(&io_comm))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2603,7 +2546,6 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li /* Free MPI groups. */ if ((ret = MPI_Group_free(&io_group))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2611,24 +2553,20 @@ int PIOc_init_async_impl(MPI_Comm world, int num_io_procs, const int *io_proc_li { if ((ret = MPI_Group_free(&group[cmp]))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } if ((ret = MPI_Group_free(&union_group[cmp]))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } } if ((ret = MPI_Group_free(&world_group))) { - GPTLstop("PIO:PIOc_init_async"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } LOG((2, "successfully done with PIO_Init_Async")); - GPTLstop("PIO:PIOc_init_async"); return PIO_NOERR; } @@ -2682,18 +2620,11 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, /* TIMING_INTERNAL implies that the timing statistics are gathered/ * displayed by pio */ -#ifdef TIMING -#ifdef TIMING_INTERNAL - pio_init_gptl(); -#endif -#endif - GPTLstart("PIO:PIOc_init_intercomm"); assert((component_count > 0) && ucomp_comms && iosysidps); if((component_count <= 0) || (ucomp_comms == NULL) || ((rearranger != PIO_REARR_BOX) && (rearranger != PIO_REARR_SUBSET)) || (iosysidps == NULL)) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__, "PIO Init (async) failed. Invalid arguments provided, component_count=%d (expected > 0), ucomp_comms is %s (expected not NULL), rearranger=%s (expected PIO_REARR_BOX or PIO_REARR_SUBSET), iosysidps is %s (expected not NULL)", component_count, (ucomp_comms) ? "not NULL" : "NULL", (rearranger == PIO_REARR_BOX) ? "PIO_REARR_BOX" : ((rearranger == PIO_REARR_SUBSET) ? "PIO_REARR_SUBSET" : "UNKNOWN REARRANGER"), (iosysidps) ? "not NULL" : "NULL"); } @@ -2710,7 +2641,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = mtimer_init(PIO_MICRO_MPI_WTIME_ROOT); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, "PIO Init (async) failed. Initializing micro timers failed"); } @@ -2729,7 +2659,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = MPI_Comm_dup(ucomp_comms[i], &(comp_comms[i])); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } } @@ -2737,7 +2666,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, } else { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory allocating %lld bytes for storing MPI communicators for the different asynchronous components", (unsigned long long) (component_count * sizeof(MPI_Comm))); } @@ -2751,7 +2679,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, iosys[i] = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)); if(!iosys[i]) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO init (async) failed. Out of memory allocating %lld bytes for storing I/O system descriptor for component %d", (unsigned long long) sizeof(iosystem_desc_t), i); } @@ -2770,6 +2697,7 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, iosys[i]->comp_comm = MPI_COMM_NULL; iosys[i]->intercomm = MPI_COMM_NULL; iosys[i]->my_comm = MPI_COMM_NULL; + iosys[i]->node_comm = MPI_COMM_NULL; iosys[i]->compgroup = MPI_GROUP_NULL; iosys[i]->iogroup = MPI_GROUP_NULL; @@ -2823,7 +2751,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Duping user I/O comm failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } } @@ -2855,14 +2782,12 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = MPI_Comm_rank(io_comm, &(iosys[i]->io_rank)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } ret = MPI_Comm_size(io_comm, &(iosys[i]->num_iotasks)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2873,7 +2798,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = MPI_Comm_rank(peer_comm, &io_grank); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } /* Find the io leader for intercomm */ @@ -2893,7 +2817,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Finding I/O leader failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2903,7 +2826,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Finding Comp leader failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2912,7 +2834,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Creating an intercomm between I/O comm and Comp comms failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2926,14 +2847,12 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Merging intercomm between I/O comm and Comp comms failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } ret = MPI_Comm_size(iosys[i]->union_comm, &(iosys[i]->num_uniontasks)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2941,7 +2860,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = MPI_Comm_rank(iosys[i]->union_comm, &(iosys[i]->union_rank)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2956,7 +2874,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, iosys[i]->ioranks = (int *)malloc(iosys[i]->num_iotasks * sizeof(int)); if(!(iosys[i]->ioranks)) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory allocating %lld bytes to store ranks of I/O processes for component %d", (unsigned long long) (iosys[i]->num_iotasks * sizeof(int)), i); } @@ -2968,7 +2885,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, iosys[i]->compranks = (int *)malloc(iosys[i]->num_comptasks * sizeof(int)); if(!(iosys[i]->compranks)) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory allocating %lld bytes to store ranks of compute processes for component %d", (unsigned long long) (iosys[i]->num_comptasks * sizeof(int)), i); } @@ -2982,7 +2898,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Unable to get process group for the union comm")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2990,7 +2905,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Unable to find procs in comp group")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -2998,7 +2912,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Unable to find procs in I/O group")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3025,20 +2938,17 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = MPI_Comm_rank(comp_comms[i], &(iosys[i]->comp_rank)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } ret = MPI_Comm_size(comp_comms[i], &(iosys[i]->num_comptasks)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } ret = MPI_Comm_rank(peer_comm, &comp_grank); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3059,7 +2969,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Finding I/O leader failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3069,7 +2978,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Finding Comp leader failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3080,7 +2988,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Creating intercomm between I/O comm and Comp comms failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3094,21 +3001,18 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Merging intercomm between I/O comm and Comp comms failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } ret = MPI_Comm_size(iosys[i]->union_comm, &(iosys[i]->num_uniontasks)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } ret = MPI_Comm_rank(iosys[i]->union_comm, &(iosys[i]->union_rank)); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3123,7 +3027,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, iosys[i]->ioranks = (int *)malloc(iosys[i]->num_iotasks * sizeof(int)); if(!(iosys[i]->ioranks)) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory allocating %lld bytes to store ranks of I/O processes for component %d", (unsigned long long) (iosys[i]->num_iotasks * sizeof(int)), i); } @@ -3135,7 +3038,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, iosys[i]->compranks = (int *)malloc(iosys[i]->num_comptasks * sizeof(int)); if(!(iosys[i]->compranks)) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "PIO Init (async) failed. Out of memory allocating %lld bytes to store ranks of compute processes for component %d", (unsigned long long) (iosys[i]->num_comptasks * sizeof(int)), i); } @@ -3149,7 +3051,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Finding MPI process group for union comm failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3157,7 +3058,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Finding MPI processes in comp group failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3165,15 +3065,26 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, if(ret != MPI_SUCCESS) { LOG((1, "PIO Init (async) failed. Finding MPI processes in io group failed")); - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } MPI_Group_free(&union_comm_group); + + /* Create the node local comm - all procs in comp_comm that are local to + * this compute node (share memory) */ + ret = MPI_Comm_split_type(iosys[i]->comp_comm, MPI_COMM_TYPE_SHARED, 0, + iosys[i]->info, &(iosys[i]->node_comm)); + if(ret != MPI_SUCCESS){ + return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); + } } } iosys[i]->my_comm = iosys[i]->union_comm; + + /* FIXME: Catch exceptions and return error */ + iosys[i]->tcomm_info = new SPIO_Util::TComm_info(iosys[i]->union_comm, iosys[i]->union_rank, iosys[i]->ioroot, iosys[i]->comproot, iosys[i]->io_comm, iosys[i]->io_rank, (iosys[i]->iomaster) ? true : false, iosys[i]->comp_comm, iosys[i]->comp_rank, (iosys[i]->compmaster) ? true : false, iosys[i]->intercomm, iosys[i]->my_comm, iosys[i]->node_comm); + /* Async I/O service message info */ iosys[i]->async_ios_msg_info.seq_num = PIO_MSG_START_SEQ_NUM; iosys[i]->async_ios_msg_info.prev_msg = PIO_MSG_INVALID; @@ -3183,16 +3094,14 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, LOG((2, "PIOc_init_intercomm : iosys[%d]->ioid=%d, iosys[%d]->uniontasks = %d, iosys[%d]->union_rank=%d, %s", i, iosys[i]->iosysid, i, iosys[i]->num_uniontasks, i, iosys[i]->union_rank, ((iosys[i]->ioproc) ? ("IS IO PROC"):((iosys[i]->compproc) ? ("IS COMPUTE PROC") : ("NEITHER IO NOR COMPUTE PROC"))) )); LOG((2, "New IOsystem added to iosystem_list iosysid = %d", iosysidps[i])); - ret = pio_create_uniq_str(iosys[i], NULL, iosys[i]->sname, PIO_MAX_NAME, "UNKNOWN:SPIO_COMP_", "_tmp_name"); - if(ret != PIO_NOERR) - { - /* Not Fatal error */ - LOG((0, "Creating a unique name for the iosystem (iosysid=%d) failed,ret = %d", iosys[i]->iosysid, ret)); - } + std::string sname; + pio_create_uniq_str(iosys[i], NULL, sname, "UNKNOWN:SPIO_COMP_", "_tmp_name"); + snprintf(iosys[i]->sname, PIO_MAX_NAME, "%s", sname.c_str()); + /* Set the timer names for this iosystem */ - snprintf(iosys[i]->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "PIO:wr_%s", iosys[i]->sname); - snprintf(iosys[i]->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "PIO:rd_%s", iosys[i]->sname); - snprintf(iosys[i]->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "PIO:tot_%s", iosys[i]->sname); + snprintf(iosys[i]->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:wr_") + sname).c_str()); + snprintf(iosys[i]->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:rd_") + sname).c_str()); + snprintf(iosys[i]->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:tot_") + sname).c_str()); } /* The comp_comms array is freed. The communicators will be freed internally @@ -3204,7 +3113,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = init_async_msgs_sign(); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, ret, __FILE__, __LINE__, "PIO Init (async) failed. Initializing asynchronous message signatures failed"); } @@ -3228,7 +3136,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = create_async_service_msg_comm(uio_comm, &msg_comm); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, ret, __FILE__, __LINE__, "PIO Init (async) failed. Creating an MPI comm for asynchronous messages failed"); } @@ -3236,7 +3143,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = MPI_Comm_rank(msg_comm, &rank); if(ret != MPI_SUCCESS) { - GPTLstop("PIO:PIOc_init_intercomm"); return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); } @@ -3245,7 +3151,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, ret = pio_msg_handler2(rank, component_count, iosys, msg_comm); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_init_intercomm"); return pio_err(NULL, NULL, ret, __FILE__, __LINE__, "PIO Init (async) failed. Error processing asynchronous messages"); LOG((2, "Returned from pio_msg_handler2(), Msg handler failed, ret = %d", ret)); @@ -3253,7 +3158,6 @@ int PIOc_init_intercomm_impl(int component_count, const MPI_Comm peer_comm, LOG((2, "Returned from pio_msg_handler2() ret = %d", ret)); } - GPTLstop("PIO:PIOc_init_intercomm"); return PIO_NOERR; } @@ -3310,7 +3214,6 @@ int PIOc_Init_Intercomm_from_F90_impl(int component_count, int f90_peer_comm, * @param newblocksize the new blocksize. * @returns 0 for success. * @ingroup PIO_set_blocksize - * @author Jim Edwards */ int PIOc_set_blocksize_impl(int newblocksize) { diff --git a/src/clib/pioc_sc.cpp b/src/clib/core/pioc_sc.cpp similarity index 99% rename from src/clib/pioc_sc.cpp rename to src/clib/core/pioc_sc.cpp index fc080a03805..15e1f7dbacc 100644 --- a/src/clib/pioc_sc.cpp +++ b/src/clib/core/pioc_sc.cpp @@ -2,8 +2,6 @@ * @file * Compute start and count arrays for the box rearranger * - * @author Jim Edwards - * @date 2014 */ #include #include diff --git a/src/clib/pioc_support.cpp b/src/clib/core/pioc_support.cpp similarity index 66% rename from src/clib/pioc_support.cpp rename to src/clib/core/pioc_support.cpp index f0cdd3e40d8..25f3d03310c 100644 --- a/src/clib/pioc_support.cpp +++ b/src/clib/core/pioc_support.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,18 +24,22 @@ #include #include "spio_io_summary.h" #include "spio_file_mvcache.h" +#include "spio_dt_converter.hpp" #include "spio_hash.h" +#include "pio_rearr_contig.hpp" +#include "spio_decomp_logger.hpp" +#include "spio_async_utils.hpp" +#include "spio_hdf5_utils.hpp" +#include "spio_async_hdf5_utils.hpp" +#include "spio_async_tcomm.hpp" +#include +#include +#include +#include +#include -/* Include headers for HDF5 compression filters */ #if PIO_USE_HDF5 #include -#ifdef _SPIO_HAS_H5Z_ZFP -#include "H5Zzfp_lib.h" -#include "H5Zzfp_props.h" -#endif -#ifdef _SPIO_HAS_H5Z_BLOSC2 -#include "blosc2_filter.h" -#endif #endif #define VERSNO 2001 @@ -60,7 +65,7 @@ extern int GPTLis_initialized (void); /* Some logging constants. */ #if PIO_ENABLE_LOGGING -#define MAX_LOG_MSG 1024 +#define MAX_LOG_MSG 8192 #define MAX_RANK_STR 12 #define ERROR_PREFIX "ERROR: " #define NC_LEVEL_DIFF 3 @@ -793,11 +798,11 @@ adios2_variable* spio_define_adios2_variable(iosystem_desc_t *ios, file_desc_t * } else if(SPIO_ADIOS2_ZFP_COMPRESSION_MODE == "ADIOS2_ZFP_MODE_RATE"){ /* Fixed Rate Mode : User specifies the number of bits to use for each value of the compressed data */ - adiosErr = adios2_add_operation(&operation_index, variable, ios->lossy_compression_operator, "rate", SPIO_ADIOS2_ZFP_RATE); + adiosErr = adios2_add_operation(&operation_index, variable, ios->lossy_compression_operator, "rate", SPIO_ADIOS2_ZFP_COMPRESSION_RATE); if(adiosErr != adios2_error_none){ pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Failed to add ZFP compression operation (rate=%s bits) to variable %s (adios2_error=%s)", - SPIO_ADIOS2_ZFP_RATE, name, convert_adios2_error_to_string(adiosErr)); + SPIO_ADIOS2_ZFP_COMPRESSION_RATE, name, convert_adios2_error_to_string(adiosErr)); } } else if(SPIO_ADIOS2_ZFP_COMPRESSION_MODE == "ADIOS2_ZFP_MODE_REVERSIBLE"){ @@ -812,7 +817,7 @@ adios2_variable* spio_define_adios2_variable(iosystem_desc_t *ios, file_desc_t * else{ pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Failed to add ZFP compression operation to variable %s (adios2_error=%s). Invalid compression mode (%s)", - SPIO_ADIOS2_ZFP_RATE, name, convert_adios2_error_to_string(adiosErr), SPIO_ADIOS2_ZFP_COMPRESSION_MODE); + SPIO_ADIOS2_ZFP_COMPRESSION_RATE, name, convert_adios2_error_to_string(adiosErr), SPIO_ADIOS2_ZFP_COMPRESSION_MODE); } } } @@ -1043,70 +1048,33 @@ void pio_finalize_gptl(void) */ void pio_log(int severity, const char *fmt, ...) { - va_list argp; - int t; - int rem_len = MAX_LOG_MSG; - char msg[MAX_LOG_MSG]; - char *ptr = msg; - char rank_str[MAX_RANK_STR]; - - /* If the severity is greater than the log level, we don't print - this message. */ - if (severity > pio_log_level) - return; + va_list argp; + int log_msg_sz = 0; - /* If the severity is 0, only print on rank 0. */ - if (severity < 1 && my_rank != 0) - return; + if(severity > pio_log_level) return; - /* If the severity is zero, this is an error. Otherwise insert that - many tabs before the message. */ - if (!severity) - { - strncpy(ptr, ERROR_PREFIX, (rem_len > 0) ? rem_len : 0); - ptr += strlen(ERROR_PREFIX); - rem_len -= strlen(ERROR_PREFIX); - } - for (t = 0; t < severity; t++) - { - strncpy(ptr++, "\t", (rem_len > 0) ? rem_len : 0); - rem_len--; - } - - /* Show the rank. */ - snprintf(rank_str, MAX_RANK_STR, "%d ", my_rank); - strncpy(ptr, rank_str, (rem_len > 0) ? rem_len : 0); - ptr += strlen(rank_str); - rem_len -= strlen(rank_str); - - /* Print out the variable list of args with vprintf. */ - va_start(argp, fmt); - vsnprintf(ptr, ((rem_len > 0) ? rem_len : 0), fmt, argp); - va_end(argp); + // Find the size of the log msg + va_start(argp, fmt); + int log_msg_size = std::vsnprintf(NULL, 0, fmt, argp); + va_end(argp); - /* Put on a final linefeed. */ - ptr = msg + strlen(msg); - rem_len = MAX_LOG_MSG - strlen(msg); - strncpy(ptr, "\n\0", (rem_len > 0) ? rem_len : 0); + if(log_msg_size == 0) return; - /* Add string delimiter to handle the case where the buffer - * is completely filled up - */ - msg[MAX_LOG_MSG - 1] = '\0'; + // Get the log msg + std::string log_msg(log_msg_size + 1, '\0'); + va_start(argp, fmt); + vsnprintf(&log_msg[0], log_msg.size(), fmt, argp); + va_end(argp); - /* Send message to log file. */ - if (LOG_FILE) - { - fprintf(LOG_FILE, "%s", msg); - fflush(LOG_FILE); - } - else - { - /* Send message to stdout. */ - fprintf(stdout, "%s", msg); - /* Ensure an immediate flush of stdout. */ - fflush(stdout); - } + // Write log msg to file + if(LOG_FILE){ + fprintf(LOG_FILE, "%s\n", log_msg.c_str()); + fflush(LOG_FILE); + } + else{ + fprintf(stdout, "%s\n", log_msg.c_str()); + fflush(stdout); + } } #endif /* PIO_ENABLE_LOGGING */ @@ -1234,69 +1202,42 @@ int check_mpi(iosystem_desc_t *ios, file_desc_t *file, int mpierr, return PIO_NOERR; } -/** - * Check the result of a netCDF API call. - * (Collective call for file/ios with error handler != PIO_RETURN_ERROR) - * - * PIO_INTERNAL_ERROR : Abort (inside PIO) on error from any MPI process - * PIO_RETURN_ERROR : Return error back to the user (Allow the user to - * handle the error. Each MPI process just returns the error code back - * to the user) - * PIO_BCAST_ERROR : Broadcast error code from I/O process with rank 0 - * (in the I/O communicator) to all processes. - * PIO_REDUCE_ERROR : Reduce error codes across all processes (and log - * the error codes from each process). This error handler detects error - * in any process. - * - * @param ios pointer to the iosystem description struct. Ignored if NULL. - * @param file pointer to the PIO structure describing this file. Ignored if NULL. - * @param status the return value from the netCDF call. - * @param fname the name of the code file. - * @param line the line number of the netCDF call in the code. - * @return the error code - */ -int check_netcdf(iosystem_desc_t *ios, file_desc_t *file, int status, - const char *fname, int line) +int spio_handle_err(iosystem_desc_t *ios, file_desc_t *file, int eh, + int status, const char *fname, int line) { - int eh = default_error_handler; /* Error handler that will be used. */ char errmsg[PIO_MAX_NAME + 1]; /* Error message. */ - int ioroot; - MPI_Comm comm; + int ioroot = 0; + MPI_Comm comm = MPI_COMM_NULL; int mpierr = MPI_SUCCESS; /* User must provide this. */ assert(ios || file); assert(fname); - LOG((1, "check_netcdf status = %d fname = %s line = %d", status, fname, line)); + LOG((1, "spio_handle_err status = %d fname = %s line = %d", status, fname, line)); - /* Find the error handler. Error handlers associated with file has - * priority over ios error handlers. - */ + assert( (eh == PIO_INTERNAL_ERROR) || + (eh == PIO_BCAST_ERROR) || + (eh == PIO_RETURN_ERROR) || + (eh == PIO_REDUCE_ERROR) ); + + LOG((2, "spio_handle_err chose error handler = %d", eh)); if(file){ - eh = file->iosystem->error_handler; ioroot = file->iosystem->ioroot; comm = file->iosystem->my_comm; } else{ assert(ios); - eh = ios->error_handler; ioroot = ios->ioroot; comm = ios->my_comm; } - assert( (eh == PIO_INTERNAL_ERROR) || - (eh == PIO_BCAST_ERROR) || - (eh == PIO_RETURN_ERROR) || - (eh == PIO_REDUCE_ERROR) ); - LOG((2, "check_netcdf chose error handler = %d", eh)); - /* Get an error message. */ if(status != PIO_NOERR){ if(eh == PIO_INTERNAL_ERROR){ int ret = PIOc_strerror_impl(status, errmsg, PIO_MAX_NAME); assert(ret == PIO_NOERR); - LOG((1, "check_netcdf errmsg = %s", errmsg)); + LOG((1, "spio_handle_err errmsg = %s", errmsg)); piodie(fname, line, "FATAL ERROR: %s (file = %s)", errmsg, (file)?(file->fname):"UNKNOWN"); } } @@ -1377,6 +1318,44 @@ int check_netcdf(iosystem_desc_t *ios, file_desc_t *file, int status, return status; } +/** + * Check the result of a netCDF API call. + * (Collective call for file/ios with error handler != PIO_RETURN_ERROR) + * + * PIO_INTERNAL_ERROR : Abort (inside PIO) on error from any MPI process + * PIO_RETURN_ERROR : Return error back to the user (Allow the user to + * handle the error. Each MPI process just returns the error code back + * to the user) + * PIO_BCAST_ERROR : Broadcast error code from I/O process with rank 0 + * (in the I/O communicator) to all processes. + * PIO_REDUCE_ERROR : Reduce error codes across all processes (and log + * the error codes from each process). This error handler detects error + * in any process. + * + * @param ios pointer to the iosystem description struct. Ignored if NULL. + * @param file pointer to the PIO structure describing this file. Ignored if NULL. + * @param status the return value from the netCDF call. + * @param fname the name of the code file. + * @param line the line number of the netCDF call in the code. + * @return the error code + */ +int check_netcdf(iosystem_desc_t *ios, file_desc_t *file, int status, + const char *fname, int line) +{ + /* User must provide this. */ + assert(ios || file); + assert(fname); + + LOG((1, "check_netcdf status = %d fname = %s line = %d", status, fname, line)); + + /* Find the error handler. Error handlers associated with file has + * priority over ios error handlers. + */ + int eh = (file) ? (file->iosystem->error_handler) : (ios->error_handler); + + return spio_handle_err(ios, file, eh, status, fname, line); +} + /** * Handle an error in PIO. This will consult the error handler * settings and either call MPI_Abort() or return an error code. @@ -1451,7 +1430,7 @@ int pio_err(iosystem_desc_t *ios, file_desc_t *file, /* If the user does not explicitly ask to return error, print * the error message in stderr on the root IO proc */ - bool print_err_msg = (ios) ? (ios->union_rank == ios->ioroot) : true; + bool print_err_msg = (ios) ? (ios->tcomm_info->get_union_comm_rank() == ios->tcomm_info->get_union_comm_io_root()) : true; if (print_err_msg) { @@ -1542,7 +1521,7 @@ void PIOc_warn(int iosysid, int ncid, ios = pio_get_iosystem_from_id(iosysid); } - bool print_warn_msg = (ios) ? (ios->union_rank == ios->ioroot) : true; + bool print_warn_msg = (ios) ? (ios->tcomm_info->get_union_comm_rank() == ios->tcomm_info->get_union_comm_io_root()) : true; if(print_warn_msg){ fprintf(stderr, "PIO: WARNING: %s, (%s:%d)\n", uwarn_msg, (fname) ? fname : "\0", line); fflush(stderr); @@ -1685,10 +1664,11 @@ int find_mpi_type(int pio_type, MPI_Datatype *mpi_type, int *type_size) * handling. * @param piotype the PIO data type (ex. PIO_FLOAT, PIO_INT, etc.). * @param ndims the number of dimensions. + * @param maplen the length of the local decomposition map * @param iodesc pointer that gets the newly allocated io_desc_t. * @returns 0 for success, error code otherwise. */ -int malloc_iodesc(iosystem_desc_t *ios, int piotype, int ndims, +int malloc_iodesc(iosystem_desc_t *ios, int piotype, int ndims, int maplen, io_desc_t **iodesc) { MPI_Datatype mpi_type; @@ -1703,24 +1683,21 @@ int malloc_iodesc(iosystem_desc_t *ios, int piotype, int ndims, LOG((1, "malloc_iodesc piotype = %d ndims = %d", piotype, ndims)); /* Get the MPI type corresponding with the PIO type. */ - if ((ret = find_mpi_type(piotype, &mpi_type, NULL))) - { - return pio_err(ios, NULL, ret, __FILE__, __LINE__, - "Internal error while allocating memory for iodesc. Unable to find MPI type corresponding to PIO type (%d)", piotype); + if((ret = find_mpi_type(piotype, &mpi_type, NULL))){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while allocating memory for iodesc. Unable to find MPI type corresponding to PIO type (%d)", piotype); } /* What is the size of the pio type? */ - if ((ret = spio_pnetcdf_inq_type(0, piotype, NULL, &type_size))) - { - return pio_err(ios, NULL, ret, __FILE__, __LINE__, - "Internal error while allocating memory for iodesc. Finding the size of PIO type (%d) failed", piotype); + if((ret = spio_pnetcdf_inq_type(0, piotype, NULL, &type_size))){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while allocating memory for iodesc. Finding the size of PIO type (%d) failed", piotype); } /* Allocate space for the io_desc_t struct. */ - if (!(*iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) - { - return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, - "Internal error while allocating memory for iodesc. Out of memory allocating %lld bytes for the I/O descriptor", (unsigned long long) sizeof(io_desc_t)); + if(!(*iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))){ + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Internal error while allocating memory for iodesc. Out of memory allocating %lld bytes for the I/O descriptor", (unsigned long long) sizeof(io_desc_t)); } /* Remember the pio type and its size. */ @@ -1731,19 +1708,32 @@ int malloc_iodesc(iosystem_desc_t *ios, int piotype, int ndims, (*iodesc)->mpitype = mpi_type; /* Get the size of the type. */ - if ((mpierr = MPI_Type_size((*iodesc)->mpitype, &(*iodesc)->mpitype_size))) - return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); + if((mpierr = MPI_Type_size((*iodesc)->mpitype, &(*iodesc)->mpitype_size))){ + return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); + } /* Initialize some values in the struct. */ (*iodesc)->maxregions = 1; (*iodesc)->ioid = -1; + (*iodesc)->maplen = maplen; (*iodesc)->ndims = ndims; /* Allocate space for, and initialize, the first region. */ - if ((ret = alloc_region2(ios, ndims, &((*iodesc)->firstregion)))) - { - return pio_err(ios, NULL, ret, __FILE__, __LINE__, - "Internal error while allocating memory for iodesc. Allocating memory for 1st region failed. Out of memory allocating memory for I/O region in the I/O descriptor"); + if((ret = alloc_region2(ios, ndims, &((*iodesc)->firstregion)))){ + return pio_err(ios, NULL, ret, __FILE__, __LINE__, + "Internal error while allocating memory for iodesc. Allocating memory for 1st region failed. Out of memory allocating memory for I/O region in the I/O descriptor"); + } + + /* Allocate memory for the local decomposition map */ + if(!((*iodesc)->map = (PIO_Offset *) malloc(sizeof(PIO_Offset) * maplen))){ + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Internal error while allocating memory (%lld bytes) to store I/O decomposition map", (unsigned long long) (sizeof(PIO_Offset) * maplen)); + } + + /* Allocate memory for storing the dimension lengths of variables that use this decomposition */ + if(!((*iodesc)->dimlen = (int *)malloc(sizeof(int) * ndims))){ + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Internal error while allocating memory (%lld bytes) for storing dimension sizes in the I/O decomposition map", (unsigned long long) (sizeof(int) * ndims)); } /* Set the swap memory settings to defaults for this IO system. */ @@ -1793,10 +1783,8 @@ int PIOc_freedecomp_impl(int iosysid, int ioid) int mpierr = MPI_SUCCESS; /* Return code from MPI function calls. */ int ret = 0; - GPTLstart("PIO:PIOc_freedecomp"); if (!(ios = pio_get_iosystem_from_id(iosysid))) { - GPTLstop("PIO:PIOc_freedecomp"); return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, "Freeing PIO decomposition failed. Invalid iosystem id (%d) provided", iosysid); } @@ -1805,7 +1793,6 @@ int PIOc_freedecomp_impl(int iosysid, int ioid) if (!(iodesc = pio_get_iodesc_from_id(ioid))) { - GPTLstop("PIO:PIOc_freedecomp"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, PIO_EBADID, __FILE__, __LINE__, "Freeing PIO decomposition failed. Invalid io decomposition id (%d) provided", ioid); @@ -1819,13 +1806,18 @@ int PIOc_freedecomp_impl(int iosysid, int ioid) PIO_SEND_ASYNC_MSG(ios, msg, &ret, iosysid, ioid); if(ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_freedecomp"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, ret, __FILE__, __LINE__, "Freeing PIO decomposition failed (iosysid = %d, iodesc id=%d). Error sending asynchronous message, PIO_MSG_FREEDECOMP, on iosystem", iosysid, ioid); } } + if(iodesc->nasync_pend_ops > 0){ + /* Let I/O desc be freed during finalize */ + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + return PIO_NOERR; + } + /* Free the map. */ free(iodesc->map); @@ -1841,7 +1833,6 @@ int PIOc_freedecomp_impl(int iosysid, int ioid) if (iodesc->rtype[i] != MPI_DATATYPE_NULL) if ((mpierr = MPI_Type_free(&iodesc->rtype[i]))) { - GPTLstop("PIO:PIOc_freedecomp"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } @@ -1855,7 +1846,6 @@ int PIOc_freedecomp_impl(int iosysid, int ioid) if (iodesc->stype[i] != MPI_DATATYPE_NULL) if ((mpierr = MPI_Type_free(iodesc->stype + i))) { - GPTLstop("PIO:PIOc_freedecomp"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } @@ -1885,38 +1875,28 @@ int PIOc_freedecomp_impl(int iosysid, int ioid) if (iodesc->rearranger == PIO_REARR_SUBSET) if ((mpierr = MPI_Comm_free(&iodesc->subset_comm))) { - GPTLstop("PIO:PIOc_freedecomp"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return check_mpi(ios, NULL, mpierr, __FILE__, __LINE__); } + if(iodesc->rearr){ + iodesc->rearr->finalize(); + delete iodesc->rearr; + } + ret = pio_delete_iodesc_from_list(ioid); if (ret != PIO_NOERR) { - GPTLstop("PIO:PIOc_freedecomp"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return pio_err(ios, NULL, ret, __FILE__, __LINE__, "Freeing PIO decomposition failed (iosysid = %d, ioid=%d). Error while trying to delete I/O descriptor from internal list", iosysid, ioid); } - GPTLstop("PIO:PIOc_freedecomp"); spio_ltimer_stop(ios->io_fstats->tot_timer_name); return ret; } -/** - * Read a decomposition map from a file. The decomp file is only read - * by task 0 in the communicator. - * - * @param file the filename - * @param ndims pointer to an int with the number of dims. - * @param gdims pointer to an array of dimension ids. - * @param fmaplen - * @param map - * @param comm - * @returns 0 for success, error code otherwise. - */ -int PIOc_readmap_impl(const char *file, int *ndims, int **gdims, PIO_Offset *fmaplen, +int PIOc_readmap_txt_impl(const char *file, int *ndims, int **gdims, PIO_Offset *fmaplen, PIO_Offset **map, MPI_Comm comm) { int npes, myrank; @@ -2049,6 +2029,58 @@ int PIOc_readmap_impl(const char *file, int *ndims, int **gdims, PIO_Offset *fma return PIO_NOERR; } +/** + * Read a decomposition map from a file. The decomp file is only read + * by task 0 in the communicator. + * + * @param file the filename + * @param ndims pointer to an int with the number of dims. + * @param gdims pointer to an array of dimension ids. + * @param fmaplen + * @param map + * @param comm + * @returns 0 for success, error code otherwise. + */ +int PIOc_readmap_impl(const char *file, int *ndims, int **gdims, PIO_Offset *fmaplen, + PIO_Offset **map, MPI_Comm comm) +{ + if(!file || !ndims || !gdims || !fmaplen || !map){ + return pio_err(NULL, NULL, PIO_EINVAL, __FILE__, __LINE__, + "Reading I/O decomposition failed. Invalid arguments provided, file is %s (expected not NULL), ndims is %s (expected not NULL), gdims is %s (expected not NULL), fmaplen is %s (expected not NULL), map is %s (expected not NULL)", PIO_IS_NULL(file), PIO_IS_NULL(ndims), PIO_IS_NULL(gdims), PIO_IS_NULL(fmaplen), PIO_IS_NULL(map)); + } + + std::string log_fname(file); + SPIO_Util::Decomp_Util::Decomp_logger *logger = + SPIO_Util::Decomp_Util::create_decomp_logger(comm, log_fname); + if(typeid(*logger) == typeid(SPIO_Util::Decomp_Util::Decomp_txt_logger)){ + delete logger; + return PIOc_readmap_txt_impl(file, ndims, gdims, fmaplen, map, comm); + } + + std::string version; + int nprocs; + std::vector rd_gdims; + std::vector rd_compmap; + + (*logger).read_only().open().get(version, nprocs, rd_gdims, rd_compmap).close(); + + *ndims = static_cast(rd_gdims.size()); + *gdims = (int *) malloc(*ndims * sizeof(int)); + if(*gdims == NULL){ + return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Unable to allocate (%lld bytes) to store gdims while reading decomposition file (%s)", static_cast(*ndims * sizeof(int)), log_fname.c_str()); + } + std::copy(rd_gdims.cbegin(), rd_gdims.cend(), *gdims); + *fmaplen = static_cast(rd_compmap.size()); + *map = (PIO_Offset *) malloc(*fmaplen * sizeof(PIO_Offset)); + if(*map == NULL){ + return pio_err(NULL, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Unable to allocate (%lld bytes) to store gmap while reading decomposition file (%s)", static_cast(*fmaplen * sizeof(PIO_Offset)), log_fname.c_str()); + } + std::copy(rd_compmap.cbegin(), rd_compmap.cend(), *map); + + delete logger; + return PIO_NOERR; +} + /** * Read a decomposition map from file. * @@ -3053,7 +3085,7 @@ int PIO_get_avail_iotypes(char *buf, size_t sz) int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char *filename, int mode) { - char tname[SPIO_TIMER_MAX_NAME]; + std::string tname; iosystem_desc_t *ios; /* Pointer to io system information. */ file_desc_t *file; /* Pointer to file information. */ int mpierr = MPI_SUCCESS; /* Return code from MPI function codes. */ @@ -3085,6 +3117,26 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * LOG((1, "PIOc_createfile iosysid = %d iotype = %d filename = %s mode = %d", iosysid, *iotype, filename, mode)); +#if PIO_USE_ASYNC_WR_THREAD + /* FIXME: Relax this wait */ + /* + if((*iotype != PIO_IOTYPE_HDF5) && (*iotype != PIO_IOTYPE_HDF5C)){ + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Creating file (%s) failed. Error waiting on all pending asynchronous HDF5 ops", filename); + } + } + */ + + ierr = spio_close_soft_closed_file(filename); + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Creating file (%s) failed. Error closing previous soft closed file", filename); + } + +#endif + /* Allocate space for the file info. */ if (!(file = (file_desc_t *) calloc(sizeof(file_desc_t), 1))) { @@ -3092,9 +3144,13 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * "Creating file (%s) failed. Out of memory allocating %lld bytes for the file descriptor", filename, (unsigned long long) (sizeof(file_desc_t))); } + file->pmtx = new std::mutex(); + assert(file->pmtx); + file->io_fstats = (spio_io_fstats_summary_t *) calloc(sizeof(spio_io_fstats_summary_t), 1); if(!(file->io_fstats)) { + delete(file->pmtx); return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Creating file (%s) failed. Out of memory allocating %lld bytes for caching file I/O statistics", filename, (unsigned long long) (sizeof(spio_io_fstats_summary_t))); } @@ -3102,19 +3158,14 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * /* Fill in some file values. */ file->fh = -1; file->reserve_extra_header_space = true; /* Set to true for creating output NetCDF files only. */ + file->in_def_mode = true; /* The file is in "define mode" when created */ file->is_reopened = false; strncpy(file->fname, filename, PIO_MAX_NAME); - ierr = pio_create_uniq_str(ios, NULL, tname, SPIO_TIMER_MAX_NAME, "tmp_", "_file"); - if(ierr != PIO_NOERR) - { - /* Not a fatal error */ - LOG((0, "Creating a unique name for the write timer for file (%s, ncid=%d) failed, ret = %d", file->fname, file->pio_ncid, ierr)); - tname[0] = '\0'; - } + pio_create_uniq_str(ios, NULL, tname, "tmp_", "_file"); - snprintf(file->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "PIO:wr_%s", tname); - snprintf(file->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "PIO:rd_%s", tname); - snprintf(file->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "PIO:tot_%s", tname); + snprintf(file->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:wr_") + tname).c_str()); + snprintf(file->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:rd_") + tname).c_str()); + snprintf(file->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:tot_") + tname).c_str()); spio_ltimer_start(file->io_fstats->wr_timer_name); spio_ltimer_start(file->io_fstats->tot_timer_name); @@ -3781,7 +3832,11 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * #ifdef _HDF5 case PIO_IOTYPE_HDF5: case PIO_IOTYPE_HDF5C: +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_iosys_async_hdf5_create_op_add(file, filename); +#else ierr = spio_hdf5_create(ios, file, filename); +#endif break; #endif } @@ -3802,12 +3857,16 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * #endif spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); + delete(file->pmtx); free(file->io_fstats); free(file); return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); } - ierr = check_netcdf(ios, file, ierr, __FILE__, __LINE__); + /* Ensure that we handle all errors, irrespective of what error handle is + * set by the user, when creating files + */ + ierr = spio_handle_err(ios, file, PIO_REDUCE_ERROR, ierr, __FILE__, __LINE__); /* If there was an error, free the memory we allocated and handle error. */ if (ierr != PIO_NOERR) { @@ -3823,6 +3882,7 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * #endif spio_ltimer_stop(file->io_fstats->wr_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); + delete(file->pmtx); free(file->io_fstats); free(file); return pio_err(ios, NULL, ierr, __FILE__, __LINE__, @@ -3850,6 +3910,11 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * */ spio_file_mvcache_init(file); +#ifdef _HDF5 + /* A datatype converter for converting user buffer to a "file type" buffer */ + file->dt_converter = static_cast(new SPIO_Util::File_Util::DTConverter()); +#endif + *ncidp = pio_add_to_file_list(file, comm); LOG((2, "Created file %s file->fh = %d file->pio_ncid = %d", filename, @@ -3868,7 +3933,6 @@ int spio_createfile_int(int iosysid, int *ncidp, const int *iotype, const char * * @param ncid the file->fh for this file (the real netCDF ncid, not * the pio_ncid). * @returns 0 if file is OK, error code otherwise. - * @author Ed Hartnett */ int check_unlim_use(int ncid) { @@ -4734,861 +4798,826 @@ static size_t adios_read_vars_vars(file_desc_t *file, size_t var_size, char *con * * @return 0 for success, error code otherwise. * @ingroup PIO_openfile - * @author Jim Edwards, Ed Hartnett */ int PIOc_openfile_retry_impl(int iosysid, int *ncidp, int *iotype, const char *filename, int mode, int retry) { - char tname[SPIO_TIMER_MAX_NAME]; - iosystem_desc_t *ios; /* Pointer to io system information. */ - file_desc_t *file; /* Pointer to file information. */ - int imode; /* Internal mode val for netcdf4 file open. */ - int mpierr = MPI_SUCCESS; /** Return code from MPI function codes. */ - int ierr = PIO_NOERR; /* Return code from function calls. */ - int ierr2 = PIO_NOERR; /* Return code from function calls. */ + std::string tname; + iosystem_desc_t *ios; /* Pointer to io system information. */ + file_desc_t *file; /* Pointer to file information. */ + int imode; /* Internal mode val for netcdf4 file open. */ + int mpierr = MPI_SUCCESS; /** Return code from MPI function codes. */ + int ierr = PIO_NOERR; /* Return code from function calls. */ + int ierr2 = PIO_NOERR; /* Return code from function calls. */ + + /* Get the IO system info from the iosysid. */ + if(!(ios = pio_get_iosystem_from_id(iosysid))){ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Opening file (%s) failed. Invalid iosystem id (%d) provided", (filename) ? filename : "UNKNOWN", iosysid); + } - /* Get the IO system info from the iosysid. */ - if (!(ios = pio_get_iosystem_from_id(iosysid))) - { - return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, - "Opening file (%s) failed. Invalid iosystem id (%d) provided", (filename) ? filename : "UNKNOWN", iosysid); - } + /* User must provide valid input for these parameters. */ + if(!ncidp || !iotype || !filename || strlen(filename) > PIO_MAX_NAME){ + return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__, + "Opening file (%s) failed. Invalid arguments provided. ncidp is %s (expected not NULL), iotype is %s (expected not NULL), filename is %s (expected not NULL), filename length = %lld (expected <= %d)", (filename) ? filename : "UNKNOWN", PIO_IS_NULL(ncidp), PIO_IS_NULL(iotype), PIO_IS_NULL(filename), (filename) ? ((unsigned long long )strlen(filename)) : 0, (int )PIO_MAX_NAME); + } - /* User must provide valid input for these parameters. */ - if (!ncidp || !iotype || !filename || strlen(filename) > PIO_MAX_NAME) - { - return pio_err(ios, NULL, PIO_EINVAL, __FILE__, __LINE__, - "Opening file (%s) failed. Invalid arguments provided. ncidp is %s (expected not NULL), iotype is %s (expected not NULL), filename is %s (expected not NULL), filename length = %lld (expected <= %d)", (filename) ? filename : "UNKNOWN", PIO_IS_NULL(ncidp), PIO_IS_NULL(iotype), PIO_IS_NULL(filename), (filename) ? ((unsigned long long )strlen(filename)) : 0, (int )PIO_MAX_NAME); - } + /* A valid iotype must be specified. */ + if(!iotype_is_valid(*iotype)){ + char avail_iotypes[PIO_MAX_NAME + 1]; + PIO_get_avail_iotypes(avail_iotypes, PIO_MAX_NAME); + return pio_err(ios, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, + "Opening file (%s) failed. Invalid iotype (%s:%d) specified. Available iotypes are : %s", filename, pio_iotype_to_string(*iotype), *iotype, avail_iotypes); + } - /* A valid iotype must be specified. */ - if (!iotype_is_valid(*iotype)) - { - char avail_iotypes[PIO_MAX_NAME + 1]; - PIO_get_avail_iotypes(avail_iotypes, PIO_MAX_NAME); - return pio_err(ios, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, - "Opening file (%s) failed. Invalid iotype (%s:%d) specified. Available iotypes are : %s", filename, pio_iotype_to_string(*iotype), *iotype, avail_iotypes); - } + spio_ltimer_start(ios->io_fstats->rd_timer_name); + spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(ios->io_fstats->rd_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); + LOG((2, "PIOc_openfile_retry iosysid = %d iotype = %d filename = %s mode = %d retry = %d", + iosysid, *iotype, filename, mode, retry)); - LOG((2, "PIOc_openfile_retry iosysid = %d iotype = %d filename = %s mode = %d retry = %d", - iosysid, *iotype, filename, mode, retry)); +#if PIO_USE_ASYNC_WR_THREAD + ierr = spio_close_soft_closed_file(filename); + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Creating file (%s) failed. Error closing previous soft closed file", filename); + } - /* Allocate space for the file info. */ - if (!(file = (file_desc_t *) calloc(sizeof(file_desc_t), 1))) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, - "Opening file (%s) failed. Out of memory allocating %lld bytes for the file structure", filename, (unsigned long long) (sizeof(*file))); + //if(((*iotype == PIO_IOTYPE_HDF5) || (*iotype == PIO_IOTYPE_HDF5C)) && !(mode & PIO_WRITE)){ + if((*iotype == PIO_IOTYPE_HDF5) || (*iotype == PIO_IOTYPE_HDF5C)){ + ierr = spio_wait_all_hdf5_async_ops(ios->iosysid); + if(ierr != PIO_NOERR){ + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Creating file (%s) failed. Error waiting on all pending asynchronous HDF5 ops", filename); } + } +#endif - file->io_fstats = (spio_io_fstats_summary_t *) calloc(sizeof(spio_io_fstats_summary_t), 1); - if(!(file->io_fstats)) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, - "Opening file (%s) failed. Out of memory allocating %lld bytes for caching file I/O statistics", filename, (unsigned long long) (sizeof(spio_io_fstats_summary_t))); - } + /* Allocate space for the file info. */ + if(!(file = (file_desc_t *) calloc(sizeof(file_desc_t), 1))){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Opening file (%s) failed. Out of memory allocating %lld bytes for the file structure", filename, (unsigned long long) (sizeof(*file))); + } - /* Fill in some file values. */ - file->fh = -1; - file->reserve_extra_header_space = false; /* Set to true for creating output NetCDF files only. */ - file->is_reopened = true; - strncpy(file->fname, filename, PIO_MAX_NAME); - ierr = pio_create_uniq_str(ios, NULL, tname, SPIO_TIMER_MAX_NAME, "tmp_", "_file"); - if(ierr != PIO_NOERR) - { - /* Not a fatal error */ - LOG((0, "Creating a unique name for the write timer for file (%s, ncid=%d) failed, ret = %d", file->fname, file->pio_ncid, ierr)); - tname[0] = '\0'; - } + file->pmtx = new std::mutex(); + assert(file->pmtx); - snprintf(file->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "PIO:wr_%s", tname); - snprintf(file->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "PIO:rd_%s", tname); - snprintf(file->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "PIO:tot_%s", tname); + file->io_fstats = (spio_io_fstats_summary_t *) calloc(sizeof(spio_io_fstats_summary_t), 1); + if(!(file->io_fstats)){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Opening file (%s) failed. Out of memory allocating %lld bytes for caching file I/O statistics", filename, (unsigned long long) (sizeof(spio_io_fstats_summary_t))); + } - /* FIXME: Files can be opened for rds and writes */ - spio_ltimer_start(file->io_fstats->rd_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); + /* Fill in some file values. */ + file->fh = -1; + file->reserve_extra_header_space = false; /* Set to true for creating output NetCDF files only. */ + file->in_def_mode = false; /* The file is NOT in "define mode" when opened */ + file->is_reopened = true; + strncpy(file->fname, filename, PIO_MAX_NAME); + pio_create_uniq_str(ios, NULL, tname, "tmp_", "_file"); - file->iotype = *iotype; + snprintf(file->io_fstats->wr_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:wr_") + tname).c_str()); + snprintf(file->io_fstats->rd_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:rd_") + tname).c_str()); + snprintf(file->io_fstats->tot_timer_name, SPIO_TIMER_MAX_NAME, "%s", (std::string("PIO:tot_") + tname).c_str()); + + /* FIXME: Files can be opened for rds and writes */ + spio_ltimer_start(file->io_fstats->rd_timer_name); + spio_ltimer_start(file->io_fstats->tot_timer_name); + + file->iotype = *iotype; #ifdef _ADIOS2 - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - /* Trying to open a file with adios unless ADIOS_BP2NC_TEST option is enabled for unit tests */ - bool adios2_file_exist = false; + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + /* Trying to open a file with adios unless ADIOS_BP2NC_TEST option is enabled for unit tests */ + bool adios2_file_exist = false; #ifndef _ADIOS_BP2NC_TEST - adios2_error adiosErr = adios2_error_none; - char declare_name[PIO_MAX_NAME] = {'\0'}; - char bpname[PIO_MAX_NAME] = {'\0'}; - struct stat sd; + adios2_error adiosErr = adios2_error_none; + char declare_name[PIO_MAX_NAME] = {'\0'}; + char bpname[PIO_MAX_NAME] = {'\0'}; + struct stat sd; - strcat(bpname, filename); - strcat(bpname, ".bp"); + strcat(bpname, filename); + strcat(bpname, ".bp"); - if (0 == stat(bpname, &sd)) - { - strncpy(file->fname, bpname, PIO_MAX_NAME); - snprintf(declare_name, PIO_MAX_NAME, "%s%lu", file->fname, get_adios2_io_cnt()); - strncpy(file->io_name_reader, declare_name, PIO_MAX_NAME); + if(0 == stat(bpname, &sd)){ + strncpy(file->fname, bpname, PIO_MAX_NAME); + snprintf(declare_name, PIO_MAX_NAME, "%s%lu", file->fname, get_adios2_io_cnt()); + strncpy(file->io_name_reader, declare_name, PIO_MAX_NAME); - file->ioH = adios2_declare_io(ios->adios_readerH, file->io_name_reader); - if (file->ioH == NULL) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to declare a new io handler", - filename); - } + file->ioH = adios2_declare_io(ios->adios_readerH, file->io_name_reader); + if(file->ioH == NULL){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to declare a new io handler", + filename); + } - adiosErr = adios2_set_parameter(file->ioH, "BufferVType", "chunk"); - if (adiosErr != adios2_error_none) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", - filename, convert_adios2_error_to_string(adiosErr)); - } + adiosErr = adios2_set_parameter(file->ioH, "BufferVType", "chunk"); + if(adiosErr != adios2_error_none){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", + filename, convert_adios2_error_to_string(adiosErr)); + } - adiosErr = adios2_set_parameter(file->ioH, "BufferChunkSize", "1Gb"); - if (adiosErr != adios2_error_none) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", - filename, convert_adios2_error_to_string(adiosErr)); - } + adiosErr = adios2_set_parameter(file->ioH, "BufferChunkSize", "1Gb"); + if(adiosErr != adios2_error_none){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", + filename, convert_adios2_error_to_string(adiosErr)); + } - adiosErr = adios2_set_parameter(file->ioH, "InitialBufferSize", "1Gb"); - if (adiosErr != adios2_error_none) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", - filename, convert_adios2_error_to_string(adiosErr)); - } + adiosErr = adios2_set_parameter(file->ioH, "InitialBufferSize", "1Gb"); + if(adiosErr != adios2_error_none){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", + filename, convert_adios2_error_to_string(adiosErr)); + } - adiosErr = adios2_set_parameter(file->ioH, "OpenTimeoutSecs", "1"); - if (adiosErr != adios2_error_none) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", - filename, convert_adios2_error_to_string(adiosErr)); - } + adiosErr = adios2_set_parameter(file->ioH, "OpenTimeoutSecs", "1"); + if(adiosErr != adios2_error_none){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to set a single parameter (adios2_error=%s)", + filename, convert_adios2_error_to_string(adiosErr)); + } - adiosErr = adios2_set_engine(file->ioH, "BP5"); - if (adiosErr != adios2_error_none) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to set the engine type for current io handler (adios2_error=%s)", - filename, convert_adios2_error_to_string(adiosErr)); - } + adiosErr = adios2_set_engine(file->ioH, "BP5"); + if(adiosErr != adios2_error_none){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to set the engine type for current io handler (adios2_error=%s)", + filename, convert_adios2_error_to_string(adiosErr)); + } - adios2_file_exist = true; + adios2_file_exist = true; - file->engineH = adios2_open(file->ioH, file->fname, adios2_mode_read); - if (file->engineH == NULL) - adios2_file_exist = false; /* Failed to open with adios2 trying pnetcdf */ - else - strncpy(file->fname, bpname, PIO_MAX_NAME); - } + file->engineH = adios2_open(file->ioH, file->fname, adios2_mode_read); + if(file->engineH == NULL){ + adios2_file_exist = false; /* Failed to open with adios2 trying pnetcdf */ + } + else{ + strncpy(file->fname, bpname, PIO_MAX_NAME); + } + } #endif - if (!adios2_file_exist) - { - /* Fall back to open as pnetcdf */ + if(!adios2_file_exist){ + /* Fall back to open as pnetcdf */ #ifdef _PNETCDF - file->iotype = PIO_IOTYPE_PNETCDF; + file->iotype = PIO_IOTYPE_PNETCDF; #else #ifdef _NETCDF4 #ifdef _MPISERIAL - file->iotype = PIO_IOTYPE_NETCDF4C; + file->iotype = PIO_IOTYPE_NETCDF4C; #else - file->iotype = PIO_IOTYPE_NETCDF4P; + file->iotype = PIO_IOTYPE_NETCDF4P; #endif #endif #endif - } } + } #endif #ifdef _HDF5 - if ((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)) - { - if (H5Fis_hdf5(filename) > 0) - { + if((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)){ + if(H5Fis_hdf5(filename) > 0){ #ifdef _NETCDF4 - /* Use NETCDF4 IO type to read files generated with HDF5 IO type so far */ + /* Use NETCDF4 IO type to read files generated with HDF5 IO type so far */ #ifdef _MPISERIAL - file->iotype = PIO_IOTYPE_NETCDF4C; + file->iotype = PIO_IOTYPE_NETCDF4C; #else - file->iotype = PIO_IOTYPE_NETCDF4P; + file->iotype = PIO_IOTYPE_NETCDF4P; #endif #else - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, - "Opening file (%s) with PIO_IOTYPE_HDF5 failed. HDF5 IO type currently requires NETCDF4 (not configured for SCORPIO) to support read operations", filename); + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, + "Opening file (%s) with PIO_IOTYPE_HDF5 failed. HDF5 IO type currently requires NETCDF4 (not configured for SCORPIO) to support read operations", filename); #endif - } - else - { + } + else{ #ifdef _PNETCDF - file->iotype = PIO_IOTYPE_PNETCDF; + file->iotype = PIO_IOTYPE_PNETCDF; #else #ifdef _NETCDF - file->iotype = PIO_IOTYPE_NETCDF; + file->iotype = PIO_IOTYPE_NETCDF; #endif #endif - } } + std::string warn_msg = std::string("Opening files with the HDF5 library is currently not supported.") + + std::string(" Switching from PIO_IOTYPE_HDF5 to ") + pio_iotype_to_string(file->iotype) + + std::string(" for file, ") + filename; + PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, warn_msg.c_str()); + } #endif - file->iosystem = ios; - file->mode = mode; - /* - file->num_unlim_dimids = 0; - file->unlim_dimids = NULL; - */ + file->iosystem = ios; + file->mode = mode; + /* + file->num_unlim_dimids = 0; + file->unlim_dimids = NULL; + */ #ifdef _ADIOS2 - file->num_dim_vars = 0; + file->num_dim_vars = 0; #endif - for (int i = 0; i < PIO_MAX_VARS; i++) - { - file->varlist[i].varid = -1; - file->varlist[i].vname[0] = '\0'; - file->varlist[i].vdesc[0] = '\0'; - file->varlist[i].rec_var = 0; - file->varlist[i].record = -1; - file->varlist[i].request = NULL; - file->varlist[i].request_sz = NULL; - file->varlist[i].nreqs = 0; - file->varlist[i].fillvalue = NULL; - file->varlist[i].pio_type = PIO_NAT; - file->varlist[i].type_size = 0; - file->varlist[i].vrsize = 0; - file->varlist[i].rb_pend = 0; - file->varlist[i].vrsize = 0; - file->varlist[i].wb_pend = 0; - file->varlist[i].use_fill = 0; - file->varlist[i].fillbuf = NULL; - } + for(int i = 0; i < PIO_MAX_VARS; i++){ + file->varlist[i].varid = -1; + file->varlist[i].vname[0] = '\0'; + file->varlist[i].vdesc[0] = '\0'; + file->varlist[i].rec_var = 0; + file->varlist[i].record = -1; + file->varlist[i].request = NULL; + file->varlist[i].request_sz = NULL; + file->varlist[i].nreqs = 0; + file->varlist[i].fillvalue = NULL; + file->varlist[i].pio_type = PIO_NAT; + file->varlist[i].type_size = 0; + file->varlist[i].vrsize = 0; + file->varlist[i].rb_pend = 0; + file->varlist[i].vrsize = 0; + file->varlist[i].wb_pend = 0; + file->varlist[i].use_fill = 0; + file->varlist[i].fillbuf = NULL; + } #ifdef _ADIOS2 - /* Set communicator for all adios processes, process rank, and I/O master node */ - file->all_comm = ios->union_comm; - file->all_rank = ios->union_rank; - file->num_alltasks = ios->num_uniontasks; + /* Set communicator for all adios processes, process rank, and I/O master node */ + file->all_comm = ios->union_comm; + file->all_rank = ios->union_rank; + file->num_alltasks = ios->num_uniontasks; #endif - /* Set to true if this task should participate in IO (only true - * for one task with netcdf serial files. */ - if (file->iotype == PIO_IOTYPE_NETCDF4P || file->iotype == PIO_IOTYPE_NETCDF4P_NCZARR || - file->iotype == PIO_IOTYPE_PNETCDF || - ios->io_rank == 0) - file->do_io = 1; + /* Set to true if this task should participate in IO (only true + * for one task with netcdf serial files. */ + if(file->iotype == PIO_IOTYPE_NETCDF4P || file->iotype == PIO_IOTYPE_NETCDF4P_NCZARR || + file->iotype == PIO_IOTYPE_PNETCDF || + ios->io_rank == 0) { file->do_io = 1; } - /* If async is in use, bcast the parameters from compute to I/O procs. */ - if(ios->async) - { - int len = strlen(filename) + 1; - PIO_SEND_ASYNC_MSG(ios, PIO_MSG_OPEN_FILE, &ierr, len, filename, file->iotype, file->mode, retry); - if(ierr != PIO_NOERR) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Opening file (%s) failed. Sending asynchronous message, PIO_MSG_OPEN_FILE, failed on iosystem (iosysid=%d)", filename, ios->iosysid); - } + /* If async is in use, bcast the parameters from compute to I/O procs. */ + if(ios->async){ + int len = strlen(filename) + 1; + PIO_SEND_ASYNC_MSG(ios, PIO_MSG_OPEN_FILE, &ierr, len, filename, file->iotype, file->mode, retry); + if(ierr != PIO_NOERR){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Opening file (%s) failed. Sending asynchronous message, PIO_MSG_OPEN_FILE, failed on iosystem (iosysid=%d)", filename, ios->iosysid); } + } #ifdef _ADIOS2 - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { - /* Get available variables and set structures. Restrict to the first step only */ - adios2_step_status status; - size_t nsteps = 0, step = 0; - adios2_error err_steps = adios2_steps(&nsteps, file->engineH); - adios2_error adiosErr = adios2_error_none; - - /* Initialize adios structure */ - assert(file->num_vars < PIO_MAX_VARS); - for (size_t var = 0; var < PIO_MAX_VARS; var++) - { - file->adios_vars[var].nc_type = PIO_NAT; - file->adios_vars[var].adios_type = adios2_type_unknown; - file->adios_vars[var].adios_type_size = 0; - file->adios_vars[var].nattrs = 0; - file->adios_vars[var].ndims = 0; - file->adios_vars[var].adios_varid = 0; - file->adios_vars[var].decomp_varid = 0; - file->adios_vars[var].frame_varid = 0; - file->adios_vars[var].fillval_varid = 0; - file->adios_vars[var].gdimids = NULL; - file->adios_vars[var].interval_map = NULL; - } - - file->cache_data_blocks = spio_hash(10000); - file->cache_block_sizes = spio_hash(10000); - file->cache_darray_info = spio_hash(10000); - - file->adios_reader_num_decomp_blocks = 0; + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ + /* Get available variables and set structures. Restrict to the first step only */ + adios2_step_status status; + size_t nsteps = 0, step = 0; + adios2_error err_steps = adios2_steps(&nsteps, file->engineH); + adios2_error adiosErr = adios2_error_none; - file->store_adios_decomp = true; + /* Initialize adios structure */ + assert(file->num_vars < PIO_MAX_VARS); + for(size_t var = 0; var < PIO_MAX_VARS; var++){ + file->adios_vars[var].nc_type = PIO_NAT; + file->adios_vars[var].adios_type = adios2_type_unknown; + file->adios_vars[var].adios_type_size = 0; + file->adios_vars[var].nattrs = 0; + file->adios_vars[var].ndims = 0; + file->adios_vars[var].adios_varid = 0; + file->adios_vars[var].decomp_varid = 0; + file->adios_vars[var].frame_varid = 0; + file->adios_vars[var].fillval_varid = 0; + file->adios_vars[var].gdimids = NULL; + file->adios_vars[var].interval_map = NULL; + } + + file->cache_data_blocks = spio_hash(10000); + file->cache_block_sizes = spio_hash(10000); + file->cache_darray_info = spio_hash(10000); + + file->adios_reader_num_decomp_blocks = 0; + + file->store_adios_decomp = true; + + while(step < nsteps && adios2_begin_step(file->engineH, adios2_step_mode_read, -1.0, &status) == adios2_error_none){ + file->begin_step_called = 1; + if(status == adios2_step_status_end_of_stream) { break; } + + size_t var_size = 0; + char **var_names = adios2_available_variables(file->ioH, &var_size); + if(var_names == NULL){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to return an array or c strings for names of available variables", + filename); + } - while (step < nsteps && adios2_begin_step(file->engineH, adios2_step_mode_read, -1.0, &status) == adios2_error_none) - { - file->begin_step_called = 1; - if (status == adios2_step_status_end_of_stream) - break; + adios_read_global_dimensions(ios, file, var_names, var_size); - size_t var_size = 0; - char **var_names = adios2_available_variables(file->ioH, &var_size); - if (var_names == NULL) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to return an array or c strings for names of available variables", - filename); - } + /* Read names of variables from variables */ + size_t nvars = adios_read_vars_vars(file, var_size, var_names); - adios_read_global_dimensions(ios, file, var_names, var_size); + /* Free memory */ + for(size_t i = 0; i < var_size; i++) { free(var_names[i]); } - /* Read names of variables from variables */ - size_t nvars = adios_read_vars_vars(file, var_size, var_names); + free(var_names); - /* Free memory */ - for (size_t i = 0; i < var_size; i++) - free(var_names[i]); + /* Additionally read variables from attributes like + * /__pio__/var/dummy_scalar_var_int/def/ndims */ + size_t attr_size = 0; + char **attr_names = adios2_available_attributes(file->ioH, &attr_size); + if(attr_names == NULL){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to return an array or c strings for names of available attributes", + filename); + } - free(var_names); + nvars = adios_read_vars_attrs(file, attr_size, attr_names); - /* Additionally read variables from attributes like - * /__pio__/var/dummy_scalar_var_int/def/ndims */ - size_t attr_size = 0; - char **attr_names = adios2_available_attributes(file->ioH, &attr_size); - if (attr_names == NULL) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to return an array or c strings for names of available attributes", - filename); - } + for(size_t i = 0; i < attr_size; i++){ free(attr_names[i]); } - nvars = adios_read_vars_attrs(file, attr_size, attr_names); + free(attr_names); - for (size_t i = 0; i < attr_size; i++) - free(attr_names[i]); + /* Initialize variables */ + for(int var_id = 0; var_id < file->num_vars; var_id++){ + adios_get_nc_type(file, var_id); + adios_get_adios_type(file, var_id); + adios_get_ndims(file, var_id); + adios_get_nc_op_tag(file, var_id); + adios_get_dim_ids(file, var_id); + adios_get_step_info(file, var_id, step, nsteps); + } - free(attr_names); + if(step == 0){ + adios_get_num_decomp_blocks(file); + adios_get_decomp_storage_info(file); + } - /* Initialize variables */ - for (int var_id = 0; var_id < file->num_vars; var_id++) - { - adios_get_nc_type(file, var_id); - adios_get_adios_type(file, var_id); - adios_get_ndims(file, var_id); - adios_get_nc_op_tag(file, var_id); - adios_get_dim_ids(file, var_id); - adios_get_step_info(file, var_id, step, nsteps); - } + adiosErr = adios2_end_step(file->engineH); + if(adiosErr != adios2_error_none){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to terminate interaction with current step (adios2_error=%s)", + filename, convert_adios2_error_to_string(adiosErr)); + } - if (step == 0) - { - adios_get_num_decomp_blocks(file); - adios_get_decomp_storage_info(file); - } + file->begin_step_called = 0; + step++; + } - adiosErr = adios2_end_step(file->engineH); - if (adiosErr != adios2_error_none) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to terminate interaction with current step (adios2_error=%s)", - filename, convert_adios2_error_to_string(adiosErr)); - } + int attr_id = 0; + size_t attr_size = 0; + char **attr_names = adios2_available_attributes(file->ioH, &attr_size); + if(attr_names == NULL){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "The low level (ADIOS) I/O library call failed to return an array or c strings for names of available attributes", + filename); + } + + /** Parse global attributes */ + for(size_t i = 0; i < attr_size; i++){ + /* Get global attributes */ + /* Strings that start with /__pio__/var/ are variables */ + if(strncmp(attr_names[i], adios_pio_global_prefix, strlen(adios_pio_global_prefix)) == 0){ + /* Get substring from string for name */ + int sub_length = strlen(attr_names[i]) - strlen(adios_pio_global_prefix); + int full_length = strlen(attr_names[i]); + int prefix_length = strlen(adios_pio_global_prefix); + + file->adios_attrs[attr_id].att_name = (char *) calloc(sub_length + 1, 1); + if(file->adios_attrs[attr_id].att_name == NULL){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "Out of memory allocating %lld bytes for global attribute %s", + filename, (long long int)(sub_length + 1), attr_names[i]); + } + + strncpy(file->adios_attrs[attr_id].att_name, &attr_names[i][prefix_length], sub_length); + file->adios_attrs[attr_id].att_varid = -1; + + adios_get_attr(file, attr_id, attr_names, i); + + attr_id++; + assert(attr_id < PIO_MAX_ATTRS); + } - file->begin_step_called = 0; - step++; - } + /* Cache decomp attributes */ + if(strncmp(attr_names[i], adios_pio_var_prefix, strlen(adios_pio_var_prefix)) == 0 && + strstr(attr_names[i], adios_def_decomp_suffix) != NULL){ + adios2_attribute const *attributeH = adios2_inquire_attribute(file->ioH, attr_names[i]); + if(attributeH != NULL){ + size_t size_attr = 0; + char *attr_data = (char *) calloc(PIO_MAX_NAME, 1); + if(attr_data == NULL){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "Out of memory allocating %lld bytes for decomposition attribute (%s)", + filename, (long long int)PIO_MAX_NAME, attr_names[i]); + } - int attr_id = 0; - size_t attr_size = 0; - char **attr_names = adios2_available_attributes(file->ioH, &attr_size); - if (attr_names == NULL) - { + adiosErr = adios2_attribute_data(attr_data, &size_attr, attributeH); + if(adiosErr != adios2_error_none){ spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to return an array or c strings for names of available attributes", - filename); - } - - /** Parse global attributes */ - for (size_t i = 0; i < attr_size; i++) - { - /* Get global attributes */ - /* Strings that start with /__pio__/var/ are variables */ - if (strncmp(attr_names[i], adios_pio_global_prefix, strlen(adios_pio_global_prefix)) == 0) - { - /* Get substring from string for name */ - int sub_length = strlen(attr_names[i]) - strlen(adios_pio_global_prefix); - int full_length = strlen(attr_names[i]); - int prefix_length = strlen(adios_pio_global_prefix); - - file->adios_attrs[attr_id].att_name = (char *) calloc(sub_length + 1, 1); - if (file->adios_attrs[attr_id].att_name == NULL) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "Out of memory allocating %lld bytes for global attribute %s", - filename, (long long int)(sub_length + 1), attr_names[i]); - } - - strncpy(file->adios_attrs[attr_id].att_name, &attr_names[i][prefix_length], sub_length); - file->adios_attrs[attr_id].att_varid = -1; - - adios_get_attr(file, attr_id, attr_names, i); - - attr_id++; - assert(attr_id < PIO_MAX_ATTRS); - } - - /* Cache decomp attributes */ - if (strncmp(attr_names[i], adios_pio_var_prefix, strlen(adios_pio_var_prefix)) == 0 && - strstr(attr_names[i], adios_def_decomp_suffix) != NULL) - { - adios2_attribute const *attributeH = adios2_inquire_attribute(file->ioH, attr_names[i]); - if (attributeH != NULL) - { - size_t size_attr = 0; - char *attr_data = (char *) calloc(PIO_MAX_NAME, 1); - if (attr_data == NULL) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "Out of memory allocating %lld bytes for decomposition attribute (%s)", - filename, (long long int)PIO_MAX_NAME, attr_names[i]); - } - - adiosErr = adios2_attribute_data(attr_data, &size_attr, attributeH); - if (adiosErr != adios2_error_none) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_EADIOS2ERR, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "The low level (ADIOS) I/O library call failed to retrieve attribute data pointer (adios2_error=%s)", - filename, convert_adios2_error_to_string(adiosErr)); - } + "The low level (ADIOS) I/O library call failed to retrieve attribute data pointer (adios2_error=%s)", + filename, convert_adios2_error_to_string(adiosErr)); + } - /* attr_data will be deleted in the cache delete operation */ - file->cache_darray_info->put(file->cache_darray_info, attr_names[i], attr_data); - } - } + /* attr_data will be deleted in the cache delete operation */ + file->cache_darray_info->put(file->cache_darray_info, attr_names[i], attr_data); } - file->num_gattrs += attr_id; + } + } - /* Parse variable attributes */ - for (size_t i = 0; i < attr_size; i++) - { - /* Get variable attribute */ - char suffix_att_name[] = "/"; + file->num_gattrs += attr_id; - for (size_t var_cnt = 0; var_cnt < file->num_vars; var_cnt++) - { - char *attr_full_prefix = adios_name(adios_pio_var_prefix, file->adios_vars[var_cnt].name, - suffix_att_name); - if (strncmp(attr_names[i], attr_full_prefix, strlen(attr_full_prefix)) == 0) - { - int sub_length = strlen(attr_names[i]) - strlen(attr_full_prefix); - int full_length = strlen(attr_names[i]); - int prefix_length = strlen(attr_full_prefix); - if (strrchr(attr_names[i], '/') > &attr_names[i][prefix_length]) - continue; - - file->adios_attrs[attr_id].att_name = (char *) calloc(sub_length + 1, 1); - if (file->adios_attrs[attr_id].att_name == NULL) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Opening file (%s) using ADIOS iotype failed. " - "Out of memory allocating %lld bytes for attribute (id = %d) name", - filename, (long long int)(sub_length + 1), attr_id); - } + /* Parse variable attributes */ + for(size_t i = 0; i < attr_size; i++){ + /* Get variable attribute */ + char suffix_att_name[] = "/"; - strncpy(file->adios_attrs[attr_id].att_name, &attr_names[i][prefix_length], sub_length); - file->adios_attrs[attr_id].att_varid = var_cnt; - adios_get_attr(file, attr_id, attr_names, i); + for(size_t var_cnt = 0; var_cnt < file->num_vars; var_cnt++){ + char *attr_full_prefix = adios_name(adios_pio_var_prefix, file->adios_vars[var_cnt].name, + suffix_att_name); + if(strncmp(attr_names[i], attr_full_prefix, strlen(attr_full_prefix)) == 0){ + int sub_length = strlen(attr_names[i]) - strlen(attr_full_prefix); + int full_length = strlen(attr_names[i]); + int prefix_length = strlen(attr_full_prefix); + if(strrchr(attr_names[i], '/') > &attr_names[i][prefix_length]) { continue; } - assert(attr_id < PIO_MAX_ATTRS); - attr_id++; - } - } + file->adios_attrs[attr_id].att_name = (char *) calloc(sub_length + 1, 1); + if(file->adios_attrs[attr_id].att_name == NULL){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Opening file (%s) using ADIOS iotype failed. " + "Out of memory allocating %lld bytes for attribute (id = %d) name", + filename, (long long int)(sub_length + 1), attr_id); + } + + strncpy(file->adios_attrs[attr_id].att_name, &attr_names[i][prefix_length], sub_length); + file->adios_attrs[attr_id].att_varid = var_cnt; + adios_get_attr(file, attr_id, attr_names, i); - free(attr_names[i]); + assert(attr_id < PIO_MAX_ATTRS); + attr_id++; } + } - free(attr_names); - file->num_attrs += attr_id; + free(attr_names[i]); } + + free(attr_names); + file->num_attrs += attr_id; + } #endif - /* If this is an IO task, then call the netCDF function. */ - if (ios->ioproc) - { - switch (file->iotype) - { + /* If this is an IO task, then call the netCDF function. */ + if(ios->ioproc){ + switch(file->iotype){ #ifdef _NETCDF4 - case PIO_IOTYPE_NETCDF4P: + case PIO_IOTYPE_NETCDF4P: #ifdef _MPISERIAL - ierr = nc_open(filename, file->mode, &file->fh); + ierr = nc_open(filename, file->mode, &file->fh); #else - imode = file->mode | NC_MPIIO; - if ((ierr = nc_open_par(filename, imode, ios->io_comm, ios->info, &file->fh))) - break; - file->mode = imode; - - /* Check the vars for valid use of unlim dims. */ - if ((ierr = check_unlim_use(file->fh))) - break; - LOG((2, "PIOc_openfile_retry:nc_open_par filename = %s mode = %d imode = %d ierr = %d", - filename, file->mode, imode, ierr)); + imode = file->mode | NC_MPIIO; + if ((ierr = nc_open_par(filename, imode, ios->io_comm, ios->info, &file->fh))) { break; } + file->mode = imode; + + /* Check the vars for valid use of unlim dims. */ + if ((ierr = check_unlim_use(file->fh))) { break; } + LOG((2, "PIOc_openfile_retry:nc_open_par filename = %s mode = %d imode = %d ierr = %d", + filename, file->mode, imode, ierr)); #endif - break; + break; - case PIO_IOTYPE_NETCDF4P_NCZARR: - file->mode = file->mode | NC_NETCDF4; - LOG((2, "Calling nc_open_par (nczarr) io_comm = %d mode = %d fh = %d", - ios->io_comm, file->mode, file->fh)); - ierr = PIO_ENOTBUILT; + case PIO_IOTYPE_NETCDF4P_NCZARR: + file->mode = file->mode | NC_NETCDF4; + LOG((2, "Calling nc_open_par (nczarr) io_comm = %d mode = %d fh = %d", + ios->io_comm, file->mode, file->fh)); + ierr = PIO_ENOTBUILT; #ifndef _MPISERIAL #if PIO_USE_NETCDF4_NCZARR - { - std::string filename_nczarr = spio_get_nczarr_fname(filename); - if(!filename_nczarr.empty()){ - ierr = nc_open_par(filename_nczarr.c_str(), file->mode, - ios->io_comm, ios->info, &file->fh); - if(ierr != NC_NOERR){ - /* The file being opened may not be NCZarr, for now retry with NetCDF4 */ - file->iotype = PIO_IOTYPE_NETCDF4P; - ierr = nc_open_par(filename, file->mode, ios->io_comm, ios->info, &file->fh); - } - } - } - LOG((2, "nc_open_par returned %d file->fh = %d", ierr, file->fh)); + { + std::string filename_nczarr = spio_get_nczarr_fname(filename); + if(!filename_nczarr.empty()){ + ierr = nc_open_par(filename_nczarr.c_str(), file->mode, + ios->io_comm, ios->info, &file->fh); + if(ierr != NC_NOERR){ + /* The file being opened may not be NCZarr, for now retry with NetCDF4 */ + file->iotype = PIO_IOTYPE_NETCDF4P; + ierr = nc_open_par(filename, file->mode, ios->io_comm, ios->info, &file->fh); + } + } + } + LOG((2, "nc_open_par returned %d file->fh = %d", ierr, file->fh)); #else - LOG((2, "nc_open_par returned %d, NCZarr support not built/enabled", ierr)); + LOG((2, "nc_open_par returned %d, NCZarr support not built/enabled", ierr)); #endif #endif - break; - case PIO_IOTYPE_NETCDF4C: - if (ios->io_rank == 0) - { - imode = file->mode | NC_NETCDF4; - if ((ierr = nc_open(filename, imode, &file->fh))) - break; - file->mode = imode; - /* Check the vars for valid use of unlim dims. */ - if ((ierr = check_unlim_use(file->fh))) - break; - } - break; + break; + case PIO_IOTYPE_NETCDF4C: + if(ios->io_rank == 0){ + imode = file->mode | NC_NETCDF4; + if((ierr = nc_open(filename, imode, &file->fh))) { break; } + file->mode = imode; + /* Check the vars for valid use of unlim dims. */ + if((ierr = check_unlim_use(file->fh))) { break; } + } + break; #endif /* _NETCDF4 */ #ifdef _NETCDF - case PIO_IOTYPE_NETCDF: - if (ios->io_rank == 0) - ierr = nc_open(filename, file->mode, &file->fh); - break; + case PIO_IOTYPE_NETCDF: + if(ios->io_rank == 0){ + ierr = nc_open(filename, file->mode, &file->fh); + } + break; #endif /* _NETCDF */ #ifdef _PNETCDF - case PIO_IOTYPE_PNETCDF: - ierr = ncmpi_open(ios->io_comm, filename, file->mode, ios->info, &file->fh); + case PIO_IOTYPE_PNETCDF: + ierr = ncmpi_open(ios->io_comm, filename, file->mode, ios->info, &file->fh); - // This should only be done with a file opened to append - if (ierr == PIO_NOERR && (file->mode & PIO_WRITE)) - { - if (ios->iomaster == MPI_ROOT) - LOG((2, "%d Setting IO buffer %ld", __LINE__, pio_buffer_size_limit)); + // This should only be done with a file opened to append + if(ierr == PIO_NOERR && (file->mode & PIO_WRITE)){ + if(ios->iomaster == MPI_ROOT) { LOG((2, "%d Setting IO buffer %ld", __LINE__, pio_buffer_size_limit)); } - /* Please refer to a previous related comment for why we ensure a minimum buffer size (16 MiB) for - buffered put APIs in PnetCDF. - */ - const PIO_Offset min_bufsize = 16777216; /* Minimum buffer size (16 MiB) for buffered put APIs in PnetCDF */ - ierr = ncmpi_buffer_attach(file->fh, (pio_buffer_size_limit > min_bufsize)? pio_buffer_size_limit : min_bufsize); - } - LOG((2, "ncmpi_open(%s) : fd = %d", filename, file->fh)); - break; + /* Please refer to a previous related comment for why we ensure a minimum buffer size (16 MiB) for + buffered put APIs in PnetCDF. + */ + const PIO_Offset min_bufsize = 16777216; /* Minimum buffer size (16 MiB) for buffered put APIs in PnetCDF */ + ierr = ncmpi_buffer_attach(file->fh, (pio_buffer_size_limit > min_bufsize)? pio_buffer_size_limit : min_bufsize); + } + LOG((2, "ncmpi_open(%s) : fd = %d", filename, file->fh)); + break; #endif #ifdef _ADIOS2 - case PIO_IOTYPE_ADIOS: - case PIO_IOTYPE_ADIOSC: - break; /* This case has been handled above */ + case PIO_IOTYPE_ADIOS: + case PIO_IOTYPE_ADIOSC: + break; /* This case has been handled above */ #endif - default: - { - char avail_iotypes[PIO_MAX_NAME + 1]; - int tmp_iotype = file->iotype; - - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - - free(file->io_fstats); - free(file); - PIO_get_avail_iotypes(avail_iotypes, PIO_MAX_NAME); - return pio_err(ios, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, - "Opening file (%s) failed. Invalid iotype (%s:%d) specified. Available iotypes are : %s", filename, pio_iotype_to_string(tmp_iotype), tmp_iotype, avail_iotypes); - } - } - - /* If the caller requested a retry, and we failed to open a - file due to an incompatible type of NetCDF, try it once - with just plain old basic NetCDF. */ - if (retry) - { -#ifdef _NETCDF - LOG((2, "retry error code ierr = %d io_rank %d", ierr, ios->io_rank)); - /* Bcast error code from io rank 0 to all io procs */ - mpierr = MPI_Bcast(&ierr, 1, MPI_INT, 0, ios->io_comm); - if(mpierr != MPI_SUCCESS){ - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return check_mpi(NULL, file, ierr, __FILE__, __LINE__); - } - if ((ierr != NC_NOERR) && (file->iotype != PIO_IOTYPE_NETCDF)) - { - /* reset file markers for NETCDF on all IO tasks */ - file->iotype = PIO_IOTYPE_NETCDF; - - /* FIXME: The changes below to modify the user-specified - * iotype needs to be propogated to all tasks. */ - /* modify the user-specified iotype on all tasks */ - /* Modifying the user-specified iotype unfortunately - * causes some E3SM model components to reset the iotype - * to NetCDF for all subsequent reads and writes - * (eventhough only some files read are in the NetCDF4 - * format) - * Commenting this out for now, until we introduce - * PIO_IOTYPE_ANY that would allow users to explicitly - * specify that the lib can internally modify iotypes - */ - /* *iotype = PIO_IOTYPE_NETCDF; */ - - /* open netcdf file serially on main task */ - if (ios->io_rank == 0) - { - char errmsg[PIO_MAX_NAME]; - - ierr2 = PIOc_strerror_impl(ierr, errmsg, PIO_MAX_NAME); - printf("PIO: WARNING: Opening file (%s) with iotype=%d (%s) failed (ierr=%d, %s). Retrying with iotype=PIO_IOTYPE_NETCDF\n", filename, *iotype, pio_iotype_to_string(*iotype), ierr, ((ierr2 == PIO_NOERR) ? errmsg : "")); - ierr = nc_open(filename, file->mode, &file->fh); - if(ierr == NC_NOERR) - { - printf("PIO: WARNING: Opening file (%s) with iotype=%d (%s) failed. But retrying with iotype PIO_IOTYPE_NETCDF was successful. Switching iotype to PIO_IOTYPE_NETCDF for this file\n", filename, *iotype, pio_iotype_to_string(*iotype)); - } - } - else - { - /* reset ierr */ - ierr = PIO_NOERR; - - file->do_io = 0; - } - } - LOG((2, "retry nc_open(%s) : fd = %d, iotype = %d, do_io = %d, ierr = %d", - filename, file->fh, file->iotype, file->do_io, ierr)); -#endif /* _NETCDF */ - } - } + default: + { + char avail_iotypes[PIO_MAX_NAME + 1]; + int tmp_iotype = file->iotype; - /* Broadcast open mode to all tasks. */ - if ((mpierr = MPI_Bcast(&file->mode, 1, MPI_INT, ios->ioroot, ios->my_comm))) - { spio_ltimer_stop(ios->io_fstats->rd_timer_name); spio_ltimer_stop(ios->io_fstats->tot_timer_name); spio_ltimer_stop(file->io_fstats->rd_timer_name); spio_ltimer_stop(file->io_fstats->tot_timer_name); + + delete(file->pmtx); free(file->io_fstats); free(file); - return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); - } + PIO_get_avail_iotypes(avail_iotypes, PIO_MAX_NAME); + return pio_err(ios, NULL, PIO_EBADIOTYPE, __FILE__, __LINE__, + "Opening file (%s) failed. Invalid iotype (%s:%d) specified. Available iotypes are : %s", filename, pio_iotype_to_string(tmp_iotype), tmp_iotype, avail_iotypes); + } + } // switch(file->iotype) - if (retry) - { - /* Broadcast IO type (might be switched on IO tasks) to all tasks. */ - if ((mpierr = MPI_Bcast(&file->iotype, 1, MPI_INT, ios->ioroot, ios->my_comm))) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - free(file->io_fstats); - free(file); - return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + /* If the caller requested a retry, and we failed to open a + file due to an incompatible type of NetCDF, try it once + with just plain old basic NetCDF. */ + if(retry){ +#ifdef _NETCDF + LOG((2, "retry error code ierr = %d io_rank %d", ierr, ios->io_rank)); + /* Bcast error code from io rank 0 to all io procs */ + mpierr = MPI_Bcast(&ierr, 1, MPI_INT, 0, ios->io_comm); + if(mpierr != MPI_SUCCESS){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + return check_mpi(NULL, file, ierr, __FILE__, __LINE__); + } + + if((ierr != NC_NOERR) && (file->iotype != PIO_IOTYPE_NETCDF)){ + /* reset file markers for NETCDF on all IO tasks */ + file->iotype = PIO_IOTYPE_NETCDF; + + /* FIXME: The changes below to modify the user-specified + * iotype needs to be propogated to all tasks. */ + /* modify the user-specified iotype on all tasks */ + /* Modifying the user-specified iotype unfortunately + * causes some E3SM model components to reset the iotype + * to NetCDF for all subsequent reads and writes + * (eventhough only some files read are in the NetCDF4 + * format) + * Commenting this out for now, until we introduce + * PIO_IOTYPE_ANY that would allow users to explicitly + * specify that the lib can internally modify iotypes + */ + /* *iotype = PIO_IOTYPE_NETCDF; */ + + /* open netcdf file serially on main task */ + if(ios->io_rank == 0){ + char errmsg[PIO_MAX_NAME]; + + ierr2 = PIOc_strerror_impl(ierr, errmsg, PIO_MAX_NAME); + printf("PIO: WARNING: Opening file (%s) with iotype=%d (%s) failed (ierr=%d, %s). Retrying with iotype=PIO_IOTYPE_NETCDF\n", filename, *iotype, pio_iotype_to_string(*iotype), ierr, ((ierr2 == PIO_NOERR) ? errmsg : "")); + ierr = nc_open(filename, file->mode, &file->fh); + if(ierr == NC_NOERR) + { + printf("PIO: WARNING: Opening file (%s) with iotype=%d (%s) failed. But retrying with iotype PIO_IOTYPE_NETCDF was successful. Switching iotype to PIO_IOTYPE_NETCDF for this file\n", filename, *iotype, pio_iotype_to_string(*iotype)); + } } - } + else{ + /* reset ierr */ + ierr = PIO_NOERR; - ierr = check_netcdf(ios, file, ierr, __FILE__, __LINE__); - /* If there was an error, free allocated memory and deal with the error. */ - if(ierr != PIO_NOERR){ - int tmp_iotype = file->iotype; - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - free(file->io_fstats); - free(file); - LOG((1, "PIOc_openfile_retry failed, ierr = %d", ierr)); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Opening file (%s) with iotype %d (%s) failed. The low level I/O library call failed", filename, tmp_iotype, pio_iotype_to_string(tmp_iotype));; - } + file->do_io = 0; + } + } + LOG((2, "retry nc_open(%s) : fd = %d, iotype = %d, do_io = %d, ierr = %d", + filename, file->fh, file->iotype, file->do_io, ierr)); +#endif /* _NETCDF */ + } // if(retry) + } // if(ios->ioproc) - /* Add this file to the list of currently open files. */ - MPI_Comm comm = MPI_COMM_NULL; - if(ios->async) - { - /* For asynchronous I/O service, since file ids are passed across - * disjoint comms we need it to be unique across the union comm - */ - comm = ios->union_comm; + /* Broadcast open mode to all tasks. */ + if((mpierr = MPI_Bcast(&file->mode, 1, MPI_INT, ios->ioroot, ios->my_comm))){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + delete(file->pmtx); + free(file->io_fstats); + free(file); + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); + } + + if(retry){ + /* Broadcast IO type (might be switched on IO tasks) to all tasks. */ + if((mpierr = MPI_Bcast(&file->iotype, 1, MPI_INT, ios->ioroot, ios->my_comm))){ + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + delete(file->pmtx); + free(file->io_fstats); + free(file); + return check_mpi(NULL, file, mpierr, __FILE__, __LINE__); } + } + + /* Ensure that we handle all errors, irrespective of the error handler + * set by the user, when opening a file + */ + ierr = spio_handle_err(ios, file, PIO_REDUCE_ERROR, ierr, __FILE__, __LINE__); + /* If there was an error, free allocated memory and deal with the error. */ + if(ierr != PIO_NOERR){ + int tmp_iotype = file->iotype; + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + delete(file->pmtx); + free(file->io_fstats); + free(file); + LOG((1, "PIOc_openfile_retry failed, ierr = %d", ierr)); + return pio_err(ios, NULL, ierr, __FILE__, __LINE__, + "Opening file (%s) with iotype %d (%s) failed. The low level I/O library call failed", filename, tmp_iotype, pio_iotype_to_string(tmp_iotype));; + } - /* Initialize the multi-variable cache used to cache data for - * multiple variables before writing it to the output file - * Note: The MVCache is not used by ADIOS or a file opened - * as "read only" + /* Add this file to the list of currently open files. */ + MPI_Comm comm = MPI_COMM_NULL; + if(ios->async){ + /* For asynchronous I/O service, since file ids are passed across + * disjoint comms we need it to be unique across the union comm */ - spio_file_mvcache_init(file); + comm = ios->union_comm; + } - *ncidp = pio_add_to_file_list(file, comm); + /* Initialize the multi-variable cache used to cache data for + * multiple variables before writing it to the output file + * Note: The MVCache is not used by ADIOS or a file opened + * as "read only" + */ + spio_file_mvcache_init(file); - LOG((2, "Opened file %s file->pio_ncid = %d file->fh = %d ierr = %d", - filename, file->pio_ncid, file->fh, ierr)); +#ifdef _HDF5 + /* A datatype converter for converting user buffer to a "file type" buffer */ + file->dt_converter = static_cast(new SPIO_Util::File_Util::DTConverter()); +#endif - /* Set ncid to ADIOS attributes */ - if ((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)) - { + *ncidp = pio_add_to_file_list(file, comm); + + LOG((2, "Opened file %s file->pio_ncid = %d file->fh = %d ierr = %d", + filename, file->pio_ncid, file->fh, ierr)); + + /* Set ncid to ADIOS attributes */ + if((file->iotype == PIO_IOTYPE_ADIOS) || (file->iotype == PIO_IOTYPE_ADIOSC)){ #ifdef _ADIOS2 - for (int i = 0; i < file->num_attrs; i++) - file->adios_attrs[i].att_ncid = *ncidp; + for(int i = 0; i < file->num_attrs; i++) { file->adios_attrs[i].att_ncid = *ncidp; } #endif + } + + spio_ltimer_stop(ios->io_fstats->rd_timer_name); + spio_ltimer_stop(ios->io_fstats->tot_timer_name); + spio_ltimer_stop(file->io_fstats->rd_timer_name); + spio_ltimer_stop(file->io_fstats->tot_timer_name); + + /* Check if the file has unlimited dimensions */ + if(!ios->async || !ios->ioproc){ + ierr = PIOc_inq_unlimdims_impl(*ncidp, &(file->num_unlim_dimids), NULL); + if(ierr != PIO_NOERR){ + ierr = pio_err(ios, file, ierr, __FILE__, __LINE__, + "Opening file (%s) failed. Although the file was opened successfully, querying the number of unlimited dimensions in the file failed", filename); } - /* Check if the file has unlimited dimensions */ - if(!ios->async || !ios->ioproc) - { - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - ierr = PIOc_inq_unlimdims_impl(*ncidp, &(file->num_unlim_dimids), NULL); - if(ierr != PIO_NOERR) - { - spio_file_mvcache_finalize(file); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Opening file (%s) failed. Although the file was opened successfully, querying the number of unlimited dimensions in the file failed", filename); - } - if(file->num_unlim_dimids > 0) - { - file->unlim_dimids = (int *)malloc(file->num_unlim_dimids * sizeof(int)); - if(!file->unlim_dimids) - { - spio_file_mvcache_finalize(file); - return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, - "Opening file (%s) failed. Out of memory allocating %lld bytes for caching the unlimited dimension ids", filename, (unsigned long long) (file->num_unlim_dimids * sizeof(int))); - } - ierr = PIOc_inq_unlimdims_impl(*ncidp, NULL, file->unlim_dimids); - if(ierr != PIO_NOERR) - { - spio_file_mvcache_finalize(file); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Opening file (%s) failed. Although the file was opened successfully, querying the unlimited dimensions in the file failed", filename); - } + if((ierr == PIO_NOERR) && (file->num_unlim_dimids > 0)){ + file->unlim_dimids = (int *)malloc(file->num_unlim_dimids * sizeof(int)); + if(!file->unlim_dimids){ + ierr = pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Opening file (%s) failed. Out of memory allocating %lld bytes for caching the unlimited dimension ids", filename, (unsigned long long) (file->num_unlim_dimids * sizeof(int))); + } + if(ierr == PIO_NOERR){ + ierr = PIOc_inq_unlimdims_impl(*ncidp, NULL, file->unlim_dimids); + if(ierr != PIO_NOERR){ + ierr = pio_err(ios, file, ierr, __FILE__, __LINE__, + "Opening file (%s) failed. Although the file was opened successfully, querying the unlimited dimensions in the file failed", filename); } - spio_ltimer_start(ios->io_fstats->rd_timer_name); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->rd_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - LOG((3, "File has %d unlimited dimensions", file->num_unlim_dimids)); + } } - spio_ltimer_stop(ios->io_fstats->rd_timer_name); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->rd_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return ierr; + if(ierr != PIO_NOERR){ + pio_delete_file_from_list(file->pio_ncid); + } + else{ + LOG((3, "File has %d unlimited dimensions", file->num_unlim_dimids)); + } + } + + return ierr; } /** @@ -5608,7 +5637,6 @@ int PIOc_openfile_retry_impl(int iosysid, int *ncidp, int *iotype, const char *f * * @return 0 for success, error code otherwise. * @ingroup PIO_openfile - * @author Ed Hartnett */ int openfile_int(int iosysid, int *ncidp, int *iotype, const char *filename, int mode, int retry) @@ -5682,197 +5710,6 @@ int spio_pnetcdf_inq_type(int ncid, nc_type xtype, char *name, return PIO_NOERR; } -/** - * This is an internal function that handles both PIOc_enddef and - * PIOc_redef. - * - * @param ncid the ncid of the file to enddef or redef - * @param is_enddef set to non-zero for enddef, 0 for redef. - * @returns PIO_NOERR on success, error code on failure. */ -int spio_change_def(int ncid, int is_enddef) -{ - iosystem_desc_t *ios; /* Pointer to io system information. */ - file_desc_t *file; /* Pointer to file information. */ - int ierr = PIO_NOERR; /* Return code from function calls. */ - - LOG((2, "spio_change_def ncid = %d is_enddef = %d", ncid, is_enddef)); - - /* Find the info about this file. When I check the return code - * here, some tests fail. ???*/ - if ((ierr = pio_get_file(ncid, &file))) - { - return pio_err(NULL, NULL, ierr, __FILE__, __LINE__, - "Changing the define mode for file (ncid = %d) failed. Invalid file id", ncid); - } - assert(file); - ios = file->iosystem; - assert(ios); - spio_ltimer_start(ios->io_fstats->tot_timer_name); - spio_ltimer_start(file->io_fstats->tot_timer_name); - - /* If async is in use, and this is not an IO task, bcast the parameters. */ - if (ios->async) - { - int msg = is_enddef ? PIO_MSG_ENDDEF : PIO_MSG_REDEF; - - PIO_SEND_ASYNC_MSG(ios, msg, &ierr, ncid); - if(ierr != PIO_NOERR) - { - LOG((1, "Error sending async msg for PIO_MSG_ENDDEF/PIO_MSG_REDEF")); - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, NULL, ierr, __FILE__, __LINE__, - "Changing the define mode for file (%s) failed. Error sending async msg, PIO_MSG_ENDDEF/PIO_MSG_REDEF, on iosystem (iosysid=%d)", pio_get_fname_from_file(file), ios->iosysid); - } - } - - /* If this is an IO task, then call the netCDF function. */ - LOG((3, "spio_change_def ios->ioproc = %d", ios->ioproc)); - if (ios->ioproc) - { - LOG((3, "spio_change_def calling netcdf function file->fh = %d file->do_io = %d iotype = %d", - file->fh, file->do_io, file->iotype)); - - /* NetCDF and PnetCDF by default do not reserve any extra space in - * the NetCDF file headers (compactness etc). However this can be a - * problem (performance related) when renaming variables etc since - * this information is stored in the NetCDF header. Adding more - * information into the header requires the libraries to resize the - * file, extend the header space and copy the contents of the file - * (similar to realloc). - * - * The solution for the performance issue is to reserve some extra - * space in the header when creating NetCDF files. The current calls - * to nc_enddef()/ncmpi_enddef() need to be replaced with nc__enddef - * ()/ncmpi__enddef() (note the double underscore). - * - * This reservation should be made only once: after the header section - * is expanded, a new reservation may involve moving (shifting) data, - * which can be very expensive if the data sections are huge. - */ -#ifdef _PNETCDF - if (file->iotype == PIO_IOTYPE_PNETCDF) - { - if (is_enddef) - { - /* ncmpi__enddef has been available since PnetCDF 1.5.0 (PNETCDF_MIN_VER_REQD is currently 1.8.1): - int ncmpi__enddef(int ncid, - MPI_Offset h_minfree, - MPI_Offset v_align, - MPI_Offset v_minfree, - MPI_Offset r_align); - - The usual call of ncmpi_enddef(ncid) is equivalent to ncmpi__enddef(ncid, 0, 0, 0, 0). - - Call ncmpi__enddef with appropriate arguments instead of ncmpi_enddef: - - To reserve a sufficiently large space for the file header if it is expected - to expand (set h_minfree > 0 explicitly). - See https://github.com/E3SM-Project/scorpio/issues/436 - - - To prevent PnetCDF (versions prior to 1.13.0) from implicitly selecting the - file striping size to align the header extent (set v_align > 0 explicitly). - See https://github.com/E3SM-Project/scorpio/issues/566 - */ - - /* Sets the pad at the end of the "header" section. */ - MPI_Offset h_minfree = 0; /* Unless extra header space is requested, this can be left as default (0) */ - - /* Controls the alignment of the beginning of the data section for fixed-size/record variables. */ - const MPI_Offset v_align = 4; /* For fixed-size variables, needs to be left as the default (4 bytes) */ - const MPI_Offset r_align = 4; /* For record variables, needs to be left as the default (4 bytes) */ - - /* Sets the pad at the end of the data section for fixed-size variables. */ - const MPI_Offset v_minfree = 0; /* This can be left as default (0) */ - - if (file->reserve_extra_header_space) - { - /* The recommended size by Charlie Zender (NCO developer) is 10 KB */ - assert(PIO_RESERVED_FILE_HEADER_SIZE >= 0); - h_minfree = PIO_RESERVED_FILE_HEADER_SIZE; - - file->reserve_extra_header_space = false; - } - - ierr = ncmpi__enddef(file->fh, h_minfree, v_align, v_minfree, r_align); - } - else - ierr = ncmpi_redef(file->fh); - } -#endif /* _PNETCDF */ -#ifdef _NETCDF - if (file->iotype != PIO_IOTYPE_PNETCDF && - file->iotype != PIO_IOTYPE_ADIOS && file->iotype != PIO_IOTYPE_ADIOSC && - file->iotype != PIO_IOTYPE_HDF5 && file->iotype != PIO_IOTYPE_HDF5C && file->do_io) - { - if (is_enddef) - { - LOG((3, "spio_change_def calling nc_enddef file->fh = %d", file->fh)); - if (file->iotype == PIO_IOTYPE_NETCDF) - { -#ifdef NETCDF_C_NC__ENDDEF_EXISTS - if (file->reserve_extra_header_space) - { - /* Sets the pad at the end of the "header" section. - * The recommended size by Charlie Zender (NCO developer) is 10 KB */ - assert(PIO_RESERVED_FILE_HEADER_SIZE >= 0); - const MPI_Offset h_minfree = PIO_RESERVED_FILE_HEADER_SIZE; - - /* Controls the alignment of the beginning of the data section for fixed-size/record variables. */ - const size_t v_align = 4; /* For fixed-size variables, needs to be left as the default (4 bytes) */ - const size_t r_align = 4; /* For record variables, needs to be left as the default (4 bytes) */ - - /* Sets the pad at the end of the data section for fixed-size variables. */ - const size_t v_minfree = 0; /* This can be left as default (0) */ - - /* nc__enddef has been available since NetCDF 3.x (NETCDF_C_MIN_VER_REQD is currently 4.3.3) */ - ierr = nc__enddef(file->fh, h_minfree, v_align, v_minfree, r_align); - - file->reserve_extra_header_space = false; - } - else - ierr = nc_enddef(file->fh); -#else - /* CAUTION: nc__enddef may not be available on future NetCDF implementations, switch back to nc_enddef. */ - ierr = nc_enddef(file->fh); -#endif - } - else - { - /* We still call nc_enddef for NetCDF4 type. According to NCO user guide, nc__enddef will improve speed of - * future metadata expansion with classic and 64bit NetCDF files, though not necessarily with NetCDF4 files. - */ - ierr = nc_enddef(file->fh); - } - } - else - ierr = nc_redef(file->fh); - } -#endif /* _NETCDF */ -#ifdef _HDF5 - if ((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)) - { - if (is_enddef) - { - ierr = spio_hdf5_enddef(ios, file); - } - } -#endif /* _HDF5 */ - } - - ierr = check_netcdf(NULL, file, ierr, __FILE__, __LINE__); - if(ierr != PIO_NOERR){ - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return pio_err(ios, file, ierr, __FILE__, __LINE__, - "Changing the define mode for file (%s) failed. Low-level I/O library API failed", pio_get_fname_from_file(file)); - } - LOG((3, "spio_change_def succeeded")); - - spio_ltimer_stop(ios->io_fstats->tot_timer_name); - spio_ltimer_stop(file->io_fstats->tot_timer_name); - return ierr; -} - /** * Check whether an IO type is valid for the build. * @@ -6273,29 +6110,28 @@ const char *get_var_desc_str(int ncid, int varid, const char *desc_prefix) { iosystem_desc_t *ios; /* Pointer to io system information. */ file_desc_t *file; /* Pointer to file information. */ - static const char EMPTY_STR[] = ""; + std::string vdesc; int ierr = PIO_NOERR; ierr = pio_get_file(ncid, &file); if(ierr != PIO_NOERR) { LOG((1, "Unable to get file corresponding to ncid = %d", ncid)); - return EMPTY_STR; + return vdesc.c_str(); } ios = file->iosystem; assert(ios != NULL); - snprintf(file->varlist[varid].vdesc, PIO_MAX_NAME, - "%s %s %s %llu %llu %llu %llu %llu", - (desc_prefix)?desc_prefix:"", - file->varlist[varid].vname, - file->fname, - (unsigned long long int)file->varlist[varid].vrsize, - (unsigned long long int)file->varlist[varid].rb_pend, - (unsigned long long int)file->varlist[varid].wb_pend, - (unsigned long long int)file->rb_pend, - (unsigned long long int)file->wb_pend - ); + if(desc_prefix) { vdesc += desc_prefix; } + vdesc += std::string(file->varlist[varid].vname) + + std::string(file->fname) + + std::to_string(static_cast(file->varlist[varid].vrsize)) + + std::to_string(static_cast(file->varlist[varid].rb_pend)) + + std::to_string(static_cast(file->varlist[varid].wb_pend)) + + std::to_string(static_cast(file->rb_pend)) + + std::to_string(static_cast(file->wb_pend)); + + snprintf(file->varlist[varid].vdesc, PIO_MAX_NAME, "%s", vdesc.c_str()); return file->varlist[varid].vdesc; } @@ -6471,1349 +6307,3 @@ PIO_Offset spio_get_nc_type_size(nc_type xtype) return -1; } - -#ifdef _HDF5 -hid_t spio_nc_type_to_hdf5_type(nc_type xtype) -{ - switch (xtype) - { - case NC_BYTE: return H5T_NATIVE_UINT8; - case NC_UBYTE: return H5T_NATIVE_UCHAR; - case NC_CHAR: return H5T_NATIVE_CHAR; - case NC_SHORT: return H5T_NATIVE_SHORT; - case NC_USHORT: return H5T_NATIVE_USHORT; - case NC_INT: return H5T_NATIVE_INT; - case NC_UINT: return H5T_NATIVE_UINT; - case NC_FLOAT : return H5T_NATIVE_FLOAT; - case NC_DOUBLE: return H5T_NATIVE_DOUBLE; - case NC_INT64: return H5T_NATIVE_INT64; - case NC_UINT64: return H5T_NATIVE_UINT64; - default: return H5I_INVALID_HID; - } - - return H5I_INVALID_HID; -} - -int spio_hdf5_create(iosystem_desc_t *ios, file_desc_t *file, const char *filename) -{ - int mpierr = MPI_SUCCESS; - - assert(ios && file && filename); - assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); - assert(ios->ioproc); - - if (file->mode & PIO_NOCLOBBER) /* Check whether HDF5 file exists */ - { - struct stat sd; - if (0 == stat(filename, &sd)) - { - return pio_err(ios, NULL, PIO_EEXIST, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The HDF5 file already exists and PIO_NOCLOBBER mode is specified", - filename); - } - } - else - { - /* Delete HDF5 file if it exists */ - if (ios->io_rank == 0) - { - struct stat sd; - if (0 == stat(filename, &sd)) - unlink(filename); - } - - /* Make sure that no task is trying to operate on the - * HDF5 file while it is being deleted */ - if ((mpierr = MPI_Barrier(ios->io_comm))) - { - return check_mpi(ios, file, mpierr, __FILE__, __LINE__); - } - } - - if (ios->info == MPI_INFO_NULL) - { - if ((mpierr = MPI_Info_create(&ios->info))) - { - return check_mpi(ios, file, mpierr, __FILE__, __LINE__); - } - } - - hid_t fcpl_id = H5Pcreate(H5P_FILE_CREATE); - if (fcpl_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new file creation property list", - filename); - } - - if (H5Pset_link_creation_order(fcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set tracking and indexing of link creation order", - filename); - } - - /* H5DSattach_scale calls (even with MPI_Barrier) might fail or hang if attribute creation - * order is tracked or indexed. Before we have a better workaround, temporarily disable - * tracking and indexing of attribute creation order. */ -#if 0 - if (H5Pset_attr_creation_order(fcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set tracking and indexing of attribute creation order", - filename); - } -#endif - - hid_t fapl_id = H5Pcreate(H5P_FILE_ACCESS); - if (fapl_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new file access property list", - filename); - } - - if (H5Pset_fapl_mpio(fapl_id, ios->io_comm, ios->info) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to store the user-supplied MPI IO parameters", - filename); - } - - file->hdf5_file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl_id, fapl_id); - if (file->hdf5_file_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new HDF5 file", - filename); - } - - if (H5Pclose(fcpl_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a file creation property list", - filename); - } - - if (H5Pclose(fapl_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a file access property list", - filename); - } - - /* Set up collective dataset transfer property list */ - file->dxplid_coll = H5Pcreate(H5P_DATASET_XFER); - if (file->dxplid_coll == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new dataset transfer property list (collective data transfer)", - filename); - } - - if (H5Pset_dxpl_mpio(file->dxplid_coll, H5FD_MPIO_COLLECTIVE) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set data transfer mode to collective", - filename); - } - - /* Set up independent dataset transfer property list */ - file->dxplid_indep = H5Pcreate(H5P_DATASET_XFER); - if (file->dxplid_indep == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new dataset transfer property list (independent data transfer)", - filename); - } - - if (H5Pset_dxpl_mpio(file->dxplid_indep, H5FD_MPIO_COLLECTIVE) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set data transfer mode to collective", - filename); - } - - if (H5Pset_dxpl_mpio_collective_opt(file->dxplid_indep, H5FD_MPIO_INDIVIDUAL_IO) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set data transfer mode to individual I/O", - filename); - } - /* Writing _NCProperties attribute */ - const char* attr_name = "_NCProperties"; - char nc_properties[PIO_MAX_NAME]; - unsigned int major, minor, release; - - if (H5get_libversion(&major, &minor, &release) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to retrieve the the version of the HDF5 library", - filename); - } - - snprintf(nc_properties, PIO_MAX_NAME, - "version=2,scorpio=%d.%d.%d,hdf5=%1u.%1u.%1u", - PIO_VERSION_MAJOR, PIO_VERSION_MINOR, PIO_VERSION_PATCH, - major, minor, release); - - hid_t attr_id; - hsize_t asize = strlen(nc_properties); - hid_t loc_id = file->hdf5_file_id; - - hid_t space_id = H5Screate(H5S_SCALAR); - if (space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new scalar dataspace", - filename); - } - - hid_t h5_string_type = H5Tcopy(H5T_C_S1); - if (h5_string_type == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to make a copy of the predefined string datatype in C", - filename); - } - - assert(asize > 0); - if (H5Tset_size(h5_string_type, asize) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set the total size (%ld bytes) for a derived C-style string datatype", - filename, asize); - } - - if (H5Tset_strpad(h5_string_type, H5T_STR_NULLTERM) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to define the type of padding (NULL-terminated) used for a derived C-style string datatype", - filename); - } - - if (H5Tset_cset(h5_string_type, H5T_CSET_ASCII) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set the character set (US ASCII) to be used in a derived C-style string datatype", - filename); - } - - /* H5Aexists() returns zero (false), a positive (true) or a negative (failure) value */ - htri_t att_exists = H5Aexists(loc_id, attr_name); - if (att_exists > 0) - { - assert(0); - } - else if (att_exists == 0) - { - attr_id = H5Acreate2(loc_id, attr_name, h5_string_type, space_id, H5P_DEFAULT, H5P_DEFAULT); - if (attr_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to the file", - filename, attr_name); - } - } - else - { - /* Error determining whether an attribute with a given name exists on an object */ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to determine whether an attribute (%s) exists on the file", - filename, attr_name); - } - - if (H5Awrite(attr_id, h5_string_type, nc_properties) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to the file", - filename, attr_name); - } - - if (H5Sclose(space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a scalar dataspace", - filename); - } - - if (H5Aclose(attr_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to the file", - filename, attr_name); - } - - if (H5Tclose(h5_string_type) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Creating file (%s) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a derived C-style string datatype " - "used by an attribute (%s) attached to the file", - filename, attr_name); - } - - return PIO_NOERR; -} - -/* Create HDF5 dataset property ID */ -static hid_t spio_create_hdf5_dataset_pid(iosystem_desc_t *ios, file_desc_t *file, const char *var_name, int var_ndims, nc_type var_type) -{ - herr_t ret; - hid_t dpid = H5I_INVALID_HID; - - assert((ios != NULL) && (file != NULL) && (var_ndims >= 0)); - - /* Initialize the compression filter property list */ - dpid = H5Pcreate(H5P_DATASET_CREATE); - assert(dpid != H5I_INVALID_HID); - - /* We currently support compression for non-scalar data */ - if((var_ndims < 1) || (var_type == NC_CHAR) || (file->iotype != PIO_IOTYPE_HDF5C)) return dpid; - - /* Check if any variables have compression disabled by the user */ - /* FIXME: Variables written out in a chunk size different from the one defined can cause hangs - * e.g. E3SM variables : decomp_type, numlev, hwrt_prec, avgflag, fillvalue, - * meridional_complement, zonal_complement - */ -#ifndef SPIO_NO_CXX_REGEX - std::regex vname_override_rgx(SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX); - if(var_name && std::regex_match(std::string(var_name), vname_override_rgx)){ - std::string msg("Disabling HDF5 compression for variable"); - msg += std::string("(name=") + std::string(var_name) + std::string(", file=") + std::string(pio_get_fname_from_file(file)) + std::string(")"); - msg += std::string(" since it matches the user specified regex"); - msg += std::string(" (SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX=\"") + std::string(SPIO_OVERRIDE_HDF5_COMPRESSION_VNAME_REGEX) + std::string("\")"); - PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, msg.c_str()); - return dpid; - } -#endif - -#ifdef _SPIO_HDF5_USE_COMPRESSION - -#ifdef _SPIO_HDF5_USE_LOSSY_COMPRESSION - -#ifdef _SPIO_HAS_H5Z_ZFP - if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_RATE"){ - /* Lossy compression : Fixed bit rate : Number of bits used for compressed values is fixed, e.g. 16 */ - ret = H5Pset_zfp_rate(dpid, SPIO_HDF5_ZFP_COMPRESSION_RATE); - if(ret < 0){ - PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter compression rate failed (ignoring specific compression bit rate)"); - } - } - else if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_PRECISION"){ - /* Lossy compression : Fixed precision Variable bit rate : Number of bits used for original value is fixed. e.g. 16 */ - ret = H5Pset_zfp_precision(dpid, SPIO_HDF5_ZFP_PRECISION); - if(ret < 0){ - PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter relative error bound failed (continuing with the default error bounds)"); - } - } - else if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_ACCURACY"){ - /* Lossy compression : Fixed accuracy Variable bit rate : Absolute error between original and compressed values is bound e.g. 0.001 */ - ret = H5Pset_zfp_accuracy(dpid, SPIO_HDF5_ZFP_ACCURACY); - if(ret < 0){ - PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter absolute error bound failed (continuing with the default error bounds)"); - } - } - else if(SPIO_HDF5_ZFP_COMPRESSION_MODE == "H5Z_ZFP_MODE_REVERSIBLE"){ - /* Lossless compression */ - ret = H5Pset_zfp_reversible(dpid); - if(ret < 0){ - PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "Setting HDF5 ZFP filter for lossless compression failed (ignoring compression option)"); - } - } -#endif - -#else /* lossless compression, ifdef _SPIO_HDF5_USE_LOSSY_COMPRESSION */ - -#ifdef _SPIO_HAS_H5Z_BLOSC2 - /* Lossless compression : Default Blosc2 + ZSTD */ - unsigned int cd_values[7]; - cd_values[4] = SPIO_HDF5_BLOSC2_COMPRESSION_LEVEL; // compression level - cd_values[5] = SPIO_HDF5_BLOSC2_SHUFFLE_METHOD; // shuffle option - cd_values[6] = SPIO_HDF5_BLOSC2_COMPRESSION_LIBRARY; // Use ZSTD for compression - ret = H5Pset_filter(dpid, FILTER_BLOSC2, H5Z_FLAG_OPTIONAL, 7, cd_values); - if(ret < 0){ - PIOc_warn(ios->iosysid, file->fh, __FILE__, __LINE__, "User requested lossless compression, but setting HDF5 Blosc2 filter failed. Writing data without compression"); - } -#endif - -#endif /* ifdef _SPIO_HDF5_USE_LOSSY_COMPRESSION */ - -#endif /* ifdef _SPIO_HDF5_USE_COMPRESSION */ - - /* By default HDF5 does not track the order of creation of the attributes. So the attributes - * appear based on the alphanumeric order, of the attribute name, in the file. However, - * H5DSattach_scale calls (even with MPI_Barrier) might fail or hang if attribute creation - * order is tracked or indexed. Before we have a better workaround, temporarily disable - * tracking and indexing of attribute creation order. - */ -#if 0 - if(H5Pset_attr_creation_order(dcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set tracking and indexing of attribute creation order", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } -#endif - - return dpid; -} - -/* Get default chunk size (no of elems) - across each dimension - for variable data. The - * max chunk size (across all dimensions) is specified via PIO_CHUNK_SIZE (in bytes) - */ -static inline std::vector spio_get_dim_chunk_sz(const std::vector &dim_sz, nc_type xtype) -{ - std::size_t ndims = dim_sz.size(); - std::vector dim_chunk_sz = dim_sz; - - /* Unlimited dimensions have a chunk size of 1 */ - std::transform(dim_chunk_sz.begin(), dim_chunk_sz.end(), dim_chunk_sz.begin(), - [](hsize_t i) { return (i != H5S_UNLIMITED) ? i : 1; }); - - /* No chunking for scalars and 1D vars */ - if(ndims <= 1){ - return dim_chunk_sz; - } - - /* Number of elements corresponding to PIO_CHUNK_SIZE */ - double chunk_nelems = static_cast(PIO_CHUNK_SIZE)/static_cast(spio_get_nc_type_size(xtype)); - /* Assuming that elements are evenly distributed across all non-unlimited dimensions, - * Total (across all dimensions) chunked elements = (d * d * ...(n -1) times), where d is the size of each - * dimension - */ - hsize_t chunk_per_dim_nelems = static_cast(pow(chunk_nelems, 1.0/(ndims - 1) )); - - for(std::size_t i = 0; i < ndims; i++){ - /* Chunk size across UNLIMITED dimension is 1 */ - dim_chunk_sz[i] = (dim_sz[i] != H5S_UNLIMITED) ? (std::min(chunk_per_dim_nelems, dim_sz[i])) : 1; - } - - return dim_chunk_sz; -} - -/* Create an HDF5 string type - ASCII + null terminated */ -static inline hid_t spio_create_hdf5_str_type(void ) -{ - hid_t st = H5Tcopy(H5T_C_S1); - if(st != H5I_INVALID_HID){ - if(H5Tset_strpad(st, H5T_STR_NULLTERM) < 0){ - H5Tclose(st); - return H5I_INVALID_HID; - } - if(H5Tset_cset(st, H5T_CSET_ASCII) < 0){ - H5Tclose(st); - return H5I_INVALID_HID; - } - } - - return st; -} - -/* Write a hidden coordinates attribute (_Netcdf4Coordinates), which lists the dimids of the variable. */ -static inline int spio_add_nc_hidden_coord(iosystem_desc_t *ios, file_desc_t *file, int varid, - int ndims, const int *dimidsp) -{ - assert(ios && file && (varid >= 0) && (ndims >= 0)); - - /* No coordinate atttribute for scalars */ - if(ndims == 0) return PIO_NOERR; - - assert(dimidsp); - - /* Writing "_Netcdf4Coordinates" hidden attribute. This attribute stores the dimension ids of the - * variable dimensions in an integer array. - * This attribute is required for NetCDF to read HDF5 output - */ - const char* attr_name = "_Netcdf4Coordinates"; - - /* Sanity check : Ensure that "_Netcdf4Coordinates" attribute does not exist */ - htri_t attr_exists = H5Aexists(file->hdf5_vars[varid].hdf5_dataset_id, attr_name); - assert(attr_exists == 0); - - /* Create dataspace for the attribute i.e., space for integer array to store the dimension ids */ - std::array coords_len = {static_cast(ndims)}; - hid_t coords_space_id = H5Screate_simple(1, coords_len.data(), NULL); - if(coords_space_id == H5I_INVALID_HID){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new simple dataspace", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - /* Create the hidden attribute */ - hid_t coords_att_id = H5Acreate2(file->hdf5_vars[varid].hdf5_dataset_id, attr_name, - H5T_NATIVE_INT, coords_space_id, H5P_DEFAULT, H5P_DEFAULT); - if(coords_att_id == H5I_INVALID_HID){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to the variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, attr_name); - } - - /* Write the dimension ids */ - if(H5Awrite(coords_att_id, H5T_NATIVE_INT, dimidsp) < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to the variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, attr_name); - } - - if(H5Aclose(coords_att_id) < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to the variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, attr_name); - } - - if(H5Sclose(coords_space_id) < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a simple dataspace", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - return PIO_NOERR; -} - -int spio_hdf5_def_var(iosystem_desc_t *ios, file_desc_t *file, const char *name, - nc_type xtype, int ndims, const int *dimidsp, int varid) -{ - assert(ios && file && name && ndims >= 0 && varid >= 0); - assert((ndims == 0) || dimidsp); - assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); - assert(ios->ioproc); - - /* Cache the dim sizes for HDF5 calls */ - std::vector dim_sz(ndims), max_dim_sz(ndims); - for(int i = 0; i < ndims; i++){ - if(file->hdf5_dims[dimidsp[i]].len != PIO_UNLIMITED){ - dim_sz[i] = max_dim_sz[i] = file->hdf5_dims[dimidsp[i]].len; - } - else{ - dim_sz[i] = 1; - max_dim_sz[i] = H5S_UNLIMITED; - } - } - - /* Create HDF5 dataset (and optionally add filters as needed) */ - hid_t dcpl_id = spio_create_hdf5_dataset_pid(ios, file, name, ndims, xtype); - if(dcpl_id == H5I_INVALID_HID){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new dataset creation property list", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - file->hdf5_vars[varid].hdf5_type = (xtype == NC_CHAR) ? spio_create_hdf5_str_type() : spio_nc_type_to_hdf5_type(xtype); - assert(file->hdf5_vars[varid].hdf5_type != H5I_INVALID_HID); - - /* Set default chunk size for variable data */ - if(ndims > 0){ - if(H5Pset_chunk(dcpl_id, ndims, spio_get_dim_chunk_sz(max_dim_sz, xtype).data()) < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set the size of the chunks used to store a chunked layout dataset", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - - /* Create a simple dataspace to define the global variable dimensions */ - hid_t sid = H5Screate_simple(ndims, dim_sz.data(), max_dim_sz.data()); - if(sid == H5I_INVALID_HID){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new simple dataspace", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - /* Define the variable */ - const char* dataset_name = (file->hdf5_vars[varid].alt_name == NULL)? name : file->hdf5_vars[varid].alt_name; - file->hdf5_vars[varid].hdf5_dataset_id = H5Dcreate2(file->hdf5_file_id, dataset_name, file->hdf5_vars[varid].hdf5_type, - sid, H5P_DEFAULT, dcpl_id, H5P_DEFAULT); - if(file->hdf5_vars[varid].hdf5_dataset_id < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new dataset (%s) for the variable", - name, varid, pio_get_fname_from_file(file), file->pio_ncid, dataset_name); - } - - if(H5Sclose(sid) < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a simple dataspace", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if(H5Pclose(dcpl_id) < 0){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a dataset creation property list", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - /* Add a hidden attribute, "_Netcdf4Coordinates", to store var dimension ids so that NetCDF can read the var */ - if(spio_add_nc_hidden_coord(ios, file, varid, ndims, dimidsp) != PIO_NOERR){ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Defining variable (%s, varid = %d) in file (%s, ncid=%d) using HDF5 iotype failed. " - "Adding NetCDF hidden coordinate attribute failed", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - return PIO_NOERR; -} - -int spio_hdf5_enddef(iosystem_desc_t *ios, file_desc_t *file) -{ - int i; - - assert(ios && file); - assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); - assert(ios->ioproc); - - for (i = 0; i < file->hdf5_num_dims; i++) - { - if (!file->hdf5_dims[i].has_coord_var) - { - hid_t space_id, dcpl_id = H5I_INVALID_HID, dimscale_id; - hsize_t dims[1], max_dims[1], chunk_dims[1] = {1}; - - dcpl_id = H5Pcreate(H5P_DATASET_CREATE); - if (dcpl_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new dataset creation property list", - pio_get_fname_from_file(file), file->pio_ncid); - } - - /* H5DSattach_scale calls (even with MPI_Barrier) might fail or hang if attribute creation - * order is tracked or indexed. Before we have a better workaround, temporarily disable - * tracking and indexing of attribute creation order. */ -#if 0 - if (H5Pset_attr_creation_order(dcpl_id, H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set tracking and indexing of attribute creation order", - pio_get_fname_from_file(file), file->pio_ncid); - } -#endif - - /* Set size of dataset to size of dimension. */ - max_dims[0] = dims[0] = file->hdf5_dims[i].len; - - /* If this dimension scale is unlimited, set up chunking with a chunksize of 1. */ - if (max_dims[0] == PIO_UNLIMITED) - { - max_dims[0] = H5S_UNLIMITED; - - if (H5Pset_chunk(dcpl_id, 1, chunk_dims) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set the size of the chunks used to store a chunked layout dataset", - pio_get_fname_from_file(file), file->pio_ncid); - } - } - - space_id = H5Screate_simple(1, dims, max_dims); - if (space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new simple dataspace", - pio_get_fname_from_file(file), file->pio_ncid); - } - - /* Create a dataset that will be converted to a dimension scale. */ - dimscale_id = H5Dcreate2(file->hdf5_file_id, file->hdf5_dims[i].name, H5T_IEEE_F32BE, - space_id, H5P_DEFAULT, dcpl_id, H5P_DEFAULT); - if (dimscale_id < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new dataset (%s) that will be converted to a dimension scale", - pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name); - } - - char dimscale_name[PIO_MAX_NAME]; - snprintf(dimscale_name, PIO_MAX_NAME, "%s%10d", "This is a netCDF dimension but not a netCDF variable.", (int)dims[0]); - - if (H5DSset_scale(dimscale_id, dimscale_name) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to convert a dataset (for dimension %s) to a dimension scale", - pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name); - } - - file->hdf5_dims[i].hdf5_dataset_id = dimscale_id; - - /* Write a special attribute (_Netcdf4Dimid) for the netCDF-4 dimension ID. */ - hid_t dimid_att_id; - htri_t attr_exists; - - hid_t dimid_space_id = H5Screate(H5S_SCALAR); - if (dimid_space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new scalar dataspace", - pio_get_fname_from_file(file), file->pio_ncid); - } - - /* Writing _Netcdf4Dimid attribute */ - const char* attr_name = "_Netcdf4Dimid"; - - /* H5Aexists() returns zero (false), a positive (true) or a negative (failure) value */ - attr_exists = H5Aexists(dimscale_id, attr_name); - if (attr_exists > 0) - { - assert(0); - } - else if (attr_exists == 0) - { - dimid_att_id = H5Acreate2(dimscale_id, attr_name, - H5T_NATIVE_INT, dimid_space_id, H5P_DEFAULT, H5P_DEFAULT); - if (dimid_att_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); - } - } - else - { - /* Error determining whether an attribute with a given name exists on an object */ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to determine whether an attribute (%s) exists on a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); - } - - if (H5Awrite(dimid_att_id, H5T_NATIVE_INT, &i) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); - } - - if (H5Sclose(dimid_space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a scalar dataspace", - pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Aclose(dimid_att_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_dims[i].name); - } - - if (H5Sclose(space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a simple dataspace", - pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Pclose(dcpl_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a dataset creation property list", - pio_get_fname_from_file(file), file->pio_ncid); - } - - } - } - - for (i = 0; i < file->hdf5_num_vars; i++) - { - /* Upgrade the dataset of a coordinate variable to a dimension scale */ - if (file->hdf5_vars[i].is_coord_var) - { - if (H5DSset_scale(file->hdf5_vars[i].hdf5_dataset_id, file->hdf5_vars[i].name) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to convert a dataset (for coordinate variable %s) to a dimension scale", - pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_vars[i].name); - } - - assert(file->hdf5_vars[i].ndims > 0); - int dimid = file->hdf5_vars[i].hdf5_dimids[0]; - file->hdf5_dims[dimid].hdf5_dataset_id = file->hdf5_vars[i].hdf5_dataset_id; - - /* Write a special attribute (_Netcdf4Dimid) for the netCDF-4 dimension ID. */ - hid_t dimscale_id = file->hdf5_vars[i].hdf5_dataset_id; - hid_t dimid_att_id; - htri_t attr_exists; - - hid_t dimid_space_id = H5Screate(H5S_SCALAR); - if (dimid_space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new scalar dataspace", - pio_get_fname_from_file(file), file->pio_ncid); - } - - /* Writing _Netcdf4Dimid attribute */ - const char* attr_name = "_Netcdf4Dimid"; - - attr_exists = H5Aexists(dimscale_id, attr_name); - if (attr_exists > 0) - { - assert(0); - } - else if (attr_exists == 0) - { - dimid_att_id = H5Acreate2(dimscale_id, attr_name, - H5T_NATIVE_INT, dimid_space_id, H5P_DEFAULT, H5P_DEFAULT); - if (dimid_att_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new attribute (%s) attached to a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); - } - } - else - { - /* Error determining whether an attribute with a given name exists on an object */ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to determine whether an attribute (%s) exists on a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); - } - - if (H5Awrite(dimid_att_id, H5T_NATIVE_INT, &dimid) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to write an attribute (%s) attached to a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); - } - - if (H5Sclose(dimid_space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a scalar dataspace", - pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Aclose(dimid_att_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close an attribute (%s) attached to a dimension scale (%s)", - pio_get_fname_from_file(file), file->pio_ncid, attr_name, file->hdf5_vars[i].name); - } - } - } - - for (i = 0; i < file->hdf5_num_vars; i++) - { - if (!file->hdf5_vars[i].is_coord_var) - { - int ndims = file->hdf5_vars[i].ndims; - if (ndims > 0) - { - int* dimids = file->hdf5_vars[i].hdf5_dimids; - for (int j = 0; j < ndims; j++) - { - if (H5DSattach_scale(file->hdf5_vars[i].hdf5_dataset_id, file->hdf5_dims[dimids[j]].hdf5_dataset_id, j) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Ending the define mode for file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to attach a dimension scale (for dimension %s) to %dth dimension of a dataset (for variable %s)", - pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[dimids[j]].name, j, file->hdf5_vars[i].name); - } - - /* According to HDF5 developers, the H5DS routines are not parallel, so all the ranks are going to be - * doing the same operations. At some point, with enough iterations of the loop, HDF5 might get out of - * step between the ranks. - * Workaround: place a barrier to sync H5DSattach_scale calls. - */ - MPI_Barrier(ios->io_comm); - } - } - } - } - - return PIO_NOERR; -} - -int spio_hdf5_put_att(iosystem_desc_t *ios, file_desc_t *file, int varid, const char *name, - nc_type atttype, PIO_Offset len, const void *op) -{ - hid_t attr_id; - hid_t space_id; - hsize_t asize = len; - htri_t att_exists; - hid_t loc_id; - hid_t h5_xtype; - - assert(ios && file && name && op); - assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); - assert(ios->ioproc); - - if (varid == PIO_GLOBAL) - loc_id = file->hdf5_file_id; - else - loc_id = file->hdf5_vars[varid].hdf5_dataset_id; - - if (atttype == NC_CHAR) - { - /* String type */ - if (asize == 0) - space_id = H5Screate(H5S_NULL); - else - space_id = H5Screate(H5S_SCALAR); - - if (space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new scalar dataspace", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - h5_xtype = H5Tcopy(H5T_C_S1); - if (h5_xtype == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to make a copy of the predefined string datatype in C", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - /* For empty strings, asize is 0, while H5Tset_size() requires that size must be positive */ - if (H5Tset_size(h5_xtype, (asize == 0 ? 1 : asize)) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set the total size (%ld bytes) for a derived C-style string datatype", - name, varid, pio_get_fname_from_file(file), file->pio_ncid, (asize == 0 ? 1 : asize)); - } - - if (H5Tset_strpad(h5_xtype, H5T_STR_NULLTERM) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to define the type of padding (NULL-terminated) used for a derived C-style string datatype", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Tset_cset(h5_xtype, H5T_CSET_ASCII) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set the character set (US ASCII) to be used in a derived C-style string datatype", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - else - { - space_id = H5Screate_simple(1, &asize, &asize); - if (space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new simple dataspace", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - h5_xtype = spio_nc_type_to_hdf5_type(atttype); - if (h5_xtype == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "Unsupported variable type (type=%x)", - name, varid, pio_get_fname_from_file(file), file->pio_ncid, atttype); - } - } - - /* H5Aexists() returns zero (false), a positive (true) or a negative (failure) value */ - att_exists = H5Aexists(loc_id, name); - if (att_exists > 0) - { - attr_id = H5Aopen(loc_id, name, H5P_DEFAULT); - if (attr_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) with HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to open this existing attribute", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - else if (att_exists == 0) - { - attr_id = H5Acreate2(loc_id, name, h5_xtype, space_id, H5P_DEFAULT, H5P_DEFAULT); - if (attr_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new attribute", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - else - { - /* Error determining whether an attribute with a given name exists on an object */ - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to determine whether this attribute exists", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Awrite(attr_id, h5_xtype, op) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to write this attribute", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Sclose(space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a simple dataspace", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Aclose(attr_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close this attribute", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - /* String attribute */ - if (atttype == NC_CHAR) - { - if (H5Tclose(h5_xtype) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing attribute (%s) associated with variable (varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a derived C-style string datatype used by this attribute", - name, varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - - return PIO_NOERR; -} - -int spio_hdf5_put_var(iosystem_desc_t *ios, file_desc_t *file, int varid, - const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, nc_type xtype, const void *buf) -{ - hsize_t dims[H5S_MAX_RANK]; - hsize_t mdims[H5S_MAX_RANK]; - - assert(ios && file && varid >= 0 && buf); - assert((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)); - assert(ios->ioproc); - - hid_t file_space_id = H5Dget_space(file->hdf5_vars[varid].hdf5_dataset_id); - if (file_space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to make a copy of the dataspace of the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Sget_simple_extent_dims(file_space_id, dims, mdims) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to retrieve dimension size and maximum size of a dataspace copied from the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - int ndims = file->hdf5_vars[varid].ndims; - - /* Extend record dimension if needed */ - if (ndims > 0 && start != NULL && count != NULL && mdims[0] == H5S_UNLIMITED && dims[0] < (hsize_t)(start[0] + count[0])) - { - dims[0] = (hsize_t) (start[0] + count[0]); - - if (H5Sclose(file_space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a dataspace copied from the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Dextend(file->hdf5_vars[varid].hdf5_dataset_id, dims) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to extend the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - file_space_id = H5Dget_space(file->hdf5_vars[varid].hdf5_dataset_id); - if (file_space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to make a copy of the dataspace of the dataset (extended) associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - - hsize_t hstart[(ndims > 0) ? ndims : 1]; - hsize_t hcount[(ndims > 0) ? ndims : 1]; - hsize_t hstride[(ndims > 0) ? ndims : 1]; - hsize_t nelems = 0; - - for(int i = 0; i < ndims; i++){ - if(start){ - hstart[i] = (hsize_t) start[i]; - } - else{ - hstart[i] = 0; - } - hcount[i] = 0; - hstride[i] = 1; - } - - /* Only the IO master does the IO */ - if(ios->iomaster == MPI_ROOT){ - if(count){ - for(int i = 0; i < ndims; i++){ - hcount[i] = (hsize_t)count[i]; - } - } - else{ - for(int i = 0; i < ndims; i++){ - hcount[i] = dims[i]; - } - } - - nelems = (ndims > 0) ? 1 : 0; - for(int i = 0; i < ndims; i++){ - nelems *= hcount[i]; - } - - if(stride){ - for(int i = 0; i < ndims; i++){ - hstride[i] = (stride[i] > 0) ? ((hsize_t)stride[i]) : 1 ; - } - } - } - - hid_t mem_space_id = H5Screate_simple(ndims, hcount, hcount); - if (mem_space_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to create a new simple dataspace", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (ndims > 0) - { - if (H5Sselect_hyperslab(file_space_id, H5S_SELECT_SET, hstart, hstride, hcount, NULL) < 0) - { - H5Eprint2(H5E_DEFAULT, stderr); - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to select a hyperslab region for a dataspace copied from the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - - hid_t mem_type_id; - if (xtype == NC_CHAR) - { - /* String type */ - mem_type_id = H5Tcopy(H5T_C_S1); - if (mem_type_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to make a copy of the predefined string datatype in C", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Tset_strpad(mem_type_id, H5T_STR_NULLTERM) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to define the type of padding (NULL-terminated) used for a derived C-style string datatype", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Tset_cset(mem_type_id, H5T_CSET_ASCII) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to set the character set (US ASCII) to be used in a derived C-style string datatype", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - else - { - mem_type_id = spio_nc_type_to_hdf5_type(xtype); - if (mem_type_id == H5I_INVALID_HID) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "Unsupported memory type (type=%x)", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid, xtype); - } - } - - /* Independent write */ - if (H5Dwrite(file->hdf5_vars[varid].hdf5_dataset_id, mem_type_id, mem_space_id, - file_space_id, file->dxplid_indep, buf) < 0) - { - H5Eprint2(H5E_DEFAULT, stderr); - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to write the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Sclose(mem_space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a simple dataspace", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - if (xtype == NC_CHAR) - { - if (H5Tclose(mem_type_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a derived C-style string datatype used by this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - } - - if (H5Sclose(file_space_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Writing variable (%s, varid=%d) to file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a dataspace copied from the dataset associated with this variable", - pio_get_vname_from_file(file, varid), varid, pio_get_fname_from_file(file), file->pio_ncid); - } - - return PIO_NOERR; -} - -int spio_hdf5_close(iosystem_desc_t *ios, file_desc_t *file) -{ - int i; - - assert(ios && file); - assert((file->iotype == PIO_IOTYPE_HDF5) || ((file->iotype == PIO_IOTYPE_HDF5C))); - assert(ios->ioproc); - - if (H5Pclose(file->dxplid_coll) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Closing file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a dataset transfer property list (collective data transfer)", - pio_get_fname_from_file(file), file->pio_ncid); - } - - if (H5Pclose(file->dxplid_indep) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Closing file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a dataset transfer property list (independent data transfer)", - pio_get_fname_from_file(file), file->pio_ncid); - } - - for (i = 0; i < file->hdf5_num_dims; i++) - { - if (!file->hdf5_dims[i].has_coord_var) - { - if (H5Dclose(file->hdf5_dims[i].hdf5_dataset_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Closing file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a dataset created for a dimension (%s, dimid = %d)", - pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name, i); - } - } - } - - for (i = 0; i < file->hdf5_num_vars; i++) - { - if (H5Dclose(file->hdf5_vars[i].hdf5_dataset_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Closing file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to close a dataset created for a variable (%s, varid = %d)", - pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_vars[i].name, i); - } - - if (file->hdf5_vars[i].nc_type == NC_CHAR) - { - if (H5Tclose(file->hdf5_vars[i].hdf5_type) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Closing file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to release a derived C-style string datatype created for a variable (%s, varid = %d)", - pio_get_fname_from_file(file), file->pio_ncid, file->hdf5_dims[i].name, i); - } - } - } - - if (H5Fclose(file->hdf5_file_id) < 0) - { - return pio_err(ios, file, PIO_EHDF5ERR, __FILE__, __LINE__, - "Closing file (%s, ncid=%d) using HDF5 iotype failed. " - "The low level (HDF5) I/O library call failed to terminate access to an HDF5 file", - pio_get_fname_from_file(file), file->pio_ncid); - } - - return PIO_NOERR; -} -#endif diff --git a/src/clib/core/progress_engine/CMakeLists.txt b/src/clib/core/progress_engine/CMakeLists.txt new file mode 100644 index 00000000000..35e77775ce3 --- /dev/null +++ b/src/clib/core/progress_engine/CMakeLists.txt @@ -0,0 +1,23 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C Core (progress engine)... =====") +# SCORPIO C library - core +set (spio_core_progress_engine_src + spio_async_utils.cpp + spio_async_tpool.cpp + spio_async_tcomm.cpp) + +add_library (spio_core_progress_engine OBJECT + ${spio_core_progress_engine_src}) + +set (spio_clib_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/../..") +set (spio_clib_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/../..") + +target_include_directories(spio_core_progress_engine + PUBLIC . + PRIVATE ${spio_clib_src_dir} ${spio_clib_bin_dir} ${spio_clib_src_dir}/util ${spio_clib_src_dir}/core ${spio_clib_src_dir}/core/util ${spio_clib_src_dir}/core/iolib/hdf5) + +target_link_libraries(spio_core_progress_engine + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/core/progress_engine/spio_async_mtq.hpp b/src/clib/core/progress_engine/spio_async_mtq.hpp new file mode 100644 index 00000000000..33a40332088 --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_mtq.hpp @@ -0,0 +1,242 @@ +#ifndef _SPIO_ASYNC_MTQ_HPP_ +#define _SPIO_ASYNC_MTQ_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include "pio_internal.h" + +namespace PIO_Util{ + +template +class PIO_mtq{ + public: + /* Multi-threaded queue exceptions */ + class Mtq_exception{ + public: + Mtq_exception(const std::string &msg) : msg_(msg){} + std::string what(void ) const { return msg_; } + private: + std::string msg_; + }; + /* Exception : Signal received on MT queue */ + class Qsignal_exception : public Mtq_exception{ + public: + Qsignal_exception(const std::string &msg) : Mtq_exception(msg){} + }; + /* Exception : Queue is empty + * e.g. After a signal was received, queue is empty + */ + class Qempty_exception : public Mtq_exception{ + public: + Qempty_exception(const std::string &msg) : Mtq_exception(msg){} + }; + + typedef enum SigTypes{ + PIO_MTQ_SIG_INVALID=1, + /* Stop processing elements in the queue */ + PIO_MTQ_SIG_STOP, + /* Complete processing elements in queue and quit */ + PIO_MTQ_SIG_COMPLETE + }SigTypes_t; + PIO_mtq(); + void enqueue(const T& val); + int dequeue(T &val); + T dequeue(void ); + void signal(SigTypes_t sig); + int size(void ); + + template + friend std::ostream & operator<<(std::ostream &ostr, PIO_mtq &mtq); + private: + void thread_yield(std::unique_lock &lk) const; + std::mutex mtx_; + std::queue queue_; + std::condition_variable cv_; + SigTypes_t sig_; + std::condition_variable cv_sig_; +}; + +template +PIO_mtq::PIO_mtq():sig_(PIO_mtq::PIO_MTQ_SIG_INVALID) +{ + LOG((2, "PIO_mtq:PIO_mtq: Creating MT queue")); +} + +template +void PIO_mtq::enqueue(const T& val) +{ + std::unique_lock lk(mtx_); + LOG((2, "PIO_mtq:enqueue: Enqueing val to mtq")); + queue_.push(val); + lk.unlock(); + cv_.notify_all(); +} + +template +int PIO_mtq::dequeue(T &val) +{ + std::unique_lock lk(mtx_); + do{ + LOG((2, "PIO_mtq:dequeue: Waiting for dequeueing val from mtq...")); + while(queue_.empty() && (sig_ == PIO_MTQ_SIG_INVALID)){ + cv_.wait(lk, [this]{ return (queue_.empty() && (sig_ == PIO_MTQ_SIG_INVALID));}); + /* + cv_.wait_for(lk, DEFAULT_TIMEOUT, + [this]{ return (queue_.empty() && (sig_ == PIO_MTQ_SIG_INVALID));}); + */ + /* At least on Linux (Ubuntu 4.5.0-040500-generic) just waiting on the condition + * variable (with/without timeouts) does not allow other threads to acquire + * the lock. So we explicitly unlock, sleep for 0 seconds and reacquire the + * lock to allow other threads to be able to get scheduled and acquire the lock + */ + thread_yield(lk); + } + if(sig_ == PIO_MTQ_SIG_STOP){ + LOG((2, "PIO_mtq:dequeue: Received STOP signal on mtq (exiting...)")); + return 1; + } + else if(sig_ == PIO_MTQ_SIG_COMPLETE){ + if(queue_.empty()){ + /* Reset signal */ + sig_ = PIO_MTQ_SIG_INVALID; + lk.unlock(); + /* Notify threads that all async ops are complete */ + cv_sig_.notify_all(); + lk.lock(); + } + else{ + break; + } + } + else{ + break; + } + }while(true); + val = queue_.front(); + queue_.pop(); + LOG((2, "PIO_mtq:dequeue: Successfully dequeued val from mtq")); + lk.unlock(); + return 0; +} + +template +T PIO_mtq::dequeue(void ) +{ + std::unique_lock lk(mtx_); + do{ + LOG((2, "PIO_mtq:dequeue: Waiting for dequeueing val from mtq...")); + while(queue_.empty() && (sig_ == PIO_MTQ_SIG_INVALID)){ + cv_.wait(lk, [this]{ return (queue_.empty() && (sig_ == PIO_MTQ_SIG_INVALID));}); + /* + cv_.wait_for(lk, DEFAULT_TIMEOUT, + [this]{ return (queue_.empty() && (sig_ == PIO_MTQ_SIG_INVALID));}); + */ + /* At least on Linux (Ubuntu 4.5.0-040500-generic) just waiting on the condition + * variable (with/without timeouts) does not allow other threads to acquire + * the lock. So we explicitly unlock, sleep for 0 seconds and reacquire the + * lock to allow other threads to be able to get scheduled and acquire the lock + */ + thread_yield(lk); + } + if(sig_ == PIO_MTQ_SIG_STOP){ + LOG((2, "PIO_mtq:dequeue: Received STOP signal on mtq (exiting...)")); + throw Qsignal_exception("Received STOP signal on multi-threaded queue"); + } + else if(sig_ == PIO_MTQ_SIG_COMPLETE){ + if(queue_.empty()){ + /* Reset signal */ + sig_ = PIO_MTQ_SIG_INVALID; + lk.unlock(); + /* Notify threads that all async ops are complete */ + cv_sig_.notify_all(); + lk.lock(); + } + else{ + break; + } + } + else{ + break; + } + }while(true); + + if(!queue_.empty()){ + T val = queue_.front(); + queue_.pop(); + LOG((2, "PIO_mtq:dequeue: Successfully dequeued val from mtq")); + lk.unlock(); + return val; + } + else{ + lk.unlock(); + throw Qempty_exception("Multi-threaded queue is empty"); + } +} + +template +void PIO_mtq::signal(PIO_mtq::SigTypes_t sig) +{ + std::unique_lock lk(mtx_); + sig_ = sig; + lk.unlock(); + cv_.notify_all(); + + lk.lock(); + if(sig == PIO_MTQ_SIG_COMPLETE){ + LOG((2, "PIO_mtq:signal: Received PIO_MTQ_SIG_COMPLETE, Waiting for async ops to complete")); + /* Wait for all async ops in the queue to complete */ + while(sig_ == PIO_MTQ_SIG_COMPLETE){ + cv_sig_.wait(lk, [this]{ return (sig_ == PIO_MTQ_SIG_COMPLETE);}); + thread_yield(lk); + } + } + lk.unlock(); +} + +template +int PIO_mtq::size(void ) +{ + int sz = 0; + std::unique_lock lk(mtx_); + sz = queue_.size(); + lk.unlock(); + return sz; +} + +template +void PIO_mtq::thread_yield(std::unique_lock &lk) const +{ + /* At least on Linux (Ubuntu 4.5.0-040500-generic) just waiting on the condition + * variable (with/without timeouts) does not allow other threads to acquire + * the lock. So we explicitly unlock, sleep for 0 seconds and reacquire the + * lock to allow other threads to be able to get scheduled and acquire the lock + */ + const std::chrono::milliseconds ZERO_TIMEOUT = std::chrono::milliseconds(0); + lk.unlock(); + //LOG((2, "PIO_mtq:thread_yield: Yielding for 0 secs")); + std::this_thread::sleep_for(ZERO_TIMEOUT); + lk.lock(); +} + +template +std::ostream &operator<<(std::ostream &ostr, PIO_Util::PIO_mtq &q) +{ + std::unique_lock lk(q.mtx_); + /* We need to copy the queue since it is not iterable */ + std::queue tq = q.queue_; + while(!tq.empty()){ + ostr << tq.front() << ", "; + tq.pop(); + } + lk.unlock(); + return ostr; +} + +} // namespace PIO_Util + +#endif // _SPIO_ASYNC_MTQ_HPP_ diff --git a/src/clib/core/progress_engine/spio_async_op.hpp b/src/clib/core/progress_engine/spio_async_op.hpp new file mode 100644 index 00000000000..1cf1f5f4e3d --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_op.hpp @@ -0,0 +1,86 @@ +#ifndef __SPIO_ASYNC_OP_HPP__ +#define __SPIO_ASYNC_OP_HPP__ + +#include +#include + +namespace SPIO_Util{ + +/** + * PIO asynchronous op + */ +class Async_op{ + public: + /** + * PIO asynchronous operation types + */ + enum class Op_type{ + SPIO_ASYNC_INVALID_OP, + SPIO_ASYNC_REARR_OP, + SPIO_ASYNC_PNETCDF_WRITE_OP, + SPIO_ASYNC_HDF5_CREATE_OP, + SPIO_ASYNC_HDF5_DEF_VAR_OP, + SPIO_ASYNC_HDF5_PUT_ATT_OP, + SPIO_ASYNC_HDF5_ENDDEF_OP, + SPIO_ASYNC_HDF5_SET_FRAME_OP, + SPIO_ASYNC_HDF5_PUT_VAR_OP, + SPIO_ASYNC_HDF5_WRITE_OP, + SPIO_ASYNC_FILE_WRITE_OPS, + SPIO_ASYNC_FILE_CLOSE_OP + }; + + static std::string op_type_to_string(Op_type type){ + const std::string UNKNOWN_OP("SPIO_ASYNC_INVALID_OP"); + switch(type){ + case Op_type::SPIO_ASYNC_INVALID_OP : return "SPIO_ASYNC_INVALID_OP"; + case Op_type::SPIO_ASYNC_REARR_OP : return "SPIO_ASYNC_REARR_OP"; + case Op_type::SPIO_ASYNC_PNETCDF_WRITE_OP : return "SPIO_ASYNC_PNETCDF_WRITE_OP"; + case Op_type::SPIO_ASYNC_HDF5_CREATE_OP : return "SPIO_ASYNC_HDF5_CREATE_OP"; + case Op_type::SPIO_ASYNC_HDF5_DEF_VAR_OP : return "SPIO_ASYNC_HDF5_DEF_VAR_OP"; + case Op_type::SPIO_ASYNC_HDF5_PUT_ATT_OP : return "SPIO_ASYNC_HDF5_PUT_ATT_OP"; + case Op_type::SPIO_ASYNC_HDF5_ENDDEF_OP : return "SPIO_ASYNC_HDF5_ENDDEF_OP"; + case Op_type::SPIO_ASYNC_HDF5_SET_FRAME_OP : return "SPIO_ASYNC_HDF5_SET_FRAME_OP"; + case Op_type::SPIO_ASYNC_HDF5_PUT_VAR_OP : return "SPIO_ASYNC_HDF5_PUT_VAR_OP"; + case Op_type::SPIO_ASYNC_HDF5_WRITE_OP : return "SPIO_ASYNC_HDF5_WRITE_OP"; + case Op_type::SPIO_ASYNC_FILE_WRITE_OPS : return "SPIO_ASYNC_FILE_WRITE_OPS"; + case Op_type::SPIO_ASYNC_FILE_CLOSE_OP : return "SPIO_ASYNC_FILE_CLOSE_OP"; + default : return UNKNOWN_OP; + } + } + + Async_op(Op_type type, void *pdata, + std::function wait_fn, + std::function poke_fn, + std::function free_fn): + type_(type), pdata_(pdata), + wait_fn_(wait_fn), poke_fn_(poke_fn), free_fn_(free_fn){} + + Op_type type(void ) const { return type_; } + void *data(void ) const { return pdata_; } + int wait(void ) { return wait_fn_(pdata_); } + int poke(bool &is_complete) { return poke_fn_(pdata_, is_complete); } + int poke(void ) { bool is_complete = false; return poke_fn_(pdata_, is_complete); } + void free(void ) { return free_fn_(pdata_); } + + private: + Op_type type_; + void *pdata_; + /* Blocking wait function for this async op + * param 1 : A user defined data pointer + * return : PIO_NOERR on success, pio error code on failure + */ + std::function wait_fn_; + /* Non-blocking function for making progress on this async op + * param 1 : A user defined data pointer + * param 2 : Pointer to a flag that is set to true if async op + * is complete, false otherwise + * return : PIO_NOERR on success, pio error code on failure + */ + std::function poke_fn_; + /* Free function for user defined pdata */ + std::function free_fn_; +}; + +} // namespace SPIO_Util + +#endif // __SPIO_ASYNC_OP_HPP__ diff --git a/src/clib/core/progress_engine/spio_async_tcomm.cpp b/src/clib/core/progress_engine/spio_async_tcomm.cpp new file mode 100644 index 00000000000..a4f953fabe4 --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_tcomm.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include "spio_async_tcomm.hpp" +#include "spio_async_tpool.hpp" + +/* Static variables */ +thread_local std::size_t SPIO_Util::TComm_info::tidx_ = -1; +thread_local bool SPIO_Util::TComm_info::is_thread_init_ = false; + +SPIO_Util::TComm_info::TComm_info(MPI_Comm union_comm, int union_comm_rank, int union_comm_io_root, int union_comm_comp_root, MPI_Comm io_comm, int io_comm_rank, bool is_io_master, MPI_Comm comp_comm, int comp_comm_rank, bool is_comp_master, MPI_Comm intercomm, MPI_Comm my_comm, MPI_Comm node_comm): union_comm_rank_(union_comm_rank), union_comm_io_root_(union_comm_io_root), union_comm_comp_root_(union_comm_comp_root), io_comm_rank_(io_comm_rank), is_io_master_(is_io_master), comp_comm_rank_(comp_comm_rank), is_comp_master_(is_comp_master) +{ + tids_ = PIO_Util::PIO_async_tpool_manager::get_tpool_instance()->get_thread_ids(); + + /* Total number of comms = Main/Default thread + Number of threads in thread pool + * Main thread info is the first in the list + */ + tidx_ = 0; is_thread_init_ = true; + + union_comms_.push_back(union_comm); + io_comms_.push_back(io_comm); + comp_comms_.push_back(comp_comm); + intercomms_.push_back(intercomm); + my_comms_.push_back(my_comm); + node_comms_.push_back(node_comm); + + int ret = MPI_SUCCESS; + for(std::size_t i = 0; i < tids_.size(); i++){ + MPI_Comm tmp_union_comm = MPI_COMM_NULL; + if(union_comm != MPI_COMM_NULL){ + ret = MPI_Comm_dup(union_comm, &tmp_union_comm); assert(ret == MPI_SUCCESS); + } + union_comms_.push_back(tmp_union_comm); + + MPI_Comm tmp_io_comm = MPI_COMM_NULL; + if(io_comm != MPI_COMM_NULL){ + ret = MPI_Comm_dup(io_comm, &tmp_io_comm); assert(ret == MPI_SUCCESS); + } + io_comms_.push_back(tmp_io_comm); + + MPI_Comm tmp_comp_comm = MPI_COMM_NULL; + if(comp_comm != MPI_COMM_NULL){ + ret = MPI_Comm_dup(comp_comm, &tmp_comp_comm); assert(ret == MPI_SUCCESS); + } + comp_comms_.push_back(tmp_comp_comm); + + MPI_Comm tmp_comm = MPI_COMM_NULL; + if(intercomm != MPI_COMM_NULL){ + ret = MPI_Comm_dup(intercomm, &tmp_comm); assert(ret == MPI_SUCCESS); + } + intercomms_.push_back(tmp_comm); + + /* my_comm, see iosystem_desc_t for details, is just a copy (not dup) + * of union_comm or comp_comm + */ + if(my_comm == union_comm){ + my_comms_.push_back(tmp_union_comm); + } + else if(my_comm == comp_comm){ + my_comms_.push_back(tmp_comp_comm); + } + else if(my_comm == io_comm){ + my_comms_.push_back(tmp_io_comm); + } + else{ + assert(0); + } + + tmp_comm = MPI_COMM_NULL; + if(node_comm != MPI_COMM_NULL){ + ret = MPI_Comm_dup(node_comm, &tmp_comm); assert(ret == MPI_SUCCESS); + } + node_comms_.push_back(tmp_comm); + } +} + +MPI_Comm SPIO_Util::TComm_info::get_union_comm(void ) +{ + return union_comms_[get_tidx()]; +} + +MPI_Comm SPIO_Util::TComm_info::get_io_comm(void ) +{ + return io_comms_[get_tidx()]; +} + +MPI_Comm SPIO_Util::TComm_info::get_comp_comm(void ) +{ + return comp_comms_[get_tidx()]; +} + +MPI_Comm SPIO_Util::TComm_info::get_intercomm(void ) +{ + return intercomms_[get_tidx()]; +} + +MPI_Comm SPIO_Util::TComm_info::get_my_comm(void ) +{ + return my_comms_[get_tidx()]; +} + +MPI_Comm SPIO_Util::TComm_info::get_node_comm(void ) +{ + return node_comms_[get_tidx()]; +} + +MPI_Info *SPIO_Util::TComm_info::create_mpi_info(void ) +{ + int ret = MPI_SUCCESS; + MPI_Info info; + std::mutex mtx; + std::lock_guard lg(mtx); + + ret = MPI_Info_create(&info); assert(ret == MPI_SUCCESS); + + comm_infos_.push_back(info); + return &(comm_infos_.back()); +} + +SPIO_Util::TComm_info::~TComm_info() +{ + /* The main thread comms, in tidx == 0, was just copied over (not duped) + * Similarly my_comms are also just copied over + * Free other comms + */ + + /* Number of threads including the main thread */ + std::size_t nthreads = tids_.size() + 1; + assert(union_comms_.size() == nthreads); + for(std::size_t tidx = 1; tidx < nthreads; tidx++){ + if(union_comms_[tidx] != MPI_COMM_NULL) { MPI_Comm_free(&union_comms_[tidx]); } + if(io_comms_[tidx] != MPI_COMM_NULL) { MPI_Comm_free(&io_comms_[tidx]); } + if(comp_comms_[tidx] != MPI_COMM_NULL) { MPI_Comm_free(&comp_comms_[tidx]); } + if(intercomms_[tidx] != MPI_COMM_NULL) { MPI_Comm_free(&intercomms_[tidx]); } + if(node_comms_[tidx] != MPI_COMM_NULL) { MPI_Comm_free(&node_comms_[tidx]); } + } + + std::for_each(comm_infos_.begin(), comm_infos_.end(), [](MPI_Info &info) { MPI_Info_free(&info); }); +} + +void SPIO_Util::TComm_info::init_thread_info(void ) +{ + tidx_ = INVALID_IDX; + /* Since we do this once per thread, and have a small number of threads a linear + * search should be ok + */ + std::hash tid_hasher; + std::size_t tid = tid_hasher(std::this_thread::get_id()); + + for(std::size_t i = 0; i < tids_.size(); i++){ + if(tid == tids_[i]){ + /* First tidx_ (tidx_ == 0) corresponds to main thread info */ + tidx_ = i + 1; + assert(tidx_ < union_comms_.size()); + break; + } + } + + assert(tidx_ != INVALID_IDX); + is_thread_init_ = true; +} + +std::size_t SPIO_Util::TComm_info::get_tidx(void ) +{ + if(!is_thread_init_) { init_thread_info(); } + return tidx_; +} diff --git a/src/clib/core/progress_engine/spio_async_tcomm.hpp b/src/clib/core/progress_engine/spio_async_tcomm.hpp new file mode 100644 index 00000000000..36dd8711d44 --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_tcomm.hpp @@ -0,0 +1,76 @@ +#ifndef __SPIO_ASYNC_TCOMM_HPP__ +#define __SPIO_ASYNC_TCOMM_HPP__ + +#include +#include +#include +#include +#include "mpi.h" +#include "pio_config.h" +#include "spio_async_tpool.hpp" + +namespace SPIO_Util{ + +/* Keep track of thread-specific MPI communicators */ +class TComm_info{ + public: + /* See iosystem_desc_t{} for info on the communicators cached here */ + TComm_info(MPI_Comm union_comm, int union_comm_rank, int union_comm_io_root, int union_comm_comp_root, MPI_Comm io_comm, int io_comm_rank, bool is_io_master, MPI_Comm comp_comm, int comp_comm_rank, bool is_comp_master, MPI_Comm intercomm, MPI_Comm my_comm, MPI_Comm node_comm); + MPI_Comm get_union_comm(void ); + int get_union_comm_rank(void ) const { return union_comm_rank_; } + int get_union_comm_io_root(void ) const { return union_comm_io_root_; } + int get_union_comm_comp_root(void ) const { return union_comm_comp_root_; } + + MPI_Comm get_io_comm(void ); + int get_io_comm_rank(void ) const { return io_comm_rank_; } + bool is_io_master(void ) const { return is_io_master_; } + + MPI_Comm get_comp_comm(void ); + int get_comp_comm_rank(void ) const { return comp_comm_rank_; } + bool is_comp_master(void ) const { return is_comp_master_; } + + MPI_Comm get_intercomm(void ); + MPI_Comm get_my_comm(void ); + MPI_Comm get_node_comm(void ); + + /* The MPI_Info object is owned by this class. The user can use it + * but its a "weak pointer" + */ + MPI_Info *create_mpi_info(void ); + + ~TComm_info(); + private: + const std::size_t INVALID_IDX = -1; + /* Index to thread-specific comm in the comm vectors. Index 0 + * is reserved for the main thread + * e.g. union_comms_[tidx_] is the union comm for current thread + */ + static thread_local std::size_t tidx_; + static thread_local bool is_thread_init_; + + int union_comm_rank_; + int union_comm_io_root_; + int union_comm_comp_root_; + int io_comm_rank_; + bool is_io_master_; + int comp_comm_rank_; + bool is_comp_master_; + + std::vector tids_; + std::vector union_comms_; + std::vector io_comms_; + std::vector comp_comms_; + std::vector intercomms_; + std::vector my_comms_; + std::vector node_comms_; + + /* Note: We return pointers to elements of this list back to user */ + std::list comm_infos_; + + void init_thread_info(void ); + std::size_t get_tidx(void ); +}; + +} // namespace SPIO_Util + +#endif // __SPIO_ASYNC_TCOMM_HPP__ diff --git a/src/clib/core/progress_engine/spio_async_tpool.cpp b/src/clib/core/progress_engine/spio_async_tpool.cpp new file mode 100644 index 00000000000..f96d2717175 --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_tpool.cpp @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include +extern "C"{ +#include "pio_config.h" +} // extern "C" +#include "spio_async_mtq.hpp" +#include "spio_async_tpool.hpp" +#include "pio_internal.h" + +int pio_async_init_cnt = 0; +PIO_Util::PIO_async_tpool *PIO_Util::PIO_async_tpool_manager::tpool_ = NULL; +static PIO_Util::PIO_async_tpool_manager tpool_mgr; + +void PIO_Util::PIO_async_tpool::enqueue(const SPIO_Util::Async_op &op) +{ + LOG((2, "PIO_async_tpool:enqueue: Enqueing async op, kind = %s", SPIO_Util::Async_op::op_type_to_string(op.type()).c_str())); + mtq_.enqueue(op); +} + +void PIO_Util::PIO_async_tpool::finalize(void ) +{ + mtq_.signal(PIO_Util::PIO_mtq::PIO_MTQ_SIG_COMPLETE); +} + +std::vector PIO_Util::PIO_async_tpool::get_thread_ids(void ) const +{ + std::vector htids; + + std::hash tid_hasher; + std::for_each(pool_threads_.cbegin(), pool_threads_.cend(), + [&tid_hasher, &htids](const std::thread &t) { htids.push_back(tid_hasher(t.get_id())); }); + + return htids; +} + +PIO_Util::PIO_async_tpool::PIO_async_tpool(int nthreads) +{ + LOG((2, "PIO_async_tpool:PIO_async_tpool: Creating %d threads", nthreads)); + for(int i=0; i::PIO_MTQ_SIG_STOP); + for(std::vector::iterator iter = pool_threads_.begin(); + iter != pool_threads_.end(); ++iter){ + if(iter->joinable()){ + iter->join(); + } + } +} + +int PIO_Util::PIO_async_tpool::dequeue_and_process( + PIO_Util::PIO_async_tpool *tpool) +{ + int ret = PIO_NOERR; + assert(tpool); + /* Wait in an infinite loop (until the threads receive a signal) for + * pending asynchronous operations queued in the thread pool + */ + while(true){ + LOG((2, "PIO_async_tpool:dequeue_and_process: Waiting for async ops...")); + + try{ + SPIO_Util::Async_op op = tpool->mtq_.dequeue(); + ret = op.wait(); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, + "Internal error dequeuing and processing asynchronous operation (%s) in the thread pool. Internal error waiting on asynchronous file write operation", + SPIO_Util::Async_op::op_type_to_string(op.type()).c_str()); + } + op.free(); + }catch(const PIO_Util::PIO_mtq::Mtq_exception &e){ + /* MTQ exceptions are for MTQ signal handling, stop waiting on the queue */ + LOG((1, "MTQ exception : %s", e.what().c_str())); + break; + } + } + + return PIO_NOERR; +} + +PIO_Util::PIO_async_tpool * + PIO_Util::PIO_async_tpool_manager::get_tpool_instance(void ) +{ + /* We need to make NUM_THREADS configurable by the user (compile-time) */ + const int NUM_THREADS = SPIO_ASYNC_NTHREADS; + if(tpool_ == NULL){ + LOG((2, "PIO_async_tpool_manager:get_tpool_instance: Creating new tpool instance")); + tpool_ = new PIO_Util::PIO_async_tpool(NUM_THREADS); + } + else{ + LOG((2, "PIO_async_tpool_manager:get_tpool_instance: Retrieving tpool instance")); + } + return tpool_; +} + +PIO_Util::PIO_async_tpool_manager::~PIO_async_tpool_manager() +{ + if(tpool_){ + LOG((2, "PIO_async_tpool_manager:~PIO_async_tpool_manager: Finalizing tpool")); + tpool_->finalize(); + delete(tpool_); + tpool_ = NULL; + } + else{ + LOG((2, "PIO_async_tpool_manager:~PIO_async_tpool_manager: tpool is already finalized/deleted")); + } +} + +int pio_async_tpool_create(void ) +{ + /* Although creation and deletion of the thread pool is managed by the + * thread pool manager, we still need reference counting to decide on + * when to send signals to async threads to finish queue async ops + * (Note: Multiple iosystems init/finalize but use the same thread pool) + */ + LOG((2, "pio_async_tpool_create: Creating tpool (by ref)")); + pio_async_init_cnt++; + + return PIO_NOERR; +} + +int pio_async_tpool_op_add(const SPIO_Util::Async_op &op) +{ + PIO_Util::PIO_async_tpool *tpool = tpool_mgr.get_tpool_instance(); + assert(tpool); + LOG((2, "pio_async_tpool_op_add(): Adding op")); + tpool->enqueue(op); + + return PIO_NOERR; +} + +int pio_async_tpool_ops_wait(void ) +{ + /* Currently we don't support waiting on async ops, unless we are + * terminating the async threads in *tpool_finalize() + */ + assert(0); +} + +int pio_async_tpool_finalize(void ) +{ + pio_async_init_cnt--; + if(pio_async_init_cnt == 0){ + LOG((2, "pio_async_tpool_finalize: Finalizing tpool instance")); + PIO_Util::PIO_async_tpool *tpool = tpool_mgr.get_tpool_instance(); + assert(tpool); + /* Signal threads waiting on queue to finish up/complete + * the queued async ops and exit + */ + tpool->finalize(); + } + else{ + LOG((2, "pio_async_tpool_finalize: Decrement ref cnt")); + } + + return PIO_NOERR; +} diff --git a/src/clib/core/progress_engine/spio_async_tpool.hpp b/src/clib/core/progress_engine/spio_async_tpool.hpp new file mode 100644 index 00000000000..8a6a9d0cf7f --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_tpool.hpp @@ -0,0 +1,44 @@ +#ifndef _SPIO_ASYNC_TPOOL_HPP_ +#define _SPIO_ASYNC_TPOOL_HPP_ + +#include +#include +#include +#include "spio_async_op.hpp" +#include "spio_async_mtq.hpp" +#include "spio_async_utils.hpp" +#include "pio_internal.h" + +namespace PIO_Util{ + +class PIO_async_tpool{ + public: + void enqueue(const SPIO_Util::Async_op &op); + void finalize(void ); + std::vector get_thread_ids(void ) const; + private: + friend class PIO_async_tpool_manager; + PIO_async_tpool(int nthreads); + ~PIO_async_tpool(); + static int dequeue_and_process(PIO_async_tpool *tpool); + PIO_Util::PIO_mtq mtq_; + std::vector pool_threads_; +}; + +class PIO_async_tpool_manager{ + public: + static PIO_async_tpool *get_tpool_instance(void ); + ~PIO_async_tpool_manager(); + private: + static PIO_async_tpool *tpool_; +}; + +} // namespace PIO_Util + +/* FIXME: Move it inside the SPIO_Util namespace */ +int pio_async_tpool_create(void ); +int pio_async_tpool_op_add(const SPIO_Util::Async_op &op); +int pio_async_tpool_ops_wait(void ); +int pio_async_tpool_finalize(void ); + +#endif // _SPIO_ASYNC_TPOOL_HPP_ diff --git a/src/clib/core/progress_engine/spio_async_utils.cpp b/src/clib/core/progress_engine/spio_async_utils.cpp new file mode 100644 index 00000000000..44fd924923e --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_utils.cpp @@ -0,0 +1,889 @@ +/** @file + * Support functions for the PIO library. + */ + +#include +#include +#include +#include +#include +#include +#include + +extern "C"{ + +#include +#include +#include +#include +#if PIO_ENABLE_LOGGING +#include +#endif /* PIO_ENABLE_LOGGING */ +#include +} // extern "C" +#include "pio_timer.h" +#include +#include "spio_async_op.hpp" +#include "spio_async_utils.hpp" +#include "spio_async_tpool.hpp" +#include "spio_file_mvcache.h" +#include "spio_dbg_utils.hpp" +#include "spio_dt_converter.hpp" +#include "spio_hdf5_utils.hpp" + +/* Use this function for op kinds with no wait functions + * We use it to indicate, + * 1) No wait function available + * 2) Make sure that the wait function is not called (a more + * optimized wait function is available, so make sure that + * the wait function for this async op is not called) + */ +int pio_async_wait_func_unavail(void *pdata) +{ + assert(0); +} + +/* Use this function for op kinds with no poke function + * Some asynchronous operations have no poke/test functions + *pio_iosys_async_op_hdf5_write so any generic code that uses the poke function must + * check the existence of this function before using it + */ +int pio_async_poke_func_unavail(void *pdata, bool &flag) +{ + assert(0); +} + +/* Wait for pending asynchronous operations on this file + * This is the generic wait function for waiting on all + * async ops on a file + * @param file Pointer to the file_desc for the file + * Returns PIO_NOERR on success, a pio error code otherwise + */ +int pio_file_async_pend_ops_wait(file_desc_t *file) +{ + int ret; + assert(file != NULL); + + while(!file->async_pend_ops.empty()){ + SPIO_Util::Async_op op = file->async_pend_ops.front(); + LOG((2, "Waiting on async op, kind = %s", SPIO_Util::Async_op::op_type_to_string(op.type()).c_str())); + + ret = op.wait(); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, + "Error waiting for pending asynchronous operation (%s) on file, %s (ncid = %d)", + SPIO_Util::Async_op::op_type_to_string(op.type()).c_str(), + pio_get_fname_from_file(file), file->pio_ncid); + } + + op.free(); + + file->async_pend_ops.pop_front(); + } + + return PIO_NOERR; +} + +#ifdef PIO_MICRO_TIMING +/* A struct to store information on pnetcdf write timers */ +typedef struct pio_async_pnetcdf_wr_timer_info{ + int nvars; + bool *var_timer_was_running; + mtimer_t wait_timer; +} pio_async_pnetcdf_wr_timer_info_t; + +/* Pause all var wr timers, create a temp wait timer and return it */ +int pio_async_pnetcdf_setup_wr_timers(file_desc_t *file, var_desc_t **vdescs, + int nvdescs, + pio_async_pnetcdf_wr_timer_info_t *wr_info) +{ + int ret; + + if(nvdescs == 0){ + return PIO_NOERR; + } + assert(file && vdescs && wr_info); + wr_info->nvars = nvdescs; + wr_info->var_timer_was_running = (bool *)malloc(wr_info->nvars * sizeof(bool)); + if(!wr_info->var_timer_was_running){ + return pio_err(file->iosystem, file, PIO_ENOMEM, __FILE__, __LINE__, + "Setting up asynchronous write timers for the PIO_IOTYPE_PNETCDF I/O type failed on file (%s, ncid=%d). Unable to allocate %lld bytes to store timer state for the multiple variables (number of variables=%d) in the file", pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long) (wr_info->nvars * sizeof(bool)), wr_info->nvars); + } + + /* Timer to keep track of wait time */ + wr_info->wait_timer = mtimer_create("Temp_wait_timer", file->iosystem->my_comm, + "piowaitlog"); + if(!mtimer_is_valid(wr_info->wait_timer)){ + return pio_err(file->iosystem, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Setting up asynchronous write timers for the PIO_IOTYPE_PNETCDF I/O type failed on file (%s, ncid=%d). Unable to create a temporary timer to measure wait time for asynchronous operations on the file", pio_get_fname_from_file(file), file->pio_ncid); + } + + /* Pause all timers */ + for(int i=0; ivar_timer_was_running[i] = false; + assert(vdescs[i]); + if(mtimer_is_valid(vdescs[i]->wr_mtimer)){ + ret = mtimer_pause(vdescs[i]->wr_mtimer, &(wr_info->var_timer_was_running[i])); + if(ret != PIO_NOERR){ + LOG((1, "Unable to pause timer")); + return ret; + } + } + } + + ret = mtimer_start(wr_info->wait_timer); + if(ret != PIO_NOERR){ + LOG((1, "Unable to start the temp wait timer")); + return ret; + } + + return PIO_NOERR; +} + +/* + * 1) Update the variable wr timers with the wait time + * 2) Restart timers (stopped during setup phase), if needed + * 3) Release the wr_info + */ +int pio_async_pnetcdf_finalize_wr_timers(file_desc_t *file, var_desc_t **vdescs, + int nvdescs, + pio_async_pnetcdf_wr_timer_info_t *wr_info) +{ + int ret; + + if(nvdescs == 0){ + return PIO_NOERR; + } + assert(file && vdescs && wr_info); + assert(nvdescs == wr_info->nvars); + + /* Calculate the wait time */ + ret = mtimer_pause(wr_info->wait_timer, NULL); + if(ret != PIO_NOERR){ + LOG((1, "Unable to pause temp wait timer")); + return ret; + } + + double wait_time = 0.0; + ret = mtimer_get_wtime(wr_info->wait_timer, &wait_time); + if(ret != PIO_NOERR){ + LOG((1, "Unable to get time from wait timer")); + return ret; + } + + ret = mtimer_destroy(&(wr_info->wait_timer)); + if(ret != PIO_NOERR){ + LOG((1, "Destroying temp wait timer failed")); + /* Continue */ + } + + /* Find avg wait time per variable */ + wait_time /= nvdescs; + + /* Update timers for vars with the avg wait time */ + for(int i=0; invars; i++){ + ret = mtimer_update(vdescs[i]->wr_mtimer, wait_time); + if(ret != PIO_NOERR){ + LOG((1, "Unable to update var write timer")); + return ret; + } + + /* Wait is now complete - no more async events in progress */ + ret = mtimer_async_event_in_progress(vdescs[i]->wr_mtimer, false); + if(ret != PIO_NOERR){ + LOG((1, "Unable to disable async events for var")); + return ret; + } + + /* If the timer was already running before we paused it in setup phase, + * restart it or else flush it */ + if(wr_info->var_timer_was_running[i]){ + ret = mtimer_resume(vdescs[i]->wr_mtimer); + if(ret != PIO_NOERR){ + LOG((1, "Unable to resume variable write timer")); + return ret; + } + } + else{ + ret = mtimer_flush(vdescs[i]->wr_mtimer, + get_var_desc_str(file->pio_ncid, vdescs[i]->varid, NULL)); + if(ret != PIO_NOERR){ + LOG((1, "Unable to flush timer")); + return ret; + } + } + } + + free(wr_info->var_timer_was_running); + wr_info->var_timer_was_running = NULL; + + return PIO_NOERR; +} +#endif + +/* Reset a vdesc after writing all data associated with it */ +int pio_async_pnetcdf_reset_vdesc(var_desc_t **vdescs, int nvdescs) +{ + /* FIXME: These operations need to be asynchronous */ + for(int i=0; iwb_pend = 0; + if(vdescs[i]->fillbuf){ + brel(vdescs[i]->fillbuf); + vdescs[i]->fillbuf = NULL; + } + } + return PIO_NOERR; +} + +/* Free a variable iobuf cache */ +void pio_viobuf_free(void *p) +{ + viobuf_cache_t *pviobuf = (viobuf_cache_t *)p; + assert(pviobuf); + + /* iobuf can be NULL associated with writes that are just + * fillvalues (these writes don't have individual iobufs, + * they just use a single common buffer associated with + * the vdesc, vdesc->fillbuf + * These dummy viobufs created to represent writes that + * contain only fillvalues (SUBSET) don't have any + * iobuf or fillvalue associated with it + * The dummy viobufs are also used to represent + * ncmpi_bput_* requests and since data is buffered by + * pnetcdf we don't have an iobuf associated with these + * requests + */ + if(pviobuf->iobuf){ + brel(pviobuf->iobuf); + pviobuf->iobuf = NULL; + } + + /* pviobuf->fillvalue is only valid if + * 1) Var has a fillvalue defined in file, vdesc->fillvalue is valid + * or + * 2) iodesc->needsfill is true + */ + if(pviobuf->fillvalue){ + free(pviobuf->fillvalue); + } + + free(p); +} + +/* Optimized wait function for pnetcdf writes */ +/* Instead of waiting for each async op of type, PNETCDF_WRITE_OP, + * wait for the ops collectively + */ +int pio_async_pnetcdf_write_kwait(void *f) +{ +#ifdef HAVE_PNETCDF + int ret; + file_desc_t *file = (file_desc_t *)f; + assert(file); + assert(file->iotype == PIO_IOTYPE_PNETCDF); + + if(file->async_pend_ops.empty()) { return PIO_NOERR; } + + /* Gather up all requests corresponding to all vdescs associated + * with the pending async ops + * Also delete these async ops from the list since we wait for + * the requests associated with the ops here + * */ + std::vector reqs; + std::vector vdescs; + std::vector viobufs; + + for(std::deque::iterator iter = file->async_pend_ops.begin(); iter != file->async_pend_ops.end();){ + SPIO_Util::Async_op op = *iter; + /* Only process PnetCDF write ops */ + if(op.type() == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_PNETCDF_WRITE_OP){ + viobuf_cache_t *pviobuf = static_cast(op.data()); + assert(pviobuf); + + reqs.push_back(pviobuf->req); + viobufs.push_back(pviobuf); + vdescs.push_back(pviobuf->vdesc); + + iter = file->async_pend_ops.erase(iter); + } + else{ + ++iter; + } + } + + if(reqs.empty()) { return PIO_NOERR; } + + /* We don't expect any other pending operations when writes are + * pending on this file + * This was a constraint that was introduced by resuing a + * single file buffer, file->iobuf, for rearrange and write. + * So a write needed to complete before a rearrange occurs. + * FIXME: Since this is no longer a constraint for async writes + * investigate on how to relax the constraint + */ + assert(reqs.size() <= file->async_pend_ops.size()); + LOG((2, "pio_async_pnetcdf_write_kwait(): nreqs= %d, file->nasync_pend_ops= %d\n", + reqs.size(), file->async_pend_ops.size())); + +#ifdef PIO_MICRO_TIMING + pio_async_pnetcdf_wr_timer_info_t wr_info; + ret = pio_async_pnetcdf_setup_wr_timers(file, vdescs.data(), vdescs.size(), &wr_info); + if(ret != PIO_NOERR){ + LOG((1, "Initializing var write timers failed")); + return ret; + } +#endif + + /* Wait on all requests in one call */ + /* We don't care about the status of each request, we + * only care whether wait succeeded or not + * Requires pnetcdf ver >= 1.7.0 to support a + * NULL value for the status array + */ + /* FIXME: Replace with op.wait() */ + ret = ncmpi_wait_all(file->fh, reqs.size(), reqs.data(), NULL); + if(ret != NC_NOERR){ + return pio_err(file->iosystem, file, ret, __FILE__, __LINE__, + "Error while waiting for asynchronous writes to complete for the PIO_IOTYPE_PNETCDF I/O type on file (%s, ncid=%d). Internal I/O library error while waiting for pending PnetCDF operations.", pio_get_fname_from_file(file), file->pio_ncid); + } + + /* FIXME: Replace with op.free() */ + std::for_each(viobufs.begin(), viobufs.end(), [](viobuf_cache_t *pv) { free(pv); }); + +#ifdef PIO_MICRO_TIMING + ret = pio_async_pnetcdf_finalize_wr_timers(file, vdescs.data(), vdescs.size(), &wr_info); + if(ret != PIO_NOERR){ + LOG((1, "Finalizing var write timers failed")); + return ret; + } +#endif + ret = pio_async_pnetcdf_reset_vdesc(vdescs.data(), vdescs.size()); + if(ret != PIO_NOERR){ + return pio_err(file->iosystem, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Error while waiting for asynchronous writes to complete for the PIO_IOTYPE_PNETCDF I/O type on file (%s, ncid=%d). Resetting variable descriptors associated with the completed asynchronous operations failed", pio_get_fname_from_file(file), file->pio_ncid); + } + + return PIO_NOERR; +#else + assert(0); +#endif +} + +/* Wait only for rearr async ops on a file */ +int pio_async_rearr_kwait(void *f) +{ + int ret; + file_desc_t *file = (file_desc_t *)f; + assert(file); + + if(file->async_pend_ops.empty()) { return PIO_NOERR; } + + for(std::deque::iterator iter = file->async_pend_ops.begin(); iter != file->async_pend_ops.end();){ + SPIO_Util::Async_op op = *iter; + if(op.type() == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_REARR_OP){ + ret = op.wait(); + if(ret != PIO_NOERR){ + LOG((1, "Waiting for rearr async op failed")); + return pio_err(file->iosystem, file, PIO_EINTERNAL, __FILE__, __LINE__, "Internal error while waiting for asynchronous rearrangement operations (number of pending ops = %zu) on file (%s, ncid=%d)", file->async_pend_ops.size(), pio_get_fname_from_file(file), file->pio_ncid); + } + op.free(); + iter = file->async_pend_ops.erase(iter); + } + else{ + ++iter; + } + } + + return PIO_NOERR; +} + +int pio_async_hdf5_write_kwait(void *file) +{ + assert(0); +} + +/* Wait for pending asynchronous operations of kind, op_kind, on this file + * @param file Pointer to the file_desc for the file + * Returns PIO_NOERR on success, a pio error code otherwise + */ +int pio_file_async_pend_ops_kwait(file_desc_t *file, SPIO_Util::Async_op::Op_type op_kind) +{ + assert(file); + + int ret = PIO_NOERR; + switch(op_kind){ + case SPIO_Util::Async_op::Op_type::SPIO_ASYNC_REARR_OP : ret = pio_async_rearr_kwait(file); break; + case SPIO_Util::Async_op::Op_type::SPIO_ASYNC_PNETCDF_WRITE_OP : ret = pio_async_pnetcdf_write_kwait(file); break; + default : ret = pio_async_wait_func_unavail(file); break; + } + + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, + "Internal error while waiting for pending asynchronous operations on file (%s, ncid=%d)", pio_get_fname_from_file(file), file->pio_ncid); + } + + return PIO_NOERR; +} + +/* Add an async op to the list of pending ops for a file + * @param file Pointer to the file_desc for the file + * @param op_type Type of asynchronous operation added + * @param pdata Pointer to user defined data for this async op + * Returns PIO_NOERR on success, a pio error code otherwise + */ +int pio_file_async_pend_op_add(file_desc_t *file, + SPIO_Util::Async_op::Op_type op_type, void *pdata) +{ + assert(file != NULL); + assert( (op_type != SPIO_Util::Async_op::Op_type::SPIO_ASYNC_INVALID_OP) && + ( (op_type == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_REARR_OP) || + (op_type == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_PNETCDF_WRITE_OP) ) ); + + assert(pdata != NULL); + + if(op_type == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_REARR_OP){ + file->async_pend_ops.push_back({op_type, pdata, + pio_swapm_wait, pio_swapm_iwait, pio_swapm_req_free}); + } + else if(op_type == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_PNETCDF_WRITE_OP){ + file->async_pend_ops.push_back({op_type, pdata, + pio_async_wait_func_unavail, pio_async_poke_func_unavail, pio_viobuf_free}); + } + else{ + assert(0); + } + + return PIO_NOERR; +} + +/* Start rearranging data pointed to by buf and cache the rearranged + * data. The rearranged data is cached in the ioprocs and the data + * pointed to by buf is expected to be valid until the rearrange + * operation completes. + * Note: Called by all procs + */ +int pio_var_rearr_and_cache(file_desc_t *file, var_desc_t *vdesc, + io_desc_t *iodesc, void *buf, + size_t buflen, void *fillvalue, int rec_num) +{ + int ierr; + iosystem_desc_t *ios; + + LOG((2, "pio_var_rearr_and_cache : file=%p, vdesc=%p, iodesc=%p, rec_num=%d\n", + file, vdesc, iodesc, rec_num)); + /* Note: buf can be NULL or buflen can be 0 if a compute process + * has no data to send to io procs + */ + assert(file && vdesc && iodesc && ((rec_num >= -1))); + ios = file->iosystem; + assert(ios); + + void *sbuf = buf; + void *rbuf = NULL; + viobuf_cache_t *pnew = NULL; + /* iodesc->maxiobuflen can be zero if all procs have + * no data to receive from other procs in a subset + * of procs (SUBSET rearranger) + */ + if((ios->ioproc) && (iodesc->maxiobuflen > 0)){ + /* viobuf cache list keeps track of iobuf that contains the + * rearranged data + */ + //viobuf_cache_t *p = vdesc->viobuf_ltail; + pnew = (viobuf_cache_t *)calloc(1, sizeof(viobuf_cache_t)); + if(!pnew){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Error while queueing asynchronous operation to start rearranging of data on file (%s, ncid=%d). Unable to allocate %lld bytes for internal data structure to keep track of cached user data", pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long) (sizeof(viobuf_cache_t))); + } + + pnew->ubuf = buf; + pnew->ubuf_sz = buflen; + /* Allocate mem for rearranged data, since different procs could + * potentially have different amount of rearranged data (SUBSET) + * allocate memory for max memory required among all procs, + * i.e., maxiobuflen + */ + pnew->iobuf_sz = iodesc->mpitype_size * iodesc->maxiobuflen; + pnew->iobuf = bget(pnew->iobuf_sz); + if(!(pnew->iobuf)){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Error while queueing asynchronous operation to start rearranging of data on file (%s, ncid=%d). Unable to allocate %lld bytes for caching rearranged data", pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long) (pnew->iobuf_sz)); + } + rbuf = pnew->iobuf; + pnew->fillvalue = NULL; + pnew->fillvalue_sz = 0; + if(iodesc->needsfill){ + pnew->fillvalue = malloc(iodesc->mpitype_size); + if(!(pnew->fillvalue)){ + return pio_err(ios, file, PIO_ENOMEM, __FILE__, __LINE__, + "Error while queueing asynchronous operation to start rearranging of data on file (%s, ncid=%d). Unable to allocate %lld bytes for caching fillvalue associated with user data", pio_get_fname_from_file(file), file->pio_ncid, (unsigned long long) (iodesc->mpitype_size)); + } + pnew->fillvalue_sz = iodesc->mpitype_size; + assert(fillvalue || vdesc->fillvalue); + if(fillvalue){ + /* Use the given fillvalue (explicit fillvalue specified + * by the user + */ + memcpy(pnew->fillvalue, fillvalue, iodesc->mpitype_size); + } + else{ + /* Use default fillvalue for the variable */ + memcpy(pnew->fillvalue, vdesc->fillvalue, iodesc->mpitype_size); + } + } + + /* For the BOX rearranger we need contiguous blocks of data on + * each io process, including the fill values. Only data that + * are not fillvalues are transferred from compute to io procs, + * so init the buffer, to store rearranged data, with fill + * values + */ + if(iodesc->needsfill && (iodesc->rearranger == PIO_REARR_BOX)){ + assert(pnew->fillvalue); + for(int i=0; imaxiobuflen; i++){ + memcpy(&((char *)pnew->iobuf)[iodesc->mpitype_size * i], + pnew->fillvalue, iodesc->mpitype_size); + } + } + + pnew->rec_num = rec_num; + /* The requests associated with data rearrangemnent is handled + * by separate async operations. These reqs are used to keep + * track of writes (after data rearrangement) using iobuf + */ + pnew->req = PIO_REQ_NULL; + pnew->vdesc = vdesc; + } + + /* Start rearrange of the data - asynchronous op, so will return before + * rearrange is complete + */ + int nvars = 1; + ierr = rearrange_comp2io(ios, iodesc, file, sbuf, rbuf, nvars); + if(ierr != PIO_NOERR){ + return pio_err(ios, file, ierr, __FILE__, __LINE__, + "Error while rearranging data from compute processes to I/O processes before writing it out to file (%s, ncid=%d). Internal error occured while rearranging data", pio_get_fname_from_file(file), file->pio_ncid); + } + + if((ios->ioproc) && (iodesc->maxiobuflen > 0)){ + /* Add the new node to the end of the list */ + if(vdesc->viobuf_ltail){ + vdesc->viobuf_ltail->next = pnew; + vdesc->viobuf_ltail = pnew; + } + else{ + /* First node */ + vdesc->viobuf_ltail = pnew; + } + /* First node in the list, update the head */ + if(!(vdesc->viobuf_lhead)){ + vdesc->viobuf_lhead = vdesc->viobuf_ltail; + } + } + + return PIO_NOERR; +} + +/* Get the cached data associated with a variable + */ +int pio_var_get_cache_data(var_desc_t *vdesc, int rec, void **buf, size_t *buflen) +{ + /* Use pio_var_del_cache_data that returns the iobuf and deletes the cache + * instead + */ + assert(0); +} + +/* Remove the cached viobuf corresponding to rec_num for a variable defined by vdesc + * The removed viobuf_cache is returned via pviobuf + * Note : Only called from io procs (to get the cached rearranged data) + */ +int pio_var_rem_cache_data(var_desc_t *vdesc, int rec_num, viobuf_cache_t **pviobuf) +{ + viobuf_cache_t *p = vdesc->viobuf_lhead; + viobuf_cache_t *prev = p; + + assert(vdesc && (rec_num >= -1) && pviobuf); + + /* We expect the list to have at least one element */ + assert(vdesc->viobuf_lhead && vdesc->viobuf_ltail); + *pviobuf = NULL; + + /* If the frames/records are accessed/deleted in the same order as they + * were added this search will always end at the head + */ + while(p && (p->rec_num != rec_num)){ + prev = p; + p = p->next; + } + + /* We assume that the search always succeeds */ + assert(p); + + *pviobuf = p; + + /* Delete node from list */ + if(vdesc->viobuf_lhead == vdesc->viobuf_ltail){ + /* Single node in the list */ + vdesc->viobuf_lhead = NULL; + vdesc->viobuf_ltail = NULL; + } + else{ + if(p == vdesc->viobuf_lhead){ + vdesc->viobuf_lhead = p->next; + } + + if(p == vdesc->viobuf_ltail){ + vdesc->viobuf_ltail = prev; + } + + prev->next = p->next; + } + + return PIO_NOERR; +} + +/* A helper function to copy rearranged data corresponding to variables + * specified by varids array to a single buffer (and the var frames + * referred by the frames array). + * The varids array can have duplicates, where the corresponding + * element in the frames array have different values + * The dest buffer is expected to be a contiguous region of valid + * values, rearranged as specified by iodesc + * This helper function is used by serial writes to copy data corresponding + * to multiple variables to a single buffer, this makes transferring data for + * multiple variables more efficient (compared to separately transferring + * data for each variable) + * Note: The function expects that data rearrangement is already complete + * and the rearranged data is available in viodesc caches in var_desc + */ +int pio_file_compact_and_copy_rearr_data(void *dest, size_t dest_sz, + io_desc_t *iodesc, file_desc_t *file, const int *varids, + const int *frames, int nvars) +{ + int ret; + size_t off = 0; + size_t rem_sz = dest_sz; + + for(int i=0; ivarlist + varids[i], + cur_frame, &pviobuf); + if(ret != PIO_NOERR){ + return pio_err(file->iosystem, file, ret, __FILE__, __LINE__, + "Error while compacting and copying rearranged data for asynchronous writes for %d variables on file (%s, ncid=%d). Getting internal buffer with rearranged data for var %d failed", nvars, pio_get_fname_from_file(file), file->pio_ncid, i); + } + assert(pviobuf); + + /* Copy this iobuf to dest buffer */ + /* Note that for each variable we only copy iodesc->llen values, that + * contain the valid values. Each pviobuf->iobuf is of size + * pviobuf->iobuf_sz (== iodesc->maxiobuflen) + * >= iodesc->llen * iodesc->mpitype_size. + */ + size_t iobuf_sz = iodesc->mpitype_size * iodesc->llen; + assert(rem_sz >= iobuf_sz); + memcpy((void *) ((char *)dest + off), pviobuf->iobuf, iobuf_sz); + off += iobuf_sz; + rem_sz -= iobuf_sz; + + pio_viobuf_free(pviobuf); + } + + return PIO_NOERR; +} + +/* Wait for all pending asynchronous operations on this iosystem + * This is the generic wait function for waiting on all + * async ops on an iosystem + * @param iosys Pointer to the iosystem_desc for the iosystem + * Returns PIO_NOERR on success, a pio error code otherwise + */ +int pio_iosys_async_pend_ops_wait(iosystem_desc_t *iosys) +{ + int ret; + assert(iosys != NULL); + + if(iosys->async_pend_ops.empty()) { return PIO_NOERR; } + + for(std::deque::iterator iter = iosys->async_pend_ops.begin(); + iter != iosys->async_pend_ops.end();){ + SPIO_Util::Async_op op = *iter; + + assert(op.type() == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_FILE_WRITE_OPS); + ret = op.wait(); + if(ret != PIO_NOERR){ + return pio_err(iosys, NULL, PIO_EINTERNAL, __FILE__, __LINE__, + "Internal error waiting for pending asynchronous operations on iosystem (iosysid=%d). Waiting for an asynchronous operation (%s) failed.", + iosys->iosysid, SPIO_Util::Async_op::op_type_to_string(op.type()).c_str()); + } + + op.free(); + iter = iosys->async_pend_ops.erase(iter); + } + + return PIO_NOERR; +} + +/* Wait for all pending asynchronous operations on a file + * This is the generic wait function for waiting on all + * async ops a file + * @param pdata Pointer to user data (pointer to file_desc + * corresponding to a file) + * Returns PIO_NOERR on success, a pio error code otherwise + */ +int pio_file_async_pend_op_wait(void *pdata) +{ + int ret; + + file_desc_t *file = static_cast(pdata); + assert(file); + + if(file->async_pend_ops.empty()) { return PIO_NOERR; } + + /* We only wait for pending pnetcdf writes. So the caller + * needs to make sure that no data rearrangement ops are + * pending */ + ret = pio_file_async_pend_ops_kwait(file, SPIO_Util::Async_op::Op_type::SPIO_ASYNC_PNETCDF_WRITE_OP); + if(ret != PIO_NOERR){ + return pio_err(file->iosystem, file, ret, __FILE__, __LINE__, + "Internal error while waiting for pending asynchronous write operations on file (%s, ncid=%d) for the PIO_IOTYPE_PNETCDF iotype", + pio_get_fname_from_file(file), file->pio_ncid); + } + + file->wb_pend = 0; + file->npend_ops = 0; + + return PIO_NOERR; +} + +/* Free file_desc and close the file + * @param pdata Pointer to user data (pointer to file_desc + * corresponding to a file) + */ +void pio_file_close_and_free(void *pdata) +{ + int ret; + file_desc_t *file = (file_desc_t *)pdata; + assert(file); + + bool sync_with_ioprocs = false; + ret = spio_hard_closefile(file->iosystem, file, sync_with_ioprocs); + if(ret != PIO_NOERR){ + LOG((1, "Closing file (id=%d) failed (ignoring the error)", file->pio_ncid)); + } +} + + +/* Add an async op to the list of pending ops for an iosystem + * @param iosys Pointer to the iosystem_desc + * @param op_type Type of asynchronous operation added + * @param pdata Pointer to user defined data for this async op + * Returns PIO_NOERR on success, a pio error code otherwise + */ +int pio_iosys_async_pend_op_add(iosystem_desc_t *iosys, + SPIO_Util::Async_op::Op_type op_type, void *pdata) +{ + assert((iosys != NULL) && (pdata != NULL)); + assert(op_type != SPIO_Util::Async_op::Op_type::SPIO_ASYNC_INVALID_OP); + + assert(op_type == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_FILE_WRITE_OPS); + iosys->async_pend_ops.push_back({op_type, pdata, + pio_file_async_pend_op_wait, pio_async_poke_func_unavail, pio_file_close_and_free}); + + return PIO_NOERR; +} + +/* Add an async op to the list of pending ops in the thread pool + * @param iosys Pointer to the iosystem_desc + * @param op_type Type of asynchronous operation added + * @param pdata Pointer to user defined data for this async op + * Returns PIO_NOERR on success, a pio error code otherwise + */ +int pio_tpool_async_pend_op_add(iosystem_desc_t *iosys, + SPIO_Util::Async_op::Op_type op_type, void *pdata) +{ + int ret; + assert(iosys && pdata); + assert(op_type == SPIO_Util::Async_op::Op_type::SPIO_ASYNC_FILE_WRITE_OPS); + + SPIO_Util::Async_op op = {op_type, pdata, pio_file_async_pend_op_wait, + pio_async_poke_func_unavail, pio_file_close_and_free}; + + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(iosys, NULL, ret, __FILE__, __LINE__, + "Internal error while adding asynchronous pending operation to the thread pool (iosystem = %d). Adding the asynchronous operation failed", iosys->iosysid); + } + + return PIO_NOERR; +} + +int pio_iosys_async_file_close_op_wait(void *pdata) +{ + int ret = PIO_NOERR; + file_desc_t *file = static_cast(pdata); + assert(file); + + //file->npend_ops--; + if(file->npend_ops == 0){ + ret = spio_hard_closefile(file->iosystem, file, false); + if(ret != PIO_NOERR){ + return pio_err(file->iosystem, file, ret, __FILE__, __LINE__, + "Closing file (%s, ncid=%d) asynchronously failed", + pio_get_fname_from_file(file), file->pio_ncid); + } + } + else{ + ret = pio_iosys_async_file_close_op_add(file); + if(ret != PIO_NOERR){ + return pio_err(file->iosystem, file, ret, __FILE__, __LINE__, + "Requeuing async op to close file (%s, ncid=%d) asynchronously failed", + pio_get_fname_from_file(file), file->pio_ncid); + } + } + + SPIO_Util::GVars::npend_hdf5_async_ops--; + + return PIO_NOERR; +} + +void pio_iosys_async_file_close_op_free_no_op(void *pdata) +{ + /* The file and associated structures should be freed during a "hard close" + * in the wait function + * Nothing to do here + */ + return; +} + +int pio_iosys_async_file_close_op_add(file_desc_t *file) +{ + int ret = PIO_NOERR; + + assert(file); + + /* Create async task */ + SPIO_Util::Async_op op = {SPIO_Util::Async_op::Op_type::SPIO_ASYNC_FILE_CLOSE_OP, + static_cast(file), + pio_iosys_async_file_close_op_wait, + pio_async_poke_func_unavail, + pio_iosys_async_file_close_op_free_no_op}; + + //file->npend_ops++; + SPIO_Util::GVars::npend_hdf5_async_ops++; + + /* Get the mt queue and queue the async task */ + ret = pio_async_tpool_op_add(op); + if(ret != PIO_NOERR){ + LOG((1, "Adding file pending ops to tpool failed, ret = %d", ret)); + return pio_err(file->iosystem, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Queuing asynchronous op/task for closing file (%s, ncid=%d) using PIO_IOTYPE_HDF5x failed. Adding async op to thread pool failed", + pio_get_fname_from_file(file), file->pio_ncid); + } + + return PIO_NOERR; +} diff --git a/src/clib/core/progress_engine/spio_async_utils.hpp b/src/clib/core/progress_engine/spio_async_utils.hpp new file mode 100644 index 00000000000..b49543a7bad --- /dev/null +++ b/src/clib/core/progress_engine/spio_async_utils.hpp @@ -0,0 +1,33 @@ +#ifndef _SPIO_ASYNC_UTILS_HPP_ +#define _SPIO_ASYNC_UTILS_HPP_ + +#include +#include +#include +#include +/* FIXME: Avoid including HDF5 async hdr here */ +#include "spio_async_hdf5_utils.hpp" +#include "spio_async_op.hpp" + +int pio_async_poke_func_unavail(void *pdata, bool &flag); +int pio_file_async_pend_ops_wait(file_desc_t *file); +int pio_file_async_pend_op_add(file_desc_t *file, + SPIO_Util::Async_op::Op_type op_type, void *pdata); +int pio_var_rearr_and_cache(file_desc_t *file, var_desc_t *vdesc, + io_desc_t *iodesc, void *buf, + size_t buflen, void *fillvalue, int rec_num); +int pio_var_rem_cache_data(var_desc_t *vdesc, int rec_num, viobuf_cache_t **pviobuf); +int pio_file_compact_and_copy_rearr_data(void *dest, size_t dest_sz, + io_desc_t *iodesc, file_desc_t *file, const int *varids, + const int *frames, int nvars); + +int pio_iosys_async_pend_op_add(iosystem_desc_t *iosys, + SPIO_Util::Async_op::Op_type op_type, void *pdata); +#if PIO_USE_ASYNC_WR_THREAD +int pio_tpool_async_pend_op_add(iosystem_desc_t *iosys, + SPIO_Util::Async_op::Op_type op_type, void *pdata); +#endif // PIO_USE_ASYNC_WR_THREAD +int pio_iosys_async_file_close_op_add(file_desc_t *file); + +#endif // _SPIO_ASYNC_UTILS_HPP_ + diff --git a/src/clib/core/rearr/CMakeLists.txt b/src/clib/core/rearr/CMakeLists.txt new file mode 100644 index 00000000000..742fe8da16f --- /dev/null +++ b/src/clib/core/rearr/CMakeLists.txt @@ -0,0 +1,21 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C Core (Data Rearrangers)... =====") + +set (spio_core_rearr_src + pio_rearrange.cpp + pio_rearr_utils.cpp + pio_rearr_contig.cpp + spio_rearrange_any.cpp) + +add_library (spio_core_rearr OBJECT + ${spio_core_rearr_src}) + +target_include_directories(spio_core_rearr + PUBLIC . + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/../.. ${CMAKE_CURRENT_SOURCE_DIR}/../../util) + +target_link_libraries(spio_core_rearr + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/core/rearr/pio_rearr_contig.cpp b/src/clib/core/rearr/pio_rearr_contig.cpp new file mode 100644 index 00000000000..4138a412eb6 --- /dev/null +++ b/src/clib/core/rearr/pio_rearr_contig.cpp @@ -0,0 +1,1450 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pio_rearr_contig.hpp" +#include "pio_rearr_utils.hpp" +#include "spio_dbg_utils.hpp" +#include "spio_gptl_utils.hpp" + +namespace SPIO_Util{ + template + static void exscan(CIter ibegin, CIter iend, Iter obegin, + typename std::iterator_traits::value_type ival, + std::function::value_type + (typename std::iterator_traits::value_type)> scale_func) + { + typename std::iterator_traits::value_type psum = ival; + for(;ibegin != iend; ++ibegin, ++obegin){ + *obegin = scale_func(psum); + psum += *ibegin; + } + } + + template + static void exscan(CIter ibegin, CIter iend, Iter obegin, + typename std::iterator_traits::value_type ival) + { + typename std::iterator_traits::value_type psum = ival; + for(;ibegin != iend; ++ibegin, ++obegin){ + *obegin = psum; + psum += *ibegin; + } + } + + template + static void vec_map_sort(std::vector &v, const std::vector &map) + { + std::vector vtmp(v); + assert(v.size() == map.size()); + + for(std::size_t i = 0; i < vtmp.size(); i++){ + v[map[i]] = vtmp[i]; + } + } +} // namespace SPIO_Util + +int SPIO::DataRearr::Contig_rearr::init(int pio_type, + const PIO_Offset *compmap, std::size_t compmap_sz, + const int *gdimlen, int ndims, io_desc_t *iodesc) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer contig_init_timer("PIO:Contig_rearr::init"); + + assert(ios_); + assert(ndims > 0); + + lcompmap_sz_ = compmap_sz; + gdecomp_sz_ = std::accumulate(gdimlen, gdimlen + ndims, 1, std::multiplies()); + std::copy(gdimlen, gdimlen + ndims, std::back_inserter(gdimlen_)); + dim_chunk_sz_.resize(ndims); + dim_chunk_sz_[ndims - 1] = 1; + for(int i = ndims - 2; i >= 0; i--){ + dim_chunk_sz_[i] = dim_chunk_sz_[i + 1] * gdimlen_[i + 1]; + } + + elem_pio_type_ = pio_type; + ret = find_mpi_type(elem_pio_type_, &elem_mpi_type_, &elem_mpi_type_sz_); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to find MPI type corresponding to PIO type (%d)", pio_type); + } + + /* Calculate gather scatter info for aggregation. Each I/O node is an aggregator */ + + /* Create aggregate comm, rank = 0 in each aggregate comm is an I/O process */ + ret = create_agg_comm(); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create aggregator comm (iosysid = %d)", ios_->iosysid); + } + + /* Create rearranger comm, dup of I/O comm - contains aggregator/IO procs */ + ret = create_rearr_comm(); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create rearranger comm (iosysid = %d)", ios_->iosysid); + } + + set_rearr_comm_iochunk_sz(ndims, gdimlen); + + /* Aggregate compmaps into the aggregating procs */ + std::size_t non_fval_compmap_sz = 0; + std::vector aggcompmap; + std::vector non_fval_compmap_counts, non_fval_compmap_displs; + std::vector aggcompmap_counts, aggcompmap_displs; + ret = aggregate_compmap(compmap, compmap_sz, non_fval_compmap_sz, non_fval_compmap_counts, non_fval_compmap_displs, + aggcompmap, aggcompmap_counts, aggcompmap_displs); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to aggregate local compmaps (iosysid = %d)", ios_->iosysid); + } + + /* Each MPI process sends compmap_sz elements to the aggregating node */ + std::vector to_proc; + ret = get_rearr_toproc_map(aggcompmap, to_proc); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create to proc map (iosysid = %d)", ios_->iosysid); + } + + /* Set up the types and info required to aggregate data */ + ret = setup_data_agg_info(compmap, compmap_sz, non_fval_compmap_sz, non_fval_compmap_counts, non_fval_compmap_displs, + aggcompmap, aggcompmap_counts, aggcompmap_displs, to_proc); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to setup data aggregation (iosysid = %d)", ios_->iosysid); + } + + /* Set up the types and info required to rearrange data */ + ret = setup_data_rearr_info(aggcompmap, to_proc, gdimlen, ndims); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to setup data rearrangement (iosysid = %d)", ios_->iosysid); + } + + is_init_ = true; + + return ret; +} + +/* +void SPIO::DataRearr::Contig_rearr::get_contig_ranges_from_off_range(std::vector > &contig_map_ranges, PIO_Offset start_off, PIO_Offset count, std::size_t dim_idx) const +{ + assert((dim_idx >= 0) && (dim_idx < dim_chunk_sz_.size())); + if(count == 0) return; + + PIO_Offset end_off = start_off + count; + PIO_Offset cur_start_off = start_off; + for(std::size_t idx = dim_idx; idx < dim_chunk_sz_.size(); idx++){ + PIO_Offset off_from_last_dim_mult = cur_start_off % dim_chunk_sz_[idx]; + PIO_Offset start_off_dim_mult = cur_start_off; + if(off_from_last_dim_mult != 0){ + start_off_dim_mult = cur_start_off + (dim_chunk_sz_[idx] - off_from_last_dim_mult); + } + if(start_off_dim_mult < end_off){ + PIO_Offset ndim_idx = (end_off - start_off_dim_mult) / dim_chunk_sz_[idx]; + if(ndim_idx > 0){ + if(off_from_last_dim_mult > 0){ + get_contig_ranges_from_off_range(contig_map_ranges, cur_start_off, (dim_chunk_sz_[idx] - off_from_last_dim_mult), dim_idx + 1); + } + PIO_Offset cur_range_count = ndim_idx * dim_chunk_sz_[idx]; + contig_map_ranges.push_back(std::make_pair(start_off_dim_mult, cur_range_count)); + cur_start_off = start_off_dim_mult + cur_range_count; + assert(cur_start_off <= end_off); + count -= (cur_start_off - start_off); + assert(count >= 0); + if(count > 0){ + get_contig_ranges_from_off_range(contig_map_ranges, cur_start_off, count, dim_idx + 1); + } + break; + } + } + } +} +*/ +void SPIO::DataRearr::Contig_rearr::get_contig_ranges_from_off_range(std::vector > &contig_map_ranges, + PIO_Offset start_off, PIO_Offset count) +{ + std::size_t last_contig_dim = 0; + std::size_t ndims = gdimlen_.size(); + std::vector start_dim_idx(ndims, 0); + + while(count > 0){ + convert_off_to_start_dim_idx(start_off, start_dim_idx, last_contig_dim); + PIO_Offset last_contig_dim_nchunks = gdimlen_[last_contig_dim] - start_dim_idx[last_contig_dim]; + PIO_Offset dim_nchunks = count / dim_chunk_sz_[last_contig_dim]; + PIO_Offset range_sz = 0; + if(dim_nchunks > 0){ + dim_nchunks = std::min(dim_nchunks, last_contig_dim_nchunks); + range_sz = dim_nchunks * dim_chunk_sz_[last_contig_dim]; + } + else{ + for(std::size_t idx = last_contig_dim + 1; idx < ndims; idx++){ + dim_nchunks = count / dim_chunk_sz_[idx]; + if(dim_nchunks > 0){ + dim_nchunks = std::min(dim_nchunks, static_cast(gdimlen_[idx])); + range_sz = dim_nchunks * dim_chunk_sz_[idx]; + break; + } + } + } + contig_map_ranges.push_back(std::make_pair(start_off, range_sz)); + start_off += range_sz; + count -= range_sz; + } + + assert(count == 0); +} + +std::size_t SPIO::DataRearr::Contig_rearr::get_ncontig_ranges_from_off_range(PIO_Offset start_off, PIO_Offset count) const +{ + std::size_t nranges = 0; + std::size_t last_contig_dim = 0; + std::size_t ndims = gdimlen_.size(); + std::vector start_dim_idx(ndims, 0); + + while(count > 0){ + convert_off_to_start_dim_idx(start_off, start_dim_idx, last_contig_dim); + PIO_Offset last_contig_dim_nchunks = gdimlen_[last_contig_dim] - start_dim_idx[last_contig_dim]; + PIO_Offset dim_nchunks = count / dim_chunk_sz_[last_contig_dim]; + PIO_Offset range_sz = 0; + if(dim_nchunks > 0){ + dim_nchunks = std::min(dim_nchunks, last_contig_dim_nchunks); + range_sz = dim_nchunks * dim_chunk_sz_[last_contig_dim]; + } + else{ + for(std::size_t idx = last_contig_dim + 1; idx < ndims; idx++){ + dim_nchunks = count / dim_chunk_sz_[idx]; + if(dim_nchunks > 0){ + dim_nchunks = std::min(dim_nchunks, static_cast(gdimlen_[idx])); + range_sz = dim_nchunks * dim_chunk_sz_[idx]; + break; + } + } + } + nranges++; + start_off += range_sz; + count -= range_sz; + } + + assert(count == 0); + + return nranges; +} + +std::vector > SPIO::DataRearr::Contig_rearr::get_rearr_decomp_map_contig_ranges(int iorank) +{ + assert(gdecomp_sz_ > 0); + assert(rearr_comm_iochunk_sz_ > 0); + + PIO_Offset iochunk = rearr_comm_iochunk_sz_; + PIO_Offset start_off = iorank * iochunk; + PIO_Offset end_off = start_off + iochunk; + if(iorank == (rearr_comm_sz_ - 1)){ + end_off = gdecomp_sz_; + } + + assert((gdimlen_.size() > 0) && (dim_chunk_sz_.size() == gdimlen_.size()) && + (start_off >= 0) && (end_off >= start_off)); + + std::vector > contig_map_ranges; + get_contig_ranges_from_off_range(contig_map_ranges, start_off, end_off - start_off); + + /* + int ndims = static_cast(gdimlen_.size()); + + PIO_Offset cur_tot_count = end_off - start_off; + + if(cur_tot_count == 0) return contig_map_ranges; + + // First get start_off to a multiple of last dim len + // e.g. v[z][y][x], get range to get to multiple of x + PIO_Offset cur_range_start = start_off; + PIO_Offset cur_range_count = 0; + PIO_Offset off_from_last_dim_mult = cur_range_start % gdimlen_[ndims - 1]; + if(off_from_last_dim_mult != 0){ + cur_range_count = gdimlen_[ndims - 1] - off_from_last_dim_mult; + if(start_off + cur_range_count > end_off){ + cur_range_count = end_off - start_off; + } + contig_map_ranges.push_back(std::make_pair(cur_range_start, cur_range_count)); + + cur_range_start += cur_range_count; + cur_tot_count -= cur_range_count; + } + + if(cur_tot_count == 0) return contig_map_ranges; + + // Break/partition the cur_range_start to end_off to contiguous dim chunks + // e.g. v[z][y][x], break up remaining range to ranges of sizes y*x, x, 1 + + PIO_Offset cur_range_end = cur_range_start; + for(int i = 0; i < ndims; i++){ + cur_range_end = dim_chunk_sz_[i] * (end_off / dim_chunk_sz_[i]); + if(cur_range_end != 0){ + cur_range_count = cur_range_end - cur_range_start; + if(cur_range_count > 0){ + contig_map_ranges.push_back(std::make_pair(cur_range_start, cur_range_count)); + + cur_range_start += cur_range_count; + cur_tot_count -= cur_range_count; + } + } + if(cur_tot_count == 0) break; + } + + assert(cur_tot_count == 0); + */ + + return contig_map_ranges; +} + +// FIXME: Change start/count (on the caller side the list with C ptrs for start/count) to be +// vectors +void SPIO::DataRearr::Contig_rearr::off_range_to_dim_range(PIO_Offset start_off, PIO_Offset count_off, + PIO_Offset *start, PIO_Offset *count) const +{ + assert((start_off >= 0) && (count_off >= 0)); + assert(start && count); + + convert_off_to_start_dim_idx(start_off, start); + convert_off_to_count_dim_idx(count_off, count); +} + +int SPIO::DataRearr::Contig_rearr::rearrange_comp2io(const void *sbuf, std::size_t sbuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars) +{ + int ret = PIO_NOERR; + assert(is_init_); + + std::size_t agg_data_nelems = agg_iochunk_sz_; + + /* Aggregate data */ + void *agg_buf = NULL; + std::size_t agg_buf_sz = nvars * agg_data_nelems * elem_mpi_type_sz_; + if(is_agg_root_){ + agg_buf = malloc(agg_buf_sz); + if(!agg_buf){ + return pio_err(ios_, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Internal error while aggregating data from compute processes to aggregating processes (PIO_REARR_CONTIG). Unable to allocate memory to recv data (%lld bytes) for data aggregation (iosysid = %d)", static_cast (agg_buf_sz), ios_->iosysid); + } + } + + ret = aggregate_data(sbuf, sbuf_sz, agg_buf, agg_buf_sz, nvars); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while aggregating data from compute processes to aggregating processes (PIO_REARR_CONTIG). Data aggregation failed(aggregating buffer size = %lld bytes, receive buffer size = %lld bytes, nvars = %d, iosysid = %d)", static_cast(agg_buf_sz), + static_cast(rbuf_sz), nvars, ios_->iosysid); + } + + /* Rearrange data - aggregate processes to rearrange processes */ + ret = rearrange_data(agg_buf, agg_buf_sz, rbuf, rbuf_sz, nvars, true); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while rearranging data between IO/aggregating processes (PIO_REARR_CONTIG). Data rearrangement failed(aggregating buffer size = %lld bytes, receive buffer size = %lld bytes, nvars = %d, iosysid = %d)", static_cast(agg_buf_sz), + static_cast(rbuf_sz), nvars, ios_->iosysid); + } + + if(agg_buf){ + free(agg_buf); + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::aggregate_data(const void *sbuf, std::size_t sbuf_sz, + void *abuf, std::size_t abuf_sz, int nvars) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer contig_agg_timer("PIO:Contig_rearr::aggregate_data"); + + assert(is_init_); + + /* Gather data from compute processes in this aggregating comm to the aggregating/IO process */ + MPI_Datatype agg_stype_nvars = MPI_DATATYPE_NULL; + std::vector agg_rtypes_nvars; + + std::size_t agg_data_nelems = agg_iochunk_sz_; + assert(abuf_sz == nvars * agg_data_nelems * elem_mpi_type_sz_); + +// std::cout << "DBG: sbuf[], abuf[] before gather :" << abuf_sz << "," << nvars << "," << elem_mpi_type_sz_ << ":" << std::flush; +// SPIO_Util::Dbg_Util::print_1dvec((int *)sbuf, (int *)((char *)sbuf + sbuf_sz * nvars)); +// SPIO_Util::Dbg_Util::print_1dvec((int *)abuf, (int *)((char *)abuf + abuf_sz)); + + if(nvars > 1){ + /* We are aggregating a block of variables */ + /* The stride between each block - each variable - is the total size of the data aggregated from + * all compute processes in the aggregator. On the compute procs its a contiguous block of data + * - composed on nvars blocks, such that each block is data corresponding to a single var + */ + if(is_agg_root_){ + MPI_Aint stride_between_vars = static_cast(agg_data_nelems * elem_mpi_type_sz_); + for(std::size_t i = 0; i < agg_gs_info_.rtypes.size(); i++){ + MPI_Datatype agg_rtype = MPI_DATATYPE_NULL; + if(agg_gs_info_.rtypes[i] != MPI_DATATYPE_NULL){ + ret = MPI_Type_hvector(nvars, 1, stride_between_vars, agg_gs_info_.rtypes[i], &agg_rtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&agg_rtype); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while aggregating data from compute processes to aggregating processes (PIO_REARR_CONTIG). Unable to create vector types to recv data for data aggregation (iosysid = %d)", ios_->iosysid); + } + } + agg_rtypes_nvars.push_back(agg_rtype); + } + } + if(agg_gs_info_.stype != MPI_DATATYPE_NULL){ + MPI_Aint stride_between_vars = static_cast(sbuf_sz / nvars); + ret = MPI_Type_hvector(nvars, 1, stride_between_vars, agg_gs_info_.stype, &agg_stype_nvars); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&agg_stype_nvars); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while aggregating data from compute processes to aggregating processes (PIO_REARR_CONTIG). Unable to create vector types to send data for data aggregation (iosysid = %d)", ios_->iosysid); + } + } + } + + int scount = (agg_gs_info_.stype != MPI_DATATYPE_NULL) ? 1 : 0; + + ret = SPIO_Util::Rearr_Util::gatherw(sbuf, scount, + (nvars > 1) ? agg_stype_nvars : agg_gs_info_.stype, + abuf, agg_gs_info_.rcounts, agg_data_byte_displs_, + (nvars > 1) ? agg_rtypes_nvars : agg_gs_info_.rtypes, + 0, agg_comm_, NULL); + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while aggregating data from compute processes to aggregating processes (PIO_REARR_CONTIG). Unable to gather data during data aggregation (iosysid = %d)", ios_->iosysid); + } + +// if(is_agg_root_){ +// std::cout << "DBG: sbuf[], abuf[] after gather :\n" << std::flush; +// SPIO_Util::Dbg_Util::print_1dvec((int *)sbuf, (int *)((char *)sbuf + sbuf_sz * nvars)); +// SPIO_Util::Dbg_Util::print_1dvec((int *)abuf, (int *)((char *)abuf + abuf_sz)); +// } + + if(agg_stype_nvars != MPI_DATATYPE_NULL){ + MPI_Type_free(&agg_stype_nvars); + } + + for(std::size_t i = 0; i < agg_rtypes_nvars.size(); i++){ + if(agg_rtypes_nvars[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(agg_rtypes_nvars[i])); + } + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::disperse_data(const void *abuf, std::size_t abuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer contig_dis_timer("PIO:Contig_rearr::disperse_data"); + + assert(is_init_); + + /* When scattering/dispersing data from aggregate process to compute processes, + * agg_gs_info_.stype, the send type used for sending data to aggregating processes during + * aggregation, is now the recv type + * Similarly agg_gs_info_.rtypes, the recv types used for aggregating data on the aggregating + * process from the compute processes, is now the type use to send/disperse data + */ + MPI_Datatype dis_rtype = agg_gs_info_.stype; + MPI_Datatype dis_rtype_nvars = MPI_DATATYPE_NULL; + std::vector dis_stypes_nvars; + + std::size_t agg_data_nelems = agg_iochunk_sz_; + assert(abuf_sz == nvars * agg_data_nelems * elem_mpi_type_sz_); + + if(nvars > 1){ + /* We are dispersing a block of variables */ + /* The stride between each block - each variable - is the total size of the data aggregated from + * all compute processes in the aggregator. On the compute procs its a contiguous block of data + * - composed on nvars blocks, such that each block is data corresponding to a single var + */ + if(is_agg_root_){ + MPI_Aint stride_between_vars = static_cast(agg_data_nelems * elem_mpi_type_sz_); + for(std::size_t i = 0; i < agg_gs_info_.rtypes.size(); i++){ + MPI_Datatype dis_stype = MPI_DATATYPE_NULL; + if(agg_gs_info_.rtypes[i] != MPI_DATATYPE_NULL){ + ret = MPI_Type_hvector(nvars, 1, stride_between_vars, agg_gs_info_.rtypes[i], &dis_stype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&dis_stype); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while dispersing data from aggregating processes to compute processes (PIO_REARR_CONTIG). Unable to create vector types to send data during data dispersion (iosysid = %d)", ios_->iosysid); + } + } + dis_stypes_nvars.push_back(dis_stype); + } + } + if(agg_gs_info_.stype != MPI_DATATYPE_NULL){ + MPI_Aint stride_between_vars = static_cast(rbuf_sz / nvars); + ret = MPI_Type_hvector(nvars, 1, stride_between_vars, agg_gs_info_.stype, &dis_rtype_nvars); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&dis_rtype_nvars); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while dispersing data from aggregating processes to compute processes (PIO_REARR_CONTIG). Unable to create vector types to recv data for data dispersion (iosysid = %d)", ios_->iosysid); + } + } + } + + int rcount = (dis_rtype != MPI_DATATYPE_NULL) ? 1 : 0; + + //std::cout << "DBG: abuf[], rbuf[] before final scatter:\n" << std::flush; + //SPIO_Util::Dbg_Util::print_1dvec((double *)abuf, (double *)((char *)abuf + abuf_sz)); + //SPIO_Util::Dbg_Util::print_1dvec((double *)rbuf, (double *)((char *)rbuf + rbuf_sz)); + ret = SPIO_Util::Rearr_Util::scatterw(abuf, agg_gs_info_.rcounts, + agg_data_byte_displs_, + (nvars > 1) ? dis_stypes_nvars : agg_gs_info_.rtypes, + rbuf, rcount, + (nvars > 1) ? dis_rtype_nvars : dis_rtype, + 0, agg_comm_, NULL); + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while dispersing data from aggregating processes to compute processes (PIO_REARR_CONTIG). Unable to disperse/scatter data during data dispersion (iosysid = %d)", ios_->iosysid); + } + + //std::cout << "DBG: rbuf[] after final scatter:\n" << std::flush; + //SPIO_Util::Dbg_Util::print_1dvec((double *)rbuf, (double *)((char *)rbuf + rbuf_sz)); + if(dis_rtype_nvars != MPI_DATATYPE_NULL){ + MPI_Type_free(&dis_rtype_nvars); + } + + for(std::size_t i = 0; i < dis_stypes_nvars.size(); i++){ + if(dis_stypes_nvars[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(dis_stypes_nvars[i])); + } + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::rearrange_io2comp(const void *sbuf, std::size_t sbuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars) +{ + int ret = PIO_NOERR; + assert(is_init_); + + std::size_t agg_data_nelems = agg_iochunk_sz_; + + /* Disperse/scatter data */ + void *agg_buf = NULL; + std::size_t agg_buf_sz = nvars * agg_data_nelems * elem_mpi_type_sz_; + if(is_agg_root_){ + agg_buf = malloc(agg_buf_sz); + if(!agg_buf){ + return pio_err(ios_, NULL, PIO_ENOMEM, __FILE__, __LINE__, + "Internal error while dispersing data from aggregating processes to compute processes (PIO_REARR_CONTIG). Unable to allocate memory to store data (%lld bytes) for data dispersion (iosysid = %d)", static_cast (agg_buf_sz), ios_->iosysid); + } + } + + /* Rearrange data - rearrange processes to aggregate processes */ + ret = rearrange_data(sbuf, sbuf_sz, agg_buf, agg_buf_sz, nvars, false); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while rearranging data between IO/aggregating processes (PIO_REARR_CONTIG). Data rearrangement failed(aggregating buffer size = %lld bytes, receive buffer size = %lld bytes, nvars = %d, iosysid = %d)", static_cast(agg_buf_sz), + static_cast(rbuf_sz), nvars, ios_->iosysid); + } + + ret = disperse_data(agg_buf, agg_buf_sz, rbuf, rbuf_sz, nvars); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while dispersing data from aggregating processes to compute processes (PIO_REARR_CONTIG). Data dispersion/scatter failed(aggregating buffer size = %lld bytes, receive buffer size = %lld bytes, nvars = %d, iosysid = %d)", static_cast(agg_buf_sz), + static_cast(rbuf_sz), nvars, ios_->iosysid); + } + + if(agg_buf){ + free(agg_buf); + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::finalize(void ) +{ + int ret = PIO_NOERR; + + /* Free the MPI datatypes used for aggregation and rearrangement */ + /* We ignore the return values from free()s, since we want to free as much as possible here */ + if(agg_gs_info_.stype != MPI_DATATYPE_NULL){ + MPI_Type_free(&(agg_gs_info_.stype)); + } + for(std::size_t i = 0; i < agg_gs_info_.rtypes.size(); i++){ + if(agg_gs_info_.rtypes[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(agg_gs_info_.rtypes[i])); + } + } + + for(std::size_t i = 0; i < rearr_alltoall_info_.stypes.size(); i++){ + if(rearr_alltoall_info_.stypes[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(rearr_alltoall_info_.stypes[i])); + } + } + + for(std::size_t i = 0; i < rearr_alltoall_info_.rtypes.size(); i++){ + if(rearr_alltoall_info_.rtypes[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(rearr_alltoall_info_.rtypes[i])); + } + } + + /* Free aggregate and rearranger comms */ + if(agg_comm_ != MPI_COMM_NULL){ + MPI_Comm_free(&agg_comm_); + } + if(rearr_comm_ != MPI_COMM_NULL){ + MPI_Comm_free(&rearr_comm_); + } + + is_init_ = false; + + return ret; +} + +inline int SPIO::DataRearr::Contig_rearr::create_agg_comm(void ) +{ + int ret = MPI_SUCCESS; + + assert(ios_); + + /* Divide union comm into (ios_->num_iotasks) groups */ + int agg_range_sz = ios_->num_uniontasks/ios_->num_iotasks; + /* Ensure that each group/color gets an I/O process - which is also the aggregating process */ + int color = (ios_->ioproc) ? ios_->io_rank : (ios_->union_rank/agg_range_sz); + /* Put the extra set of procs, after evenly dividing procs among I/O procs, with the last I/O proc */ + color = (color >= ios_->num_iotasks) ? (ios_->num_iotasks - 1) : color; + + /* Each I/O process is rank 0 in the new aggregator comm, and is also the aggregating proc */ + int key = (ios_->ioproc) ? 0 : 1; + + ret = MPI_Comm_split(ios_->union_comm, color, key, &agg_comm_); + if(ret == MPI_SUCCESS){ + is_agg_root_ = (key == 0) ? true : false; + ret = MPI_Comm_size(agg_comm_, &agg_comm_sz_); + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::aggregate_compmap(const PIO_Offset *lcompmap, std::size_t lcompmap_sz, + std::size_t &non_fval_lcompmap_sz, + std::vector &non_fval_lcompmap_counts, + std::vector &non_fval_lcompmap_displs, + std::vector &gcompmap, + std::vector &gcompmap_counts, + std::vector &gcompmap_displs) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer ftimer("PIO:Contig_rearr::aggregate_compmap"); + assert(ios_ && (agg_comm_ != MPI_COMM_NULL)); + + /* Get the compmap sizes from all procs in this aggregation comm */ + if(is_agg_root_){ + gcompmap_counts.resize(agg_comm_sz_); + gcompmap_displs.resize(agg_comm_sz_); + } + + // We only send/recv non-fillval elements & map values + get_non_fval_lcompmap_counts_displs(lcompmap, lcompmap_sz, + non_fval_lcompmap_sz, non_fval_lcompmap_counts, non_fval_lcompmap_displs); + + //std::cout << "DBG: non_fval_lcompmap_sz = " << non_fval_lcompmap_sz << "\n" << std::flush; + int non_fval_lcompmap_sz_int = static_cast(non_fval_lcompmap_sz); + ret = MPI_Gather(&non_fval_lcompmap_sz_int, 1, MPI_INT, gcompmap_counts.data(), 1, MPI_INT, 0, agg_comm_); + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to gather local compmap sizes (iosysid=%d)", ios_->iosysid); + } + + if(is_agg_root_){ + int total_agg_compmap_sz = std::accumulate(gcompmap_counts.cbegin(), gcompmap_counts.cend(), 0); + gcompmap.resize(total_agg_compmap_sz); + + /* Calculate the displacements for local compmaps in the global compmap, gcompmap */ + SPIO_Util::exscan(gcompmap_counts.cbegin(), gcompmap_counts.cend(), gcompmap_displs.begin(), 0); + int elem_type_sz = this->elem_mpi_type_sz_; + agg_data_byte_displs_.resize(gcompmap_displs.size()); + std::transform(gcompmap_displs.cbegin(), gcompmap_displs.cend(), agg_data_byte_displs_.begin(), + [elem_type_sz](int i) { return i * elem_type_sz; } ); + } + + /* Gather local compmaps */ + MPI_Datatype non_fval_lcompmap_stype = MPI_DATATYPE_NULL; + int non_fval_lcompmap_scount = 0; + if(non_fval_lcompmap_sz > 0){ + ret = MPI_Type_indexed(static_cast(non_fval_lcompmap_counts.size()), + non_fval_lcompmap_counts.data(), non_fval_lcompmap_displs.data(), + PIO_OFFSET, &non_fval_lcompmap_stype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&non_fval_lcompmap_stype); + } + + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create/commit indexed type to send local compmaps (iosysid=%d)", ios_->iosysid); + } + non_fval_lcompmap_scount = 1; + } + + // Some MPI implementations do not like MPI_DATATYPE_NULL even when count is 0 + MPI_Datatype stype = (non_fval_lcompmap_stype != MPI_DATATYPE_NULL) ? non_fval_lcompmap_stype : MPI_CHAR; + ret = MPI_Gatherv(lcompmap, non_fval_lcompmap_scount, stype, + gcompmap.data(), gcompmap_counts.data(), gcompmap_displs.data(), PIO_OFFSET, + 0, agg_comm_); + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to gather local compmaps (iosysid=%d)", ios_->iosysid); + } + + if(non_fval_lcompmap_stype != MPI_DATATYPE_NULL){ + MPI_Type_free(&non_fval_lcompmap_stype); + } + +// std::cout << "DBG: Sent/Gathered lcompmap/gcompmap : " << std::flush; +// SPIO_Util::Dbg_Util::print_1dvec(lcompmap, lcompmap + lcompmap_sz); +// SPIO_Util::Dbg_Util::print_1dvec(gcompmap); + + return ret; +} + +void SPIO::DataRearr::Contig_rearr::get_non_fval_lcompmap_counts_displs(const PIO_Offset *lcompmap, + std::size_t lcompmap_sz, + std::size_t &non_fval_lcompmap_sz, + std::vector &non_fval_lcompmap_counts, + std::vector &non_fval_lcompmap_displs) +{ + non_fval_lcompmap_sz = 0; + if((lcompmap_sz == 0) || (lcompmap == NULL)){ + return; + } + + /* Compmap values that are -1 indicate fillvalues, we do not aggregate/disperse these values */ + const PIO_Offset FILLVAL = -1; + const int INVALID_DISP = -1; + /* The count and disp in lcompmap for the current range of non-fillvalues */ + std::pair non_fval_lcompmap_cur_range = {0, INVALID_DISP}; + for(std::size_t i = 0; i < lcompmap_sz; i++){ + if(lcompmap[i] != FILLVAL){ + non_fval_lcompmap_sz++; + non_fval_lcompmap_cur_range.first++; + if(non_fval_lcompmap_cur_range.second == INVALID_DISP){ + non_fval_lcompmap_cur_range.second = static_cast(i); + } + } + else{ + if(non_fval_lcompmap_cur_range.first > 0){ + non_fval_lcompmap_counts.push_back(non_fval_lcompmap_cur_range.first); + non_fval_lcompmap_displs.push_back(non_fval_lcompmap_cur_range.second); + } + non_fval_lcompmap_cur_range = {0, INVALID_DISP}; + } + } + if(non_fval_lcompmap_cur_range.first > 0){ + non_fval_lcompmap_counts.push_back(non_fval_lcompmap_cur_range.first); + non_fval_lcompmap_displs.push_back(non_fval_lcompmap_cur_range.second); + } +} + +int SPIO::DataRearr::Contig_rearr::setup_data_agg_info(const PIO_Offset *lcompmap, + std::size_t lcompmap_sz, + std::size_t non_fval_lcompmap_sz, + const std::vector &non_fval_lcompmap_counts, + const std::vector &non_fval_lcompmap_displs, + const std::vector &gcompmap, + const std::vector &gcompmap_counts, + const std::vector &gcompmap_displs, + const std::vector &to_proc) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer ftimer("PIO:Contig_rearr::setup_data_agg_info"); + + assert(ios_); + /* Setup gather scatter info for sending/receiving data from compute procs to + * aggregate/IO procs + * Sending/receiving 1 contiguous array of lcompmap_sz length + */ + ret = init_agg_send_type(lcompmap, non_fval_lcompmap_sz, non_fval_lcompmap_counts, non_fval_lcompmap_displs); + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create/commit indexed type (local compmap size=%d, num of non-fillvals=%d) on compute process for data aggregation (iosysid=%d)", static_cast(lcompmap_sz), static_cast(non_fval_lcompmap_sz), ios_->iosysid); + } + + if(!is_agg_root_){ + /* Non-aggregating processes use the agg_gs_info_.stype to send data to aggregator process + * and receive data from it + */ + return ret; + } + /* Aggregating process, root in aggregator comm, sends a contiguous array of + * lcompmap_sz length, if its also a compute process, but receives data from + * all non-aggregating procs in the aggregator comm in a way that makes it + * easier to rearrange the data with other aggregating procs - data to send to + * each aggregating proc clustered together such that data sent to an aggregating + * proc is sorted based on the compmap + * The recv type will be calculated later in the function, initializing to NULL + */ + + assert(gcompmap.size() == to_proc.size()); + + /* Sort the indices of gcompmap to a view that we need for data rearrangement between + * aggregator/IO procs + */ + /* + std::vector gcompmap_idx(gcompmap.size()); + std::iota(gcompmap_idx.begin(), gcompmap_idx.end(), 0); + + GPTLstart("PIO:Contig_rearr::setup_data_agg_info::sort"); + std::sort(gcompmap_idx.begin(), gcompmap_idx.end(), + [&gcompmap,&to_proc](PIO_Offset a, PIO_Offset b){ + if(to_proc[a] == to_proc[b]){ + // Sort all data to send to each proc + return gcompmap[a] < gcompmap[b]; + } + else{ + // Aggregate all data to send to a proc together + return to_proc[a] < to_proc[b]; + } + }); + GPTLstop("PIO:Contig_rearr::setup_data_agg_info::sort"); + + // Aggregate compmap sorter can be used to sort any user data based on gcompmap + agg_iochunk_sz_ = gcompmap.size(); + agg_compmap_sorter_.resize(agg_iochunk_sz_); + for(std::size_t i = 0; i < gcompmap_idx.size(); i++){ + agg_compmap_sorter_[gcompmap_idx[i]] = i; + } + */ + /* + for(std::size_t i = 0; i < gcompmap_idx.size(); i++){ + if(gcompmap[gcompmap_idx[i]] == -1){ + agg_compmap_sorter_skip_idx_end_ = i + 1; + } + else{ + break; + } + } + + std::cout << "DBG: agg_compmap_sorter_skip_idx_ : " << agg_compmap_sorter_skip_idx_end_ << "\n" << std::flush; + */ +// std::cout << "DBG: agg_compmap_sorter_ : " << std::flush; +// SPIO_Util::Dbg_Util::print_1dvec(agg_compmap_sorter_); + + agg_iochunk_sz_ = gcompmap.size(); + GPTLstart("PIO:Contig_rearr::setup_data_agg_info::sort"); + agg_compmap_sorter_.init(gcompmap.size(), + [&gcompmap,&to_proc](std::size_t a, std::size_t b){ + // Sort all data to send to each proc + return gcompmap[a] < gcompmap[b]; + }); + GPTLstop("PIO:Contig_rearr::setup_data_agg_info::sort"); + + ret = init_agg_recv_types(gcompmap_counts, agg_compmap_sorter_.get_sort_map()); + return ret; +} + +int SPIO::DataRearr::Contig_rearr::init_agg_send_type(const PIO_Offset *lcompmap, + std::size_t lcompmap_sz, + const std::vector &lcompmap_counts, + const std::vector &lcompmap_displs) +{ + int ret = PIO_NOERR; + + assert(ios_); + /* Setup gather scatter info for sending/receiving data from compute procs to + * aggregate/IO procs + * Sending/receiving only non-fillval elements (hence using indexed type instead of a contiguous type) + */ + agg_gs_info_.stype = MPI_DATATYPE_NULL; + if(lcompmap_sz > 0){ + ret = MPI_Type_indexed(static_cast(lcompmap_counts.size()), lcompmap_counts.data(), lcompmap_displs.data(), + elem_mpi_type_, &(agg_gs_info_.stype)); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&(agg_gs_info_.stype)); + } + + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create/commit indexed type on compute process for data aggregation (iosysid=%d)", ios_->iosysid); + } + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::init_agg_recv_types(const std::vector &gcompmap_counts, + const std::vector &compmap_sorter) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer ftimer("PIO:Contig_rearr::init_agg_recv_types"); + /* The compmap_sorter has the indices of the sorted data (based on to_proc & compmap) + * So look for contiguous ranges in compmap_sorter array + * We need to receive data in the aggregating process such that all data being sent + * to another aggregating process, during rearrangement, is contiguous + * compmap_sorter[i] gives the final intended location of data in index i received on + * the aggregating process from compute processes (the final intended location is such + * that all data sent to to_proc[i] are contiguous) + */ + + std::size_t compmap_sorter_idx = 0; + for(std::vector::const_iterator citer = gcompmap_counts.cbegin(); + citer != gcompmap_counts.cend(); ++citer){ + /* During data aggregation we receive, gcompmap_counts[i] elements from rank i */ + /* We need to keep track of data received in the recv type, so ignore procs that + * have nothing to send */ + if(*citer != 0){ + std::vector counts; + std::vector displs; + std::size_t nelems = static_cast(*citer); + /* The dest range - in the recv buffer of agg process - of elements from this compute proc is : + * compmap_sorter[compmap_sorter_idx, compmap_sorter_idx + nelems) + */ + counts.push_back(1); + displs.push_back(static_cast(compmap_sorter[compmap_sorter_idx] - compmap_sorter_idx)); + assert(compmap_sorter_idx + nelems - 1 < compmap_sorter.size()); + for(std::size_t i = 1; i < nelems; i++){ + int disp = static_cast(compmap_sorter[compmap_sorter_idx + i] - compmap_sorter_idx); + if(disp == displs.back() + counts.back()){ + /* The range in the compmap_sorter is contiguous : receiving contiguous data into recv + * buf from this compute proc + */ + counts.back() += 1; + } + else{ + /* Data from this compute proc is not received contiguously, start a new range */ + counts.push_back(1); + displs.push_back(disp); + } + } + + assert(counts.size() == displs.size()); + MPI_Datatype rtype = MPI_DATATYPE_NULL; + ret = MPI_Type_indexed(static_cast(counts.size()), counts.data(), displs.data(), + elem_mpi_type_, &rtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&rtype); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create/commit indexed type (size=%d, nranges=%d) on compute process to recv data for data aggregation (iosysid=%d)", static_cast(nelems), static_cast(counts.size()), ios_->iosysid); + } + agg_gs_info_.rtypes.push_back(rtype); + agg_gs_info_.rcounts.push_back(1); + compmap_sorter_idx += nelems; + } + else{ + agg_gs_info_.rtypes.push_back(MPI_DATATYPE_NULL); + agg_gs_info_.rcounts.push_back(0); + } + } + + return ret; +} + +inline int SPIO::DataRearr::Contig_rearr::create_rearr_comm(void ) +{ + int ret = PIO_NOERR; + assert(ios_); + assert(gdecomp_sz_ > 0); + + if(!ios_->ioproc){ + return ret; + } + + ret = MPI_Comm_dup(ios_->io_comm, &rearr_comm_); + if(ret == MPI_SUCCESS){ + ret = MPI_Comm_size(rearr_comm_, &rearr_comm_sz_); + if(ret == MPI_SUCCESS){ + ret = MPI_Comm_rank(rearr_comm_, &rearr_comm_rank_); + } + } + + /* + if(ret == MPI_SUCCESS){ + rearr_comm_iochunk_sz_ = gdecomp_sz_ / rearr_comm_sz_; + } + */ + + return ret; +} + +inline void SPIO::DataRearr::Contig_rearr::set_rearr_comm_iochunk_sz(int ndims, const int *gdimlen) +{ + assert((ndims > 0) && gdimlen); + assert(ios_); + assert(gdecomp_sz_ > 0); + + if(ios_->ioproc){ + assert((rearr_comm_ != MPI_COMM_NULL) && (rearr_comm_sz_ > 0)); + + rearr_comm_iochunk_sz_ = gdecomp_sz_ / rearr_comm_sz_; + } + else{ + rearr_comm_iochunk_sz_ = 0; + } + /* + // Get the largest contiguous chunk of data on an I/O process for data rearrangement + + // Number of contiguous regions in dim id + // gdimlen = {2, 5, 7} + // => dim id 0 has 2 contiguous regions of dims {5, 7} + // => dim id 1 has 10 contiguous regions of dims {7} + // => ncontig_dim_regions_in_idx = {2, 10, 70} respectively + PIO_Offset ncontig_dim_regions_in_idx = 1; + int idx_to_chunk = 0; + for(;idx_to_chunk < ndims; idx_to_chunk++){ + ncontig_dim_regions_in_idx *= gdimlen[idx_to_chunk]; + if(rearr_comm_sz_ < ncontig_dim_regions_in_idx){ + break; + } + } + assert(idx_to_chunk < ndims); + + // Size of a single contiguous dim region in chunk dim id + // e.g. gdimlen = {2, 5, 7}, + // 1) nprocs = 2 => idx_to_chunk = 0 + // sz_contig_dim_region_in_chunk_idx = size of contig region represented by {5, 7} => 5 * 7 + // 2) nprocs = 3 => idx_to_chunk = 1 + // sz_contig_dim_region_in_chunk_idx = size of contig region represented by {7} => 7 + PIO_Offset sz_contig_dim_region_in_chunk_idx = 1; + for(int i = idx_to_chunk + 1; i < ndims; i++){ + sz_contig_dim_region_in_chunk_idx *= static_cast(gdimlen[i]); + } + + // FIXME: Look at better way to partition data across procs. This method is ok when + // rearr_comm_sz_ << ncontig_dim_regions_in_idx + rearr_comm_iochunk_sz_ = ncontig_dim_regions_in_idx/rearr_comm_sz_ * sz_contig_dim_region_in_chunk_idx; + */ +} + +int SPIO::DataRearr::Contig_rearr::get_rearr_toproc_map(const std::vector &gcompmap, + std::vector &to_proc) +{ + int ret = PIO_NOERR; + assert(ios_); + + if(!ios_->ioproc){ + return ret; + } + assert(rearr_comm_sz_ > 0); + + to_proc.resize(gcompmap.size()); + for(std::size_t i = 0; i < gcompmap.size(); i++){ + /* Note: The last rearranger proc gets the leftover gcompmap range */ + assert(gcompmap[i] >= 0); + to_proc[i] = std::min(static_cast(gcompmap[i]/rearr_comm_iochunk_sz_), rearr_comm_sz_ - 1); + /* + if(gcompmap[i] >= 0){ + to_proc[i] = std::min(static_cast(gcompmap[i]/rearr_comm_iochunk_sz_), rearr_comm_sz_ - 1); + } + else{ + to_proc[i] = -1; + } + */ + } + + //std::cout << "DBG: gcompmap/to_proc : \n" << std::flush; + //SPIO_Util::Dbg_Util::print_1dvec(gcompmap); + //SPIO_Util::Dbg_Util::print_1dvec(to_proc); + return ret; +} + +int SPIO::DataRearr::Contig_rearr::setup_data_rearr_info(std::vector &gcompmap, + std::vector &to_proc, + const int *gdimlen, int ndims) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer ftimer("PIO:Contig_rearr::setup_data_rearr_info"); + assert(ios_); + + if(!ios_->ioproc){ + return ret; + } + assert(rearr_comm_sz_ > 0); + assert(gcompmap.size() == agg_compmap_sorter_.size()); + assert(to_proc.size() == gcompmap.size()); + + /* Sort gcompmap (the aggregated compmap from compute procs) based on compmap sorter map */ + //SPIO_Util::vec_map_sort(gcompmap, agg_compmap_sorter_); + //SPIO_Util::vec_map_sort(to_proc, agg_compmap_sorter_); + agg_compmap_sorter_.sort(gcompmap); + agg_compmap_sorter_.sort(to_proc); + + /* Exchange information about the data being sent from each process, + * 1) The number of regions (a single region is a contiguous chunk/block of elements/data) + * 2) Start/Count defining each region - We calculate the displacement for each receiving + * proc on the sending proc & send that information instead. For region i, a process sends + * + * displs_i -> The local displacement at which to receive the data on the receiving process + * counts_i -> Number of contiguous elements - size of a contiguous block - of data being sent + * + * The counts & displacements (starts) for all the receiving processes are compiled into a + * single displs_counts_sent[] array. The nregion_infos array specifies the number of region + * infos for each process - nregion_infos[i] specifies the number of region info pairs, + * the number of {displ, count} pairs for rank i - and can be used to traverse the + * displs_counts_sent[] array to find the offset in the displs_counts_sent[] + * array for rank i + * + * Each process sends a contiguous block of data and receives an indexed block of data + * based on the start/count info from each process + */ + std::vector nregion_infos_sent(rearr_comm_sz_, 0); + /* displs_counts_sent[i] = displacement, local to rank k, for region i + * displs_counts_sent[i+1] = size of region i + * displs_counts_sent_rank_offs[k] = offset to displs_counts_sent[] array for regions to send to rank k + */ + std::vector displs_counts_sent; + std::vector displs_counts_sent_rank_offs(rearr_comm_sz_); + + std::size_t to_proc_gcompmap_idx = 0; + if(to_proc.size() > 0){ + assert(gdecomp_sz_ > 0); + /* This aggregating proc has some data to send to other aggregating/IO procs */ + for(int i = 0; (i < rearr_comm_sz_) && (to_proc_gcompmap_idx < to_proc.size()); i++){ + assert(to_proc_gcompmap_idx < to_proc.size()); + assert(to_proc_gcompmap_idx < gcompmap.size()); + displs_counts_sent_rank_offs[i] = + static_cast(displs_counts_sent.size() * sizeof(MPI_Offset)); + /* to_proc is sorted based on rank */ + //PIO_Offset to_proc_start_disp = get_rearr_decomp_map_range(i).first; + PIO_Offset to_proc_start_disp = get_rearr_decomp_map_range(to_proc[to_proc_gcompmap_idx]).first; + while((to_proc_gcompmap_idx < gcompmap.size()) && + (to_proc[to_proc_gcompmap_idx] == i)){ + /* There is some data to send to proc i, find the next contig region to sent to proc i */ + nregion_infos_sent[i] += 2; + /* Add displ - displ is converted to local displ in the receiving process */ + PIO_Offset prev_disp = gcompmap[to_proc_gcompmap_idx++] - to_proc_start_disp; + displs_counts_sent.push_back(prev_disp); + /* Add count */ + displs_counts_sent.push_back(1); + /* Parse the entire contig region */ + while(to_proc_gcompmap_idx < gcompmap.size()){ + PIO_Offset cur_disp = gcompmap[to_proc_gcompmap_idx] - to_proc_start_disp; + if((to_proc[to_proc_gcompmap_idx] != i) || + (cur_disp != prev_disp + 1)){ + break; + } + /* Increment the count for this region */ + displs_counts_sent.back()++; + prev_disp = cur_disp; + to_proc_gcompmap_idx++; + } + } + } + } + + /* Exchange the number of region infos sent/recvd to/from each process */ + std::vector nregion_infos_recvd(rearr_comm_sz_, 0); + ret = SPIO_Util::Rearr_Util::alltoall(nregion_infos_sent.data(), 1, MPI_INT, + nregion_infos_recvd.data(), 1, MPI_INT, + rearr_comm_, &(ios_->rearr_opts.comp2io)); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to exchange number of regions for data rearrangement (iosysid=%d)", ios_->iosysid); + } + + /* Calculate total number of region info pairs received by this process */ + int tot_nregion_infos_recvd = + std::accumulate(nregion_infos_recvd.begin(), nregion_infos_recvd.end(), 0); + + std::vector displs_counts_recvd(tot_nregion_infos_recvd, 0); + std::vector displs_counts_recvd_rank_offs(rearr_comm_sz_); + + SPIO_Util::exscan(nregion_infos_recvd.cbegin(), nregion_infos_recvd.cend(), + displs_counts_recvd_rank_offs.begin(), 0, + [](int off) { return off * sizeof(MPI_Offset); }); + + std::vector info_types(rearr_comm_sz_, MPI_OFFSET); + /* Exchange the info - start/count - of regions sent/recvd to/from each process */ + ret = SPIO_Util::Rearr_Util::alltoallw(displs_counts_sent.data(), nregion_infos_sent.data(), + displs_counts_sent_rank_offs.data(), info_types.data(), + displs_counts_recvd.data(), nregion_infos_recvd.data(), + displs_counts_recvd_rank_offs.data(), info_types.data(), + rearr_comm_, &(ios_->rearr_opts.comp2io)); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to exchange region info for data rearrangement (iosysid=%d)", ios_->iosysid); + } + + /* Create send types for data rearrangement - for each process */ + ret = init_rearr_send_types(nregion_infos_sent, displs_counts_sent); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create MPI datatypes for sending data for data rearrangement (iosysid=%d)", ios_->iosysid); + } + + /* Create recv types for data rearrangement - for each process based on region info exchanged */ + ret = init_rearr_recvd_types(nregion_infos_recvd, displs_counts_recvd); + if(ret != PIO_NOERR){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create MPI datatypes for receiving data for data rearrangement (iosysid=%d)", ios_->iosysid); + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::init_rearr_send_types( + const std::vector &nregion_infos_sent, + const std::vector &displs_counts_sent) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer ftimer("PIO:Contig_rearr::init_rearr_send_types"); + + assert(ios_ && ios_->ioproc); + assert(rearr_comm_sz_ > 0); + assert(static_cast(nregion_infos_sent.size()) == rearr_comm_sz_); + + rearr_alltoall_info_.scounts.reserve(rearr_comm_sz_); + rearr_alltoall_info_.sdispls.reserve(rearr_comm_sz_); + rearr_alltoall_info_.stypes.reserve(rearr_comm_sz_); + + /* Point the idx for the first count in displs_counts_sent[] array */ + std::size_t displs_counts_sent_idx = 1; + std::size_t data_idx = 0; + for(int i = 0; i < rearr_comm_sz_; i++){ + if(nregion_infos_sent[i] != 0){ + assert(nregion_infos_sent[i] > 0); + /* Since we rearrange data sorted on to_proc() - the process to send to during rearr - + * we can send a contiguous chunk/block of data */ + /* Accumulate counts from all regions to get the total size of the contiguous chunk/block + * of data to send to this process, i */ + int data_ranki_idx = static_cast(data_idx); + int nregions = nregion_infos_sent[i] / 2; + int nelems = 0; + /* Add the counts for all regions to get the total elements, nelems, sent to proc i */ + for(int i = 0; i < nregions; i++, displs_counts_sent_idx += 2){ + assert(displs_counts_sent_idx < displs_counts_sent.size()); + nelems += displs_counts_sent[displs_counts_sent_idx]; + } + assert(nelems > 0); + data_idx += nelems; + + MPI_Datatype stype; + ret = MPI_Type_contiguous(nelems, elem_mpi_type_, &stype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&stype); + } + + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create MPI contig datatype (nelems=%d, sz =%lld) for sending data for data rearrangement (iosysid=%d)", nelems, static_cast(nelems * elem_mpi_type_sz_), ios_->iosysid); + } + + rearr_alltoall_info_.scounts.push_back(1); + rearr_alltoall_info_.sdispls.push_back(data_ranki_idx * elem_mpi_type_sz_); + rearr_alltoall_info_.stypes.push_back(stype); + } + else{ + rearr_alltoall_info_.scounts.push_back(0); + rearr_alltoall_info_.sdispls.push_back(0); + rearr_alltoall_info_.stypes.push_back(MPI_DATATYPE_NULL); + } + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::init_rearr_recvd_types( + const std::vector &nregion_infos_recvd, + const std::vector &displs_counts_recvd) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer ftimer("PIO:Contig_rearr::init_rearr_recvd_types"); + + assert(ios_ && ios_->ioproc); + assert(rearr_comm_sz_ > 0); + assert(static_cast(nregion_infos_recvd.size()) == rearr_comm_sz_); + + rearr_alltoall_info_.rcounts.reserve(rearr_comm_sz_); + rearr_alltoall_info_.rdispls.reserve(rearr_comm_sz_); + rearr_alltoall_info_.rtypes.reserve(rearr_comm_sz_); + + assert(displs_counts_recvd.size() % 2 == 0); + + /* Point the idx for the first displ in displs_counts_recvd[] array */ + std::size_t displs_counts_recvd_idx = 0; + std::size_t data_idx = 0; + for(int i = 0; i < rearr_comm_sz_; i++){ + if(nregion_infos_recvd[i] != 0){ + /* We receive an indexed type based on the region info we received from each process */ + //int data_ranki_idx = static_cast(data_idx); + int nregions = nregion_infos_recvd[i] / 2; + + assert(nregions > 0); + + std::vector displs; + std::vector counts; + + displs.reserve(nregions); + counts.reserve(nregions); + /* Parse all the regions recvd from rank i and find the displs/counts for each region */ + int nelems = 0; + for(int i = 0; i < nregions; i++, displs_counts_recvd_idx += 2){ + displs.push_back(displs_counts_recvd[displs_counts_recvd_idx]); + counts.push_back(displs_counts_recvd[displs_counts_recvd_idx + 1]); + nelems += displs_counts_recvd[displs_counts_recvd_idx + 1]; + } + + assert(nelems > 0); + data_idx += nelems; + + MPI_Datatype rtype; + ret = MPI_Type_indexed(nregions, counts.data(), displs.data(), elem_mpi_type_, &rtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&rtype); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while initializing PIO_REARR_CONTIG. Unable to create MPI indexed datatype (nelems=%d, nregions = %d, sz =%lld) for sending data for data rearrangement (iosysid=%d)", nelems, nregions, static_cast(nelems * elem_mpi_type_sz_), ios_->iosysid); + } + + rearr_alltoall_info_.rcounts.push_back(1); + //rearr_alltoall_info_.rdispls.push_back(data_ranki_idx * elem_mpi_type_sz_); + /* The displacements are already built into the indexed type, rtype, received */ + rearr_alltoall_info_.rdispls.push_back(0); + rearr_alltoall_info_.rtypes.push_back(rtype); + } + else{ + rearr_alltoall_info_.rcounts.push_back(0); + rearr_alltoall_info_.rdispls.push_back(0); + rearr_alltoall_info_.rtypes.push_back(MPI_DATATYPE_NULL); + } + } + + return ret; +} + +int SPIO::DataRearr::Contig_rearr::rearrange_data(const void *sbuf, std::size_t sbuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars, bool agg2rearr) +{ + int ret = PIO_NOERR; + SPIO_Util::GPTL_Util::GPTL_timer contig_rearr_timer("PIO:Contig_rearr::rearrange_data"); + + assert(is_init_); + + assert(ios_); + if(!ios_->ioproc){ + return PIO_NOERR; + } + + /* Rearrange read/aggregated data between aggregating/IO processes */ + //MPI_Datatype agg_stype = agg_gs_info_.stype; + //std::vector agg_rtypes_nvars; + std::vector rearr_stypes_nvars, rearr_rtypes_nvars; + + std::size_t agg_data_nelems = agg_iochunk_sz_; + //assert(abuf_sz == nvars * agg_data_nelems * elem_mpi_type_sz_); + + //std::cout << "DBG: sbuf[], rbuf[] before rearrange :\n" << std::flush; + //SPIO_Util::Dbg_Util::print_1dvec((int *)sbuf, (int *)((char *)sbuf + sbuf_sz)); + //SPIO_Util::Dbg_Util::print_1dvec((int *)rbuf, (int *)((char *)rbuf + rbuf_sz)); + + if(nvars > 1){ + /* We are rearranging a block of variables */ + /* The stride between each block - data for each variable + * Aggregate procs to rearr procs (write) : + * Each proc sends aggregated data (aggregated from compute processes) and receives + * the contig block of data to write + * Rearr procs to aggregate procs (read) : + * Each proc sends the contig block of data read and each proc receives the aggregated + * block of data that needs to be dispersed to compute processes + */ + MPI_Aint sstride_between_vars = 0, rstride_between_vars = 0; + MPI_Aint stride_between_agg_vars = 0, stride_between_rearr_vars = 0; + + stride_between_agg_vars = agg_data_nelems * elem_mpi_type_sz_; + std::pair map_range = get_rearr_decomp_map_range(rearr_comm_rank_); + stride_between_rearr_vars = + static_cast( (map_range.second - map_range.first) * elem_mpi_type_sz_); + if(agg2rearr){ + /* Rearranging from aggregating procs to rearr procs - data write - comp2io path */ + sstride_between_vars = stride_between_agg_vars; + rstride_between_vars = stride_between_rearr_vars; + } + else{ + /* Rearranging from rearr procs to aggregating procs - data read - io2comp path */ + sstride_between_vars = stride_between_rearr_vars; + rstride_between_vars = stride_between_agg_vars; + } + + rearr_stypes_nvars.reserve(rearr_comm_sz_); + rearr_rtypes_nvars.reserve(rearr_comm_sz_); + assert(static_cast(rearr_alltoall_info_.stypes.size()) == rearr_comm_sz_); + assert(static_cast(rearr_alltoall_info_.rtypes.size()) == rearr_comm_sz_); + for(int i = 0; i < rearr_comm_sz_; i++){ + MPI_Datatype rearr_stype = MPI_DATATYPE_NULL; + MPI_Datatype rearr_rtype = MPI_DATATYPE_NULL; + /* Send type for block of vars */ + if((agg2rearr && (rearr_alltoall_info_.stypes[i] != MPI_DATATYPE_NULL)) || (!agg2rearr && (rearr_alltoall_info_.rtypes[i] != MPI_DATATYPE_NULL))){ + ret = MPI_Type_hvector(nvars, 1, sstride_between_vars, + (agg2rearr) ? rearr_alltoall_info_.stypes[i] : rearr_alltoall_info_.rtypes[i], &rearr_stype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&rearr_stype); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while rearranging data from aggregating processes to IO processes (PIO_REARR_CONTIG). Unable to create vector types to send data for data aggregation (iosysid = %d, nvars = %d)", ios_->iosysid, nvars); + } + } + /* Recv type for block of vars */ + if((agg2rearr && (rearr_alltoall_info_.rtypes[i] != MPI_DATATYPE_NULL)) || (!agg2rearr && (rearr_alltoall_info_.stypes[i] != MPI_DATATYPE_NULL))){ + ret = MPI_Type_hvector(nvars, 1, rstride_between_vars, + (agg2rearr) ? rearr_alltoall_info_.rtypes[i] : rearr_alltoall_info_.stypes[i], &rearr_rtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&rearr_rtype); + } + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while rearranging data from aggregating processes to IO processes (PIO_REARR_CONTIG). Unable to create vector types to receive data for data aggregation (iosysid = %d, nvars = %d)", ios_->iosysid, nvars); + } + } + rearr_stypes_nvars.push_back(rearr_stype); + rearr_rtypes_nvars.push_back(rearr_rtype); + } + } + + ret = SPIO_Util::Rearr_Util::alltoallw( + sbuf, + (agg2rearr) ? rearr_alltoall_info_.scounts.data() : rearr_alltoall_info_.rcounts.data(), + (agg2rearr) ? rearr_alltoall_info_.sdispls.data() : rearr_alltoall_info_.rdispls.data(), + (nvars > 1) ? rearr_stypes_nvars.data() : ((agg2rearr) ? rearr_alltoall_info_.stypes.data() : rearr_alltoall_info_.rtypes.data()), + rbuf, + (agg2rearr) ? rearr_alltoall_info_.rcounts.data() : rearr_alltoall_info_.scounts.data(), + (agg2rearr) ? rearr_alltoall_info_.rdispls.data() : rearr_alltoall_info_.sdispls.data(), + (nvars > 1) ? rearr_rtypes_nvars.data() : ((agg2rearr) ? rearr_alltoall_info_.rtypes.data() : rearr_alltoall_info_.stypes.data()), + rearr_comm_, + (agg2rearr) ? &(ios_->rearr_opts.comp2io) : &(ios_->rearr_opts.io2comp)); + if(ret != MPI_SUCCESS){ + return pio_err(ios_, NULL, ret, __FILE__, __LINE__, + "Internal error while aggregating data from compute processes to aggregating processes (PIO_REARR_CONTIG). Unable to gather data during data aggregation (iosysid = %d)", ios_->iosysid); + } + + //std::cout << "DBG: sbuf[], rbuf[] after rearrange :\n" << std::flush; + //SPIO_Util::Dbg_Util::print_1dvec((int *)sbuf, (int *)((char *)sbuf + sbuf_sz)); + //SPIO_Util::Dbg_Util::print_1dvec((int *)rbuf, (int *)((char *)rbuf + rbuf_sz)); + + for(std::size_t i = 0; i < rearr_stypes_nvars.size(); i++){ + if(rearr_stypes_nvars[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(rearr_stypes_nvars[i])); + } + } + + for(std::size_t i = 0; i < rearr_rtypes_nvars.size(); i++){ + if(rearr_rtypes_nvars[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(rearr_rtypes_nvars[i])); + } + } + + return ret; +} diff --git a/src/clib/core/rearr/pio_rearr_contig.hpp b/src/clib/core/rearr/pio_rearr_contig.hpp new file mode 100644 index 00000000000..f9d37c8255a --- /dev/null +++ b/src/clib/core/rearr/pio_rearr_contig.hpp @@ -0,0 +1,302 @@ +#ifndef __PIO_REARR_CONTIG_HPP__ +#define __PIO_REARR_CONTIG_HPP__ + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" +#include "pio_types.hpp" +#include "spio_map_sorter.hpp" + +#include +#include +#include + +namespace SPIO{ + namespace DataRearr{ + struct Alltoall_info{ + std::vector scounts; + std::vector sdispls; + std::vector stypes; + + std::vector rcounts; + std::vector rdispls; + std::vector rtypes; + }; + + /* The stype is used by sending/compute processes during data aggregation to the + * aggregating/IO process, + * i.e., when writing data - gathering phase + * The stype is used by receiving/compute processes during data dispersion from the + * aggregating/IO process, + * i.e., when reading data - scatter phase + * Since different - counts/types - amount of data is received from each compute process + * the aggregating process has a receive type for each compute process - rtypes[i] for + * rank i in the aggregating comm + * The rtypes[] is used in the aggregating/IO process for aggregating/gathering data + * from compute processes during data aggregation - gathering phase + * i.e., when writing data + * The rtypes[] is used in the aggregating/IO process for dispersing/scattering data + * to compute processes during data dispersion - scatter phase + * i.e., when reading data + */ + struct GatherScatter_info{ + MPI_Datatype stype; + std::vector rcounts; + std::vector rtypes; + }; + + /* This rearranger, rearranges data from compute processes to I/O processes in two + * phases/steps, + * Phase 1 : Data aggregation (from compute processes to aggregating/IO processes) + * Phase 2 : Data rearrangement (between aggregating/IO processes) + * + * The rearranged data is a contiguous block of data (one block/range of data per I/O + * process) and can be written out to the file system. + * Note: Data caching is done outside the rearranger. The rearranger is passed in + * data (cached data) from multiple variables for data rearrangement. + */ + class Contig_rearr{ + public: + Contig_rearr(iosystem_desc_t *ios):is_init_(false), ios_(ios), iodesc_(NULL), + lcompmap_sz_(0), elem_pio_type_(PIO_NAT), elem_mpi_type_(MPI_DATATYPE_NULL), + elem_mpi_type_sz_(0), agg_comm_(MPI_COMM_NULL), is_agg_root_(false), + agg_comm_sz_(0), agg_iochunk_sz_(0), rearr_comm_(MPI_COMM_NULL), rearr_comm_sz_(0), + rearr_comm_iochunk_sz_(0){} + /* Initialize the rearranger */ + int init(int pio_type, const PIO_Offset *compmap, std::size_t compmap_sz, + const int *gdimlen, int ndims, io_desc_t *iodesc); + + bool is_init(void ) const{ return is_init_; } + + /* Get decomp info */ + std::size_t get_decomp_map_lsz(void ) const{ + assert(is_init_); + return static_cast(lcompmap_sz_); + } + + std::size_t get_decomp_gsz(void ) const{ + assert(is_init_); + return static_cast(gdecomp_sz_); + } + + /* Get rearrange info */ + std::size_t get_rearrange_buf_sz(void ) const{ + assert(is_init_ && ios_); + if(!ios_->ioproc){ + return 0; + } + + /* FIXME: Should we return the buffer size in bytes ? */ + PIO_Offset iochunk = rearr_comm_iochunk_sz_; + if(rearr_comm_rank_ != (rearr_comm_sz_ - 1)){ + return iochunk; + } + else{ + /* The last I/O process gets the remaining data */ + return (gdecomp_sz_ - rearr_comm_rank_ * iochunk); + } + } + + std::vector > get_rearr_decomp_map_contig_ranges(int iorank); + + std::vector > get_rearr_decomp_map_contig_ranges(void ) + { + return get_rearr_decomp_map_contig_ranges(rearr_comm_rank_); + } + + std::size_t get_rearr_decomp_map_contig_max_nranges(void ) + { + std::size_t max_nranges = 0; + for(int i = 0; i < rearr_comm_sz_; i++){ + max_nranges = std::max(max_nranges, get_rearr_decomp_map_nranges(i)); + } + return max_nranges; + } + + void off_range_to_dim_range(PIO_Offset start_off, PIO_Offset end_off, + PIO_Offset *start, PIO_Offset *count) const; + + /* Rearrange data */ + int rearrange_comp2io(const void *sbuf, std::size_t sbuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars); + int rearrange_io2comp(const void *sbuf, std::size_t sbuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars); + /* Finalize the rearranger */ + int finalize(void ); + private: + bool is_init_; + iosystem_desc_t *ios_; + io_desc_t *iodesc_; + + /* The size of the compmap local to this process */ + PIO_Offset lcompmap_sz_; + /* The global size represented by this I/O decomposition. This is the same as the + * global size of the variable that is written out using this I/O decomposition + * Note: This can be different from the global decomp map size since the map may + * not represent all elements of a variable/array + */ + PIO_Offset gdecomp_sz_; + std::vector gdimlen_; + std::vector dim_chunk_sz_; + + int elem_pio_type_; + MPI_Datatype elem_mpi_type_; + int elem_mpi_type_sz_; + + /* Gather scatter info for data aggregation */ + MPI_Comm agg_comm_; + bool is_agg_root_; + int agg_comm_sz_; + /* Size/Num of elements of the data chunk in this aggregating process */ + std::size_t agg_iochunk_sz_; + //std::vector agg_compmap_sorter_; + SPIO_Util::Map_sorter agg_compmap_sorter_; + /* The byte displacements for each process, that is part of agg_comm_, in the aggregated data */ + std::vector agg_data_byte_displs_; + GatherScatter_info agg_gs_info_; + + /* All to all info for data rearrangement */ + MPI_Comm rearr_comm_; + int rearr_comm_rank_; + int rearr_comm_sz_; + PIO_Offset rearr_comm_iochunk_sz_; + Alltoall_info rearr_alltoall_info_; + + // FIXME: Change start_dim_idx ptr to a vector (requires changing the C array in ioregion list) + void convert_off_to_start_dim_idx(PIO_Offset start_off, PIO_Offset *start_dim_idx) const + { + std::size_t ndims = gdimlen_.size(); + for(std::size_t i = 0; i < ndims; i++){ + start_dim_idx[i] = 0; + } + for(std::size_t i = 0; i < ndims; i++){ + start_dim_idx[i] = start_off / dim_chunk_sz_[i]; + + start_off -= start_dim_idx[i] * dim_chunk_sz_[i]; + if(start_off == 0) break; + } + assert(start_off == 0); + } + + // FIXME: Change count_dim_idx ptr to a vector (requires changing the C array in ioregion list) + void convert_off_to_count_dim_idx(PIO_Offset count_off, PIO_Offset *count_dim_idx) const + { + std::size_t ndims = gdimlen_.size(); + for(std::size_t i = 0; i < ndims; i++){ + count_dim_idx[i] = gdimlen_[i]; + } + for(std::size_t i = 0; i < ndims; i++){ + count_dim_idx[i] = count_off / dim_chunk_sz_[i]; + + count_off -= count_dim_idx[i] * dim_chunk_sz_[i]; + if(count_off == 0) break; + } + + assert(count_off == 0); + for(std::size_t i = 0; i < ndims; i++){ + if(count_dim_idx[i] != 0){ + break; + } + else{ + count_dim_idx[i] = 1; + } + } + } + + void convert_off_to_start_dim_idx(PIO_Offset start_off, std::vector &start_dim_idx, + std::size_t &last_contig_dim) const + { + last_contig_dim = 0; + for(std::size_t i = 0; i < start_dim_idx.size(); i++){ + if(start_off == 0){ + start_dim_idx[i] = 0; + } + else{ + start_dim_idx[i] = start_off / dim_chunk_sz_[i]; + start_off -= start_dim_idx[i] * dim_chunk_sz_[i]; + if(start_dim_idx[i] != 0) last_contig_dim = i; + } + } + assert(start_off == 0); + } + + std::pair get_rearr_decomp_map_range(int iorank) const + { + assert(gdecomp_sz_ > 0); + assert(rearr_comm_iochunk_sz_ > 0); + + PIO_Offset iochunk = rearr_comm_iochunk_sz_; + PIO_Offset start = iorank * iochunk; + PIO_Offset end = start + iochunk; + if(iorank == (rearr_comm_sz_ - 1)){ + end = gdecomp_sz_; + } + return std::make_pair(start, end); + } + + std::size_t get_rearr_decomp_map_nranges(int iorank) const + { + assert(gdecomp_sz_ > 0); + assert(rearr_comm_iochunk_sz_ > 0); + + PIO_Offset iochunk = rearr_comm_iochunk_sz_; + PIO_Offset start = iorank * iochunk; + PIO_Offset end = start + iochunk; + if(iorank == (rearr_comm_sz_ - 1)){ + end = gdecomp_sz_; + } + return get_ncontig_ranges_from_off_range(start, end - start); + } + + void get_contig_ranges_from_off_range(std::vector > &contig_map_ranges, + PIO_Offset start_off, PIO_Offset count); + std::size_t get_ncontig_ranges_from_off_range(PIO_Offset start_off, PIO_Offset count) const; + int create_agg_comm(void ); + static void get_non_fval_lcompmap_counts_displs(const PIO_Offset *lcompmap, + std::size_t lcompmap_sz, + std::size_t &non_fval_lcompmap_sz, + std::vector &non_fval_lcompmap_counts, + std::vector &non_fval_lcompmap_displs); + int aggregate_compmap(const PIO_Offset *lcompmap, std::size_t lcompmap_sz, + std::size_t &non_fval_lcompmap_sz, + std::vector &non_fval_lcompmap_counts, + std::vector &non_fval_lcompmap_displs, + std::vector &gcompmap, + std::vector &gcompmap_counts, + std::vector &gcompmap_displs); + int setup_data_agg_info(const PIO_Offset *lcompmap, std::size_t lcompmap_sz, + std::size_t non_fval_lcompmap_sz, + const std::vector &non_fval_lcompmap_counts, + const std::vector &non_fval_lcompmap_displs, + const std::vector &gcompmap, + const std::vector &gcompmap_counts, + const std::vector &gcompmap_displs, + const std::vector &to_proc); + int init_agg_send_type(const PIO_Offset *lcompmap, std::size_t lcompmap_sz, + const std::vector &lcompmap_counts, + const std::vector &lcompmap_displs); + int init_agg_recv_types(const std::vector &gcompmap_counts, + const std::vector &compmap_sorter); + int aggregate_data(const void *sbuf, std::size_t sbuf_sz, + void *abuf, std::size_t abuf_sz, int nvars); + int disperse_data(const void *abuf, std::size_t abuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars); + + int create_rearr_comm(void ); + void set_rearr_comm_iochunk_sz(int ndims, const int *gdimlen); + int get_rearr_toproc_map(const std::vector &gcompmap, + std::vector &to_proc); + int setup_data_rearr_info(std::vector &gcompmap, std::vector &to_proc, + const int *gdimlen, int ndims); + int init_rearr_send_types(const std::vector &nregion_infos_sent, + const std::vector &displs_counts_sent); + int init_rearr_recvd_types(const std::vector &nregion_infos_recvd, + const std::vector &displs_counts_recvd); + int rearrange_data(const void *sbuf, std::size_t sbuf_sz, + void *rbuf, std::size_t rbuf_sz, int nvars, bool agg2rearr); + + }; + } // namespace DataRearr + +} // namespace SPIO +#endif // __PIO_REARR_CONTIG_HPP__ diff --git a/src/clib/core/rearr/pio_rearr_utils.cpp b/src/clib/core/rearr/pio_rearr_utils.cpp new file mode 100644 index 00000000000..dc18b5b33a2 --- /dev/null +++ b/src/clib/core/rearr/pio_rearr_utils.cpp @@ -0,0 +1,981 @@ +#include "pio_rearr_utils.hpp" +#include "spio_gptl_utils.hpp" +#include +#include + +namespace SPIO_Util{ + namespace Rearr_Util{ + char gsend_hs = 'h'; + int isend_hs(int to_proc, MPI_Comm comm, int comm_rank, int comm_sz, MPI_Request &req) + { + /* Tag identifies who the data is from */ + int tag = comm_sz + comm_rank; + + return MPI_Isend(&gsend_hs, 1, MPI_CHAR, to_proc, tag, comm, &req); + } + + char grecv_hs; + int irecv_hs(int from_proc, MPI_Comm comm, int comm_rank, int comm_sz, MPI_Request &req) + { + /* Tag identifies who the data is from */ + int tag = comm_sz + from_proc; + + return MPI_Irecv(&grecv_hs, 1, MPI_CHAR, from_proc, tag, comm, &req); + } + + }// namespace Rearr_Util +}// namespace SPIO_Util + +int SPIO_Util::Rearr_Util::gatherw(const void *sendbuf, int sendcount, + MPI_Datatype sendtype, + void *recvbuf, const std::vector &recvcounts, + const std::vector &rdispls, + const std::vector &recvtypes, + int root, MPI_Comm comm, const rearr_comm_fc_opt_t *fc) +{ + int ret = PIO_NOERR, comm_rank = -1, comm_sz = 0; + + SPIO_Util::GPTL_Util::GPTL_timer("PIO:SPIO_Util::Rearr_Util::gatherw"); + + //assert((sendcount == 0) || sendbuf); + + ret = MPI_Comm_rank(comm, &comm_rank); + if(ret != MPI_SUCCESS){ + return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); + } + + ret = MPI_Comm_size(comm, &comm_sz); + if(ret != MPI_SUCCESS){ + return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); + } + + if(comm_rank == root){ + /* No data to gather, return */ + /* FIXME: Handle erroneous calls with senders trying to send data with invalid/null recvbuf */ + if(!recvbuf){ + return PIO_NOERR; + } + } + else{ + if(!sendbuf || (sendcount == 0)){ + return PIO_NOERR; + } + } + + /* Post the receives on root, send a handshake to indicate that receives are posted and + * send the data to root. + */ + + /* Post recv for handshake - to indicate gather buffer is ready - on all non-root procs */ + if(comm_rank != root){ + MPI_Request hs_req, send_req; + MPI_Status hs_status, send_status; + + /* Tag identifies the sender */ + int tag = comm_sz + comm_rank; + + ret = irecv_hs(root, comm, comm_rank, comm_sz, hs_req); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting handshake for gather failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + + ret = MPI_Wait(&hs_req, &hs_status); + if(ret != MPI_SUCCESS){ + /* FIXME: Take a look at the status too */ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Waiting on handshake for gather failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + + /* The non-blocking nature of the send can be used in future to avoid blocking on gather */ + /* Send the data to root */ + ret = MPI_Isend(sendbuf, sendcount, sendtype, root, tag, comm, &send_req); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting send for data to gather failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + + /* We are blocking on the gather now, but this could change in future */ + ret = MPI_Wait(&send_req, &send_status); + if(ret != MPI_SUCCESS){ + /* FIXME: Take a look at the status too */ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Waiting on sending data for gather failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + } + else{ + assert((recvcounts.size() == rdispls.size()) && (rdispls.size() == recvtypes.size())); + assert(recvcounts.size() == static_cast(comm_sz)); + + /* Post receives for the data to gather */ + std::vector recv_reqs(comm_sz, MPI_REQUEST_NULL); + for(int i=0; i < comm_sz; i++){ + if(recvcounts[i] > 0){ + /* Tag identifies who the data is from - the sender */ + int tag = comm_sz + i; + ret = MPI_Irecv(static_cast(recvbuf) + rdispls[i], recvcounts[i], recvtypes[i], + i, tag, comm, &recv_reqs[i]); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting recv for data from proc %d to gather failed on proc (%d), comm = (id=%x, sz=%d)", + i, comm_rank, comm, comm_sz); + } + } + } + + /* Send data to self */ + MPI_Request send_req = MPI_REQUEST_NULL; + if(sendcount > 0){ + /* Tag identifies who the data is from - the sender */ + int send_tag = comm_sz + comm_rank; + ret = MPI_Isend(sendbuf, sendcount, sendtype, root, send_tag, comm, &send_req); + if(ret != MPI_SUCCESS){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting send for data to gather failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + } + + /* Send handshakes */ + std::vector hs_reqs(comm_sz, MPI_REQUEST_NULL); + for(int i=0; i < comm_sz; i++){ + if(i == root){ + /* We don't need handshake for root process - since recvs are already posted before sends + * on the root + */ + continue; + } + if(recvcounts[i] > 0){ + ret = isend_hs(i, comm, comm_rank, comm_sz, hs_reqs[i]); + if(ret != MPI_SUCCESS){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting send for handshake from root (%d) to proc (%d), comm = (id=%x, sz=%d)", + comm_rank, i, comm, comm_sz); + } + } + } + + /* Wait on sent data, handshakes & then receives - gather the data */ + ret = MPI_Wait(&send_req, MPI_STATUS_IGNORE); + if(ret == MPI_SUCCESS){ + ret = MPI_Waitall(static_cast(hs_reqs.size()), hs_reqs.data(), MPI_STATUSES_IGNORE); + if(ret == MPI_SUCCESS){ + ret = MPI_Waitall(static_cast(recv_reqs.size()), recv_reqs.data(), MPI_STATUSES_IGNORE); + } + } + + if(ret != MPI_SUCCESS){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Waiting on data/handshakes/recvs failed on root process(%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + } + + return ret; +} + +int SPIO_Util::Rearr_Util::scatterw(const void *sendbuf, const std::vector &sendcounts, + const std::vector &sdispls, + const std::vector &sendtypes, + void *recvbuf, int recvcount, + MPI_Datatype recvtype, + int root, MPI_Comm comm, const rearr_comm_fc_opt_t *fc) +{ + int ret = PIO_NOERR, comm_rank = -1, comm_sz = 0; + + SPIO_Util::GPTL_Util::GPTL_timer("PIO:SPIO_Util::Rearr_Util::scatterw"); + + ret = MPI_Comm_rank(comm, &comm_rank); + if(ret != MPI_SUCCESS){ + return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); + } + + ret = MPI_Comm_size(comm, &comm_sz); + if(ret != MPI_SUCCESS){ + return check_mpi(NULL, NULL, ret, __FILE__, __LINE__); + } + + if(comm_rank == root){ + /* No data to scatter, return */ + /* FIXME: Handle erroneous calls with receivers waiting for data etc */ + if(!sendbuf){ + return PIO_NOERR; + } + } + else{ + if(!recvbuf || (recvcount == 0)){ + return PIO_NOERR; + } + } + + /* Post the receives on compute procs, send a handshake to indicate that receives are posted and + * send the data from root. + */ + + /* Send handshake - to indicate scatter recv buffer is ready - on all non-root procs */ + if(comm_rank != root){ + MPI_Request hs_req, recv_req; + MPI_Status hs_status, recv_status; + + /* Tag identifies the sender */ + int tag = comm_sz + root; + + /* Post recv for data from root */ + ret = MPI_Irecv(recvbuf, recvcount, recvtype, root, tag, comm, &recv_req); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting recv for data for scatter failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + + /* Send handshake to root to indicate that recv has been posted */ + ret = isend_hs(root, comm, comm_rank, comm_sz, hs_req); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting handshake for scatter failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + + ret = MPI_Wait(&hs_req, &hs_status); + if(ret != MPI_SUCCESS){ + /* FIXME: Take a look at the status too */ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Waiting on handshake, sent to root, for scatter failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + + /* We are blocking on scatter data from root now, but this could change in future */ + ret = MPI_Wait(&recv_req, &recv_status); + if(ret != MPI_SUCCESS){ + /* FIXME: Take a look at the status too */ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Waiting on receiving data for scatter failed on proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + } + else{ + assert((sendcounts.size() == sdispls.size()) && (sdispls.size() == sendtypes.size())); + assert(sendcounts.size() == static_cast(comm_sz)); + + /* Wait for handshakes from processes - to indicate that recvs are posted for the scatter data */ + /* Post receives for handshakes */ + std::vector hs_reqs(comm_sz, MPI_REQUEST_NULL); + for(int i=0; i < comm_sz; i++){ + if(i == root){ + /* We don't need handshake for root process - since recvs are posted before sends + * on the root + */ + continue; + } + if(sendcounts[i] > 0){ + ret = irecv_hs(i, comm, comm_rank, comm_sz, hs_reqs[i]); + if(ret != MPI_SUCCESS){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting recv for handshake to root (%d) from proc (%d), comm = (id=%x, sz=%d)", + comm_rank, i, comm, comm_sz); + } + } + } + + /* Wait on handshakes from procs */ + ret = MPI_Waitall(static_cast(hs_reqs.size()), hs_reqs.data(), MPI_STATUSES_IGNORE); + if(ret != MPI_SUCCESS){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Receiving handshakes on root (%d) failed, comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + + /* Post recv for the data from scatter */ + /* Tag identifies the sender */ + int tag = comm_sz + root; + MPI_Request recv_req = MPI_REQUEST_NULL; + + /* Post recv for data from self/root */ + if(recvcount > 0){ + ret = MPI_Irecv(recvbuf, recvcount, recvtype, root, tag, comm, &recv_req); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting recv for data from self for scatter failed on root proc (%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + } + + /* Send the data - scatter data to procs */ + std::vector send_reqs(comm_sz, MPI_REQUEST_NULL); + for(int i=0; i < comm_sz; i++){ + if(sendcounts[i] > 0){ + /* Tag identifies who the data is from - the sender */ + int tag = comm_sz + root; + ret = MPI_Isend(static_cast(sendbuf) + sdispls[i], sendcounts[i], sendtypes[i], + i, tag, comm, &send_reqs[i]); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Posting send for data to proc %d for scatter failed on proc (%d), comm = (id=%x, sz=%d)", + i, comm_rank, comm, comm_sz); + } + } + } + + /* Wait on sends to other processes and then wait on data received from self */ + ret = MPI_Waitall(static_cast(send_reqs.size()), send_reqs.data(), MPI_STATUSES_IGNORE); + if(ret == MPI_SUCCESS){ + ret = MPI_Wait(&recv_req, MPI_STATUS_IGNORE); + } + if(ret != MPI_SUCCESS){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Waiting on data/recvs failed on root process(%d), comm = (id=%x, sz=%d)", + comm_rank, comm, comm_sz); + } + } + + return ret; +} + +/* FIXME: Refactor all the functions below */ +namespace SPIO_Util{ + namespace Rearr_Util{ + static inline int ceil2(int i) + { + int p = 1; + while(p < i){ + p *= 2; + } + + return(p); + } + + static inline int pair(int np, int p, int k) + { + int q = (p + 1) ^ k ; + int pair = (q > np - 1) ? -1 : q; + return pair; + } + } +} + +int SPIO_Util::Rearr_Util::alltoallw(const void *sendbuf, const int *sendcounts, + const int *sdispls, const MPI_Datatype *sendtypes, + void *recvbuf, const int *recvcounts, + const int *rdispls, const MPI_Datatype *recvtypes, + MPI_Comm comm, const rearr_comm_fc_opt_t *fc) +{ + int ntasks; /* Number of tasks in communicator comm. */ + int my_rank; /* Rank of this task in comm. */ + int tag; + int offset_t; + int steps; + int istep; + int rstep; + int p; + int maxreq; + int maxreqh; + int hs = 1; /* Used for handshaking. */ + void *ptr; + MPI_Status status; /* Not actually used - replace with MPI_STATUSES_IGNORE. */ + int mpierr; /* Return code from MPI functions. */ + + SPIO_Util::GPTL_Util::GPTL_timer("PIO:SPIO_Util::Rearr_Util::alltoallw"); + LOG((2, "pio_swapm fc->hs = %d fc->isend = %d fc->max_pend_req = %d", fc->hs, + fc->isend, fc->max_pend_req)); + + /* Get my rank and size of communicator. */ + if((mpierr = MPI_Comm_size(comm, &ntasks))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if((mpierr = MPI_Comm_rank(comm, &my_rank))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + LOG((2, "ntasks = %d my_rank = %d", ntasks, my_rank)); + + /* Now we know the size of these arrays. */ + int swapids[ntasks]; + MPI_Request rcvids[ntasks]; + MPI_Request sndids[ntasks]; + MPI_Request hs_rcvids[ntasks]; + + /* If fc->max_pend_req == 0 no throttling is requested and the default + * mpi_alltoallw function is used. */ + if(fc->max_pend_req == 0){ + /* Some MPI implementations (some vers of OpenMPI, MPICH 4.0 etc) do not + * allow passing MPI_DATATYPE_NULL to comm functions (MPI_Alltoallw) even + * though the send or recv length is 0, so using a dummy MPI type instead + * of MPI_DATATYPE_NULL + */ + MPI_Datatype dummy_dt = MPI_CHAR; + MPI_Datatype sndtypes[ntasks], rcvtypes[ntasks]; + for(int i = 0; i < ntasks; i++){ + sndtypes[i] = sendtypes[i]; + if(sndtypes[i] == MPI_DATATYPE_NULL){ + sndtypes[i] = dummy_dt; + } + rcvtypes[i] = recvtypes[i]; + if(rcvtypes[i] == MPI_DATATYPE_NULL){ + rcvtypes[i] = dummy_dt; + } + } + + /* Call the MPI alltoall without flow control. */ + LOG((3, "Calling MPI_Alltoallw without flow control.")); + if((mpierr = MPI_Alltoallw(sendbuf, sendcounts, sdispls, sndtypes, recvbuf, + recvcounts, rdispls, rcvtypes, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + return PIO_NOERR; + } + + /* an index for communications tags */ + offset_t = ntasks; + + /* Send to self. */ + if(sendcounts[my_rank] > 0){ + void *sptr, *rptr; + tag = my_rank + offset_t; + sptr = (char *)sendbuf + sdispls[my_rank]; + rptr = (char *)recvbuf + rdispls[my_rank]; + +#ifdef ONEWAY + /* If ONEWAY is true we will post mpi_sendrecv comms instead + * of irecv/send. */ + if((mpierr = MPI_Sendrecv(sptr, sendcounts[my_rank],sendtypes[my_rank], + my_rank, tag, rptr, recvcounts[my_rank], recvtypes[my_rank], + my_rank, tag, comm, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#else + if((mpierr = MPI_Irecv(rptr, recvcounts[my_rank], recvtypes[my_rank], + my_rank, tag, comm, rcvids))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if((mpierr = MPI_Send(sptr, sendcounts[my_rank], sendtypes[my_rank], + my_rank, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + if((mpierr = MPI_Wait(rcvids, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#endif + } + + LOG((2, "Done sending to self... sending to other procs")); + + /* When send to self is complete there is nothing left to do if + * ntasks==1. */ + if(ntasks == 1){ + return PIO_NOERR; + } + + for(int i = 0; i < ntasks; i++){ + rcvids[i] = MPI_REQUEST_NULL; + swapids[i] = 0; + } + + if(fc->isend){ + for (int i = 0; i < ntasks; i++){ + sndids[i] = MPI_REQUEST_NULL; + } + } + + if(fc->hs){ + for(int i = 0; i < ntasks; i++){ + hs_rcvids[i] = MPI_REQUEST_NULL; + } + } + + steps = 0; + for(istep = 0; istep < SPIO_Util::Rearr_Util::ceil2(ntasks) - 1; istep++){ + p = SPIO_Util::Rearr_Util::pair(ntasks, istep, my_rank); + if(p >= 0 && (sendcounts[p] > 0 || recvcounts[p] > 0)){ + swapids[steps++] = p; + } + } + + LOG((3, "steps=%d", steps)); + + if(steps == 0){ + return PIO_NOERR; + } + + if(steps == 1){ + maxreq = 1; + maxreqh = 1; + } + else{ + if(fc->max_pend_req == PIO_REARR_COMM_UNLIMITED_PEND_REQ){ + maxreq = steps; + maxreqh = steps; + } + else if(fc->max_pend_req > 1 && fc->max_pend_req < steps){ + maxreq = fc->max_pend_req; + maxreqh = maxreq / 2; + } + else if(fc->max_pend_req == 1){ + /* Note that steps >= 2 here */ + maxreq = 2; + maxreqh = 1; + } + else{ + maxreq = steps; + maxreqh = steps; + } + } + + LOG((2, "fc->max_pend_req=%d, maxreq=%d, maxreqh=%d", fc->max_pend_req, maxreq, maxreqh)); + + /* If handshaking is in use, do a nonblocking recieve to listen + * for it. */ + if(fc->hs){ + for(istep = 0; istep < maxreq; istep++){ + p = swapids[istep]; + if(sendcounts[p] > 0){ + tag = my_rank + offset_t; + if((mpierr = MPI_Irecv(&hs, 1, MPI_INT, p, tag, comm, hs_rcvids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + } + + /* Post up to maxreq irecv's. */ + for(istep = 0; istep < maxreq; istep++){ + p = swapids[istep]; + if(recvcounts[p] > 0){ + tag = p + offset_t; + ptr = (char *)recvbuf + rdispls[p]; + + if((mpierr = MPI_Irecv(ptr, recvcounts[p], recvtypes[p], p, tag, comm, + rcvids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + if(fc->hs){ + if((mpierr = MPI_Send(&hs, 1, MPI_INT, p, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + } + + /* Tell the paired task that this tasks' has posted it's irecvs'. */ + rstep = maxreq; + for(istep = 0; istep < steps; istep++){ + p = swapids[istep]; + if(sendcounts[p] > 0){ + tag = my_rank + offset_t; + /* If handshake is enabled don't post sends until the + * receiving task has posted recvs. */ + if(fc->hs){ + if((mpierr = MPI_Wait(hs_rcvids + istep, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + hs_rcvids[istep] = MPI_REQUEST_NULL; + } + ptr = (char *)sendbuf + sdispls[p]; + + /* On some software stacks MPI_Irsend() is either not available, not + * a major issue anymore, or is buggy. With PIO1 we have found that + * although the code correctly posts receives before the irsends, + * on some systems (software stacks) the code hangs. However the + * code works fine with isends. The _USE_MPI_RSEND macro should be + * used to use mpi_irsends, the default is mpi_isend + */ + if(fc->hs && fc->isend){ +#ifndef _USE_MPI_RSEND + if((mpierr = MPI_Isend(ptr, sendcounts[p], sendtypes[p], p, tag, comm, + sndids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#else + if((mpierr = MPI_Irsend(ptr, sendcounts[p], sendtypes[p], p, tag, comm, + sndids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#endif + } + else if(fc->isend){ + if((mpierr = MPI_Isend(ptr, sendcounts[p], sendtypes[p], p, tag, comm, + sndids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + else{ + if((mpierr = MPI_Send(ptr, sendcounts[p], sendtypes[p], p, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + + /* We did comms in sets of size max_reqs, if istep > maxreqh-1 + * then there is a remainder that must be handled. */ + if(istep > maxreqh - 1){ + p = istep - maxreqh; + if(rcvids[p] != MPI_REQUEST_NULL){ + if((mpierr = MPI_Wait(rcvids + p, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + rcvids[p] = MPI_REQUEST_NULL; + } + if(rstep < steps){ + p = swapids[rstep]; + if(fc->hs && sendcounts[p] > 0){ + tag = my_rank + offset_t; + if((mpierr = MPI_Irecv(&hs, 1, MPI_INT, p, tag, comm, hs_rcvids+rstep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + if(recvcounts[p] > 0){ + tag = p + offset_t; + + ptr = (char *)recvbuf + rdispls[p]; + if((mpierr = MPI_Irecv(ptr, recvcounts[p], recvtypes[p], p, tag, comm, rcvids + rstep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if(fc->hs){ + if((mpierr = MPI_Send(&hs, 1, MPI_INT, p, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + rstep++; + } + } + } + + /* If steps > 0 there could still be outstanding messages, wait for + * them here. */ + if(steps > 0){ + LOG((2, "Waiting for outstanding msgs")); + if((mpierr = MPI_Waitall(steps, rcvids, MPI_STATUSES_IGNORE))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if(fc->isend){ + if((mpierr = MPI_Waitall(steps, sndids, MPI_STATUSES_IGNORE))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + + return PIO_NOERR; +} + +/* FIXME: Refactor alltoall and alltoallw to combine common code */ +int SPIO_Util::Rearr_Util::alltoall(const void *sendbuf, int sendcount, + MPI_Datatype sendtype, + void *recvbuf, int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, const rearr_comm_fc_opt_t *fc) +{ + int ntasks; /* Number of tasks in communicator comm. */ + int my_rank; /* Rank of this task in comm. */ + int tag; + int offset_t; + int steps; + int istep; + int rstep; + int p; + int maxreq; + int maxreqh; + int hs = 1; /* Used for handshaking. */ + void *ptr; + MPI_Status status; /* Not actually used - replace with MPI_STATUSES_IGNORE. */ + int mpierr; /* Return code from MPI functions. */ + + int sendtype_sz = 0, recvtype_sz = 0; + + if(sendtype != MPI_DATATYPE_NULL){ + if((mpierr = MPI_Type_size(sendtype, &sendtype_sz))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + + if(recvtype != MPI_DATATYPE_NULL){ + if((mpierr = MPI_Type_size(recvtype, &recvtype_sz))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + + SPIO_Util::GPTL_Util::GPTL_timer("PIO:SPIO_Util::Rearr_Util::alltoallw"); + LOG((2, "pio_swapm fc->hs = %d fc->isend = %d fc->max_pend_req = %d", fc->hs, + fc->isend, fc->max_pend_req)); + + /* Get my rank and size of communicator. */ + if((mpierr = MPI_Comm_size(comm, &ntasks))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if((mpierr = MPI_Comm_rank(comm, &my_rank))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + LOG((2, "ntasks = %d my_rank = %d", ntasks, my_rank)); + + /* Now we know the size of these arrays. */ + int swapids[ntasks]; + MPI_Request rcvids[ntasks]; + MPI_Request sndids[ntasks]; + MPI_Request hs_rcvids[ntasks]; + + /* If fc->max_pend_req == 0 no throttling is requested and the default + * mpi_alltoallw function is used. */ + if(fc->max_pend_req == 0){ + /* Some MPI implementations (some vers of OpenMPI, MPICH 4.0 etc) do not + * allow passing MPI_DATATYPE_NULL to comm functions (MPI_Alltoall) even + * though the send or recv length is 0, so using a dummy MPI type instead + * of MPI_DATATYPE_NULL + */ + MPI_Datatype dummy_dt = MPI_CHAR; + MPI_Datatype tmp_sendtype = sendtype, tmp_recvtype = recvtype; + if(tmp_sendtype == MPI_DATATYPE_NULL){ + tmp_sendtype = dummy_dt; + } + if(tmp_recvtype == MPI_DATATYPE_NULL){ + tmp_recvtype = dummy_dt; + } + + /* Call the MPI alltoall without flow control. */ + LOG((3, "Calling MPI_Alltoall without flow control.")); + if((mpierr = MPI_Alltoall(sendbuf, sendcount, tmp_sendtype, + recvbuf, recvcount, tmp_recvtype, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + return PIO_NOERR; + } + + /* an index for communications tags */ + offset_t = ntasks; + + /* Send to self. */ + if(sendcount > 0){ + void *sptr, *rptr; + tag = my_rank + offset_t; + sptr = (char *)sendbuf + my_rank * sendcount * sendtype_sz; + rptr = (char *)recvbuf + my_rank * recvcount * recvtype_sz; + +#ifdef ONEWAY + /* If ONEWAY is true we will post mpi_sendrecv comms instead + * of irecv/send. */ + if((mpierr = MPI_Sendrecv(sptr, sendcount,sendtype, + my_rank, tag, rptr, recvcount, recvtype, + my_rank, tag, comm, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#else + if((mpierr = MPI_Irecv(rptr, recvcount, recvtype, + my_rank, tag, comm, rcvids))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if((mpierr = MPI_Send(sptr, sendcount, sendtype, + my_rank, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + if((mpierr = MPI_Wait(rcvids, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#endif + } + + LOG((2, "Done sending to self... sending to other procs")); + + /* When send to self is complete there is nothing left to do if + * ntasks==1. */ + if(ntasks == 1){ + return PIO_NOERR; + } + + for(int i = 0; i < ntasks; i++){ + rcvids[i] = MPI_REQUEST_NULL; + swapids[i] = 0; + } + + if(fc->isend){ + for (int i = 0; i < ntasks; i++){ + sndids[i] = MPI_REQUEST_NULL; + } + } + + if(fc->hs){ + for(int i = 0; i < ntasks; i++){ + hs_rcvids[i] = MPI_REQUEST_NULL; + } + } + + steps = 0; + for(istep = 0; istep < SPIO_Util::Rearr_Util::ceil2(ntasks) - 1; istep++){ + p = SPIO_Util::Rearr_Util::pair(ntasks, istep, my_rank); + if(p >= 0 && (sendcount > 0 || recvcount > 0)){ + swapids[steps++] = p; + } + } + + LOG((3, "steps=%d", steps)); + + if(steps == 0){ + return PIO_NOERR; + } + + if(steps == 1){ + maxreq = 1; + maxreqh = 1; + } + else{ + if(fc->max_pend_req == PIO_REARR_COMM_UNLIMITED_PEND_REQ){ + maxreq = steps; + maxreqh = steps; + } + else if(fc->max_pend_req > 1 && fc->max_pend_req < steps){ + maxreq = fc->max_pend_req; + maxreqh = maxreq / 2; + } + else if(fc->max_pend_req == 1){ + /* Note that steps >= 2 here */ + maxreq = 2; + maxreqh = 1; + } + else{ + maxreq = steps; + maxreqh = steps; + } + } + + LOG((2, "fc->max_pend_req=%d, maxreq=%d, maxreqh=%d", fc->max_pend_req, maxreq, maxreqh)); + + /* If handshaking is in use, do a nonblocking recieve to listen + * for it. */ + if(fc->hs){ + for(istep = 0; istep < maxreq; istep++){ + p = swapids[istep]; + if(sendcount > 0){ + tag = my_rank + offset_t; + if((mpierr = MPI_Irecv(&hs, 1, MPI_INT, p, tag, comm, hs_rcvids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + } + + /* Post up to maxreq irecv's. */ + for(istep = 0; istep < maxreq; istep++){ + p = swapids[istep]; + if(recvcount > 0){ + tag = p + offset_t; + ptr = (char *)recvbuf + p * recvcount * recvtype_sz; + + if((mpierr = MPI_Irecv(ptr, recvcount, recvtype, p, tag, comm, + rcvids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + + if(fc->hs){ + if((mpierr = MPI_Send(&hs, 1, MPI_INT, p, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + } + + /* Tell the paired task that this tasks' has posted it's irecvs'. */ + rstep = maxreq; + for(istep = 0; istep < steps; istep++){ + p = swapids[istep]; + if(sendcount > 0){ + tag = my_rank + offset_t; + /* If handshake is enabled don't post sends until the + * receiving task has posted recvs. */ + if(fc->hs){ + if((mpierr = MPI_Wait(hs_rcvids + istep, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + hs_rcvids[istep] = MPI_REQUEST_NULL; + } + ptr = (char *)sendbuf + p * sendcount * sendtype_sz; + + /* On some software stacks MPI_Irsend() is either not available, not + * a major issue anymore, or is buggy. With PIO1 we have found that + * although the code correctly posts receives before the irsends, + * on some systems (software stacks) the code hangs. However the + * code works fine with isends. The _USE_MPI_RSEND macro should be + * used to use mpi_irsends, the default is mpi_isend + */ + if(fc->hs && fc->isend){ +#ifndef _USE_MPI_RSEND + if((mpierr = MPI_Isend(ptr, sendcount, sendtype, p, tag, comm, + sndids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#else + if((mpierr = MPI_Irsend(ptr, sendcount, sendtype, p, tag, comm, + sndids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } +#endif + } + else if(fc->isend){ + if((mpierr = MPI_Isend(ptr, sendcount, sendtype, p, tag, comm, + sndids + istep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + else{ + if((mpierr = MPI_Send(ptr, sendcount, sendtype, p, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + + /* We did comms in sets of size max_reqs, if istep > maxreqh-1 + * then there is a remainder that must be handled. */ + if(istep > maxreqh - 1){ + p = istep - maxreqh; + if(rcvids[p] != MPI_REQUEST_NULL){ + if((mpierr = MPI_Wait(rcvids + p, &status))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + rcvids[p] = MPI_REQUEST_NULL; + } + if(rstep < steps){ + p = swapids[rstep]; + if(fc->hs && sendcount > 0){ + tag = my_rank + offset_t; + if((mpierr = MPI_Irecv(&hs, 1, MPI_INT, p, tag, comm, hs_rcvids+rstep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + if(recvcount > 0){ + tag = p + offset_t; + + ptr = (char *)recvbuf + p * recvcount * recvtype_sz; + if((mpierr = MPI_Irecv(ptr, recvcount, recvtype, p, tag, comm, rcvids + rstep))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if(fc->hs){ + if((mpierr = MPI_Send(&hs, 1, MPI_INT, p, tag, comm))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + rstep++; + } + } + } + + /* If steps > 0 there could still be outstanding messages, wait for + * them here. */ + if(steps > 0){ + LOG((2, "Waiting for outstanding msgs")); + if((mpierr = MPI_Waitall(steps, rcvids, MPI_STATUSES_IGNORE))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + if(fc->isend){ + if((mpierr = MPI_Waitall(steps, sndids, MPI_STATUSES_IGNORE))){ + return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); + } + } + } + + return PIO_NOERR; +} diff --git a/src/clib/core/rearr/pio_rearr_utils.hpp b/src/clib/core/rearr/pio_rearr_utils.hpp new file mode 100644 index 00000000000..77e6b5b2cf5 --- /dev/null +++ b/src/clib/core/rearr/pio_rearr_utils.hpp @@ -0,0 +1,41 @@ +#ifndef __PIO_REARR_UTILS_HPP__ +#define __PIO_REARR_UTILS_HPP__ + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" +#include "pio_types.hpp" + +#include + +namespace SPIO_Util{ + namespace Rearr_Util{ + int gatherw(const void *sendbuf, int sendcount, + MPI_Datatype sendtype, + void *recvbuf, const std::vector &recvcounts, + const std::vector &rdispls, + const std::vector &recvtypes, + int root, MPI_Comm comm, const rearr_comm_fc_opt_t *fc); + int scatterw(const void *sendbuf, const std::vector &sendcounts, + const std::vector &sdispls, + const std::vector &sendtypes, + void *recvbuf, int recvcount, + MPI_Datatype recvtype, + int root, MPI_Comm comm, const rearr_comm_fc_opt_t *fc); + + int alltoallw(const void *sendbuf, const int *sendcounts, + const int *sdispls, const MPI_Datatype *sendtypes, + void *recvbuf, const int *recvcounts, + const int *rdispls, const MPI_Datatype *recvtypes, + MPI_Comm comm, const rearr_comm_fc_opt_t *fc); + + int alltoall(const void *sendbuf, int sendcount, + MPI_Datatype sendtype, + void *recvbuf, int recvcount, + MPI_Datatype recvtype, + MPI_Comm comm, const rearr_comm_fc_opt_t *fc); + + } // namespace Rearr_Util +} // namespace SPIO_Util + +#endif // __PIO_REARR_UTILS_HPP__ diff --git a/src/clib/pio_rearrange.cpp b/src/clib/core/rearr/pio_rearrange.cpp similarity index 99% rename from src/clib/pio_rearrange.cpp rename to src/clib/core/rearr/pio_rearrange.cpp index 89a51532b6e..514594d2215 100644 --- a/src/clib/pio_rearrange.cpp +++ b/src/clib/core/rearr/pio_rearrange.cpp @@ -1,7 +1,6 @@ /** @file * Code to map IO to model decomposition. * - * @author Jim Edwards */ #include #include @@ -45,7 +44,6 @@ void init_rearr_opts(iosystem_desc_t *iosys) * of data. * @param dim_list array of length ndims that will get the dimensions * corresponding to this index. - * @author Jim Edwards, Ed Hartnett */ inline void idx_to_dim_list(int ndims, const int *gdimlen, PIO_Offset idx, PIO_Offset *dim_list) @@ -91,7 +89,6 @@ inline void idx_to_dim_list(int ndims, const int *gdimlen, PIO_Offset idx, * @param max_size array of size dim + 1 that contains the maximum * sizes along that dimension. * @param count array of size dim + 1 that gets the new counts. - * @author Jim Edwards */ void expand_region(int dim, const int *gdimlen, int maplen, const PIO_Offset *map, int region_size, int region_stride, const int *max_size, @@ -168,7 +165,6 @@ void expand_region(int dim, const int *gdimlen, int maplen, const PIO_Offset *ma * @param count array (length ndims) that will get counts of found * region. * @returns length of the region found. - * @author Jim Edwards */ PIO_Offset find_region(int ndims, const int *gdimlen, int maplen, const PIO_Offset *map, PIO_Offset *start, PIO_Offset *count) @@ -215,7 +211,6 @@ PIO_Offset find_region(int ndims, const int *gdimlen, int maplen, const PIO_Offs * @param lcoord pointer to an offset. * @param count array of counts. * @returns the local array index. - * @author Jim Edwards */ inline PIO_Offset coord_to_lindex(int ndims, const PIO_Offset *lcoord, const PIO_Offset *count) { @@ -242,7 +237,6 @@ inline PIO_Offset coord_to_lindex(int ndims, const PIO_Offset *lcoord, const PIO * @param io_comm the IO communicator * @param iodesc a pointer to the io_desc_t struct. * @returns 0 for success, error code otherwise. - * @author Jim Edwards */ int compute_maxIObuffersize(MPI_Comm io_comm, io_desc_t *iodesc) { @@ -292,7 +286,6 @@ int compute_maxIObuffersize(MPI_Comm io_comm, io_desc_t *iodesc) * @param mtype pointer to an array (length msgcnt) which gets the * created datatypes. Will be NULL when iodesc->nrecvs == 0. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int create_mpi_datatypes(MPI_Datatype mpitype, int msgcnt, const PIO_Offset *mindex, const int *mcount, int *mfrom, @@ -442,7 +435,6 @@ int create_mpi_datatypes(MPI_Datatype mpitype, int msgcnt, * @param ios pointer to the iosystem_desc_t struct. * @param iodesc a pointer to the io_desc_t struct. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int define_iodesc_datatypes(iosystem_desc_t *ios, io_desc_t *iodesc) { @@ -561,7 +553,6 @@ int define_iodesc_datatypes(iosystem_desc_t *ios, io_desc_t *iodesc) * @param dest_ioproc an array (length maplen) of IO task numbers. * @param dest_ioindex an array (length maplen) of IO indicies. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int compute_counts(iosystem_desc_t *ios, io_desc_t *iodesc, const int *dest_ioproc, const PIO_Offset *dest_ioindex) @@ -846,10 +837,9 @@ int compute_counts(iosystem_desc_t *ios, io_desc_t *iodesc, * @param rbuf receive buffer. May be NULL. * @param nvars number of variables. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ -int rearrange_comp2io(iosystem_desc_t *ios, io_desc_t *iodesc, const void *sbuf, - void *rbuf, int nvars) +int rearrange_comp2io(iosystem_desc_t *ios, io_desc_t *iodesc, file_desc_t *file, + const void *sbuf, void *rbuf, int nvars) { int ntasks; /* Number of tasks in communicator. */ int niotasks; /* Number of IO tasks. */ @@ -1085,7 +1075,6 @@ int rearrange_comp2io(iosystem_desc_t *ios, io_desc_t *iodesc, const void *sbuf, * @param sbuf send buffer. * @param rbuf receive buffer. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int rearrange_io2comp(iosystem_desc_t *ios, io_desc_t *iodesc, const void *sbuf, void *rbuf) @@ -1220,7 +1209,6 @@ int rearrange_io2comp(iosystem_desc_t *ios, io_desc_t *iodesc, const void *sbuf, * entire var (for non-record vars). * @param compmap only used for the box communicator. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int determine_fill(iosystem_desc_t *ios, io_desc_t *iodesc, const int *gdimlen, const PIO_Offset *compmap) @@ -1302,7 +1290,6 @@ int determine_fill(iosystem_desc_t *ios, io_desc_t *iodesc, const int *gdimlen, * @param iodesc a pointer to the io_desc_t struct, which must be * allocated before this function is called. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int box_rearrange_create(iosystem_desc_t *ios, int maplen, const PIO_Offset *compmap, const int *gdimlen, int ndims, io_desc_t *iodesc) @@ -1956,7 +1943,6 @@ int box_rearrange_create_with_holes(iosystem_desc_t *ios, int maplen, const PIO_ * @param a pointer to an offset. * @param b pointer to another offset. * @returns 0 if offsets are the same or either pointer is NULL. - * @author Jim Edwards */ int compare_offsets(const void *a, const void *b) { @@ -1987,7 +1973,6 @@ int compare_offsets(const void *a, const void *b) * @param maxregions * @param firstregion pointer to the first region. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int get_regions(int ndims, const int *gdimlen, int maplen, const PIO_Offset *map, int *maxregions, io_region *firstregion) @@ -2074,7 +2059,6 @@ int get_regions(int ndims, const int *gdimlen, int maplen, const PIO_Offset *map * @param ios pointer to the iosystem_desc_t struct. * @param iodesc a pointer to the io_desc_t struct. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int default_subset_partition(iosystem_desc_t *ios, io_desc_t *iodesc) { @@ -2199,7 +2183,6 @@ int default_subset_partition(iosystem_desc_t *ios, io_desc_t *iodesc) * @param ndims the number of dimensions. * @param iodesc a pointer to the io_desc_t struct. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ int subset_rearrange_create(iosystem_desc_t *ios, int maplen, PIO_Offset *compmap, const int *gdimlen, int ndims, io_desc_t *iodesc) @@ -2731,7 +2714,6 @@ int subset_rearrange_create(iosystem_desc_t *ios, int maplen, PIO_Offset *compma * @param ios pointer to the iosystem description struct. * @param iodesc pointer to the IO description struct. * @returns 0 on success, error code otherwise. - * @author Jim Edwards */ void performance_tune_rearranger(iosystem_desc_t *ios, io_desc_t *iodesc) { @@ -2785,7 +2767,7 @@ void performance_tune_rearranger(iosystem_desc_t *ios, io_desc_t *iodesc) if ((mpierr = MPI_Barrier(mycomm))) return check_mpi(NULL, NULL, mpierr, __FILE__, __LINE__); GPTLstamp(&wall[0], &usr[0], &sys[0]); - rearrange_comp2io(ios, iodesc, cbuf, ibuf, 1); + rearrange_comp2io(ios, iodesc, file, cbuf, ibuf, 1); rearrange_io2comp(ios, iodesc, ibuf, cbuf); GPTLstamp(&wall[1], &usr[1], &sys[1]); mintime = wall[1]-wall[0]; diff --git a/src/clib/spio_rearrange_any.cpp b/src/clib/core/rearr/spio_rearrange_any.cpp similarity index 100% rename from src/clib/spio_rearrange_any.cpp rename to src/clib/core/rearr/spio_rearrange_any.cpp diff --git a/src/clib/spio_rearrange_any.h b/src/clib/core/rearr/spio_rearrange_any.h similarity index 94% rename from src/clib/spio_rearrange_any.h rename to src/clib/core/rearr/spio_rearrange_any.h index 47145ccce1d..605d2da0ee8 100644 --- a/src/clib/spio_rearrange_any.h +++ b/src/clib/core/rearr/spio_rearrange_any.h @@ -3,7 +3,7 @@ #include "pio_config.h" #include "pio.h" -//#include "pio_internal.h" +#include "pio_internal.h" #if defined(__cplusplus) extern "C" { diff --git a/src/clib/core/util/CMakeLists.txt b/src/clib/core/util/CMakeLists.txt new file mode 100644 index 00000000000..1cd1437475e --- /dev/null +++ b/src/clib/core/util/CMakeLists.txt @@ -0,0 +1,27 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C Core (util)... =====") +# SCORPIO C library - core +set (spio_core_util_src + spio_decomp_txt_logger.cpp + spio_decomp_nc_logger.cpp + spio_decomp_map_info_pool.cpp + pio_lists.cpp + spio_io_summary.cpp + spio_file_mvcache.cpp + pio_sdecomps_regex.cpp) + +add_library (spio_core_util OBJECT + ${spio_core_util_src}) + +set (spio_clib_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/../..") +set (spio_clib_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/../..") + +target_include_directories(spio_core_util + PUBLIC . + PRIVATE ${spio_clib_src_dir} ${spio_clib_bin_dir} ${spio_clib_src_dir}/util ${spio_clib_src_dir}/core) + +target_link_libraries(spio_core_util + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/README_pio_sdecomps_regex.txt b/src/clib/core/util/README_pio_sdecomps_regex.txt similarity index 100% rename from src/clib/README_pio_sdecomps_regex.txt rename to src/clib/core/util/README_pio_sdecomps_regex.txt diff --git a/src/clib/io_perf_summary.json_expected b/src/clib/core/util/io_perf_summary.json_expected similarity index 100% rename from src/clib/io_perf_summary.json_expected rename to src/clib/core/util/io_perf_summary.json_expected diff --git a/src/clib/io_perf_summary.schema.json b/src/clib/core/util/io_perf_summary.schema.json similarity index 100% rename from src/clib/io_perf_summary.schema.json rename to src/clib/core/util/io_perf_summary.schema.json diff --git a/src/clib/io_perf_summary.text_expected b/src/clib/core/util/io_perf_summary.text_expected similarity index 100% rename from src/clib/io_perf_summary.text_expected rename to src/clib/core/util/io_perf_summary.text_expected diff --git a/src/clib/io_perf_summary.xml_expected b/src/clib/core/util/io_perf_summary.xml_expected similarity index 100% rename from src/clib/io_perf_summary.xml_expected rename to src/clib/core/util/io_perf_summary.xml_expected diff --git a/src/clib/core/util/pio_lists.cpp b/src/clib/core/util/pio_lists.cpp new file mode 100644 index 00000000000..0e0507002dd --- /dev/null +++ b/src/clib/core/util/pio_lists.cpp @@ -0,0 +1,530 @@ +/** + * @file + * PIO list functions. + */ +#include +#include +#include +#include +#include +#include +#include +#ifdef PIO_MICRO_TIMING +#include "pio_timer.h" +#endif +#include "spio_file_mvcache.h" +#include "spio_io_summary.h" +#include "spio_hash.h" +#include "spio_dt_converter.hpp" +#include +#include +#include + +namespace SPIO_Util{ + namespace SPIO_Lists{ + namespace GVars{ + std::map pio_iodesc_list; + std::map pio_iosystem_list; + /* This list is independent of the I/O system because users only provide the + * file id during a call - hence instead of deducing the I/O system that the file + * belongs to, the file list is separate from the I/O system (list) + */ + std::map pio_file_list; + } + } +} + +/* Arbitrary start ids for structures */ +const int PIO_FILE_START_ID = 16; +const int PIO_IOSYSTEM_START_ID = 2048; + +/** + * Add a new entry to the global list of open files. + * + * This function guarantees that files (id of the + * files) are unique across the comm provided + * + * @param file Pointer to the file_desc_t struct for the file. + * @param comm MPI Communicator across which the files + * need to be unique + * @returns The id for the file added to the list + */ +int pio_add_to_file_list(file_desc_t *file, MPI_Comm comm) +{ + static int pio_file_next_id = PIO_FILE_START_ID; + + assert(file); + LOG((2, "pio_add_to_file_list file = %p", file)); + + if(comm != MPI_COMM_NULL){ + int tmp_id = pio_file_next_id; + int mpierr = MPI_Allreduce(&tmp_id, &pio_file_next_id, 1, MPI_INT, MPI_MAX, comm); + assert(mpierr == MPI_SUCCESS); + } + file->pio_ncid = pio_file_next_id; + pio_file_next_id++; + + SPIO_Util::SPIO_Lists::GVars::pio_file_list.insert({file->pio_ncid, file}); + LOG((2, "file %p (%s, pio_ncid=%d) added to list", + file, pio_get_fname_from_file(file), file->pio_ncid)); + + return file->pio_ncid; +} + +/** + * Get the file structure (file_desc_t) associated with the file id + * + * @param ncid The file id associated with the file being looked up + * @param pfile Pointer to the file structure pointer to contain the + * returned file structure pointer + * + * @returns 0 for success, error code otherwise. + */ +int pio_get_file(int ncid, file_desc_t **pfile) +{ + LOG((2, "pio_get_file ncid = %d", ncid)); + + file_desc_t *file = NULL; + + if(ncid == PIO_GLOBAL){ + /* Don't handle error, return a pio_err(), here since this function is called for + * PIO_GLOBAL and in that case is handled by the caller */ + return PIO_EBADID; + } + + try{ + file = SPIO_Util::SPIO_Lists::GVars::pio_file_list.at(ncid); + } catch(const std::out_of_range &e){ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Searching for file (ncid=%d) in internal file list failed. Invalid file id provided", ncid); + } + + if(pfile){ + assert(file && file->iosystem && iotype_is_valid(file->iotype)); + *pfile = file; + } + + return PIO_NOERR; +} + +/** + * Free a file structure (file_desc_t) + * + * @param file Pointer to the file structure (file_desc_t) + * associated with the file + * @returns 0 for success, error code otherwise + */ +int pio_free_file(file_desc_t *file) +{ + assert(file); + + LOG((2, "pio_free_file(%s, ncid=%d)", pio_get_fname_from_file(file), file->pio_ncid)); + /* Free any fill values that were allocated. */ + for (int v = 0; v < PIO_MAX_VARS; v++){ + if (file->varlist[v].fillvalue){ + free(file->varlist[v].fillvalue); + } +#ifdef PIO_MICRO_TIMING + mtimer_destroy(&(file->varlist[v].rd_mtimer)); + mtimer_destroy(&(file->varlist[v].rd_rearr_mtimer)); + mtimer_destroy(&(file->varlist[v].wr_mtimer)); + mtimer_destroy(&(file->varlist[v].wr_rearr_mtimer)); +#endif + } + + delete(file->pmtx); + + free(file->unlim_dimids); + free(file->io_fstats); + spio_file_mvcache_finalize(file); + +#ifdef _HDF5 + if(file->dt_converter != NULL){ + SPIO_Util::File_Util::DTConverter *dt_conv = static_cast(file->dt_converter); + dt_conv->clear(); + delete(dt_conv); + } + + if((file->iotype == PIO_IOTYPE_HDF5) || (file->iotype == PIO_IOTYPE_HDF5C)){ + for(int i = 0; i < file->hdf5_num_dims; i++){ + if(file->hdf5_dims[i].name) { free(file->hdf5_dims[i].name); } + } + file->hdf5_num_dims = 0; + + for(int i = 0; i < file->hdf5_num_vars; i++){ + if(file->hdf5_vars[i].name) { free(file->hdf5_vars[i].name); } + if(file->hdf5_vars[i].alt_name) { free(file->hdf5_vars[i].alt_name); } + if(file->hdf5_vars[i].hdf5_dimids) { free(file->hdf5_vars[i].hdf5_dimids); } + } + file->hdf5_num_vars = 0; + + for(int i = 0; i < file->hdf5_num_attrs; i++) { + if(file->hdf5_attrs[i].att_name) { free(file->hdf5_attrs[i].att_name); } + } + file->hdf5_num_attrs = 0; + file->hdf5_num_gattrs = 0; + } +#endif + +#ifdef _ADIOS2 + if (file->cache_data_blocks != NULL){ + /* This call also frees file->cache_data_blocks */ + file->cache_data_blocks->free(file->cache_data_blocks); + } + + if (file->cache_block_sizes != NULL){ + /* This call also frees file->cache_block_sizes */ + file->cache_block_sizes->free(file->cache_block_sizes); + } + + if (file->cache_darray_info != NULL){ + /* This call also frees file->cache_darray_info */ + file->cache_darray_info->free(file->cache_darray_info); + } +#endif + + /* Free the memory used for this file. */ + free(file); + + return PIO_NOERR; +} + +/** + * Delete a file from the list of open files. + * Note: The file is deleted from the list & freed + * + * @param ncid The file id associated with the file to be deleted + * @returns 0 for success, error code otherwise + */ +int pio_delete_file_from_list(int ncid) +{ + LOG((2, "pio_delete_file_from_list(ncid=%d)", ncid)); + std::map::iterator iter = SPIO_Util::SPIO_Lists::GVars::pio_file_list.find(ncid); + if(iter == SPIO_Util::SPIO_Lists::GVars::pio_file_list.end()){ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Deleting file (ncid=%d) from internal list failed. Invalid file id provided", ncid); + } + + file_desc_t *file = (*iter).second; + SPIO_Util::SPIO_Lists::GVars::pio_file_list.erase(iter); + + return pio_free_file(file); +} + +int spio_close_all_files_and_delete_from_list(int iosysid) +{ + int ret = PIO_NOERR; + std::vector ncids_to_del_from_list; + for(std::map::iterator iter = SPIO_Util::SPIO_Lists::GVars::pio_file_list.begin(); + iter != SPIO_Util::SPIO_Lists::GVars::pio_file_list.end(); ++iter){ + file_desc_t *file = iter->second; + assert(file); + if(file->iosystem->iosysid == iosysid){ + //ret = spio_hard_closefile(file->iosystem, file, true); + ret = spio_wait_on_hard_close(file->iosystem, file); + if(ret != PIO_NOERR){ + return pio_err(file->iosystem, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Error closing file (hard close failed)"); + } + ncids_to_del_from_list.push_back(file->pio_ncid); + } + } + for(std::vector::iterator iter = ncids_to_del_from_list.begin(); + iter != ncids_to_del_from_list.end(); ++iter){ + ret = pio_delete_file_from_list(*iter); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, + "Error deleting file from global list"); + } + } + + return PIO_NOERR; +} + +/* Check if the file is still open - due to async ops */ +int spio_close_soft_closed_file(const char *filename) +{ + int ret = PIO_NOERR; + std::vector ncids_to_del_from_list; + + assert(filename); + for(std::map::iterator iter = SPIO_Util::SPIO_Lists::GVars::pio_file_list.begin(); + iter != SPIO_Util::SPIO_Lists::GVars::pio_file_list.end(); ++iter){ + file_desc_t *file = iter->second; + /* No need to worry about "read only" files */ + if((std::string(file->fname) == std::string(filename)) && (file->mode & PIO_WRITE)){ + //ret = spio_hard_closefile(file->iosystem, file, true); + ret = spio_wait_on_hard_close(file->iosystem, file); + if(ret != PIO_NOERR){ + return pio_err(file->iosystem, file, PIO_EINTERNAL, __FILE__, __LINE__, + "Error closing file (hard close failed)"); + } + ncids_to_del_from_list.push_back(file->pio_ncid); + } + } + + for(std::vector::iterator iter = ncids_to_del_from_list.begin(); + iter != ncids_to_del_from_list.end(); ++iter){ + ret = pio_delete_file_from_list(*iter); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, PIO_EINTERNAL, __FILE__, __LINE__, + "Error deleting file from global list"); + } + } + + return PIO_NOERR; +} + +/** Print I/O stats for all files in the iosystem + * + * This call can be expensive when a lot of files are open + * (and not expensive when called during I/O system finalize, + * when all the I/O systems finalize at the same time and the + * user is careful about closing all open files before the + * I/O systems are finalized) + * This call can be used to force writing out file I/O stats + * for all the files in an I/O system (e.g. not all files + * are closed and we need the file I/O stats of these files) + * @param iosysp Pointer to the I/O system, iosystem_desc_t + * @returns PIO_NOERR on success, error code otherwise + */ +int spio_write_all_file_iostats(iosystem_desc_t *iosysp) +{ + for(std::map::iterator iter = + SPIO_Util::SPIO_Lists::GVars::pio_file_list.begin(); + iter != SPIO_Util::SPIO_Lists::GVars::pio_file_list.end(); ++iter){ + if(iter->second->iosystem == iosysp){ + assert(iter->second->iosystem->iosysid == iosysp->iosysid); + return spio_write_file_io_summary(iter->second); + } + } + return PIO_NOERR; +} + +static int finalize_file_list(void ) +{ + /* No valid I/O systems, clear out the file list */ + for(std::map::iterator fiter = + SPIO_Util::SPIO_Lists::GVars::pio_file_list.begin(); + fiter != SPIO_Util::SPIO_Lists::GVars::pio_file_list.end(); ++fiter){ + file_desc_t *file = (*fiter).second; + pio_free_file(file); + } + + SPIO_Util::SPIO_Lists::GVars::pio_file_list.clear(); + + return PIO_NOERR; +} + +/** + * Delete iosystem (iosytem_desc_t) from the global list of + * available iosystems + * + * @param iosysid The id of the iosystem to delete + * @returns 0 on success, error code otherwise + */ +int pio_delete_iosystem_from_list(int iosysid) +{ + LOG((2, "pio_delete_iosystem_from_list(iosysid=%d)", iosysid)); + std::map::iterator iter = + SPIO_Util::SPIO_Lists::GVars::pio_iosystem_list.find(iosysid); + if(iter != SPIO_Util::SPIO_Lists::GVars::pio_iosystem_list.end()){ + iosystem_desc_t *iosys = (*iter).second; + free(iosys); + SPIO_Util::SPIO_Lists::GVars::pio_iosystem_list.erase(iter); + if(SPIO_Util::SPIO_Lists::GVars::pio_iosystem_list.size() == 0){ + /* Unfortunately in some cases not all procs cleanup the files as needed + * e.g. PIO_RETURN_ERROR + non-I/O procs on a create file call + */ + return finalize_file_list(); + } + } + else{ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Deleting iosystem (iosysid=%d) from the internal global list failed. Invalid iosystem id provided", iosysid); + } + return PIO_NOERR; +} + +/** + * Add iosystem (iosystem_desc_t) to a global list. + * This function guarantees that iosystems (ioid of the + * iosystems) are unique across the comm provided + * + * @param ios Pointer to the iosystem info (iosystem_desc_t) + * to add to the list. + * @param comm MPI Communicator across which the iosystems + * need to be unique + * @returns The id of the newly added iosystem. + */ +int pio_add_to_iosystem_list(iosystem_desc_t *ios, MPI_Comm comm) +{ + static int pio_iosystem_next_ioid = PIO_IOSYSTEM_START_ID; + + assert(ios); + LOG((2, "pio_add_to_iosystem_list(ios=%p)", ios)); + + if(comm != MPI_COMM_NULL){ + int tmp_id = pio_iosystem_next_ioid; + int mpierr = MPI_Allreduce(&tmp_id, &pio_iosystem_next_ioid, 1, + MPI_INT, MPI_MAX, comm); + assert(mpierr == MPI_SUCCESS); + } + ios->iosysid = pio_iosystem_next_ioid; + pio_iosystem_next_ioid++; + + SPIO_Util::SPIO_Lists::GVars::pio_iosystem_list.insert({ios->iosysid, ios}); + LOG((2, "iosystem %p (iosysid=%d) added to the global list", ios, ios->iosysid)); + + return ios->iosysid; +} + +/** + * Get iosystem info (iosystem_desc_t) from the global list of available + * iosystems. + * + * @param iosysid The id of the iosystem to lookup + * @returns pointer to iosystem_desc_t, or NULL if not found. + */ +iosystem_desc_t *pio_get_iosystem_from_id(int iosysid) +{ + LOG((2, "pio_get_iosystem_from_id(iosysid=%d)", iosysid)); + + iosystem_desc_t *ios = NULL; + try{ + ios = SPIO_Util::SPIO_Lists::GVars::pio_iosystem_list.at(iosysid); + } catch(const std::out_of_range &e){ + LOG((1, "Finding iosytem corresponding to iosysid = %d failed. Invalid iosystem id provided", iosysid)); + } + + return ios; +} + +/** + * Get the number of open/available/valid iosystems. + * + * @param niosysid Pointer to integer that will receive the number of + * valid iosystems + * @returns 0 for success. + */ +int pio_num_iosystem(int *niosys) +{ + assert(niosys); + + *niosys = static_cast(SPIO_Util::SPIO_Lists::GVars::pio_iosystem_list.size()); + + return PIO_NOERR; +} + +/** + * Add an I/O descriptor (io_desc_t) to the global + * list of valid iodescs. + * This function guarantees that iodescs (id of the + * iodescs) are unique across the comm provided + * + * @param io_desc_t Pointer to the I/O descriptor (io_desc_t) + * to add to the list + * @param comm MPI Communicator across which the iosystems + * need to be unique + * @returns the ioid of the newly added iodesc. + */ +int pio_add_to_iodesc_list(io_desc_t *iodesc, MPI_Comm comm) +{ + static int pio_iodesc_next_id = PIO_IODESC_START_ID; + + assert(iodesc); + LOG((2, "pio_add_to_iodesc_list(iodesc=%p)", iodesc)); + + if(comm != MPI_COMM_NULL){ + int tmp_id = pio_iodesc_next_id; + int mpierr = MPI_Allreduce(&tmp_id, &pio_iodesc_next_id, 1, MPI_INT, MPI_MAX, comm); + assert(mpierr == MPI_SUCCESS); + } + iodesc->ioid = pio_iodesc_next_id; + pio_iodesc_next_id++; + + SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.insert({iodesc->ioid, iodesc}); + LOG((2, "Added iodesc %p (ioid=%d) to the global list", iodesc, iodesc->ioid)); + return iodesc->ioid; +} + +/** + * Get the I/O descriptor (io_desc_t) associated with a I/O descriptor id. + * + * @param ioid The id of the I/O descriptor (io_desc_t) to lookup + * @returns Pointer to the I/O descriptor (io_desc_t) associated with the id + */ +io_desc_t *pio_get_iodesc_from_id(int ioid) +{ + LOG((2, "pio_get_iodesc_from_id(ioid=%d)", ioid)); + + io_desc_t *iodesc = NULL; + try{ + iodesc = SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.at(ioid); + } catch(const std::out_of_range &e){ + LOG((1, "Finding I/O descriptor corresponding to ioid = %d failed. Invalid I/O descriptor id provided", ioid)); + } + + return iodesc; +} + +/** + * Delete a I/O descriptor from the global list of valid I/O descriptors + * + * @param ioid The id of the I/O descriptor (io_desc_t) to delete + * @returns 0 on success, error code otherwise. + */ +int pio_delete_iodesc_from_list(int ioid) +{ + LOG((2, "pio_delete_iodesc_from_list(ioid=%d)", ioid)); + std::map::iterator iter = SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.find(ioid); + if(iter != SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.end()){ + io_desc_t *iodesc = (*iter).second; + assert(iodesc); + if(iodesc->nasync_pend_ops == 0){ + free(iodesc); + SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.erase(iter); + } + } + else{ + return pio_err(NULL, NULL, PIO_EBADID, __FILE__, __LINE__, + "Deleting I/O descriptor info (ioid=%d) from internal global list failed. Invalid I/O descriptor id provided", ioid); + } + return PIO_NOERR; +} + +static int spio_wait_async_iodesc_ops(io_desc_t *iodesc) +{ + const int SLEEP_TIME_IN_MILLISECONDS = 500; + while(iodesc->nasync_pend_ops > 0){ + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_IN_MILLISECONDS)); + } + + return PIO_NOERR; +} + +int pio_delete_all_iodescs(int iosysid) +{ + int ret = PIO_NOERR; + /* Delete the head of the list, one at a time - to delete all I/O descs */ + std::map::iterator iter = SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.begin(); + while(iter != SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.end()){ + io_desc_t *iodesc = (*iter).second; + ret = spio_wait_async_iodesc_ops(iodesc); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Deleting I/O descriptor (ioid = %d) failed. Error while waiting for async ops on I/O descriptor", iodesc->ioid); + } + ret = PIOc_freedecomp_impl(iosysid, iodesc->ioid); + if(ret != PIO_NOERR){ + return pio_err(NULL, NULL, ret, __FILE__, __LINE__, + "Deleting I/O descriptor (ioid = %d) failed. Error when freeing I/O decomp after waiting for async ops on I/O descriptor", iodesc->ioid); + } + + /* Get the latest head */ + iter = SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.begin(); + } + + SPIO_Util::SPIO_Lists::GVars::pio_iodesc_list.clear(); + return PIO_NOERR; +} diff --git a/src/clib/pio_sdecomps_regex.cpp b/src/clib/core/util/pio_sdecomps_regex.cpp similarity index 97% rename from src/clib/pio_sdecomps_regex.cpp rename to src/clib/core/util/pio_sdecomps_regex.cpp index 03dd26f41f3..d4c085965f7 100644 --- a/src/clib/pio_sdecomps_regex.cpp +++ b/src/clib/core/util/pio_sdecomps_regex.cpp @@ -616,6 +616,12 @@ bool pio_save_decomps_regex_match(int ioid, const char *fname, const char *vname /* Match everything */ return true; } + +bool PIO_Util::spio_chunk_regex_match(int ioid, const std::string &fname, const std::string &vname) +{ + return true; +} + #else /* SPIO_NO_CXX_REGEX */ static PIO_Util::PIO_save_decomp_regex pio_sdecomp_regex(PIO_SAVE_DECOMPS_REGEX); @@ -629,4 +635,15 @@ bool pio_save_decomps_regex_match(int ioid, const char *fname, const char *vname std::string vname_str((vname) ? vname : ""); return pio_sdecomp_regex.matches(ioid, fname_str, vname_str); } + +static PIO_Util::PIO_save_decomp_regex spio_chunk_regex(SPIO_ENABLE_CHUNKING_REGEX); +bool PIO_Util::spio_chunk_regex_match(int ioid, const std::string &fname, const std::string &vname) +{ + bool ioid_is_valid = (ioid >= 0) ? true : false; + if(!ioid_is_valid && fname.empty() && vname.empty()){ + return false; + } + + return spio_chunk_regex.matches(ioid, fname, vname); +} #endif /* SPIO_NO_CXX_REGEX */ diff --git a/src/clib/pio_sdecomps_regex.h b/src/clib/core/util/pio_sdecomps_regex.h similarity index 100% rename from src/clib/pio_sdecomps_regex.h rename to src/clib/core/util/pio_sdecomps_regex.h diff --git a/src/clib/pio_sdecomps_regex.hpp b/src/clib/core/util/pio_sdecomps_regex.hpp similarity index 98% rename from src/clib/pio_sdecomps_regex.hpp rename to src/clib/core/util/pio_sdecomps_regex.hpp index 31e71f23d32..cc9261360eb 100644 --- a/src/clib/pio_sdecomps_regex.hpp +++ b/src/clib/core/util/pio_sdecomps_regex.hpp @@ -190,6 +190,9 @@ namespace PIO_Util{ */ std::vector postfix_exp_; }; + + bool spio_chunk_regex_match(int ioid, const std::string &fname, const std::string &vname); + } // namespace PIO_Util #endif // __PIO_SDECOMPS_REGEX_HPP__ diff --git a/src/clib/core/util/spio_decomp_logger.hpp b/src/clib/core/util/spio_decomp_logger.hpp new file mode 100644 index 00000000000..ffb0198e92e --- /dev/null +++ b/src/clib/core/util/spio_decomp_logger.hpp @@ -0,0 +1,220 @@ +#ifndef __SPIO_DECOMP_LOGGER__ +#define __SPIO_DECOMP_LOGGER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" +#include "mpi.h" + +namespace SPIO_Util{ + namespace Decomp_Util{ + + /* FIXME: Allow multiple decompositions dumped into the same file */ + class Decomp_logger{ + public: + Decomp_logger(MPI_Comm comm, MPI_Comm io_comm, MPI_Comm agg_comm, std::string &log_fname):comm_(comm), io_comm_(io_comm), agg_comm_(agg_comm), is_io_proc_(io_comm != MPI_COMM_NULL), comm_sz_(-1), comm_rank_(-1), io_comm_sz_(-1), io_comm_rank_(-1), agg_comm_sz_(-1), agg_comm_rank_(-1), log_fname_(log_fname), mode_(OPEN_MODE_RD){ + int ret = MPI_SUCCESS; + + assert(comm_ != MPI_COMM_NULL); + assert(agg_comm_ != MPI_COMM_NULL); + + ret = MPI_Comm_size(comm_, &comm_sz_); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_rank(comm_, &comm_rank_); assert(ret == MPI_SUCCESS); + if(is_io_proc_){ + ret = MPI_Comm_size(io_comm_, &io_comm_sz_); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_rank(io_comm_, &io_comm_rank_); assert(ret == MPI_SUCCESS); + } + ret = MPI_Comm_size(agg_comm_, &agg_comm_sz_); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_rank(agg_comm_, &agg_comm_rank_); assert(ret == MPI_SUCCESS); + } + + Decomp_logger &read_only(void ){ + mode_ |= OPEN_MODE_RD; + mode_ &= ~OPEN_MODE_WR; + return *this; + } + + Decomp_logger &write_only(void ){ + mode_ |= OPEN_MODE_WR; + mode_ &= ~OPEN_MODE_RD; + return *this; + } + + bool is_read_only(void ) const { + return (((mode_ & OPEN_MODE_WR) == 0) && ((mode_ & OPEN_MODE_RD) != 0)); + } + bool is_write_only(void ) const { + return (((mode_ & OPEN_MODE_RD) == 0) && ((mode_ & OPEN_MODE_WR) != 0)); + } + + std::string get_log_fname(void ) const { return log_fname_; } + + virtual Decomp_logger &open(void ) = 0; + + virtual void get_info(std::string &version, int &nprocs, int &ngdims, PIO_Offset &lcompmap_sz) = 0; + virtual void get_gdims(int *gdims, std::size_t gdims_sz) = 0; + virtual void get_lcompmap(PIO_Offset *lcompmap, std::size_t lcompmap_sz) = 0; + + virtual Decomp_logger &get(std::string &version, int &nprocs, std::vector &gdims, std::vector &lcompmap) = 0; + + virtual Decomp_logger &put(io_desc_t *iodesc) = 0; + + virtual void close(void ) = 0; + + virtual ~Decomp_logger(){ + MPI_Comm_free(&comm_); + if(io_comm_ != MPI_COMM_NULL){ + MPI_Comm_free(&io_comm_); + } + MPI_Comm_free(&agg_comm_); + } + protected: + static const char OPEN_MODE_RD = 0x1; + static const char OPEN_MODE_WR = 0x10; + + MPI_Comm comm_; + MPI_Comm io_comm_; + MPI_Comm agg_comm_; + bool is_io_proc_; + int comm_sz_; + int comm_rank_; + int io_comm_sz_; + int io_comm_rank_; + int agg_comm_sz_; + int agg_comm_rank_; + std::string log_fname_; + char mode_; + }; + + class Decomp_txt_logger : public Decomp_logger{ + public: + Decomp_txt_logger(MPI_Comm comm, MPI_Comm io_comm, MPI_Comm agg_comm, std::string log_fname) : Decomp_logger(comm, io_comm, agg_comm, log_fname){} + virtual Decomp_logger &open(void ); + + virtual void get_info(std::string &version, int &nprocs, int &ngdims, PIO_Offset &lcompmap_sz); + virtual void get_gdims(int *gdims, std::size_t gdims_sz); + virtual void get_lcompmap(PIO_Offset *lcompmap, std::size_t lcompmap_sz); + + virtual Decomp_logger &get(std::string &version, int &nprocs, std::vector &gdims, std::vector &lcompmap); + + virtual Decomp_logger &put(io_desc_t *iodesc); + + virtual void close(void ); + + virtual ~Decomp_txt_logger() {} + private: + }; + + class Decomp_nc_logger : public Decomp_logger{ + public: + Decomp_nc_logger(MPI_Comm comm, MPI_Comm io_comm, MPI_Comm agg_comm, std::string log_fname) : Decomp_logger(comm, io_comm, agg_comm, log_fname), ncid_(INVALID_ID), comm_sz_dimid_(INVALID_ID), info_cached_(false), version_att_name_("version"), nprocs_att_name_("nprocs"), ndims_att_name_("ndims"), gdimlen_att_name_("gdimlen"), comm_sz_dim_name_("comm_sz"), counts_var_name_("counts"), nregions_var_name_("nregions"), gmaplen_dim_name_("gmaplen"), gmap_var_name_("gmap"), gmap_nregions_dim_name_("gmap_nregions"), gmap_regions_var_name_("gmap_regions"), ioid_att_name_("ioid"), nprocs_(0){ + } + virtual Decomp_logger &open(void ); + + virtual void get_info(std::string &version, int &nprocs, int &ngdims, PIO_Offset &lcompmap_sz); + virtual void get_gdims(int *gdims, std::size_t gdims_sz); + virtual void get_lcompmap(PIO_Offset *lcompmap, std::size_t lcompmap_sz); + + virtual Decomp_logger &get(std::string &version, int &nprocs, std::vector &gdims, std::vector &lcompmap); + + virtual Decomp_logger &put(io_desc_t *iodesc); + + virtual void close(void ); + + virtual ~Decomp_nc_logger() { assert(ncid_ == INVALID_ID); } + private: + static const int INVALID_ID = -1; + int ncid_; + int comm_sz_dimid_; + bool info_cached_; + + std::string version_; + const std::string version_att_name_; + const std::string nprocs_att_name_; + std::vector gdims_; + const std::string ndims_att_name_; + const std::string gdimlen_att_name_; + std::vector lcompmap_; + const std::string comm_sz_dim_name_; + const std::string counts_var_name_; + const std::string nregions_var_name_; + const std::string gmaplen_dim_name_; + const std::string gmap_var_name_; + const std::string gmap_nregions_dim_name_; + const std::string gmap_regions_var_name_; + const std::string ioid_att_name_; + int nprocs_; + + void gather_starts_counts(std::vector &agg_starts, std::vector &agg_counts, MPI_Offset &agg_io_chunk_sz, io_desc_t *iodesc); + void gather_nregions_starts_counts(std::vector &agg_nregions_starts, std::vector &agg_nregions_counts, MPI_Offset &agg_nregions, const std::vector &lregions); + void gather_gmap(const std::vector &starts, const std::vector &counts, std::vector &gmap_chunk, io_desc_t *iodesc); + void gather_gmap_regions(const std::vector &starts, const std::vector &counts, std::vector &gmap_regions, const std::vector &lregions); + void read_and_cache_info(void ); + void get_contig_map_regions(std::vector &lregions, io_desc_t *iodesc); + void get_map_from_regions(std::vector &lregions, std::vector &lcompmap); + }; + + inline Decomp_logger *create_decomp_logger(MPI_Comm comm, MPI_Comm io_comm, MPI_Comm agg_comm, std::string log_fname){ +#ifdef _PNETCDF + char NC_FILE_EXTN_SEP = '.'; + std::string NC_FILE_EXTN("NC"); + std::size_t file_extn_pos = log_fname.rfind(NC_FILE_EXTN_SEP); + std::string file_extn = (file_extn_pos != std::string::npos) ? log_fname.substr(file_extn_pos + 1) : ""; + + std::transform(file_extn.begin(), file_extn.end(), file_extn.begin(), [](unsigned char c){ return std::toupper(c); }); + + if(file_extn == NC_FILE_EXTN){ + return new Decomp_nc_logger(comm, io_comm, agg_comm, log_fname); + } +#endif + return new Decomp_txt_logger(comm, io_comm, agg_comm, log_fname); + } + + inline Decomp_logger *create_decomp_logger(MPI_Comm ucomm, std::string log_fname){ + MPI_Comm comm = MPI_COMM_NULL; + MPI_Comm io_comm = MPI_COMM_NULL; + MPI_Comm agg_comm = MPI_COMM_NULL; + int agg_comm_rank = -1; + int ret = MPI_SUCCESS; + + ret = MPI_Comm_dup(ucomm, &comm); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &agg_comm); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_rank(agg_comm, &agg_comm_rank); assert(ret == MPI_SUCCESS); + + int color = (agg_comm_rank == 0) ? 0 : MPI_UNDEFINED; + ret = MPI_Comm_split(comm, color, 0, &io_comm); assert(ret == MPI_SUCCESS); + + return create_decomp_logger(comm, io_comm, agg_comm, log_fname); + } + + inline Decomp_logger *create_decomp_logger(iosystem_desc_t *ios, std::string log_fname){ + MPI_Comm comm = MPI_COMM_NULL; + MPI_Comm io_comm = MPI_COMM_NULL; + MPI_Comm agg_comm = MPI_COMM_NULL; + int agg_comm_rank = -1; + int ret = MPI_SUCCESS; + + ret = MPI_Comm_dup(ios->union_comm, &comm); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_dup(ios->node_comm, &agg_comm); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_rank(agg_comm, &agg_comm_rank); assert(ret == MPI_SUCCESS); + + int color = (agg_comm_rank == 0) ? 0 : MPI_UNDEFINED; + ret = MPI_Comm_split(comm, color, 0, &io_comm); assert(ret == MPI_SUCCESS); + return create_decomp_logger(comm, io_comm, agg_comm, log_fname); + } + + } //namespace Decomp_Util +} // namespace SPIO_Util +#endif // __SPIO_DECOMP_LOGGER__ diff --git a/src/clib/core/util/spio_decomp_map_info_pool.cpp b/src/clib/core/util/spio_decomp_map_info_pool.cpp new file mode 100644 index 00000000000..b27e2da37b3 --- /dev/null +++ b/src/clib/core/util/spio_decomp_map_info_pool.cpp @@ -0,0 +1,50 @@ +#include "spio_decomp_map_info_pool.hpp" +#include +#include +#include "mpi.h" + +SPIO_Util::Decomp_Util::Decomp_map_info_pool *SPIO_Util::Decomp_Util::Decomp_map_info_pool_manager::dpool_ = NULL; + +static SPIO_Util::Decomp_Util::Decomp_map_info_pool_manager gdpool_mgr; + +SPIO_Util::Decomp_Util::Decomp_map_info_pool * + SPIO_Util::Decomp_Util::Decomp_map_info_pool_manager::get_decomp_map_info_pool(void ) +{ + if(dpool_ == NULL){ + dpool_ = new SPIO_Util::Decomp_Util::Decomp_map_info_pool(); + } + + return dpool_; +} + +SPIO_Util::Decomp_Util::Decomp_map_info_pool_manager::~Decomp_map_info_pool_manager() +{ + if(dpool_){ + delete(dpool_); + dpool_ = NULL; + } +} + +void SPIO_Util::Decomp_Util::serialize_decomp_map_info_pool(iosystem_desc_t *ios) +{ + static bool decomp_info_serialized = false; + + assert(ios); + if(decomp_info_serialized) return; + + MPI_Comm comm = ios->union_comm; + int rank = -1; + MPI_Comm_rank(comm, &rank); + if(rank != 0) return; + + SPIO_Util::Decomp_Util::Decomp_map_info_pool *dpool = SPIO_Util::Decomp_Util::gdpool_mgr.get_decomp_map_info_pool(); + std::string decomp_map_pool_info = dpool->to_string(); + + std::string fname = std::string("pio_decomp_map_info_") + std::to_string(ios->iosysid) + std::string(".txt"); + std::ofstream ostr(fname.c_str()); + if(ostr.is_open()){ + ostr << decomp_map_pool_info.c_str() << "\n"; + ostr.close(); + decomp_info_serialized = true; + } +} diff --git a/src/clib/core/util/spio_decomp_map_info_pool.hpp b/src/clib/core/util/spio_decomp_map_info_pool.hpp new file mode 100644 index 00000000000..da1c27905c1 --- /dev/null +++ b/src/clib/core/util/spio_decomp_map_info_pool.hpp @@ -0,0 +1,98 @@ +#ifndef __SPIO_DECOMP_MAP_INFO_HPP__ +#define __SPIO_DECOMP_MAP_INFO_HPP__ + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" +#include "pio_types.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "mpi.h" + +namespace SPIO_Util{ + namespace Decomp_Util{ + class Decomp_map_info_pool{ + public: + void add_decomp_map_info(int decomp_map_id, const char *decomp_map_fname){ + decomp_map_info_[decomp_map_id] = std::string(decomp_map_fname); + } + void add_var_info(int decomp_map_id, int fid, const char *fname, + int vid, const char *vname){ + Var_info vinfo = {vid, decomp_map_id, decomp_map_info_[decomp_map_id], std::string(vname)}; + + finfo_[fid].fid = fid; + finfo_[fid].fname = std::string(fname); + finfo_[fid].vinfo[vid] = vinfo; + } + + std::string to_string(void ) const{ + std::ostringstream ostr; + for(std::map::const_iterator finfo_iter = finfo_.cbegin(); + finfo_iter != finfo_.cend(); ++finfo_iter){ + ostr << (*finfo_iter).second.to_string() << "\n"; + } + return ostr.str(); + } + private: + struct Var_info{ + int vid; + int decomp_map_id; + std::string decomp_map_fname; + std::string vname; + + std::string to_string(void ) const{ + return std::string("{") + + std::string("vid = ") + std::to_string(vid) + + std::string(", decomp_map_id = ") + std::to_string(decomp_map_id) + + std::string(", decomp_map_fname = ") + decomp_map_fname + + std::string(", vname = ") + vname + + std::string("}"); + } + }; + + struct File_info{ + int fid; + std::string fname; + std::map vinfo; + + std::string to_string(void ) const{ + std::ostringstream ostr; + ostr << "{"; + ostr << "fid = " << fid; + ostr << ", fname = " << fname.c_str(); + ostr << ", vinfo = \n"; + for(std::map::const_iterator vinfo_iter = vinfo.cbegin(); + vinfo_iter != vinfo.cend(); ++vinfo_iter){ + ostr << (*vinfo_iter).second.to_string() << "\n"; + } + ostr << "}"; + return ostr.str(); + } + }; + + std::map decomp_map_info_; + std::map finfo_; + }; + + class Decomp_map_info_pool_manager{ + public: + static Decomp_map_info_pool *get_decomp_map_info_pool(void ); + ~Decomp_map_info_pool_manager(); + private: + static Decomp_map_info_pool *dpool_; + }; + + extern Decomp_map_info_pool_manager gdpool_mgr; + + void serialize_decomp_map_info_pool(iosystem_desc_t *ios); + } // SPIO_Util +} // Decomp_Util + +#endif // __SPIO_DECOMP_MAP_INFO_HPP__ diff --git a/src/clib/core/util/spio_decomp_nc_logger.cpp b/src/clib/core/util/spio_decomp_nc_logger.cpp new file mode 100644 index 00000000000..2acea244e76 --- /dev/null +++ b/src/clib/core/util/spio_decomp_nc_logger.cpp @@ -0,0 +1,604 @@ +#include "spio_decomp_logger.hpp" +#include "spio_dbg_utils.hpp" +#include +#include +#include +#ifdef _PNETCDF +#include "pnetcdf.h" +#endif + +SPIO_Util::Decomp_Util::Decomp_logger& SPIO_Util::Decomp_Util::Decomp_nc_logger::open(void ) +{ +#ifdef _PNETCDF + int ret = MPI_SUCCESS; + + version_ = std::to_string(PIO_VERSION_MAJOR) + "." + + std::to_string(PIO_VERSION_MINOR) + "." + + std::to_string(PIO_VERSION_PATCH); + nprocs_ = comm_sz_; + + if(!is_io_proc_){ + return *this; + } + + MPI_Info info = MPI_INFO_NULL; + + ret = MPI_Info_create(&info); assert(ret == MPI_SUCCESS); + ret = MPI_Info_set(info, "nc_var_align_size", "1"); assert(ret == MPI_SUCCESS); + + int omode = NC_64BIT_DATA; + if(is_read_only()){ + omode |= NC_NOWRITE; + + ret = ncmpi_open(io_comm_, log_fname_.c_str(), omode, info, &ncid_); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Opening decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + } + else{ + assert(is_write_only()); + omode |= (NC_WRITE | NC_CLOBBER); + + ret = ncmpi_create(io_comm_, log_fname_.c_str(), omode, info, &ncid_); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Creating decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_def_dim(ncid_, comm_sz_dim_name_.c_str(), comm_sz_, &comm_sz_dimid_); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Defining comm_sz dimension in decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_put_att_text(ncid_, NC_GLOBAL, version_att_name_.c_str(), version_.size() + 1, version_.c_str()); + if(ret == NC_NOERR){ + ret = ncmpi_put_att_int(ncid_, NC_GLOBAL, nprocs_att_name_.c_str(), NC_INT, 1, &nprocs_); + } + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Adding SCORPIO version/nprocs attributes to decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_enddef(ncid_); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Ending define mode for decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + } + + MPI_Info_free(&info); +#endif + return *this; +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::get_info(std::string &version, int &nprocs, int &ngdims, PIO_Offset &lcompmap_sz) +{ +#ifdef _PNETCDF + if(!info_cached_){ + read_and_cache_info(); + } +#endif + if(info_cached_){ + version = version_; + nprocs = nprocs_; + ngdims = static_cast(gdims_.size()); + lcompmap_sz = static_cast(lcompmap_.size()); + return; + } +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::get_gdims(int *gdims, std::size_t gdims_sz) +{ +#ifdef _PNETCDF + if(!info_cached_){ + read_and_cache_info(); + } +#endif + if(info_cached_){ + assert(gdims && (gdims_sz == gdims_.size())); + std::copy(gdims_.cbegin(), gdims_.cend(), gdims); + return; + } +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::get_lcompmap(PIO_Offset *lcompmap, std::size_t lcompmap_sz) +{ +#ifdef _PNETCDF + if(!info_cached_){ + read_and_cache_info(); + } +#endif + if(info_cached_){ + assert(lcompmap && (lcompmap_sz == lcompmap_.size())); + std::copy(lcompmap_.cbegin(), lcompmap_.cend(), lcompmap); + return; + } +} + +SPIO_Util::Decomp_Util::Decomp_logger& SPIO_Util::Decomp_Util::Decomp_nc_logger::get(std::string &version, int &nprocs, std::vector &gdims, std::vector &lcompmap) +{ +#ifdef _PNETCDF + if(!info_cached_){ + read_and_cache_info(); + } +#endif + if(info_cached_){ + version = version_; + nprocs = nprocs_; + gdims.resize(gdims_.size()); + std::copy(gdims_.cbegin(), gdims_.cend(), gdims.begin()); + lcompmap.resize(lcompmap_.size()); + std::copy(lcompmap_.cbegin(), lcompmap_.cend(), lcompmap.begin()); + } + return *this; +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::read_and_cache_info(void ) +{ + int ret = NC_NOERR; +#ifdef _PNETCDF + MPI_Offset version_len = 0; + int ndims = 0; + std::vector agg_counts(agg_comm_sz_, 0); + std::vector agg_nregions(agg_comm_sz_, 0); + std::vector agg_gmap_chunk; + std::vector agg_gmap_regions; + if(is_io_proc_){ + assert(ncid_ != INVALID_ID); + + ret = ncmpi_inq_attlen(ncid_, NC_GLOBAL, version_att_name_.c_str(), &version_len); + if(ret == NC_NOERR){ + version_.resize(static_cast(version_len) + 1); + + char tmp_str[version_len + 1]; + tmp_str[version_len] = '\0'; + ret = ncmpi_get_att_text(ncid_, NC_GLOBAL, version_att_name_.c_str(), tmp_str); + version_.assign(tmp_str); + } + if(ret == NC_NOERR){ + ret = ncmpi_get_att_int(ncid_, NC_GLOBAL, nprocs_att_name_.c_str(), &nprocs_); + } + if(ret == NC_NOERR){ + ret = ncmpi_get_att_int(ncid_, NC_GLOBAL, ndims_att_name_.c_str(), &ndims); + if(ret == NC_NOERR){ + gdims_.resize(ndims); + ret = ncmpi_get_att_int(ncid_, NC_GLOBAL, gdimlen_att_name_.c_str(), gdims_.data()); + } + } + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Reading attributes (version/nprocs/ndims/gdimlen) from decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + //std::pair proc_range = {comm_rank_, comm_rank_ + agg_comm_sz_}; + + MPI_Offset start_var = comm_rank_; + MPI_Offset count_var = (comm_rank_ + agg_comm_sz_ <= comm_sz_) ? agg_comm_sz_ : (comm_sz_ - comm_rank_); + + int tmp_varid = -1; + ret = ncmpi_inq_varid(ncid_, counts_var_name_.c_str(), &tmp_varid); + if(ret == NC_NOERR){ + ret = ncmpi_get_vara_int_all(ncid_, tmp_varid, &start_var, &count_var, agg_counts.data()); + } + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Reading counts array from decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_inq_varid(ncid_, nregions_var_name_.c_str(), &tmp_varid); + if(ret == NC_NOERR){ + ret = ncmpi_get_vara_int_all(ncid_, tmp_varid, &start_var, &count_var, agg_nregions.data()); + } + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Reading nregions array from decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + MPI_Offset agg_io_chunk_sz = 0; + MPI_Offset tot_agg_nregions = 0; + for(std::size_t i = 0; i < agg_counts.size(); i++){ + agg_io_chunk_sz += agg_counts[i]; + tot_agg_nregions += agg_nregions[i]; + } + + //MPI_Offset agg_io_chunk_start = agg_io_chunk_sz; + std::vector agg_info_start = {agg_io_chunk_sz, tot_agg_nregions}; + ret = MPI_Exscan(MPI_IN_PLACE, agg_info_start.data(), + static_cast(agg_info_start.size()), MPI_OFFSET, + MPI_SUM, io_comm_); assert(ret == MPI_SUCCESS); + MPI_Offset agg_io_chunk_start = 0; + MPI_Offset agg_regions_start = 0; + if(io_comm_rank_ != 0){ + agg_io_chunk_start = agg_info_start[0]; + agg_regions_start = agg_info_start[1]; + } + + agg_gmap_chunk.resize(agg_io_chunk_sz); + ret = ncmpi_inq_varid(ncid_, gmap_var_name_.c_str(), &tmp_varid); + if(ret == NC_NOERR){ + ret = ncmpi_get_vara_longlong_all(ncid_, tmp_varid, &agg_io_chunk_start, + &agg_io_chunk_sz, agg_gmap_chunk.data()); + } + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Reading gmap array from decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + agg_gmap_regions.resize(tot_agg_nregions); + ret = ncmpi_inq_varid(ncid_, gmap_regions_var_name_.c_str(), &tmp_varid); + if(ret == NC_NOERR){ + ret = ncmpi_get_vara_longlong_all(ncid_, tmp_varid, &agg_regions_start, + &tot_agg_nregions, agg_gmap_regions.data()); + } + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Reading gmap (sc format) array from decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + } + + ret = MPI_Bcast(&version_len, 1, MPI_OFFSET, 0, agg_comm_); assert(ret == MPI_SUCCESS); + ret = MPI_Bcast(&nprocs_, 1, MPI_INT, 0, agg_comm_); assert(ret == MPI_SUCCESS); + ret = MPI_Bcast(&ndims, 1, MPI_INT, 0, agg_comm_); assert(ret == MPI_SUCCESS); + + char tmp_str[version_len + 1]; + tmp_str[version_len] = '\0'; + std::copy(version_.cbegin(), version_.cend(), tmp_str); + ret = MPI_Bcast(tmp_str, version_len, MPI_CHAR, 0, agg_comm_); assert(ret == MPI_SUCCESS); + if(agg_comm_rank_ != 0){ + version_.assign(tmp_str); + gdims_.resize(ndims); + } + ret = MPI_Bcast(gdims_.data(), ndims, MPI_INT, 0, agg_comm_); assert(ret == MPI_SUCCESS); + + int lcompmap_sz = 0; + ret = MPI_Scatter(agg_counts.data(), 1, MPI_INT, &lcompmap_sz, 1, MPI_INT, 0, agg_comm_); assert(ret == MPI_SUCCESS); + + lcompmap_.resize(lcompmap_sz); + std::vector agg_starts(agg_counts.size()); + int cur_start = 0; + for(std::size_t i = 0; i < agg_starts.size(); i++){ + agg_starts[i] = cur_start; + cur_start += agg_counts[i]; + } + ret = MPI_Scatterv(agg_gmap_chunk.data(), agg_counts.data(), agg_starts.data(), MPI_OFFSET, + lcompmap_.data(), lcompmap_sz, MPI_OFFSET, 0, agg_comm_); assert(ret == MPI_SUCCESS); + + int lcompmap_nregions = 0; + ret = MPI_Scatter(agg_nregions.data(), 1, MPI_INT, &lcompmap_nregions, 1, MPI_INT, 0, agg_comm_); assert(ret == MPI_SUCCESS); + + std::vector lcompmap_regions; + lcompmap_regions.resize(lcompmap_nregions); + std::vector lcompmap_region_starts(agg_nregions.size()); + cur_start = 0; + for(std::size_t i = 0; i < agg_nregions.size(); i++){ + lcompmap_region_starts[i] = cur_start; + cur_start += agg_nregions[i]; + } + ret = MPI_Scatterv(agg_gmap_regions.data(), agg_nregions.data(), + lcompmap_region_starts.data(), MPI_OFFSET, + lcompmap_regions.data(), lcompmap_nregions, MPI_OFFSET, + 0, agg_comm_); assert(ret == MPI_SUCCESS); + + std::vector lcompmap; + + get_map_from_regions(lcompmap_regions, lcompmap); + + assert(lcompmap.size() == lcompmap_.size()); + for(std::size_t i = 0; i < lcompmap_.size(); i++){ + assert(lcompmap[i] == lcompmap_[i]); + } + + //SPIO_Util::Dbg_Util::print_1dvec(lcompmap); + + info_cached_ = true; + +#endif +} + +SPIO_Util::Decomp_Util::Decomp_logger &SPIO_Util::Decomp_Util::Decomp_nc_logger::put(io_desc_t *iodesc) +{ +#ifdef _PNETCDF + int ret = MPI_SUCCESS; + + assert(iodesc); + + /* Cache the iodesc info */ + gdims_.resize(iodesc->ndims); + std::copy(iodesc->dimlen, iodesc->dimlen + iodesc->ndims, gdims_.begin()); + lcompmap_.resize(iodesc->maplen); + std::copy(iodesc->map, iodesc->map + iodesc->maplen, lcompmap_.begin()); + info_cached_ = true; + + std::vector agg_starts, agg_counts; + std::vector agg_nregions_starts, agg_nregions_counts; + MPI_Offset agg_io_chunk_sz = 0; + MPI_Offset agg_nregions = 0; + std::vector lregions; + + /* Aggregation of local map lengths & map happens in agg_comm_, the + * data is written out to the file using io_comm_. Each rank 0 proc in + * agg_comm is used to create the io_comm_ + */ + /* Gather/Aggregate local map lengths - map lengths of each compute proc + * should be available in agg_counts + * The starts/displacements (on the global map) for map chunks in each + * agg process should be available in agg_starts + * agg_io_chunk_sz : Total size of aggregated gmap chunk on this I/O proc + */ + agg_starts.resize(agg_comm_sz_); + agg_counts.resize(agg_comm_sz_); + gather_starts_counts(agg_starts, agg_counts, agg_io_chunk_sz, iodesc); + + agg_nregions_starts.resize(agg_comm_sz_); + agg_nregions_counts.resize(agg_comm_sz_); + get_contig_map_regions(lregions, iodesc); + gather_nregions_starts_counts(agg_nregions_starts, agg_nregions_counts, agg_nregions, lregions); + + std::vector gmap_chunk; + std::vector gmap_regions; + if(is_io_proc_){ + gmap_chunk.resize(agg_io_chunk_sz); + gmap_regions.resize(agg_nregions); + } + + /* Gather the local compmaps from compute procs to the I/O processes */ + gather_gmap(agg_starts, agg_counts, gmap_chunk, iodesc); + + gather_gmap_regions(agg_nregions_starts, agg_nregions_counts, gmap_regions, lregions); + + if(!is_io_proc_){ + return *this; + } + + assert(sizeof(PIO_Offset) == sizeof(MPI_Offset)); + MPI_Offset agg_io_chunk_count = agg_io_chunk_sz; + + /* Find starts/Displacements for "counts" var and "gmap" var among I/O processes */ + std::array starts_for_counts_and_gmap = { static_cast(agg_comm_sz_), agg_io_chunk_count, agg_nregions }; + ret = MPI_Exscan(MPI_IN_PLACE, starts_for_counts_and_gmap.data(), starts_for_counts_and_gmap.size(), MPI_OFFSET, MPI_SUM, io_comm_); assert(ret == MPI_SUCCESS); + if(io_comm_rank_ == 0){ + starts_for_counts_and_gmap = {0, 0, 0}; + } + + std::vector gmap_lsizes = {agg_io_chunk_sz, agg_nregions}; + std::vector gmap_gsizes = {0, 0}; + ret = MPI_Allreduce(gmap_lsizes.data(), gmap_gsizes.data(), + static_cast(gmap_lsizes.size()), MPI_OFFSET, + MPI_SUM, io_comm_); assert(ret == MPI_SUCCESS); + + MPI_Offset gmaplen = gmap_gsizes[0]; + MPI_Offset gmap_nregions = gmap_gsizes[1]; + + assert(ncid_ != INVALID_ID); + + ret = ncmpi_redef(ncid_); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Redefine mode for decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_put_att_int(ncid_, NC_GLOBAL, ioid_att_name_.c_str(), NC_INT, 1, &(iodesc->ioid)); + if(ret == NC_NOERR){ + ret = ncmpi_put_att_int(ncid_, NC_GLOBAL, ndims_att_name_.c_str(), NC_INT, 1, &(iodesc->ndims)); + } + if(ret == NC_NOERR){ + ret = ncmpi_put_att_int(ncid_, NC_GLOBAL, gdimlen_att_name_.c_str(), NC_INT, iodesc->ndims, iodesc->dimlen); + } + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Writing ioid/ndims/gdimlen to decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + assert(sizeof(MPI_Offset) == sizeof(int64_t)); + /* Global start offsets for reads/writes */ + int counts_varid = INVALID_ID; + int gmaplen_dimid = INVALID_ID; + int gmap_varid = INVALID_ID; + int nregions_varid = INVALID_ID; + int gmap_nregions_dimid = INVALID_ID; + int gmap_regions_varid = INVALID_ID; + + ret = ncmpi_def_var(ncid_, counts_var_name_.c_str(), NC_INT, 1, &comm_sz_dimid_, &counts_varid); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Defining counts var in decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_def_var(ncid_, nregions_var_name_.c_str(), NC_INT, 1, &comm_sz_dimid_, &nregions_varid); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Defining nregions var in decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_def_dim(ncid_, gmaplen_dim_name_.c_str(), gmaplen, &gmaplen_dimid); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Defining gmaplen dimension, size = ") + std::to_string(static_cast(gmaplen)) + std::string(", in decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_def_dim(ncid_, gmap_nregions_dim_name_.c_str(), gmap_nregions, &gmap_nregions_dimid); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Defining gmap_nregions dimension, size = ") + std::to_string(static_cast(gmaplen)) + std::string(", in decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + /* The global compmap */ + ret = ncmpi_def_var(ncid_, gmap_var_name_.c_str(), NC_INT64, 1, &gmaplen_dimid, &gmap_varid); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Defining var to store global compmap in decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_def_var(ncid_, gmap_regions_var_name_.c_str(), NC_INT64, 1, &gmap_nregions_dimid, &gmap_regions_varid); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Defining var to store global compmap regions in decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_enddef(ncid_); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Ending redefine mode for decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + /* Write compmap counts */ + MPI_Offset counts_start = starts_for_counts_and_gmap[0]; + MPI_Offset counts_count = agg_comm_sz_; + assert((counts_start < static_cast(comm_sz_)) && + (counts_count == static_cast(agg_counts.size()))); + ret = ncmpi_iput_vara_int(ncid_, counts_varid, &counts_start, &counts_count, agg_counts.data(), NULL); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Writing gmap process counts to decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + MPI_Offset nregions_start = starts_for_counts_and_gmap[0]; + MPI_Offset nregions_count = agg_comm_sz_; + assert((nregions_start < static_cast(comm_sz_)) && + (nregions_count == static_cast(agg_counts.size()))); + ret = ncmpi_iput_vara_int(ncid_, nregions_varid, &nregions_start, &nregions_count, agg_nregions_counts.data(), NULL); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Writing gmap nregions per process to decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + MPI_Offset agg_io_chunk_start = starts_for_counts_and_gmap[1]; + assert((agg_io_chunk_start < gmaplen) && (agg_io_chunk_start + agg_io_chunk_count <= gmaplen)); + /* Write compmap */ + ret = ncmpi_iput_vara_longlong(ncid_, gmap_varid, &agg_io_chunk_start, &agg_io_chunk_count, gmap_chunk.data(), NULL); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Writing gmap to decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + MPI_Offset agg_nregions_start = starts_for_counts_and_gmap[2]; + assert((agg_nregions_start < gmap_nregions) && (agg_nregions_start + agg_nregions <= gmap_nregions)); + /* Write compmap */ + ret = ncmpi_iput_vara_longlong(ncid_, gmap_regions_varid, &agg_nregions_start, &agg_nregions, gmap_regions.data(), NULL); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Writing gmap (sc format) to decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ret = ncmpi_wait_all(ncid_, NC_REQ_ALL, NULL, NULL); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Waiting on writes of gmap/counts arrays to decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + +#endif + return *this; +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::gather_starts_counts(std::vector &agg_starts, std::vector &agg_counts, MPI_Offset &agg_io_chunk_sz, io_desc_t *iodesc) +{ + int ret = MPI_SUCCESS; + + assert(iodesc && (agg_comm_sz_ > 0)); + agg_io_chunk_sz = 0; + + int lmap_sz = iodesc->maplen; + + ret = MPI_Gather(&lmap_sz, 1, MPI_INT, agg_counts.data(), 1, MPI_INT, 0, agg_comm_); assert(ret == MPI_SUCCESS); + + int cur_start = 0; + for(std::size_t i = 0; i < agg_starts.size(); i++){ + agg_starts[i] = cur_start; + cur_start += agg_counts[i]; + agg_io_chunk_sz += static_cast(agg_counts[i]); + } +} + +/* The contiguous regions in iodesc->map[] are stored in sets of {start, count} in the + * lregions array + * e.g. iodesc->map[] = {3,4,5,8,11,12} => lregions.size = starts/counts for 3 regions + * lregions = {3, 3, 8, 1, 11, 2} + */ +void SPIO_Util::Decomp_Util::Decomp_nc_logger::get_contig_map_regions(std::vector &lregions, io_desc_t *iodesc) +{ + assert(iodesc && (lregions.size() == 0)); + + PIO_Offset prev_map_val = 0; + for(int i = 0; i < iodesc->maplen; i++){ + if(lregions.size() > 0){ + if(iodesc->map[i] == prev_map_val + 1){ + /* Update region : count */ + lregions.back() += 1; + } + else if((iodesc->map[i] == 0) && (prev_map_val == 0)){ + /* Update region : count */ + lregions.back() += 1; + } + else{ + /* Add new region : start & count */ + lregions.push_back(iodesc->map[i]); + lregions.push_back(1); + } + } + else{ + /* Add first region : start & count */ + lregions.push_back(iodesc->map[i]); + lregions.push_back(1); + } + prev_map_val = iodesc->map[i]; + } +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::get_map_from_regions(std::vector &lregions, std::vector &lcompmap) +{ + assert(lregions.size() % 2 == 0); + for(std::size_t i = 0; i < lregions.size(); i += 2){ + PIO_Offset start = lregions[i]; + PIO_Offset count = lregions[i + 1]; + + std::generate_n(std::back_inserter(lcompmap), count, [&start] () mutable { return start++; }); + } +} + +/* Get the starts/counts required to write the "nregions" variable from each I/O proc */ +void SPIO_Util::Decomp_Util::Decomp_nc_logger::gather_nregions_starts_counts(std::vector &agg_nregions_starts, std::vector &agg_nregions_counts, MPI_Offset &agg_nregions, const std::vector &lregions) +{ + int ret = MPI_SUCCESS; + + assert(agg_comm_sz_ > 0); + assert(lregions.size() % 2 == 0); + + if(is_io_proc_){ + assert(static_cast(agg_nregions_starts.size()) == agg_comm_sz_); + assert(static_cast(agg_nregions_counts.size()) == agg_comm_sz_); + } + + agg_nregions = 0; + + /* lnregions = Number of region infos local to this compute proc, each region info is a {start, count} pair */ + int lnregions = static_cast(lregions.size()); + + ret = MPI_Gather(&lnregions, 1, MPI_INT, agg_nregions_counts.data(), 1, MPI_INT, 0, agg_comm_); assert(ret == MPI_SUCCESS); + + int cur_start = 0; + for(std::size_t i = 0; i < agg_nregions_starts.size(); i++){ + agg_nregions_starts[i] = cur_start; + cur_start += agg_nregions_counts[i]; + agg_nregions += static_cast(agg_nregions_counts[i]); + } +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::gather_gmap(const std::vector &starts, const std::vector &counts, std::vector &gmap_chunk, io_desc_t *iodesc) +{ + int ret = MPI_SUCCESS; + + assert(iodesc); + assert((agg_comm_rank_ != 0) || ((gmap_chunk.size() > 0) && (counts.size() > 0) && (starts.size() > 0))); + + ret = MPI_Gatherv(iodesc->map, iodesc->maplen, MPI_OFFSET, gmap_chunk.data(), + counts.data(), starts.data(), MPI_OFFSET, 0, agg_comm_); assert(ret == MPI_SUCCESS); +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::gather_gmap_regions(const std::vector &starts, const std::vector &counts, std::vector &gmap_regions, const std::vector &lregions) +{ + int ret = MPI_SUCCESS; + + assert((agg_comm_rank_ != 0) || ((gmap_regions.size() > 0) && (counts.size() > 0) && (starts.size() > 0))); + + ret = MPI_Gatherv(static_cast(lregions.data()), static_cast(lregions.size()), + MPI_OFFSET, gmap_regions.data(), + counts.data(), starts.data(), MPI_OFFSET, 0, agg_comm_); assert(ret == MPI_SUCCESS); +} + +void SPIO_Util::Decomp_Util::Decomp_nc_logger::close(void ) +{ + int ret = MPI_SUCCESS; + +#ifdef _PNETCDF + if(!is_io_proc_){ + return; + } + + assert(ncid_ != INVALID_ID); + ret = ncmpi_close(ncid_); + if(ret != NC_NOERR){ + throw std::runtime_error(std::string("Closing decomp log file, \"") + log_fname_ + std::string("\", failed, ierr =") + std::to_string(ret) + std::string(" ( ") + std::string(ncmpi_strerror(ret)) + std::string(")")); + } + + ncid_ = INVALID_ID; +#endif +} diff --git a/src/clib/core/util/spio_decomp_txt_logger.cpp b/src/clib/core/util/spio_decomp_txt_logger.cpp new file mode 100644 index 00000000000..c4aa8db8d51 --- /dev/null +++ b/src/clib/core/util/spio_decomp_txt_logger.cpp @@ -0,0 +1,32 @@ +#include "spio_decomp_logger.hpp" + +SPIO_Util::Decomp_Util::Decomp_logger& SPIO_Util::Decomp_Util::Decomp_txt_logger::open(void ) +{ + return *this; +} + +void SPIO_Util::Decomp_Util::Decomp_txt_logger::get_info(std::string &version, int &nprocs, int &ngdims, PIO_Offset &lcompmap_sz) +{ +} + +void SPIO_Util::Decomp_Util::Decomp_txt_logger::get_gdims(int *gdims, std::size_t gdims_sz) +{ +} + +void SPIO_Util::Decomp_Util::Decomp_txt_logger::get_lcompmap(PIO_Offset *lcompmap, std::size_t lcompmap_sz) +{ +} + +SPIO_Util::Decomp_Util::Decomp_logger& SPIO_Util::Decomp_Util::Decomp_txt_logger::get(std::string &version, int &nprocs, std::vector &gdims, std::vector &lcompmap) +{ + return *this; +} + +SPIO_Util::Decomp_Util::Decomp_logger& SPIO_Util::Decomp_Util::Decomp_txt_logger::put(io_desc_t *iodesc) +{ + return *this; +} + +void SPIO_Util::Decomp_Util::Decomp_txt_logger::close(void ) +{ +} diff --git a/src/clib/spio_file_mvcache.cpp b/src/clib/core/util/spio_file_mvcache.cpp similarity index 100% rename from src/clib/spio_file_mvcache.cpp rename to src/clib/core/util/spio_file_mvcache.cpp diff --git a/src/clib/spio_file_mvcache.h b/src/clib/core/util/spio_file_mvcache.h similarity index 100% rename from src/clib/spio_file_mvcache.h rename to src/clib/core/util/spio_file_mvcache.h diff --git a/src/clib/spio_file_mvcache.hpp b/src/clib/core/util/spio_file_mvcache.hpp similarity index 100% rename from src/clib/spio_file_mvcache.hpp rename to src/clib/core/util/spio_file_mvcache.hpp diff --git a/src/clib/spio_io_summary.cpp b/src/clib/core/util/spio_io_summary.cpp similarity index 100% rename from src/clib/spio_io_summary.cpp rename to src/clib/core/util/spio_io_summary.cpp diff --git a/src/clib/spio_io_summary.h b/src/clib/core/util/spio_io_summary.h similarity index 100% rename from src/clib/spio_io_summary.h rename to src/clib/core/util/spio_io_summary.h diff --git a/src/clib/spio_io_summary.hpp b/src/clib/core/util/spio_io_summary.hpp similarity index 100% rename from src/clib/spio_io_summary.hpp rename to src/clib/core/util/spio_io_summary.hpp diff --git a/src/clib/dtypes.h b/src/clib/dtypes.h deleted file mode 100644 index 9076cf0f75b..00000000000 --- a/src/clib/dtypes.h +++ /dev/null @@ -1,5 +0,0 @@ -#define TYPEDOUBLE 102 -#define TYPEINT 103 -#define TYPETEXT 100 -#define TYPELONG 104 -#define TYPEREAL 101 diff --git a/src/clib/pio.h b/src/clib/pio.h index af0d35cc1c9..a88ae6ff76c 100644 --- a/src/clib/pio.h +++ b/src/clib/pio.h @@ -1,10 +1,7 @@ /** * @file * Public headers for the PIO C interface. - * @author Jim Edwards - * @date 2014 * - * @see http://code.google.com/p/parallelio/ */ #ifndef _PIO_H_ @@ -328,111 +325,6 @@ typedef PIO_OFFSET_C_TYPENAME PIO_Offset; #define PIO_EHDF5ERR (-700) #endif -#ifdef PIO_MICRO_TIMING -/** Some fwd declarations to avoid including internal headers */ -typedef struct mtimer_info *mtimer_t; -#endif - -/* Forward decl of hash map used for ADIOS reads */ -struct spio_hmap; - -/** - * Variable description structure. - */ -typedef struct var_desc_t -{ - /* Variable ID. */ - int varid; - - /* Variable name - cached */ - char vname[PIO_MAX_NAME + 1]; - - /* Variable description */ - char vdesc[PIO_MAX_NAME + 1]; - - /* Non-zero if this is a record var (i.e. uses unlimited - * dimension). */ - int rec_var; - - /* Number of dimensions for this variable - cached data */ - int ndims; - - /** The record number to be written. Ignored if there is no - * unlimited dimension. */ - int record; - - /** FIXME: Combine request related members to a separate struct */ - /** ID of each outstanding pnetcdf request for this variable. */ - int *request; - - /** Size of each outstanding pnetcdf request for this variable */ - PIO_Offset *request_sz; - - /** Number of requests bending with pnetcdf. */ - int nreqs; - - /* Holds the fill value of this var. */ - void *fillvalue; - - /* The PIO data type (PIO_INT, PIO_FLOAT, etc.) */ - int pio_type; - - /* The size of the data type (2 for PIO_SHORT, 4 for PIO_INT, etc.) */ - PIO_Offset type_size; - - /* Size of one record of the variable (number of bytes)*/ - PIO_Offset vrsize; - - /* Bytes pending to be read */ - PIO_Offset rb_pend; - - /* Bytes pending to be written out */ - PIO_Offset wb_pend; - -#ifdef PIO_MICRO_TIMING - mtimer_t rd_mtimer; - mtimer_t rd_rearr_mtimer; - - mtimer_t wr_mtimer; - mtimer_t wr_rearr_mtimer; -#endif - - /** Non-zero if fill mode is turned on for this var. */ - int use_fill; - - /** Buffer that contains the holegrid fill values used to fill in - * missing sections of data when using the subset rearranger. */ - void *fillbuf; -} var_desc_t; - -/** - * IO region structure. - * - * Each IO region is a unit of data which can be described using start - * and count arrays. Each IO task may in general have multiple io - * regions per variable. The box rearranger will have at most one io - * region per variable. - * - * The write from a particular IO task is divided into 1 or more - * regions each of which can be described using start and count. The - * io_region typedef is a linked list of those regions. - */ -typedef struct io_region -{ - /** The offset from the beginning of the data buffer to the - * beginning of this region. */ - int loffset; - - /** Start array for this region. */ - PIO_Offset *start; - - /** Count array for this region. */ - PIO_Offset *count; - - /** Pointer to the next io_region in the list. */ - struct io_region *next; -} io_region; - /** * Rearranger comm type. The rearranger option values must match the * definitions in the fortran interface. @@ -506,698 +398,6 @@ typedef struct rearr_opt rearr_comm_fc_opt_t io2comp; } rearr_opt_t; -/** - * IO descriptor structure. - * - * This structure defines the mapping for a given variable between - * compute and IO decomposition. - */ -typedef struct io_desc_t -{ - /** The ID of this io_desc_t. */ - int ioid; - - /** The length of the decomposition map. */ - int maplen; - - /** A 1-D array with iodesc->maplen elements, which are the - * 1-based mappings to the global array for that task. */ - PIO_Offset *map; - - /** Number of tasks involved in the communication between comp and - * io tasks. */ - int nrecvs; - - /** Local size of the decomposition array on the compute node. */ - int ndof; - - /** All vars included in this io_desc_t have the same number of - * dimensions. */ - int ndims; - - /** An array of size ndims with the length of each dimension. */ - int *dimlen; - - /** The actual number of IO tasks participating. */ - int num_aiotasks; - - /** The rearranger in use for this variable. */ - int rearranger; - - /** Maximum number of regions in the decomposition. */ - int maxregions; - - /** Does this decomp leave holes in the field (true) or write - * everywhere (false) */ - bool needsfill; - - /** The maximum number of bytes of this iodesc before flushing. */ - int maxbytes; - - /** The PIO type of the data. */ - int piotype; - - /** The size of one element of the piotype. */ - int piotype_size; - - /** The MPI type of the data. */ - MPI_Datatype mpitype; - - /** The size in bytes of a datum of MPI type mpitype. */ - int mpitype_size; - - /** Length of the iobuffer on this task for a single field on the - * IO node. The arrays from compute nodes gathered and rearranged - * to the io-nodes (which are sometimes collocated with compute - * nodes), each io task contains data from the compmap of one or - * more compute tasks in the iomap array. */ - PIO_Offset llen; - - /** Maximum llen participating. */ - PIO_Offset maxiobuflen; - - /** Array (length nrecvs) of computation tasks received from. */ - int *rfrom; - - /** Array (length nrecvs) of counts of data to be received from - * each computation task by the IO tasks. */ - int *rcount; - - /** Array (length numiotasks) of data counts to send to each task - * in the communication in pio_swapm(). */ - int *scount; - - /** Array (length ndof) for the BOX rearranger with the index - * for computation taks (send side during writes). */ - PIO_Offset *sindex; - - /** Index for the IO tasks (receive side during writes). */ - PIO_Offset *rindex; - - /** Array (of length nrecvs) of receive MPI types in pio_swapm() call. */ - MPI_Datatype *rtype; - - /** Array of send MPI types in pio_swapm() call. */ - MPI_Datatype *stype; - - /** Number of send MPI types in pio_swapm() call. */ - int num_stypes; - - /** Used when writing fill data. */ - int holegridsize; - - /** max holegridsize across all io tasks, needed for netcdf and netcdf4c serial */ - int maxholegridsize; - - /** Used when writing fill data. */ - int maxfillregions; - - /** Linked list of regions. */ - io_region *firstregion; - - /** Used when writing fill data. */ - io_region *fillregion; - - /** Rearranger flow control options - * (handshake, non-blocking sends, pending requests) - */ - rearr_opt_t rearr_opts; - - /** In the subset communicator each io task is associated with a - * unique group of comp tasks this is the communicator for that - * group. */ - MPI_Comm subset_comm; - -#if PIO_SAVE_DECOMPS - /* Indicates whether this iodesc has been saved to disk (the - * decomposition is dumped to disk) - */ - bool is_saved; -#endif - - /** Pointer to the next io_desc_t in the list. */ - struct io_desc_t *next; -} io_desc_t; - -/* Forward decl for I/O file summary stats info */ -struct spio_io_fstats_summary; - -/** - * IO system descriptor structure. - * - * This structure contains the general IO subsystem data and MPI - * structure - */ -typedef struct iosystem_desc_t -{ - /** The ID of this iosystem_desc_t. This will be obtained by - * calling PIOc_Init_Intercomm() or PIOc_Init_Intracomm(). */ - int iosysid; - - /* I/O System name */ - char sname[PIO_MAX_NAME + 1]; - - /** This is an MPI intra communicator that includes all the tasks in - * both the IO and the computation communicators. */ - MPI_Comm union_comm; - - /** This is an MPI intra communicator that includes all the tasks - * involved in IO. */ - MPI_Comm io_comm; - - /** This is an MPI intra communicator that includes all the tasks - * involved in computation. */ - MPI_Comm comp_comm; - - /** This is an MPI inter communicator between IO communicator and - * computation communicator. */ - MPI_Comm intercomm; - - /** This is a copy (but not an MPI copy) of either the comp (for - * non-async) or the union (for async) communicator. */ - MPI_Comm my_comm; - - /** This MPI group contains the processors involved in - * computation. */ - MPI_Group compgroup; - - /** This MPI group contains the processors involved in I/O. */ - MPI_Group iogroup; - - /** The number of tasks in the IO communicator. */ - int num_iotasks; - - /** The number of tasks in the computation communicator. */ - int num_comptasks; - - /** The number of tasks in the union communicator (will be - * num_comptasks for non-async, num_comptasks + num_iotasks for - * async). */ - int num_uniontasks; - - /** Rank of this task in the union communicator. */ - int union_rank; - - /** The rank of this process in the computation communicator, or -1 - * if this process is not part of the computation communicator. */ - int comp_rank; - - /** The rank of this process in the IO communicator, or -1 if this - * process is not part of the IO communicator. */ - int io_rank; - - /** Set to MPI_ROOT if this task is the master of IO communicator, 0 - * otherwise. */ - int iomaster; - - /** Set to MPI_ROOT if this task is the master of comp communicator, 0 - * otherwise. */ - int compmaster; - - /** Rank of IO root task (which is rank 0 in io_comm) in the union - * communicator. Will always be 0 for async situations. */ - int ioroot; - - /** Rank of computation root task (which is rank 0 in - * comm_comms[cmp]) in the union communicator. Will always = number - * of IO tasks in async situations. */ - int comproot; - - /** An array of the ranks of all IO tasks within the union - * communicator. */ - int *ioranks; - - /** An array of the ranks of all computation tasks within the - * union communicator. */ - int *compranks; - - /** Controls handling errors. */ - int error_handler; - - /** The rearranger decides which parts of a distributed array are - * handled by which IO tasks. */ - int default_rearranger; - - /** True if asynchronous interface is in use. */ - bool async; - - /** True if this task is a member of the IO communicator. */ - bool ioproc; - - /** True if this task is a member of a computation - * communicator. */ - bool compproc; - - /** MPI Info object. */ - MPI_Info info; - - /** Async I/O service message info */ - struct async_ios_msg_info_{ - int seq_num; - int prev_msg; - } async_ios_msg_info; - - /** Index of this component in the list of components. */ - int comp_idx; - - /** Rearranger options. */ - rearr_opt_t rearr_opts; - -#ifdef _ADIOS2 - /* ADIOS handles */ - adios2_adios *adiosH; /* Handle for ADIOS write */ - adios2_adios *adios_readerH; /* Handle for ADIOS read */ - int adios_io_process; - MPI_Comm adios_comm; - int adios_rank, num_adiostasks; - /* Block merging setup */ - MPI_Comm block_comm; - int block_myrank, block_nprocs; - #ifdef _SPIO_ADIOS_USE_COMPRESSION - /* ADIOS operator for applying a specific lossless compression method (e.g., Blosc2, BZip2) */ - adios2_operator* lossless_compression_operator; - int adios_lossless_compression_method; - #ifdef _SPIO_ADIOS_USE_LOSSY_COMPRESSION - /* ADIOS operator for applying a specific lossy compression method (e.g., SZ, MGARD, ZFP) */ - adios2_operator* lossy_compression_operator; - int adios_lossy_compression_method; - #endif - #endif -#endif - - /** I/O statistics associated with this I/O system */ - struct spio_io_fstats_summary *io_fstats; - - /** Pointer to the next iosystem_desc_t in the list. */ - struct iosystem_desc_t *next; -} iosystem_desc_t; - -/** - * The multi buffer holds data from one or more variables. Data are - * accumulated in the multi-buffer. - */ -typedef struct wmulti_buffer -{ - /** The ID that describes the decomposition, as returned from - * PIOc_Init_Decomp(). */ - int ioid; - - /** Non-zero if this is a buffer for a record var. */ - int recordvar; - - /** Number of arrays of data in the multibuffer. Each array had - * data for one var or record. When multibuffer is flushed, all - * arrays are written and num_arrays returns to zero. */ - int num_arrays; - - /** Size of this variables data on local task. All vars in the - * multi-buffer have the same size. */ - int arraylen; - - /** Array of varids. */ - int *vid; - - /** An array of current record numbers, for record vars. One - * element per variable. */ - int *frame; - - /** Array of fill values used for each var. */ - void *fillvalue; - - /** Pointer to the data. */ - void *data; - - /** Pointer to the next multi-buffer in the list. */ - struct wmulti_buffer *next; -} wmulti_buffer; - -#ifdef _ADIOS2 -/* Interval map for ADIOS read */ -typedef struct adios_interval_map_t -{ - int n_adios_steps; - short **map; -} adios_interval_map_t; - -/* Maximum char array length to store "darray" or "put_var", for ADIOS read */ -#define NC_OP_TAG_MAX_LEN 8 - -/** Variable definition information saved at pioc_def_var, - * so that ADIOS can define the variable at write time when - * local dimensions and offsets are known. - */ -typedef struct adios_var_desc_t -{ - /** Variable name */ - char * name; - - /** NC type give at def_var time */ - int nc_type; - - /** Type converted from NC type to adios type */ - adios2_type adios_type; - size_t adios_type_size; - - /** Number of dimensions */ - int ndims; - - /** Global dims (dim var ids) */ - int * gdimids; - - /** Number of attributes defined for this variable */ - int nattrs; - - /** ADIOS varID, if it has already been defined. - * We avoid defining again when writing multiple records over time - */ - adios2_variable* adios_varid; // 0: undefined yet - - /* to handle PIOc_setframe with different decompositions */ - adios2_variable* decomp_varid; - adios2_variable* frame_varid; - adios2_variable* fillval_varid; - adios2_variable* num_block_writers_varid; - - /* to handle multi-dimensional temporal variables */ - adios2_variable* start_varid; - adios2_variable* count_varid; - - /* to buffer decomp id, frame id, fill value, and writer blocks */ - int32_t *decomp_buffer; - int32_t *frame_buffer; - char *fillval_buffer; - int32_t fillval_size; - int32_t *num_wb_buffer; - int32_t decomp_cnt, frame_cnt, fillval_cnt, num_wb_cnt; - int32_t max_buffer_cnt; - - /* Simplified version of an interval map implementation - * index is a frame_id and the value is the adios_step */ - adios_interval_map_t* interval_map; /* For ADIOS read */ - - /* Is this variable written with put_var or darray? */ - char nc_op_tag[NC_OP_TAG_MAX_LEN]; /* For ADIOS read */ -} adios_var_desc_t; - -/* Track attributes */ -typedef struct adios_att_desc_t -{ - /** Attribute name */ - char *att_name; - - /** NC type give at def_att time */ - nc_type att_type; - - /** length of attribute value */ - PIO_Offset att_len; - - /** ncid of the attribute */ - int att_ncid; - - /** attribute varid */ - int att_varid; - - /** Type converted from NC type to adios type */ - adios2_type adios_type; -} adios_att_desc_t; - -#ifdef _SPIO_ADIOS_USE_COMPRESSION -enum ADIOS_COMPRESSION_METHOD -{ - ADIOS_COMPRESSION_METHOD_BLOSC2 = 1, - - ADIOS_COMPRESSION_METHOD_BZIP2 = 2, - - ADIOS_COMPRESSION_METHOD_MGARD = 3, - - ADIOS_COMPRESSION_METHOD_SZ = 4, - - ADIOS_COMPRESSION_METHOD_ZFP = 5 -}; -#endif -#endif /* _ADIOS2 */ - -#ifdef _HDF5 -typedef struct hdf5_dim_desc_t -{ - /** Dimension name */ - char* name; - - /** Dimension length */ - PIO_Offset len; - - /** True if the dimension has a coordinate variable */ - bool has_coord_var; - - hid_t hdf5_dataset_id; -} hdf5_dim_desc_t; - -typedef struct hdf5_var_desc_t -{ - /** Variable name */ - char* name; - - /* Alternative name for a non-coordinate variable with the same name as a dimension */ - char* alt_name; - - /** NC type give at def_var time */ - int nc_type; - - /** Type converted from NC type to HDF5 type */ - hid_t hdf5_type; - - /** Number of dimensions */ - int ndims; - - /** Dimension IDs of the variable */ - int* hdf5_dimids; - - /** True if the variable is a coordinate variable */ - bool is_coord_var; - - hid_t hdf5_dataset_id; -} hdf5_var_desc_t; - -typedef struct hdf5_att_desc_t -{ - /** Attribute name */ - char *att_name; - - /** NC type give at def_att time */ - nc_type att_type; - - /** length of attribute value */ - PIO_Offset att_len; - - /** ncid of the attribute */ - int att_ncid; - - /** attribute varid */ - int att_varid; -} hdf5_att_desc_t; -#endif - -/** - * File descriptor structure. - * - * This structure holds information associated with each open file - */ -typedef struct file_desc_t -{ - /** The IO system ID used to open this file. */ - iosystem_desc_t *iosystem; - - /** The ncid returned for this file by the underlying library - * (netcdf or pnetcdf). */ - int fh; - -#ifdef _ADIOS2 - /** Save the filename, now just for printing it at close */ - char *filename; - - /** ADIOS file handler is 64bit integer */ - adios2_engine *engineH; - - /** Check if begin_step has been invoked. It is used to invoke end_step **/ - int begin_step_called; - int num_step_calls;; - int max_step_calls; - - /* - * Used to call adios2_end_step to avoid buffer overflow in MPI_Gatherv - * during ADIOS metadata write operation. - * - * if num_written_blocks * BLOCK_METADATA_SIZE >= BLOCK_COUNT_THRESHOLD, call adios2_end_step - * (Not implemented in this version. adios2_end_step is called if num_step_calls >= max_step_calls (= PIO_MAX_CACHED_STEPS_FOR_ADIOS)) - */ - unsigned int num_written_blocks; - - int write_decomp_id; - int write_frame_id; - int write_fillval_id; - - /** Handler for ADIOS group (of variables) */ - adios2_io *ioH; - - /** ADIOS output transport method name, POSIX or MPI_AGGREGATE */ - char transport[PIO_MAX_NAME]; - - /** Parameters for the transport method, required for MPI_AGGREGATE. - * Created automatically from the application setup */ - char params[PIO_MAX_NAME]; - - /** Need to store the dim names for finding them and using them when defining variables */ - char *dim_names[PIO_MAX_DIMS]; - PIO_Offset dim_values[PIO_MAX_DIMS]; - - /** Number of dim vars defined */ - int num_dim_vars; - - /** Variable information, max PIO_MAX_VARS variables allowed */ - struct adios_var_desc_t adios_vars[PIO_MAX_VARS]; - - /** Number of vars defined */ - int num_vars; - - /** Number of global attributes defined. Needed to support PIOc_inq_nattrs() */ - int num_gattrs; - - /* all procs rank, etc */ - MPI_Comm all_comm; - int all_rank, num_alltasks; - - /* ADIOS rank, etc */ - int adios_io_process; - MPI_Comm adios_comm; - int adios_rank, num_adiostasks; - - /* Merging distributed array blocks to reduce I/O overhead */ - /* Grouping of processes for block merging */ - MPI_Comm block_comm; - int block_myrank, block_nprocs; - int *block_list; - - /* Buffers for merging distributed array blocks */ - unsigned int *array_counts; - unsigned int array_counts_size; - unsigned int *array_disp; - unsigned int array_disp_size; - char *block_array; - size_t block_array_size; - - /* Track attributes */ - /** attribute information. Allow PIO_MAX_VARS for now. */ - struct adios_att_desc_t adios_attrs[PIO_MAX_ATTRS]; - int num_attrs; - - int fillmode; - - /* Handle PIO_Offset */ - int pio_offset_size; - adios2_type pio_offset_type; - - /** Array for decompositions that has been written already (must write only once) */ - int n_written_ioids; - int written_ioids[PIO_MAX_ADIOS_DECOMPS]; /* written_ioids[N] = ioid if that decomp has been already written, */ - - /** Store current frameid for end_step in PIO_setframe */ - int current_frame; - - /* Some caches (hash tables) for ADIOS read */ - struct spio_hmap *cache_data_blocks; - struct spio_hmap *cache_block_sizes; - struct spio_hmap *cache_darray_info; - - char io_name_reader[PIO_MAX_NAME + 1]; /* Name of io object, for ADIOS read */ - size_t adios_reader_num_decomp_blocks; /* Number of decomposition blocks, for ADIOS read */ - - /* Indicates whether the decomposition maps (for ADIOS write) need to be stored in BP files. Default is true. */ - bool store_adios_decomp; -#endif /* _ADIOS2 */ - -#ifdef _HDF5 - hid_t hdf5_file_id; - - /* Collective dataset transfer property list, used by H5Dwrite */ - hid_t dxplid_coll; - - /* Independent dataset transfer property list, used by H5Dwrite */ - hid_t dxplid_indep; - - struct hdf5_dim_desc_t hdf5_dims[PIO_MAX_DIMS]; - - /** Number of dims defined */ - int hdf5_num_dims; - - struct hdf5_var_desc_t hdf5_vars[PIO_MAX_VARS]; - - /** Number of vars defined */ - int hdf5_num_vars; - - struct hdf5_att_desc_t hdf5_attrs[PIO_MAX_ATTRS]; - - /** Number of attrs defined */ - int hdf5_num_attrs; - - /** Number of global attrs defined */ - int hdf5_num_gattrs; -#endif /* _HDF5 */ - - /* File name - cached */ - char fname[PIO_MAX_NAME + 1]; - - /** The ncid that will be returned to the user. */ - int pio_ncid; - - /** The PIO_TYPE value that was used to open this file. */ - int iotype; - - /** List of variables in this file (deprecated). */ - struct var_desc_t varlist[PIO_MAX_VARS]; - - /* Number of unlimited dim ids, if no unlimited id present = 0 */ - int num_unlim_dimids; - - /* Unlimited dim ids, if no unlimited id present = NULL */ - int *unlim_dimids; - - /** Mode used when file was opened. */ - int mode; - - /** The wmulti_buffer is used to aggregate multiple variables with - * the same communication pattern prior to a write. */ - struct wmulti_buffer buffer; - - /* Bytes pending to be read on this file*/ - PIO_Offset rb_pend; - - /* Bytes pending to be written out for this file */ - PIO_Offset wb_pend; - - /** Data buffer per IO decomposition for this file. */ - /** Cache for storing data corresponding to multiple - * variables binned on the I/O decomposition used by - * the variable */ - void *mvcache; - - /** I/O statistics associated with this file */ - struct spio_io_fstats_summary *io_fstats; - - /** Pointer to the next file_desc_t in the list of open files. */ - struct file_desc_t *next; - - /** True if this task should participate in IO (only true for one - * task with netcdf serial files. */ - int do_io; - - /** True if we need reserve some extra space in the header when - * creating NetCDF files to accommodate anticipated changes. */ - bool reserve_extra_header_space; - - /** True if this is an existing file reopened */ - bool is_reopened; -} file_desc_t; - /** * These are the supported methods of reading/writing input/output * files. The PnetCDF, NetCDF and HDF5 libraries output data in the @@ -1246,7 +446,10 @@ enum PIO_REARRANGERS PIO_REARR_SUBSET = 2, /** Let the library choose the rearranger. */ - PIO_REARR_ANY = 3 + PIO_REARR_ANY = 3, + + /** Contig rearranger (data aggregation + contiguous data) */ + PIO_REARR_CONTIG = 4, }; /** diff --git a/src/clib/pio_config.h.in b/src/clib/pio_config.h.in index c67aa650143..3c67eec64f2 100644 --- a/src/clib/pio_config.h.in +++ b/src/clib/pio_config.h.in @@ -55,12 +55,26 @@ /** Striping unit hint in bytes for a Lustre or GPFS file system. */ #define PIO_STRIPING_UNIT @PIO_STRIPING_UNIT@ +/** If MPI File sync needs to be explicitly disabled (default : false) */ +#define SPIO_DISABLE_HDF5_MPI_FILE_SYNC @SPIO_DISABLE_HDF5_MPI_FILE_SYNC@ + /** Extra bytes reserved in the header when creating NetCDF files. */ #define PIO_RESERVED_FILE_HEADER_SIZE @PIO_RESERVED_FILE_HEADER_SIZE@ /** Set chunk size (in bytes) for HDF5/PnetCDF chunked variables. */ #define PIO_CHUNK_SIZE @PIO_CHUNK_SIZE@ +/** Set dimension chunk info for HDF5 chunked variables. + * Expected format: "DIM1_NAME:DIM1_CHUNK_SZ;DIM2_NAME:DIM2_CHUNK_SZ;" + */ +#define SPIO_DIM_CHUNK_INFO "@SPIO_DIM_CHUNK_INFO@" + +/** Set the constraints/regex for enabling chunking of variables + * Regex format is same as the one used for PIO_SAVE_DECOMPS_REGEX + * e.g. '(VAR=\".*test_var1.*\")&&(FILE=\".*testfile1\")' + */ +#define SPIO_ENABLE_CHUNKING_REGEX "@SPIO_ENABLE_CHUNKING_REGEX@" + /** Maximum number of I/O decompositions registered with ADIOS type */ #define PIO_MAX_ADIOS_DECOMPS @PIO_MAX_ADIOS_DECOMPS@ @@ -169,4 +183,14 @@ * 0 otherwise */ #define PIO_USE_INDEP_MODE @USE_INDEP_MODE@ +/** Set to 1 if the library is configured to use asynchronous I/O + * 0 otherwise */ +#define PIO_USE_ASYNC_WR_THREAD @USE_ASYNC_WR_THREAD@ + +/** Maximum wait for asynchronous I/O operations (in milliseconds) */ +#define SPIO_ASYNC_OP_WAIT_TIMEOUT @SPIO_ASYNC_OP_WAIT_TIMEOUT@ + +/** Number of threads performing asynchronous I/O operations */ +#define SPIO_ASYNC_NTHREADS @SPIO_ASYNC_NTHREADS@ + #endif /* _PIO_CONFIG_ */ diff --git a/src/clib/pio_internal.h b/src/clib/pio_internal.h index 78d3c2b2767..8d0f46b24de 100644 --- a/src/clib/pio_internal.h +++ b/src/clib/pio_internal.h @@ -1,10 +1,7 @@ /** * @file * Private headers and defines for the PIO C interface. - * @author Jim Edwards - * @date 2014 * - * @see http://code.google.com/p/parallelio/ */ #ifndef __PIO_INTERNAL__ @@ -22,7 +19,6 @@ #define NETCDF_INT_FLOAT_SIZE 4 #define NETCDF_DOUBLE_INT64_SIZE 8 -#include #include #include #ifdef SPIO_ENABLE_GPTL_TIMING @@ -31,7 +27,10 @@ #include "gptl_skel.h" #endif #include -#include "spio_ltimer.h" +#include "util/bget.h" +#include "util/spio_ltimer.h" +#include "pio_types.hpp" +#include #if defined(__cplusplus) extern "C" { @@ -70,6 +69,16 @@ extern "C" { bool isend; } pio_swapm_defaults; + /** swapm request */ + typedef struct pio_swapm_req + { + MPI_Request *rcvids; + int nrcvids; + + MPI_Request *sndids; + int nsndids; + } pio_swapm_req; + /* Handle an error in the PIO library. */ #ifdef __GNUC__ /* Specify that pio_err() uses printf style formatting. This @@ -107,10 +116,13 @@ extern "C" { int pio_add_to_iodesc_list(io_desc_t *iodesc, MPI_Comm comm); io_desc_t *pio_get_iodesc_from_id(int ioid); int pio_delete_iodesc_from_list(int ioid); + int pio_delete_all_iodescs(int iosysid); int pio_num_iosystem(int *niosysid); int pio_get_file(int ncid, file_desc_t **filep); int pio_delete_file_from_list(int ncid); + int spio_close_all_files_and_delete_from_list(int iosysid); + int spio_close_soft_closed_file(const char *filename); int pio_add_to_file_list(file_desc_t *file, MPI_Comm comm); /* Get a description of the variable represented by varid */ @@ -128,10 +140,17 @@ extern "C" { int openfile_int(int iosysid, int *ncidp, int *iotype, const char *filename, int mode, int retry); + /* Close the file ("hard close") */ + int spio_wait_on_hard_close(iosystem_desc_t *ios, file_desc_t *file); + int spio_hard_closefile(iosystem_desc_t *ios, file_desc_t *file, bool sync_with_ioprocs); + int spio_soft_closefile(iosystem_desc_t *ios, file_desc_t *file); + iosystem_desc_t *pio_get_iosystem_from_id(int iosysid); int pio_add_to_iosystem_list(iosystem_desc_t *ios, MPI_Comm comm); /* Check the return code from a netCDF call, with ios pointer. */ + int spio_handle_err(iosystem_desc_t *ios, file_desc_t *file, int eh, + int status, const char *fname, int line); int check_netcdf(iosystem_desc_t *ios, file_desc_t *file, int status, const char *fname, int line); @@ -180,6 +199,15 @@ extern "C" { void *recvbuf, const int *recvcounts, const int *rdispls, const MPI_Datatype *recvtypes, MPI_Comm comm, const rearr_comm_fc_opt_t *fc); + /* Non blocking wait for pio swapm user request */ + int pio_swapm_iwait(void *p, bool &flag); + + /* Blocking wait for pio swapm user request */ + int pio_swapm_wait(void *p); + + /* Free a pio swapm request */ + void pio_swapm_req_free(void *p); + void PIO_Offset_size(MPI_Datatype *dtype, int *tsize); PIO_Offset GCDblocksize(int arrlen, const PIO_Offset *arr_in); @@ -228,11 +256,11 @@ extern "C" { int rearrange_io2comp(iosystem_desc_t *ios, io_desc_t *iodesc, const void *sbuf, void *rbuf); /* Move data from compute tasks to IO tasks. */ - int rearrange_comp2io(iosystem_desc_t *ios, io_desc_t *iodesc, const void *sbuf, void *rbuf, - int nvars); + int rearrange_comp2io(iosystem_desc_t *ios, io_desc_t *iodesc, file_desc_t *file, + const void *sbuf, void *rbuf, int nvars); /* Allocate and initialize storage for decomposition information. */ - int malloc_iodesc(iosystem_desc_t *ios, int piotype, int ndims, io_desc_t **iodesc); + int malloc_iodesc(iosystem_desc_t *ios, int piotype, int ndims, int maplen, io_desc_t **iodesc); void performance_tune_rearranger(iosystem_desc_t *ios, io_desc_t *iodesc); /* Flush contents of multi-buffer to disk. */ @@ -336,9 +364,6 @@ extern "C" { int spio_pnetcdf_inq_type(int ncid, nc_type xtype, char *name, PIO_Offset *sizep); - /* Handle end and re-defs. */ - int spio_change_def(int ncid, int is_enddef); - /* Remove a directory in the filesystem */ int spio_remove_directory(const char *path); @@ -367,7 +392,7 @@ extern "C" { char *history, char *source, char *version, int *fortran_order); /* Create a unique PIO string */ - int pio_create_uniq_str(iosystem_desc_t *ios, io_desc_t *iodesc, char *str, int len, const char *prefix, const char *suffix); + void pio_create_uniq_str(iosystem_desc_t *ios, io_desc_t *iodesc, std::string &str, const std::string &prefix, const std::string &suffix); /* Set the size limit for each block of requests to wait */ int set_file_req_block_size_limit(file_desc_t *file, PIO_Offset sz); @@ -405,11 +430,6 @@ extern "C" { const adios2_constant_dims constant_dims); #endif -#ifdef _HDF5 - hid_t nc_type_to_hdf5_type(nc_type xtype); - PIO_Offset hdf5_get_nc_type_size(nc_type xtype); -#endif - /* Asynchronous I/O services start with the following seq num */ static const int PIO_MSG_START_SEQ_NUM = 1024; /** These are the messages that can be sent over the intercomm when @@ -684,26 +704,8 @@ const char *pio_get_vnames_from_file_id(int pio_file_id, const char *pio_async_msg_to_string(int msg); #define PIO_IS_NULL(p) ((p) ? "not NULL" : "NULL") -/* Some helper functions internally used by HDF5 IO type */ -#ifdef _HDF5 -hid_t spio_nc_type_to_hdf5_type(nc_type xtype); - -int spio_hdf5_create(iosystem_desc_t *ios, file_desc_t *file, const char *filename); - -int spio_hdf5_def_var(iosystem_desc_t *ios, file_desc_t *file, const char *name, - nc_type xtype, int ndims, const int *dimidsp, int varid); - -int spio_hdf5_enddef(iosystem_desc_t *ios, file_desc_t *file); - -int spio_hdf5_put_att(iosystem_desc_t *ios, file_desc_t *file, int varid, const char *name, - nc_type atttype, PIO_Offset len, const void *op); - -int spio_hdf5_put_var(iosystem_desc_t *ios, file_desc_t *file, int varid, - const PIO_Offset *start, const PIO_Offset *count, - const PIO_Offset *stride, nc_type xtype, const void *buf); - -int spio_hdf5_close(iosystem_desc_t *ios, file_desc_t *file); -#endif +int spio_find_start_count(int ndims, const int *dimlen, int fndims, var_desc_t *vdesc, + io_region *region, size_t *start, size_t *count); #if defined(__cplusplus) } diff --git a/src/clib/pio_lists.cpp b/src/clib/pio_lists.cpp deleted file mode 100644 index 0e9b5f37ebe..00000000000 --- a/src/clib/pio_lists.cpp +++ /dev/null @@ -1,449 +0,0 @@ -/** - * @file - * PIO list functions. - */ -#include -#include -#include -#ifdef PIO_MICRO_TIMING -#include "pio_timer.h" -#endif -#include -#include -#include "spio_file_mvcache.h" -#include "spio_io_summary.h" -#include "spio_hash.h" - -static io_desc_t *pio_iodesc_list = NULL; -static io_desc_t *current_iodesc = NULL; -static iosystem_desc_t *pio_iosystem_list = NULL; -static file_desc_t *pio_file_list = NULL; -static file_desc_t *current_file = NULL; - -/** - * Add a new entry to the global list of open files. - * - * This function guarantees that files (id of the - * files) are unique across the comm provided - * - * @param file pointer to the file_desc_t struct for the new file. - * @param comm MPI Communicator across which the files - * need to be unique - * @returns The id for the file added to the list - */ -#define PIO_FILE_START_ID 16 -int pio_add_to_file_list(file_desc_t *file, MPI_Comm comm) -{ - /* Using an arbitrary start id for file ids helps - * in debugging, to distinguish between ids assigned - * to different structures in the code - * Also note that NetCDF ids start at 4, PnetCDF ids - * start at 0 and NetCDF4 ids start at 65xxx - */ - static int pio_file_next_id = PIO_FILE_START_ID; - file_desc_t *cfile; - - assert(file); - - if(comm != MPI_COMM_NULL) - { - int tmp_id = pio_file_next_id; - int mpierr = MPI_Allreduce(&tmp_id, &pio_file_next_id, 1, MPI_INT, MPI_MAX, comm); - assert(mpierr == MPI_SUCCESS); - } - file->pio_ncid = pio_file_next_id; - pio_file_next_id++; - /* This file will be at the end of the list, and have no next. */ - file->next = NULL; - - /* Get a pointer to the global list of files. */ - cfile = pio_file_list; - - /* Keep a global pointer to the current file. */ - current_file = file; - - /* If there is nothing in the list, then file will be the first - * entry. Otherwise, move to end of the list. */ - if (!cfile) - pio_file_list = file; - else - { - while (cfile->next) - cfile = cfile->next; - cfile->next = file; - } - - return file->pio_ncid; -} - -/** - * Given ncid, find the file_desc_t data for an open file. The ncid - * used is the interally generated pio_ncid. - * - * @param ncid the PIO assigned ncid of the open file. - * @param cfile1 pointer to a pointer to a file_desc_t. The pointer - * will get a copy of the pointer to the file info. - * - * @returns 0 for success, error code otherwise. - * @author Ed Hartnett - */ -int pio_get_file(int ncid, file_desc_t **cfile1) -{ - file_desc_t *cfile = NULL; - - LOG((2, "pio_get_file ncid = %d", ncid)); - - /* Caller must provide this. */ - if (!cfile1) - return PIO_EINVAL; - - /* Find the file pointer. */ - if (current_file && current_file->pio_ncid == ncid) - cfile = current_file; - else - for (cfile = pio_file_list; cfile; cfile = cfile->next) - if (cfile->pio_ncid == ncid) - { - current_file = cfile; - break; - } - - /* If not found, return error. */ - if (!cfile) - return PIO_EBADID; - - /* We depend on every file having a pointer to the iosystem. */ - if (!cfile->iosystem) - return PIO_EINVAL; - - /* Let's just ensure we have a valid IO type. */ - pioassert(iotype_is_valid(cfile->iotype), "invalid IO type", __FILE__, __LINE__); - - /* Copy pointer to file info. */ - *cfile1 = cfile; - - return PIO_NOERR; -} - -/** - * Delete a file from the list of open files. - * - * @param ncid ID of file to delete from list - * @returns 0 for success, error code otherwise - * @author Jim Edwards, Ed Hartnett - */ -int pio_delete_file_from_list(int ncid) -{ - file_desc_t *cfile, *pfile = NULL; - - /* Look through list of open files. */ - for (cfile = pio_file_list; cfile; cfile = cfile->next) - { - if (cfile->pio_ncid == ncid) - { - if (!pfile) - pio_file_list = cfile->next; - else - pfile->next = cfile->next; - - if (current_file == cfile) - current_file = pfile; - - /* Free any fill values that were allocated. */ - for (int v = 0; v < PIO_MAX_VARS; v++) - { - if (cfile->varlist[v].fillvalue) - free(cfile->varlist[v].fillvalue); -#ifdef PIO_MICRO_TIMING - mtimer_destroy(&(cfile->varlist[v].rd_mtimer)); - mtimer_destroy(&(cfile->varlist[v].rd_rearr_mtimer)); - mtimer_destroy(&(cfile->varlist[v].wr_mtimer)); - mtimer_destroy(&(cfile->varlist[v].wr_rearr_mtimer)); -#endif - } - - free(cfile->unlim_dimids); - free(cfile->io_fstats); - spio_file_mvcache_finalize(cfile); - /* Free the memory used for this file. */ -#ifdef _ADIOS2 - if (cfile->cache_data_blocks != NULL) - { - /* This call also frees cfile->cache_data_blocks */ - cfile->cache_data_blocks->free(cfile->cache_data_blocks); - } - - if (cfile->cache_block_sizes != NULL) - { - /* This call also frees cfile->cache_block_sizes */ - cfile->cache_block_sizes->free(cfile->cache_block_sizes); - } - - if (cfile->cache_darray_info != NULL) - { - /* This call also frees cfile->cache_darray_info */ - cfile->cache_darray_info->free(cfile->cache_darray_info); - } -#endif - free(cfile); - - return PIO_NOERR; - } - pfile = cfile; - } - - /* No file was found. */ - return PIO_EBADID; -} - -/** Print I/O stats for all files in the iosystem - * - * This call can be expensive when a lot of files are open - * (and not expensive when called during I/O system finalize, - * when all the I/O systems finalize at the same time and the - * user is careful about closing all open files before the - * I/O systems are finalized) - * This call can be used to force writing out file I/O stats - * for all the files in an I/O system (e.g. not all files - * are closed and we need the file I/O stats of these files) - * @param iosysp Pointer to the I/O system, iosystem_desc_t - * @returns PIO_NOERR on success, error code otherwise - */ -int spio_write_all_file_iostats(iosystem_desc_t *iosysp) -{ - int ret = PIO_NOERR; - for(file_desc_t *pf = pio_file_list; pf; pf = pf->next) - { - if(pf->iosystem == iosysp) - { - assert(pf->iosystem->iosysid == iosysp->iosysid); - ret = spio_write_file_io_summary(pf); - if(ret != PIO_NOERR) - { - return ret; - } - } - } - return ret; -} - -/** - * Delete iosystem info from list. - * - * @param piosysid the iosysid to delete - * @returns 0 on success, error code otherwise - * @author Jim Edwards - */ -int pio_delete_iosystem_from_list(int piosysid) -{ - iosystem_desc_t *ciosystem, *piosystem = NULL; - - LOG((1, "pio_delete_iosystem_from_list piosysid = %d", piosysid)); - - for (ciosystem = pio_iosystem_list; ciosystem; ciosystem = ciosystem->next) - { - LOG((3, "ciosystem->iosysid = %d", ciosystem->iosysid)); - if (ciosystem->iosysid == piosysid) - { - if (piosystem == NULL) - pio_iosystem_list = ciosystem->next; - else - piosystem->next = ciosystem->next; - free(ciosystem); - return PIO_NOERR; - } - piosystem = ciosystem; - } - return PIO_EBADID; -} - -/** - * Add iosystem info to a global list. - * This function guarantees that iosystems (ioid of the - * iosystems) are unique across the comm provided - * - * @param ios pointer to the iosystem_desc_t info to add. - * @param comm MPI Communicator across which the iosystems - * need to be unique - * @returns the id of the newly added iosystem. - */ -#define PIO_IOSYSTEM_START_ID 2048 -int pio_add_to_iosystem_list(iosystem_desc_t *ios, MPI_Comm comm) -{ - /* Using an arbitrary start id for iosystem ids helps - * in debugging, to distinguish between ids assigned - * to different structures in the code - */ - static int pio_iosystem_next_ioid = PIO_IOSYSTEM_START_ID; - iosystem_desc_t *cios; - - assert(ios); - - if(comm != MPI_COMM_NULL) - { - int tmp_id = pio_iosystem_next_ioid; - int mpierr = MPI_Allreduce(&tmp_id, &pio_iosystem_next_ioid, 1, MPI_INT, MPI_MAX, comm); - assert(mpierr == MPI_SUCCESS); - } - ios->iosysid = pio_iosystem_next_ioid; - pio_iosystem_next_ioid += 1; - - ios->next = NULL; - cios = pio_iosystem_list; - if (!cios) - pio_iosystem_list = ios; - else - { - while (cios->next) - { - cios = cios->next; - } - cios->next = ios; - } - - return ios->iosysid; -} - -/** - * Get iosystem info from list. - * - * @param iosysid id of the iosystem - * @returns pointer to iosystem_desc_t, or NULL if not found. - * @author Jim Edwards - */ -iosystem_desc_t *pio_get_iosystem_from_id(int iosysid) -{ - iosystem_desc_t *ciosystem; - - LOG((2, "pio_get_iosystem_from_id iosysid = %d", iosysid)); - - for (ciosystem = pio_iosystem_list; ciosystem; ciosystem = ciosystem->next) - if (ciosystem->iosysid == iosysid) - return ciosystem; - - return NULL; -} - -/** - * Count the number of open iosystems. - * - * @param niosysid pointer that will get the number of open iosystems. - * @returns 0 for success. - * @author Jim Edwards - */ -int pio_num_iosystem(int *niosysid) -{ - int count = 0; - - /* Count the elements in the list. */ - for (iosystem_desc_t *c = pio_iosystem_list; c; c = c->next) - count++; - - /* Return count to caller via pointer. */ - if (niosysid) - *niosysid = count; - - return PIO_NOERR; -} - -/** - * Add an iodesc to a global list. - * This function guarantees that iodescs (id of the - * iodescs) are unique across the comm provided - * - * @param io_desc_t pointer to data to add to list. - * @param comm MPI Communicator across which the iosystems - * need to be unique - * @returns the ioid of the newly added iodesc. - */ -int pio_add_to_iodesc_list(io_desc_t *iodesc, MPI_Comm comm) -{ - /* Using an arbitrary start id for iodesc ids helps - * in debugging, to distinguish between ids assigned - * to different structures in the code - */ - static int pio_iodesc_next_id = PIO_IODESC_START_ID; - io_desc_t *ciodesc = pio_iodesc_list; - - if(comm != MPI_COMM_NULL) - { - int tmp_id = pio_iodesc_next_id; - int mpierr = MPI_Allreduce(&tmp_id, &pio_iodesc_next_id, 1, MPI_INT, MPI_MAX, comm); - assert(mpierr == MPI_SUCCESS); - } - iodesc->ioid = pio_iodesc_next_id; - pio_iodesc_next_id++; - iodesc->next = NULL; - - /* Add to the global list */ - if (pio_iodesc_list == NULL) - pio_iodesc_list = iodesc; - else - { - /* pio_desc_list has atleast one node */ - while(ciodesc->next) - { - ciodesc = ciodesc->next; - } - ciodesc->next = iodesc; - } - current_iodesc = iodesc; - - return iodesc->ioid; -} - -/** - * Get an iodesc. - * - * @param ioid ID of iodesc to get. - * @returns pointer to the iodesc struc. - * @author Jim Edwards - */ -io_desc_t *pio_get_iodesc_from_id(int ioid) -{ - io_desc_t *ciodesc = NULL; - - /* Do we already have a pointer to it? */ - if (current_iodesc && current_iodesc->ioid == ioid) - return current_iodesc; - - /* Find the decomposition in the list. */ - for (ciodesc = pio_iodesc_list; ciodesc; ciodesc = ciodesc->next) - if (ciodesc->ioid == ioid) - { - current_iodesc = ciodesc; - break; - } - - return ciodesc; -} - -/** - * Delete an iodesc. - * - * @param ioid ID of iodesc to delete. - * @returns 0 on success, error code otherwise. - * @author Jim Edwards - */ -int pio_delete_iodesc_from_list(int ioid) -{ - io_desc_t *ciodesc, *piodesc = NULL; - - for (ciodesc = pio_iodesc_list; ciodesc; ciodesc = ciodesc->next) - { - if (ciodesc->ioid == ioid) - { - if (piodesc == NULL) - pio_iodesc_list = ciodesc->next; - else - piodesc->next = ciodesc->next; - - if (current_iodesc == ciodesc) - current_iodesc = pio_iodesc_list; - free(ciodesc); - return PIO_NOERR; - } - piodesc = ciodesc; - } - return PIO_EBADID; -} diff --git a/src/clib/pio_types.hpp b/src/clib/pio_types.hpp new file mode 100644 index 00000000000..7d155265160 --- /dev/null +++ b/src/clib/pio_types.hpp @@ -0,0 +1,948 @@ +#ifndef __PIO_TYPES_HPP__ +#define __PIO_TYPES_HPP__ + +#include +#include +#include +#include + +#ifdef MPI_SERIAL +#if defined(__cplusplus) +extern "C" { +#endif +#endif + +#include + +#ifdef MPI_SERIAL +#if defined(__cplusplus) +} +#endif +#endif + +#include "pio_config.h" + +#ifdef _NETCDF +#include +#ifdef _NETCDF4 +#include +#endif +#endif + +#ifdef _PNETCDF +#include +#endif + +#ifdef _ADIOS2 +#include +#include +#include +#include +#include +#endif + +#ifdef _HDF5 +#include +#include +#include +#endif + +#include +#include +#include +#include "core/progress_engine/spio_async_op.hpp" + +#ifdef PIO_MICRO_TIMING +/** Some fwd declarations to avoid including internal headers */ +typedef struct mtimer_info *mtimer_t; +#endif + +/* Forward decl of hash map used for ADIOS reads */ +struct spio_hmap; + +/* Fwd declaration to use back pointer to var_desc_t in viobuf_cache */ +struct var_desc_t; + +/* Fwd declaration of thread-specific comm class used in iosystem_desc_t */ +namespace SPIO_Util{ + class TComm_info; +} + +/** The viobuf_cache is used to cache the rearranged data for the + * variable. The iobuf inside the cache is freed when the data + * is written out. + */ +typedef struct viobuf_cache +{ + /* Pointer to user buffer, ubuf points to internal PIO + * buffer that caches the user data + */ + void *ubuf; + size_t ubuf_sz; + /* Pointer to buffer containing the rearranged data */ + void *iobuf; + size_t iobuf_sz; + /* Pointer to fillvalue for this iobuf */ + void *fillvalue; + size_t fillvalue_sz; + /* Variable record/frame number corresponding to this + * buffer. + * Note: Multiple frames for a variable can be cached + * at a time in a list of viobuf_cache s + */ + int rec_num; + /* Any outstanding request associated with this cache */ + /* Used to keep track of a write request for the iobuf */ + int req; + /* Back pointer to the var_desc that contains this cache */ + struct var_desc_t *vdesc; + /* The next iobuf cache for this variable, each iobuf + * cache corresponds to a record/frame of the variable + */ + struct viobuf_cache *next; +} viobuf_cache_t; + +/** + * Variable description structure. + */ +typedef struct var_desc_t +{ + /* Variable ID. */ + int varid; + + /* Variable name - cached */ + char vname[PIO_MAX_NAME + 1]; + + /* Variable description */ + char vdesc[PIO_MAX_NAME + 1]; + + /* Non-zero if this is a record var (i.e. uses unlimited + * dimension). */ + int rec_var; + + /* Number of dimensions for this variable - cached data */ + int ndims; + + /** The record number to be written. Ignored if there is no + * unlimited dimension. */ + int record; + + /** FIXME: Combine request related members to a separate struct */ + /** ID of each outstanding pnetcdf request for this variable. */ + int *request; + + /** Size of each outstanding pnetcdf request for this variable */ + PIO_Offset *request_sz; + + /** Number of requests bending with pnetcdf. */ + int nreqs; + + /** Data buffers (to store rearranged data) for this var */ + /** Data buffers are stored in a singly linked list */ + viobuf_cache_t *viobuf_lhead; + viobuf_cache_t *viobuf_ltail; + + /* Holds the fill value of this var. */ + void *fillvalue; + + /* The PIO data type (PIO_INT, PIO_FLOAT, etc.) */ + int pio_type; + + /* The size of the data type (2 for PIO_SHORT, 4 for PIO_INT, etc.) */ + PIO_Offset type_size; + + /* Size of one record of the variable (number of bytes)*/ + PIO_Offset vrsize; + + /* Bytes pending to be read */ + PIO_Offset rb_pend; + + /* Bytes pending to be written out */ + PIO_Offset wb_pend; + +#ifdef PIO_MICRO_TIMING + mtimer_t rd_mtimer; + mtimer_t rd_rearr_mtimer; + + mtimer_t wr_mtimer; + mtimer_t wr_rearr_mtimer; +#endif + + /** Non-zero if fill mode is turned on for this var. */ + int use_fill; + + /** Buffer that contains the holegrid fill values used to fill in + * missing sections of data when using the subset rearranger. */ + void *fillbuf; +} var_desc_t; + +/** + * IO region structure. + * + * Each IO region is a unit of data which can be described using start + * and count arrays. Each IO task may in general have multiple io + * regions per variable. The box rearranger will have at most one io + * region per variable. + * + * The write from a particular IO task is divided into 1 or more + * regions each of which can be described using start and count. The + * io_region typedef is a linked list of those regions. + */ +typedef struct io_region +{ + /** The offset from the beginning of the data buffer to the + * beginning of this region. */ + int loffset; + + /** Start array for this region. */ + PIO_Offset *start; + + /** Count array for this region. */ + PIO_Offset *count; + + /** Pointer to the next io_region in the list. */ + struct io_region *next; +} io_region; + + +namespace SPIO{ + namespace DataRearr{ + class Contig_rearr; + } +} + +/** + * IO descriptor structure. + * + * This structure defines the mapping for a given variable between + * compute and IO decomposition. + */ +typedef struct io_desc_t +{ + /** The ID of this io_desc_t. */ + int ioid; + + /** The length of the decomposition map. */ + int maplen; + + /** A 1-D array with iodesc->maplen elements, which are the + * 1-based mappings to the global array for that task. */ + PIO_Offset *map; + + /** Number of tasks involved in the communication between comp and + * io tasks. */ + int nrecvs; + + /** Local size of the decomposition array on the compute node. */ + int ndof; + + /** All vars included in this io_desc_t have the same number of + * dimensions. */ + int ndims; + + /** An array of size ndims with the length of each dimension. */ + int *dimlen; + + /** The actual number of IO tasks participating. */ + int num_aiotasks; + + /** The rearranger in use for this variable. */ + int rearranger; + + /** Maximum number of regions in the decomposition. */ + int maxregions; + + /** Does this decomp leave holes in the field (true) or write + * everywhere (false) */ + bool needsfill; + + /** The maximum number of bytes of this iodesc before flushing. */ + int maxbytes; + + /** The PIO type of the data. */ + int piotype; + + /** The size of one element of the piotype. */ + int piotype_size; + + /** The MPI type of the data. */ + MPI_Datatype mpitype; + + /** The size in bytes of a datum of MPI type mpitype. */ + int mpitype_size; + + /** Length of the iobuffer on this task for a single field on the + * IO node. The arrays from compute nodes gathered and rearranged + * to the io-nodes (which are sometimes collocated with compute + * nodes), each io task contains data from the compmap of one or + * more compute tasks in the iomap array. */ + PIO_Offset llen; + + /** Maximum llen participating. */ + PIO_Offset maxiobuflen; + + /** Array (length nrecvs) of computation tasks received from. */ + int *rfrom; + + /** Array (length nrecvs) of counts of data to be received from + * each computation task by the IO tasks. */ + int *rcount; + + /** Array (length numiotasks) of data counts to send to each task + * in the communication in pio_swapm(). */ + int *scount; + + /** Array (length ndof) for the BOX rearranger with the index + * for computation taks (send side during writes). */ + PIO_Offset *sindex; + + /** Index for the IO tasks (receive side during writes). */ + PIO_Offset *rindex; + + /** Array (of length nrecvs) of receive MPI types in pio_swapm() call. */ + MPI_Datatype *rtype; + + /** Array of send MPI types in pio_swapm() call. */ + MPI_Datatype *stype; + + /** Number of send MPI types in pio_swapm() call. */ + int num_stypes; + + /** Used when writing fill data. */ + int holegridsize; + + /** max holegridsize across all io tasks, needed for netcdf and netcdf4c serial */ + int maxholegridsize; + + /** Used when writing fill data. */ + int maxfillregions; + + /** Linked list of regions. */ + io_region *firstregion; + + /** Used when writing fill data. */ + io_region *fillregion; + + /** Rearranger flow control options + * (handshake, non-blocking sends, pending requests) + */ + rearr_opt_t rearr_opts; + + /** In the subset communicator each io task is associated with a + * unique group of comp tasks this is the communicator for that + * group. */ + MPI_Comm subset_comm; + +#if PIO_SAVE_DECOMPS + /* Indicates whether this iodesc has been saved to disk (the + * decomposition is dumped to disk) + */ + bool is_saved; +#endif + + /* FIXME: Once we have classes for subset/box this ptr should be to a base rearr class */ + SPIO::DataRearr::Contig_rearr *rearr; + + /* Number of pending async ops using this I/O desc */ + std::atomic_int nasync_pend_ops; + + /** Pointer to the next io_desc_t in the list. */ + struct io_desc_t *next; +} io_desc_t; + +/* Forward decl for I/O file summary stats info */ +struct spio_io_fstats_summary; + +/** + * IO system descriptor structure. + * + * This structure contains the general IO subsystem data and MPI + * structure + */ +typedef struct iosystem_desc_t +{ + /** The ID of this iosystem_desc_t. This will be obtained by + * calling PIOc_Init_Intercomm() or PIOc_Init_Intracomm(). */ + int iosysid; + + /* I/O System name */ + char sname[PIO_MAX_NAME + 1]; + + /** This is an MPI intra communicator that includes all the tasks in + * both the IO and the computation communicators. */ + MPI_Comm union_comm; + + /** This is an MPI intra communicator that includes all the tasks + * involved in IO. */ + MPI_Comm io_comm; + + /** This is an MPI intra communicator that includes all the tasks + * involved in computation. */ + MPI_Comm comp_comm; + + /** This is an MPI inter communicator between IO communicator and + * computation communicator. */ + MPI_Comm intercomm; + + /** This is a copy (but not an MPI copy) of either the comp (for + * non-async) or the union (for async) communicator. */ + MPI_Comm my_comm; + + /* Comm that includes procs from comp_comm that are local to this + * compute node (share the same memory) */ + MPI_Comm node_comm; + + /** This MPI group contains the processors involved in + * computation. */ + MPI_Group compgroup; + + /** This MPI group contains the processors involved in I/O. */ + MPI_Group iogroup; + + /** The number of tasks in the IO communicator. */ + int num_iotasks; + + /** The number of tasks in the computation communicator. */ + int num_comptasks; + + /** The number of tasks in the union communicator (will be + * num_comptasks for non-async, num_comptasks + num_iotasks for + * async). */ + int num_uniontasks; + + /** Rank of this task in the union communicator. */ + int union_rank; + + /** The rank of this process in the computation communicator, or -1 + * if this process is not part of the computation communicator. */ + int comp_rank; + + /** The rank of this process in the IO communicator, or -1 if this + * process is not part of the IO communicator. */ + int io_rank; + + /** Set to MPI_ROOT if this task is the master of IO communicator, 0 + * otherwise. */ + int iomaster; + + /** Set to MPI_ROOT if this task is the master of comp communicator, 0 + * otherwise. */ + int compmaster; + + /** Rank of IO root task (which is rank 0 in io_comm) in the union + * communicator. Will always be 0 for async situations. */ + int ioroot; + + /** Rank of computation root task (which is rank 0 in + * comm_comms[cmp]) in the union communicator. Will always = number + * of IO tasks in async situations. */ + int comproot; + + SPIO_Util::TComm_info *tcomm_info; + + /** An array of the ranks of all IO tasks within the union + * communicator. */ + int *ioranks; + + /** An array of the ranks of all computation tasks within the + * union communicator. */ + int *compranks; + + /** Controls handling errors. */ + int error_handler; + + /** The rearranger decides which parts of a distributed array are + * handled by which IO tasks. */ + int default_rearranger; + + /** True if asynchronous interface is in use. */ + bool async; + + /** True if this task is a member of the IO communicator. */ + bool ioproc; + + /** True if this task is a member of a computation + * communicator. */ + bool compproc; + + /** MPI Info object. */ + MPI_Info info; + + /** Async I/O service message info */ + struct async_ios_msg_info_{ + int seq_num; + int prev_msg; + } async_ios_msg_info; + + /** Index of this component in the list of components. */ + int comp_idx; + + /** Rearranger options. */ + rearr_opt_t rearr_opts; + +#ifdef _ADIOS2 + /* ADIOS handles */ + adios2_adios *adiosH; /* Handle for ADIOS write */ + adios2_adios *adios_readerH; /* Handle for ADIOS read */ + int adios_io_process; + MPI_Comm adios_comm; + int adios_rank, num_adiostasks; + /* Block merging setup */ + MPI_Comm block_comm; + int block_myrank, block_nprocs; + #ifdef _SPIO_ADIOS_USE_COMPRESSION + /* ADIOS operator for applying a specific lossless compression method (e.g., Blosc2, BZip2) */ + adios2_operator* lossless_compression_operator; + int adios_lossless_compression_method; + #ifdef _SPIO_ADIOS_USE_LOSSY_COMPRESSION + /* ADIOS operator for applying a specific lossy compression method (e.g., SZ, MGARD, ZFP) */ + adios2_operator* lossy_compression_operator; + int adios_lossy_compression_method; + #endif + #endif +#endif + + /** I/O statistics associated with this I/O system */ + struct spio_io_fstats_summary *io_fstats; + + /* List of pending async operations on this iosystem */ + std::deque async_pend_ops; + + /** Pointer to the next iosystem_desc_t in the list. */ + struct iosystem_desc_t *next; +} iosystem_desc_t; + +/** + * The multi buffer holds data from one or more variables. Data are + * accumulated in the multi-buffer. + */ +typedef struct wmulti_buffer +{ + /** The ID that describes the decomposition, as returned from + * PIOc_Init_Decomp(). */ + int ioid; + + /** Non-zero if this is a buffer for a record var. */ + int recordvar; + + /** Number of arrays of data in the multibuffer. Each array had + * data for one var or record. When multibuffer is flushed, all + * arrays are written and num_arrays returns to zero. */ + int num_arrays; + + /** Size of this variables data on local task. All vars in the + * multi-buffer have the same size. */ + int arraylen; + + /** Array of varids. */ + int *vid; + + /** An array of current record numbers, for record vars. One + * element per variable. */ + int *frame; + + /** Array of fill values used for each var. */ + void *fillvalue; + + /** Pointer to the data. */ + void *data; + + /** Pointer to the next multi-buffer in the list. */ + struct wmulti_buffer *next; +} wmulti_buffer; + +#ifdef _ADIOS2 +/* Interval map for ADIOS read */ +typedef struct adios_interval_map_t +{ + int n_adios_steps; + short **map; +} adios_interval_map_t; + +/* Maximum char array length to store "darray" or "put_var", for ADIOS read */ +#define NC_OP_TAG_MAX_LEN 8 + +/** Variable definition information saved at pioc_def_var, + * so that ADIOS can define the variable at write time when + * local dimensions and offsets are known. + */ +typedef struct adios_var_desc_t +{ + /** Variable name */ + char * name; + + /** NC type give at def_var time */ + int nc_type; + + /** Type converted from NC type to adios type */ + adios2_type adios_type; + size_t adios_type_size; + + /** Number of dimensions */ + int ndims; + + /** Global dims (dim var ids) */ + int * gdimids; + + /** Number of attributes defined for this variable */ + int nattrs; + + /** ADIOS varID, if it has already been defined. + * We avoid defining again when writing multiple records over time + */ + adios2_variable* adios_varid; // 0: undefined yet + + /* to handle PIOc_setframe with different decompositions */ + adios2_variable* decomp_varid; + adios2_variable* frame_varid; + adios2_variable* fillval_varid; + adios2_variable* num_block_writers_varid; + + /* to handle multi-dimensional temporal variables */ + adios2_variable* start_varid; + adios2_variable* count_varid; + + /* to buffer decomp id, frame id, fill value, and writer blocks */ + int32_t *decomp_buffer; + int32_t *frame_buffer; + char *fillval_buffer; + int32_t fillval_size; + int32_t *num_wb_buffer; + int32_t decomp_cnt, frame_cnt, fillval_cnt, num_wb_cnt; + int32_t max_buffer_cnt; + + /* Simplified version of an interval map implementation + * index is a frame_id and the value is the adios_step */ + adios_interval_map_t* interval_map; /* For ADIOS read */ + + /* Is this variable written with put_var or darray? */ + char nc_op_tag[NC_OP_TAG_MAX_LEN]; /* For ADIOS read */ +} adios_var_desc_t; + +/* Track attributes */ +typedef struct adios_att_desc_t +{ + /** Attribute name */ + char *att_name; + + /** NC type give at def_att time */ + nc_type att_type; + + /** length of attribute value */ + PIO_Offset att_len; + + /** ncid of the attribute */ + int att_ncid; + + /** attribute varid */ + int att_varid; + + /** Type converted from NC type to adios type */ + adios2_type adios_type; +} adios_att_desc_t; + +#ifdef _SPIO_ADIOS_USE_COMPRESSION +enum ADIOS_COMPRESSION_METHOD +{ + ADIOS_COMPRESSION_METHOD_BLOSC2 = 1, + + ADIOS_COMPRESSION_METHOD_BZIP2 = 2, + + ADIOS_COMPRESSION_METHOD_MGARD = 3, + + ADIOS_COMPRESSION_METHOD_SZ = 4, + + ADIOS_COMPRESSION_METHOD_ZFP = 5 +}; +#endif +#endif /* _ADIOS2 */ + +#ifdef _HDF5 +typedef struct hdf5_dim_desc_t +{ + /** Dimension name */ + char* name; + + /** Dimension length */ + PIO_Offset len; + + /** Chunk size, if specified. Default 0 + * if SPIO_DIM_CHUNK_INFO is provided by the user, + * - a Chunk size of 0 => no chunking on this dimension + * if SPIO_DIM_CHUNK_INFO is not provided by the user (empty string), + * - a Chunk size of 0 => default chunking (based on PIO_CHUNK_SIZE) + */ + PIO_Offset chunk_sz; + + /** True if the dimension has a coordinate variable */ + bool has_coord_var; + + hid_t hdf5_dataset_id; +} hdf5_dim_desc_t; + +typedef struct hdf5_var_desc_t +{ + /** Variable name */ + char* name; + + /* Alternative name for a non-coordinate variable with the same name as a dimension */ + char* alt_name; + + /** NC type give at def_var time */ + int nc_type; + + /** Type converted from NC type to HDF5 type */ + hid_t hdf5_type; + + /** Number of dimensions */ + int ndims; + + /** Dimension IDs of the variable */ + int* hdf5_dimids; + + /** True if the variable is a coordinate variable */ + bool is_coord_var; + + hid_t hdf5_dataset_id; +} hdf5_var_desc_t; + +typedef struct hdf5_att_desc_t +{ + /** Attribute name */ + char *att_name; + + /** NC type give at def_att time */ + nc_type att_type; + + /** length of attribute value */ + PIO_Offset att_len; + + /** ncid of the attribute */ + int att_ncid; + + /** attribute varid */ + int att_varid; +} hdf5_att_desc_t; +#endif + +/** + * File descriptor structure. + * + * This structure holds information associated with each open file + */ +typedef struct file_desc_t +{ + /** The IO system ID used to open this file. */ + iosystem_desc_t *iosystem; + + /** The ncid returned for this file by the underlying library + * (netcdf or pnetcdf). */ + int fh; + +#ifdef _ADIOS2 + /** Save the filename, now just for printing it at close */ + char *filename; + + /** ADIOS file handler is 64bit integer */ + adios2_engine *engineH; + + /** Check if begin_step has been invoked. It is used to invoke end_step **/ + int begin_step_called; + int num_step_calls;; + int max_step_calls; + + /* + * Used to call adios2_end_step to avoid buffer overflow in MPI_Gatherv + * during ADIOS metadata write operation. + * + * if num_written_blocks * BLOCK_METADATA_SIZE >= BLOCK_COUNT_THRESHOLD, call adios2_end_step + * (Not implemented in this version. adios2_end_step is called if num_step_calls >= max_step_calls (= PIO_MAX_CACHED_STEPS_FOR_ADIOS)) + */ + unsigned int num_written_blocks; + + int write_decomp_id; + int write_frame_id; + int write_fillval_id; + + /** Handler for ADIOS group (of variables) */ + adios2_io *ioH; + + /** ADIOS output transport method name, POSIX or MPI_AGGREGATE */ + char transport[PIO_MAX_NAME]; + + /** Parameters for the transport method, required for MPI_AGGREGATE. + * Created automatically from the application setup */ + char params[PIO_MAX_NAME]; + + /** Need to store the dim names for finding them and using them when defining variables */ + char *dim_names[PIO_MAX_DIMS]; + PIO_Offset dim_values[PIO_MAX_DIMS]; + + /** Number of dim vars defined */ + int num_dim_vars; + + /** Variable information, max PIO_MAX_VARS variables allowed */ + struct adios_var_desc_t adios_vars[PIO_MAX_VARS]; + + /** Number of vars defined */ + int num_vars; + + /** Number of global attributes defined. Needed to support PIOc_inq_nattrs() */ + int num_gattrs; + + /* all procs rank, etc */ + MPI_Comm all_comm; + int all_rank, num_alltasks; + + /* ADIOS rank, etc */ + int adios_io_process; + MPI_Comm adios_comm; + int adios_rank, num_adiostasks; + + /* Merging distributed array blocks to reduce I/O overhead */ + /* Grouping of processes for block merging */ + MPI_Comm block_comm; + int block_myrank, block_nprocs; + int *block_list; + + /* Buffers for merging distributed array blocks */ + unsigned int *array_counts; + unsigned int array_counts_size; + unsigned int *array_disp; + unsigned int array_disp_size; + char *block_array; + size_t block_array_size; + + /* Track attributes */ + /** attribute information. Allow PIO_MAX_VARS for now. */ + struct adios_att_desc_t adios_attrs[PIO_MAX_ATTRS]; + int num_attrs; + + int fillmode; + + /* Handle PIO_Offset */ + int pio_offset_size; + adios2_type pio_offset_type; + + /** Array for decompositions that has been written already (must write only once) */ + int n_written_ioids; + int written_ioids[PIO_MAX_ADIOS_DECOMPS]; /* written_ioids[N] = ioid if that decomp has been already written, */ + + /** Store current frameid for end_step in PIO_setframe */ + int current_frame; + + /* Some caches (hash tables) for ADIOS read */ + struct spio_hmap *cache_data_blocks; + struct spio_hmap *cache_block_sizes; + struct spio_hmap *cache_darray_info; + + char io_name_reader[PIO_MAX_NAME + 1]; /* Name of io object, for ADIOS read */ + size_t adios_reader_num_decomp_blocks; /* Number of decomposition blocks, for ADIOS read */ + + /* Indicates whether the decomposition maps (for ADIOS write) need to be stored in BP files. Default is true. */ + bool store_adios_decomp; +#endif /* _ADIOS2 */ + +#ifdef _HDF5 + hid_t hdf5_file_id; + + /* Collective dataset transfer property list, used by H5Dwrite */ + hid_t dxplid_coll; + + /* Independent dataset transfer property list, used by H5Dwrite */ + hid_t dxplid_indep; + + struct hdf5_dim_desc_t hdf5_dims[PIO_MAX_DIMS]; + + /** Number of dims defined */ + int hdf5_num_dims; + + struct hdf5_var_desc_t hdf5_vars[PIO_MAX_VARS]; + + /** Number of vars defined */ + int hdf5_num_vars; + + struct hdf5_att_desc_t hdf5_attrs[PIO_MAX_ATTRS]; + + /** Number of attrs defined */ + int hdf5_num_attrs; + + /** Number of global attrs defined */ + int hdf5_num_gattrs; + +#endif /* _HDF5 */ + /** A datatype converter for user buffers */ + void *dt_converter; + + /* File name - cached */ + char fname[PIO_MAX_NAME + 1]; + + /** The ncid that will be returned to the user. */ + int pio_ncid; + + /** The PIO_TYPE value that was used to open this file. */ + int iotype; + + /** List of variables in this file (deprecated). */ + struct var_desc_t varlist[PIO_MAX_VARS]; + + /* Number of unlimited dim ids, if no unlimited id present = 0 */ + int num_unlim_dimids; + + /* Unlimited dim ids, if no unlimited id present = NULL */ + int *unlim_dimids; + + /** Mode used when file was opened. */ + int mode; + + /** The wmulti_buffer is used to aggregate multiple variables with + * the same communication pattern prior to a write. */ + struct wmulti_buffer buffer; + + /* Bytes pending to be read on this file*/ + PIO_Offset rb_pend; + + /* Bytes pending to be written out for this file */ + PIO_Offset wb_pend; + + /** Data buffer per IO decomposition for this file. */ + /** Cache for storing data corresponding to multiple + * variables binned on the I/O decomposition used by + * the variable */ + void *mvcache; + + /** I/O statistics associated with this file */ + struct spio_io_fstats_summary *io_fstats; + + /* List of pending async operations on this file */ + std::deque async_pend_ops; + + /** Total number of pending ops on this file, including + * the ones queued in async_pend_ops. In the case where + * async_pend_ops.size() == 0, npend_ops shows any other + * pending ops (e.g. non-blocking write done of rearranged + * data, still need to wait on it) + */ + std::atomic npend_ops; + std::atomic is_hard_closed; + std::mutex *pmtx; + + /** Pointer to the next file_desc_t in the list of open files. */ + struct file_desc_t *next; + + /** True if this task should participate in IO (only true for one + * task with netcdf serial files. */ + int do_io; + + /** True if we need reserve some extra space in the header when + * creating NetCDF files to accommodate anticipated changes. */ + bool reserve_extra_header_space; + + /** True if the file is in "define mode", false otherwise */ + bool in_def_mode; + + /** True if this is an existing file reopened */ + bool is_reopened; +} file_desc_t; + +#endif // __PIO_TYPES_HPP__ diff --git a/src/clib/spio_logger.hpp b/src/clib/spio_logger.hpp deleted file mode 100644 index 02747f876dd..00000000000 --- a/src/clib/spio_logger.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef __SPIO_LOGGER_HPP__ -#define __SPIO_LOGGER_HPP__ - -#include - -#include "mpi.h" - -namespace SPIO_Util{ - namespace Logger{ - /* FIXME: Add a generic logger class */ - /* FIXME : Allow logging from multiple classes */ - /* An MPI logger */ - template - class MPI_logger{ - public: - MPI_logger() : mpi_comm_(MPI_COMM_NULL), ostr_(NULL), is_io_proc_(false) {} - - MPI_logger(MPI_Comm comm, TStream *ostr); - - MPI_logger(const MPI_logger &other) = default; - - MPI_logger &operator=(const MPI_logger &other) = default; - - /* Log message */ - void log(const std::string &log_msg); - - /* Singleton log : Logging is only done once - e.g. writing headers etc */ - void slog(const std::string &slog_msg); - - /* Explicitly flush the contents of the logger */ - void flush(void ); - - /* Get associated stream */ - TStream *get_log_stream(void ) const; - - ~MPI_logger(){} - private: - MPI_Comm mpi_comm_; - TStream *ostr_; - bool is_io_proc_; - bool slog_done_; - - static const int MPI_RANK_ROOT = 0; - static const char LOG_SEP = '\n'; - }; - } // namespace Logger -} // namespace SPIO_Util - -template -SPIO_Util::Logger::MPI_logger::MPI_logger(MPI_Comm comm, TStream *ostr):mpi_comm_(comm), ostr_(ostr), is_io_proc_(false), slog_done_(false) -{ - int rank; - int ret; - - ret = MPI_Comm_rank(mpi_comm_, &rank); - assert(ret == MPI_SUCCESS); - if(rank == MPI_RANK_ROOT){ - is_io_proc_ = true; - } -} - -template -void SPIO_Util::Logger::MPI_logger::log(const std::string &log_msg) -{ - if(is_io_proc_){ - (*ostr_) << log_msg.c_str() << LOG_SEP; - } -} - -template -void SPIO_Util::Logger::MPI_logger::slog(const std::string &slog_msg) -{ - if(!slog_done_){ - log(slog_msg); - slog_done_ = true; - } -} - -template -void SPIO_Util::Logger::MPI_logger::flush() -{ - if(is_io_proc_){ - ostr_->flush(); - } -} - -template -TStream *SPIO_Util::Logger::MPI_logger::get_log_stream(void ) const -{ - return ostr_; -} -#endif /* __SPIO_LOGGER_HPP__ */ diff --git a/src/clib/util/CMakeLists.txt b/src/clib/util/CMakeLists.txt new file mode 100644 index 00000000000..45adf17f613 --- /dev/null +++ b/src/clib/util/CMakeLists.txt @@ -0,0 +1,27 @@ +#============================================================================== +# DEFINE THE TARGET LIBRARY +#============================================================================== +message(STATUS "===== Configuring SCORPIO C lib utils... =====") +# Util source - Utils used across the library +set (spio_util_src + spio_ltimer.cpp + spio_dt_converter.cpp + spio_dbg_ds.cpp + pio_timer.cpp + bget.cpp + topology.cpp + spio_hash.cpp + spio_tracer.cpp + spio_tracer_mdata.cpp + spio_tracer_decomp.cpp + spio_serializer.cpp + pio_print.cpp + pio_mpi_timer.cpp) + +add_library (spio_util OBJECT ${spio_util_src}) +target_include_directories(spio_util + PUBLIC . + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/.. ${SCORPIO_SOURCE_DIR}/util) +target_link_libraries(spio_util + PUBLIC spio_default_public_options + PRIVATE spio_default_private_options) diff --git a/src/clib/README_pio_micro_timers.txt b/src/clib/util/README_pio_micro_timers.txt similarity index 100% rename from src/clib/README_pio_micro_timers.txt rename to src/clib/util/README_pio_micro_timers.txt diff --git a/src/clib/bget.cpp b/src/clib/util/bget.cpp similarity index 99% rename from src/clib/bget.cpp rename to src/clib/util/bget.cpp index 46352a92dc9..885db7e267b 100644 --- a/src/clib/bget.cpp +++ b/src/clib/util/bget.cpp @@ -1292,8 +1292,8 @@ void bufdump(void *buf) } while (bdlen > 0) { - int i, dupes = 0; - bufsize l = bdlen; + int dupes = 0; + bufsize i = 0, l = bdlen; char bhex[50], bascii[20]; if (l > 16) { @@ -1348,17 +1348,16 @@ void bpoold(void *buf, int dumpalloc, int dumpfree) bufdump((void *) (((char *) b) + sizeof(struct bhead))); } } else { - char *lerr = ""; - assert(bs > 0); if ((b->ql.blink->ql.flink != b) || (b->ql.flink->ql.blink != b)) { - lerr = " (Bad free list links)"; + LOG((0, "Free block: size %6ld bytes. (Bad free list links)", (long) bs)); + } + else{ + LOG((0, "Free block: size %6ld bytes.", (long) bs)); } - LOG((0, "Free block: size %6ld bytes.%s", - (long) bs, lerr)); #ifdef FreeWipe - lerr = ((char *) b) + sizeof(struct bfhead); + char *lerr = ((char *) b) + sizeof(struct bfhead); if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) || (memcmp(lerr, lerr + 1, (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) { @@ -1391,21 +1390,18 @@ int bpoolv(void *buf) if (bs < 0) { bs = -bs; } else { - char *lerr = ""; - assert(bs > 0); if (bs <= 0) { return 0; } if ((b->ql.blink->ql.flink != b) || (b->ql.flink->ql.blink != b)) { - LOG((0, "Free block: size %6ld bytes. (Bad free list links)", - (long) bs)); + LOG((0, "Free block: size %6ld bytes. (Bad free list links)", (long) bs)); assert(0); return 0; } #ifdef FreeWipe - lerr = ((char *) b) + sizeof(struct bfhead); + char *lerr = ((char *) b) + sizeof(struct bfhead); if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) || (memcmp(lerr, lerr + 1, (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) { diff --git a/src/clib/bget.h b/src/clib/util/bget.h similarity index 95% rename from src/clib/bget.h rename to src/clib/util/bget.h index 9929b31cb01..43467c5bd06 100644 --- a/src/clib/bget.h +++ b/src/clib/util/bget.h @@ -11,7 +11,9 @@ //#undef NDEBUG //#endif -typedef long bufsize; +#include + +typedef size_t bufsize; #if defined(__cplusplus) extern "C" { diff --git a/src/clib/gptl_skel.h b/src/clib/util/gptl_skel.h similarity index 100% rename from src/clib/gptl_skel.h rename to src/clib/util/gptl_skel.h diff --git a/src/clib/pio_minmax.h b/src/clib/util/pio_minmax.h similarity index 100% rename from src/clib/pio_minmax.h rename to src/clib/util/pio_minmax.h diff --git a/src/clib/pio_mpi_timer.cpp b/src/clib/util/pio_mpi_timer.cpp similarity index 100% rename from src/clib/pio_mpi_timer.cpp rename to src/clib/util/pio_mpi_timer.cpp diff --git a/src/clib/pio_print.cpp b/src/clib/util/pio_print.cpp similarity index 99% rename from src/clib/pio_print.cpp rename to src/clib/util/pio_print.cpp index c768b7ee9cb..b8c53b58e4c 100644 --- a/src/clib/pio_print.cpp +++ b/src/clib/util/pio_print.cpp @@ -19,6 +19,10 @@ const char *pio_iotype_to_string(int iotype) return "PIO_IOTYPE_ADIOS"; case PIO_IOTYPE_ADIOSC: return "PIO_IOTYPE_ADIOSC"; + case PIO_IOTYPE_HDF5: + return "PIO_IOTYPE_HDF5"; + case PIO_IOTYPE_HDF5C: + return "PIO_IOTYPE_HDF5C"; default: return "UNKNOWN"; } diff --git a/src/clib/pio_timer.cpp b/src/clib/util/pio_timer.cpp similarity index 95% rename from src/clib/pio_timer.cpp rename to src/clib/util/pio_timer.cpp index b7d2de60c17..90f8c178ed7 100644 --- a/src/clib/pio_timer.cpp +++ b/src/clib/util/pio_timer.cpp @@ -1,6 +1,7 @@ #include "pio_config.h" #include "pio_timer.h" #include "pio_internal.h" +#include /* This structure stores information on a timer type * init -> The init function for the timer @@ -76,7 +77,7 @@ int mtimer_init(mtimer_type_t type) * comm : MPI communicator where the timer runs * log_fname : File name for the timer logs */ -mtimer_t mtimer_create(const char *name, MPI_Comm comm, char *log_fname) +mtimer_t mtimer_create(const char *name, MPI_Comm comm, const char *log_fname) { assert((name != NULL) && (log_fname != NULL)); mtimer_t mt = (mtimer_t )malloc(sizeof(struct mtimer_info)); @@ -129,7 +130,6 @@ int mtimer_stop(mtimer_t mt, const char *log_msg) { int ret = PIO_NOERR; double elapsed_time = 0; - char tmp_log_msg[PIO_MAX_NAME]; if(mt == NULL) { LOG((3, "ERROR: Micro timer failed to stop, the timer handle is invalid")); @@ -154,10 +154,13 @@ int mtimer_stop(mtimer_t mt, const char *log_msg) /* Flush timer log message if no asynchronous events are pending */ if(!mt->is_async_event_in_progress) { - snprintf(tmp_log_msg, PIO_MAX_NAME, "%s %s time=%11.8f s", mt->name, (log_msg)?(log_msg):"", mt->total_time); if(pio_timer_type == PIO_MICRO_MPI_WTIME_ROOT) { - ret = mtimer_flush_root(mt, tmp_log_msg, mt->comm); + std::string tmp_log_msg(mt->name); + if(log_msg) { tmp_log_msg += log_msg; } + tmp_log_msg += std::string("time=") + std::to_string(mt->total_time); + + ret = mtimer_flush_root(mt, tmp_log_msg.c_str(), mt->comm); mt->total_time = 0; } else @@ -365,7 +368,6 @@ int mtimer_update(mtimer_t mt, double time) int mtimer_flush(mtimer_t mt, const char *log_msg) { int ret = PIO_NOERR; - char tmp_log_msg[PIO_MAX_NAME]; if(mt == NULL) { LOG((3, "ERROR: Flushing timer failed, invalid handle")); @@ -384,10 +386,13 @@ int mtimer_flush(mtimer_t mt, const char *log_msg) */ if(!mt->is_async_event_in_progress && (mt->total_time > 0)) { - snprintf(tmp_log_msg, PIO_MAX_NAME, "%s %s time=%11.8f s", mt->name, (log_msg)?(log_msg):"", mt->total_time); if(pio_timer_type == PIO_MICRO_MPI_WTIME_ROOT) { - ret = mtimer_flush_root(mt, tmp_log_msg, mt->comm); + std::string tmp_log_msg(mt->name); + if(log_msg) { tmp_log_msg += log_msg; } + tmp_log_msg += std::string("time=") + std::to_string(mt->total_time); + + ret = mtimer_flush_root(mt, tmp_log_msg.c_str(), mt->comm); mt->total_time = 0; } else diff --git a/src/clib/pio_timer.h b/src/clib/util/pio_timer.h similarity index 97% rename from src/clib/pio_timer.h rename to src/clib/util/pio_timer.h index f737c41b41c..4c472a00c6f 100644 --- a/src/clib/pio_timer.h +++ b/src/clib/util/pio_timer.h @@ -69,7 +69,7 @@ extern "C" { /* Init timer framework - needs to be called once before using timers */ int mtimer_init(mtimer_type_t type); /* Create/Start/Stop/Destroy a timer */ -mtimer_t mtimer_create(const char *name, MPI_Comm comm, char *log_fname); +mtimer_t mtimer_create(const char *name, MPI_Comm comm, const char *log_fname); int mtimer_start(mtimer_t mt); int mtimer_stop(mtimer_t mt, const char *log_msg); int mtimer_destroy(mtimer_t *pmt); diff --git a/src/clib/util/spio_dbg_ds.cpp b/src/clib/util/spio_dbg_ds.cpp new file mode 100644 index 00000000000..fae0cd18242 --- /dev/null +++ b/src/clib/util/spio_dbg_ds.cpp @@ -0,0 +1,76 @@ +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" +#include "pio_types.hpp" +#include "spio_dbg_utils.hpp" +#include "execinfo.h" + +#include +#include +#include +#include +#include +#include +#include + +std::string SPIO_Util::Dbg_Util::get_iodesc_info(io_desc_t *iodesc) +{ + std::ostringstream ostr; + if(iodesc){ + ostr << "iodesc{"; + ostr << "ioid = " << iodesc->ioid << ", maplen =" << iodesc->maplen; + ostr << ", map = ["; + // std::copy(iodesc->map, iodesc->map + iodesc->maplen, std::ostream_iterator(ostr, ",")); + ostr << "..."; + ostr << "], ndof = " << iodesc->ndof << ", ndims = " << iodesc->ndims; + ostr << ", dimlen = ["; + std::copy(iodesc->dimlen, iodesc->dimlen + iodesc->ndims, std::ostream_iterator(ostr, ",")); + ostr << "], rearranger = " << iodesc->rearranger; + ostr << ", maxregions =" << iodesc->maxregions; + ostr << ", llen = " << iodesc->llen; + io_region *cur_region = iodesc->firstregion; + for(int i = 0; i < iodesc->maxregions; i++){ + if(cur_region == NULL){ + ostr << ", region " << i << "{NULL}"; continue; + } + ostr << ", region " << i << " {"; + ostr << "loffset = " << cur_region->loffset; + ostr << ", start = ["; + std::copy(cur_region->start, cur_region->start + iodesc->ndims, std::ostream_iterator(ostr, ",")); + ostr << "], "; + ostr << "count = ["; + std::copy(cur_region->count, cur_region->count + iodesc->ndims, std::ostream_iterator(ostr, ",")); + ostr << "]"; + ostr << " }"; + cur_region = cur_region->next; + } + ostr << "}"; + } + else{ + ostr<< "iodesc{NULL}"; + } + + return ostr.str(); +} + +void SPIO_Util::Dbg_Util::get_stack_trace(std::vector &st) +{ + const std::size_t MAX_STACK_DEPTH = 20; + std::array st_addrs; + + std::size_t st_sz = backtrace(st_addrs.data(), st_addrs.size()); + char **st_syms = backtrace_symbols(st_addrs.data(), st_sz); + + if(!st_syms){ return; } + + std::for_each(st_syms, st_syms + st_sz, [&st](char *sname) { st.push_back(sname); }); + + free(st_syms); +} + +std::string SPIO_Util::Dbg_Util::stack_trace_to_string(const std::vector &st) +{ + std::ostringstream ostr; + std::for_each(st.cbegin(), st.cend(), [&ostr](const std::string &str) { ostr << str << "\n"; }); + return ostr.str(); +} diff --git a/src/clib/util/spio_dbg_utils.hpp b/src/clib/util/spio_dbg_utils.hpp new file mode 100644 index 00000000000..749afb120d1 --- /dev/null +++ b/src/clib/util/spio_dbg_utils.hpp @@ -0,0 +1,74 @@ +#ifndef __SPIO_DBG_UTILS_HPP__ +#define __SPIO_DBG_UTILS_HPP__ + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" +#include "pio_types.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace SPIO_Util{ + namespace Dbg_Util{ + + /* Debug print a vector */ + template + void print_1dvec(const std::vector &v) + { + std::ostringstream ostr; + ostr << "["; + std::copy(v.cbegin(), v.cend(), std::ostream_iterator(ostr, ",")); + ostr << "]"; + std::cout << ostr.str().c_str() << "\n" << std::flush; + } + + /* Debug print a vector of vectors */ + template + void print_1dvec(std::initializer_list > vecs) + { + std::ostringstream ostr; + for(typename std::initializer_list >::iterator citer = vecs.begin(); + citer != vecs.end(); ++citer){ + ostr << "["; + std::copy((*citer).cbegin(), (*citer).cend(), std::ostream_iterator(ostr, ",")); + ostr << "] "; + } + std::cout << ostr.str().c_str() << "\n" << std::flush; + } + + /* Debug print an array */ + template + void print_1dvec(const T *vbegin, const T *vend) + { + std::ostringstream ostr; + ostr << "["; + std::copy(vbegin, vend, std::ostream_iterator(ostr, ",")); + ostr << "]"; + std::cout << ostr.str().c_str() << "\n" << std::flush; + } + + /* Convert vector to string */ + template + std::string vec1d_to_string(const T *vbegin, const T *vend) + { + std::ostringstream ostr; + ostr << "["; + std::copy(vbegin, vend, std::ostream_iterator(ostr, ",")); + ostr << "]"; + return ostr.str(); + } + + std::string get_iodesc_info(io_desc_t *ios); + + void get_stack_trace(std::vector &st); + std::string stack_trace_to_string(const std::vector &st); + } // namespace Dbg_Util +} // namespace SPIO_Util + +#endif // __SPIO_DBG_UTILS_HPP__ diff --git a/src/clib/util/spio_dt_converter.cpp b/src/clib/util/spio_dt_converter.cpp new file mode 100644 index 00000000000..c3572c22baf --- /dev/null +++ b/src/clib/util/spio_dt_converter.cpp @@ -0,0 +1,60 @@ +#include "spio_dt_converter.hpp" + +void *SPIO_Util::File_Util::DTConverter::convert(int ncid, void *buf, std::size_t sz, nc_type from_pio_type, nc_type to_pio_type) +{ + if(from_pio_type == to_pio_type){ + /* No conversion required, return the buffer as is */ + return buf; + } + + std::size_t nelems = sz / size_of(from_pio_type); + + Cachebuf cbuf = { bget(nelems * size_of(to_pio_type)), to_pio_type }; + + copy_to(buf, from_pio_type, cbuf.buf, to_pio_type, nelems); + + std::map >::iterator cbufs_iter = cbufs_.find(ncid); + if(cbufs_iter != cbufs_.end()){ + cbufs_iter->second.push_back(cbuf); + } + else{ + cbufs_.insert(std::make_pair(ncid, std::vector({cbuf}))); + } + + return cbuf.buf; +} + +void *SPIO_Util::File_Util::DTConverter::convert(const void *buf, std::size_t sz, nc_type from_pio_type, nc_type to_pio_type) +{ + std::size_t nelems = sz / size_of(from_pio_type); + + void *rbuf = bget(nelems * size_of(to_pio_type)); + + copy_to(buf, from_pio_type, rbuf, to_pio_type, nelems); + + return rbuf; +} + +void SPIO_Util::File_Util::DTConverter::free(int ncid) +{ + std::map >::iterator cbufs_iter = cbufs_.find(ncid); + if(cbufs_iter != cbufs_.end()){ + for(std::vector::iterator iter = cbufs_iter->second.begin(); + iter != cbufs_iter->second.end(); ++iter){ + brel(iter->buf); + } + cbufs_.erase(cbufs_iter); + } +} + +void SPIO_Util::File_Util::DTConverter::clear(void) +{ + for(std::map >::iterator cbufs_iter = cbufs_.begin(); + cbufs_iter != cbufs_.end(); ++cbufs_iter){ + for(std::vector::iterator iter = cbufs_iter->second.begin(); + iter != cbufs_iter->second.end(); ++iter){ + brel(iter->buf); + } + } + cbufs_.clear(); +} diff --git a/src/clib/util/spio_dt_converter.hpp b/src/clib/util/spio_dt_converter.hpp new file mode 100644 index 00000000000..b37f1857cab --- /dev/null +++ b/src/clib/util/spio_dt_converter.hpp @@ -0,0 +1,108 @@ +#ifndef __SPIO_DT_CONVERTER_HPP__ +#define __SPIO_DT_CONVERTER_HPP__ + +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" + +namespace SPIO_Util{ + namespace File_Util{ + /* Datatype converter class. + * Caches temp buffers (as a result of conversion) and these + * buffers can be freed using free() + */ + class DTConverter{ + public: + /* Convert buffer to requested type, buffer size, sz, is in bytes + * - the returned buffer (converted buffer) is owned by the datatype converter & + * can be freed via the free(ncid) member function + */ + void *convert(int ncid, void *buf, std::size_t sz, nc_type from_pio_type, nc_type to_pio_type); + /* Convert buffer to requested type, buffer size, sz, is in bytes + * - the returned buffer (converted buffer) is owned by the caller & + * needs to be freed using the brel(PTR_RETURNED_BY_FUNCTION) function + * by the caller + */ + void *convert(const void *buf, std::size_t sz, nc_type from_pio_type, nc_type to_pio_type); + /* Check if the converter has any cached buffers - useful for debugging */ + bool empty(void ) const { return cbufs_.empty(); } + /* Free the scratch/temp buffers associated with ncid/file */ + void free(int ncid); + /* Clear all scratch/temp buffers */ + void clear(void ); + + static inline std::size_t size_of(nc_type pio_type){ + switch(pio_type){ + case PIO_DOUBLE : return sizeof(double); + case PIO_FLOAT : return sizeof(float); + case PIO_INT : return sizeof(int); + case PIO_UINT : return sizeof(unsigned int); + case PIO_SHORT : return sizeof(short int); + case PIO_USHORT : return sizeof(unsigned short int); + case PIO_INT64 : return sizeof(int64_t); + case PIO_UINT64 : return sizeof(uint64_t); + case PIO_CHAR : return sizeof(char); + case PIO_BYTE : return sizeof(char); + case PIO_UBYTE : return sizeof(unsigned char); + default : assert(0); + } + } + + private: + struct Cachebuf{ + void *buf; + int piotype; + }; + /* Internal map to store scrach/temp bufs for each file/ncid */ + std::map > cbufs_; + + template + static inline void copy_to(F *from_buf, T *to_buf, std::size_t nelems){ + std::transform(from_buf, from_buf + nelems, to_buf, + [](F val){ return static_cast(val); }); + } + + template + static inline void copy_to(F *from_buf, void *to_buf, nc_type to_pio_type, std::size_t nelems){ + switch(to_pio_type){ + case PIO_DOUBLE : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_FLOAT : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_INT : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_UINT : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_SHORT : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_USHORT : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_INT64 : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_UINT64 : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_CHAR : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_BYTE : copy_to(from_buf, static_cast(to_buf), nelems); break; + case PIO_UBYTE : copy_to(from_buf, static_cast(to_buf), nelems); break; + default : assert(0); + } + } + + static inline void copy_to(const void *from_buf, nc_type from_pio_type, void *to_buf, nc_type to_pio_type, std::size_t nelems){ + switch(from_pio_type){ + case PIO_DOUBLE : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_FLOAT : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_INT : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_UINT : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_SHORT : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_USHORT : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_INT64 : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_UINT64 : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_CHAR : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_BYTE : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + case PIO_UBYTE : copy_to(static_cast(from_buf), to_buf, to_pio_type, nelems); break; + default : assert(0); + } + } + }; + } // namespace File_Util +} // namespace SPIO_Util + +#endif // __SPIO_DT_CONVERTER_HPP__ diff --git a/src/clib/util/spio_gptl_utils.hpp b/src/clib/util/spio_gptl_utils.hpp new file mode 100644 index 00000000000..193f3711958 --- /dev/null +++ b/src/clib/util/spio_gptl_utils.hpp @@ -0,0 +1,43 @@ +#ifndef __SPIO_GPTL_UTILS_HPP__ +#define __SPIO_GPTL_UTILS_HPP__ + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" + +#include + +namespace SPIO_Util{ + namespace GPTL_Util{ + class GPTL_wrapper{ + public: + GPTL_wrapper(const std::string &gptl_timer_name):gptl_timer_name_(gptl_timer_name), is_valid_(false){ + if(!gptl_timer_name_.empty()){ + GPTLstart(gptl_timer_name_.c_str()); + is_valid_ = true; + } + } + + ~GPTL_wrapper(){ + if(is_valid_) { GPTLstop(gptl_timer_name_.c_str()); } + } + + private: + const std::string gptl_timer_name_; + /* Is a valid GPTL timer - has a valid timer name */ + bool is_valid_; + }; + + class GPTL_timer : public GPTL_wrapper{ + public: + GPTL_timer(const std::string &name) : GPTL_wrapper(name) {} + }; + + class GPTL_cond_timer : public GPTL_timer{ + public: + GPTL_cond_timer(const std::string &name, bool cond) : GPTL_timer((cond) ? name : "") {} + }; + + } // namespace GPTL_Util +} // namespace SPIO_Util +#endif // __SPIO_GPTL_UTILS_HPP__ diff --git a/src/clib/spio_hash.cpp b/src/clib/util/spio_hash.cpp similarity index 100% rename from src/clib/spio_hash.cpp rename to src/clib/util/spio_hash.cpp diff --git a/src/clib/spio_hash.h b/src/clib/util/spio_hash.h similarity index 100% rename from src/clib/spio_hash.h rename to src/clib/util/spio_hash.h diff --git a/src/clib/spio_ltimer.cpp b/src/clib/util/spio_ltimer.cpp similarity index 89% rename from src/clib/spio_ltimer.cpp rename to src/clib/util/spio_ltimer.cpp index 1f87349471f..c31a396d2eb 100644 --- a/src/clib/spio_ltimer.cpp +++ b/src/clib/util/spio_ltimer.cpp @@ -8,6 +8,7 @@ #include "pio_internal.h" #include "spio_ltimer.h" #include "spio_ltimer.hpp" +#include "spio_dbg_utils.hpp" /* Global timer cache */ static std::map gtimers; @@ -19,6 +20,7 @@ void PIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer::start(void ) start_ = MPI_Wtime(); } lvl_++; + // push_stack_trace(); } void PIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer::stop(void ) @@ -34,6 +36,7 @@ void PIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer::stop(void ) stop_ = MPI_Wtime(); wtime_ += stop_ - start_; //start_ = 0.0; + // pop_stack_trace(); } double PIO_Util::SPIO_Ltimer_Utils::SPIO_ltimer::get_wtime(void ) const @@ -57,5 +60,7 @@ void spio_ltimer_stop(const char *timer_name) double spio_ltimer_get_wtime(const char *timer_name) { /* Note : If the timer is not present we return 0 */ + // gtimers[timer_name].sanity_check(timer_name); + return gtimers[timer_name].get_wtime(); } diff --git a/src/clib/spio_ltimer.h b/src/clib/util/spio_ltimer.h similarity index 100% rename from src/clib/spio_ltimer.h rename to src/clib/util/spio_ltimer.h diff --git a/src/clib/spio_ltimer.hpp b/src/clib/util/spio_ltimer.hpp similarity index 54% rename from src/clib/spio_ltimer.hpp rename to src/clib/util/spio_ltimer.hpp index dc7786fbab9..0d6f4dddafd 100644 --- a/src/clib/spio_ltimer.hpp +++ b/src/clib/util/spio_ltimer.hpp @@ -1,6 +1,11 @@ #ifndef __SPIO_LTIMER_HPP__ #define __SPIO_LTIMER_HPP__ +#include +#include +#include +#include "spio_dbg_utils.hpp" + namespace PIO_Util{ namespace SPIO_Ltimer_Utils{ /* A simple timer class */ @@ -17,6 +22,16 @@ namespace PIO_Util{ inline double get_stop_time(void ) const { return stop_; } /* Get elapsed wallclock time */ double get_wtime(void ) const; + + void sanity_check(const std::string &msg) const { + if(lvl_ != 0){ + std::cerr << "WARNING: Sanity check failed, trying to get timer before its stopped, level = " << lvl_ << "(" << msg.c_str() << ")\n"; + if(st_.size() > 0){ + std::cerr << "Printing first (of " << st_.size() << ") stack traces:\n"; + std::cerr << SPIO_Util::Dbg_Util::stack_trace_to_string(st_[0]).c_str() << "\n"; + } + } + } private: /* Start time for the most recent start() call */ double start_; @@ -28,6 +43,20 @@ namespace PIO_Util{ * recursive calls to this timer */ int lvl_; + + /* Stack traces of calls to timer - for debugging */ + std::vector > st_; + + void push_stack_trace(void ){ + std::vector st; + SPIO_Util::Dbg_Util::get_stack_trace(st); + st_.push_back(st); + } + + void pop_stack_trace(void ){ + assert(st_.size() > 0); + st_.pop_back(); + } }; } // namespace SPIO_Ltimer_Utils } // namespace PIO_Util diff --git a/src/clib/util/spio_ltimer_utils.hpp b/src/clib/util/spio_ltimer_utils.hpp new file mode 100644 index 00000000000..586e2cc6fd0 --- /dev/null +++ b/src/clib/util/spio_ltimer_utils.hpp @@ -0,0 +1,27 @@ +#ifndef __SPIO_LTIMER_UTILS_HPP__ +#define __SPIO_LTIMER_UTILS_HPP__ + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" + +#include "spio_ltimer.h" + +#include + +namespace SPIO_Util{ + namespace SPIO_Ltimer_Utils{ + class SPIO_ltimer_wrapper{ + public: + SPIO_ltimer_wrapper(const char *timer_name):timer_name_(timer_name){ + spio_ltimer_start(timer_name_.c_str()); + } + ~SPIO_ltimer_wrapper(){ + spio_ltimer_stop(timer_name_.c_str()); + } + private: + const std::string timer_name_; + }; + } // namespace SPIO_Ltimer_Utils +} // namespace SPIO_Util +#endif // __SPIO_LTIMER_UTILS_HPP__ diff --git a/src/clib/util/spio_map_sorter.hpp b/src/clib/util/spio_map_sorter.hpp new file mode 100644 index 00000000000..1a2ec1ffc56 --- /dev/null +++ b/src/clib/util/spio_map_sorter.hpp @@ -0,0 +1,104 @@ +#ifndef __SPIO_MAP_SORTER_HPP__ +#define __SPIO_MAP_SORTER_HPP__ + +#include "pio_config.h" +#include "pio.h" +#include "pio_internal.h" +#include "pio_types.hpp" + +#include +#include +#include +#include + +namespace SPIO_Util{ + + class Map_sorter{ + public: + Map_sorter(){} + + /* Create sorter map based on values in vector v, the comparator passed + should be for comparing values in v + */ + template > + Map_sorter(const std::vector &v, Comp cmp = Comp{}):sort_map_(v.size()) + { + auto sort_map_cmp = [&v, &cmp](T a, T b){ return cmp(v[a], v[b]); }; + init_sort_map(sort_map_cmp); + } + + /* Create sorter based on the given comparator, the comparator passed + should be for comparing sort map values + */ + template + Map_sorter(std::size_t sz, Comp cmp):sort_map_(sz) + { + init_sort_map(cmp); + } + + /* Init sorter map based on values in vector v, the comparator passed + should be for comparing values in v + */ + template > + void init(const std::vector &v, Comp cmp = Comp{}) + { + /* Don't allow reinits */ + assert(sort_map_.size() == 0); + + sort_map_.resize(v.size()); + auto sort_map_cmp = [&v, &cmp](T a, T b){ return cmp(v[a], v[b]); }; + init_sort_map(sort_map_cmp); + } + + /* Init sorter based on the given comparator, the comparator passed + should be for comparing sort map values + */ + template + void init(std::size_t sz, Comp cmp) + { + /* Don't allow reinits */ + assert(sort_map_.size() == 0); + + sort_map_.resize(sz); + init_sort_map(cmp); + } + + template + void sort(std::vector &v) + { + std::vector vtmp(v); + assert(v.size() == sort_map_.size()); + + for(std::size_t i = 0; i < vtmp.size(); i++){ + v[sort_map_[i]] = vtmp[i]; + } + } + + /* FIXME: Get rid of this function that exposes the sort map + Current code needs this map for creating recv types. We need to find + an alternate way to create these types + */ + const std::vector &get_sort_map(void ) { return sort_map_; } + + std::size_t size(void ) const { return sort_map_.size(); } + private: + std::vector sort_map_; + + template + void init_sort_map(Comp cmp) + { + /* FIXME: Do we need a push sort map - isn't pull map enough ? */ + /* pull_sort_map => mapping for pulling values from source to sorted */ + std::vector pull_sort_map(sort_map_.size()); + std::iota(pull_sort_map.begin(), pull_sort_map.end(), 0); + std::sort(pull_sort_map.begin(), pull_sort_map.end(), cmp); + /* sort_map_ is push map => mapping for pushing values from source to sorted */ + for(std::size_t i = 0; i < pull_sort_map.size(); i++){ + sort_map_[pull_sort_map[i]] = i; + } + } + }; + +} // namespace SPIO_Util + +#endif // __SPIO_MAP_SORTER_HPP__ diff --git a/src/clib/spio_serializer.cpp b/src/clib/util/spio_serializer.cpp similarity index 100% rename from src/clib/spio_serializer.cpp rename to src/clib/util/spio_serializer.cpp diff --git a/src/clib/spio_serializer.hpp b/src/clib/util/spio_serializer.hpp similarity index 100% rename from src/clib/spio_serializer.hpp rename to src/clib/util/spio_serializer.hpp diff --git a/src/clib/util/spio_sort_utils.hpp b/src/clib/util/spio_sort_utils.hpp new file mode 100644 index 00000000000..640e88897c9 --- /dev/null +++ b/src/clib/util/spio_sort_utils.hpp @@ -0,0 +1,88 @@ +#ifndef _SPIO_SORT_UTILS_HPP_ +#define _SPIO_SORT_UTILS_HPP_ + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace SPIO_Util{ + /* K-way merge sort a vector with multiple sorted ranges, where ranges are in {start_idx, end_idx + 1} format + e.g. vec_kway_merge_sort({2, 3, 4, 6, 7}, {{0, 3}, {3, 5}}) + */ + template> + void vec_kway_merge_sort(std::vector &v, const std::vector > &ranges, ContigRangeChecker &&crc, Comp cmp = Comp{}) + { + std::vector vtmp(v); + + /* Each pair in the priority queue : + (idx of range in ranges, idx of next element in v in this range) */ + auto range_comp = [&vtmp, &cmp](const std::pair &a, const std::pair &b){ return cmp(vtmp[b.second], vtmp[a.second]); }; + std::priority_queue, std::vector >, decltype(range_comp)> range_idx_info(range_comp); + + std::vector cur_range_idx(ranges.size()); + /* Add range info from each range to the priority queue */ + for(std::size_t i = 0; i < ranges.size(); i++){ + cur_range_idx[i] = ranges[i].first; + + if(cur_range_idx[i] < ranges[i].second){ + range_idx_info.push(std::make_pair(i, cur_range_idx[i])); + } + } + + std::size_t v_idx = 0; + while(!range_idx_info.empty()){ + const std::pair &range_info = range_idx_info.top(); + + /* current_range : Index of current range in ranges[] */ + std::size_t current_range = range_info.first; + /* r_idx : Index in v[] of the next element to be sorted in this range */ + std::size_t r_idx = range_info.second; + + if(r_idx < ranges[current_range].second){ + /* This element, vtmp[r_idx], is the next element in the sorted result, v[] */ + v[v_idx++] = vtmp[r_idx++]; + } + + /* Add consecutive elements in this sorted range to the final sorted list, v[] */ + while(r_idx < ranges[current_range].second){ + /* If vtmp[r_idx] is consecutive in range, after vtmp[r_idx - 1], add it to the sorted list */ + if(crc(vtmp[r_idx - 1], vtmp[r_idx])){ + v[v_idx++] = vtmp[r_idx++]; + } + else{ + break; + } + } + + range_idx_info.pop(); + + /* If there are still elements in this range (after adding all consecutive elems), add it in to + * the priority queue - for comparison across ranges + */ + if(r_idx < ranges[current_range].second){ + /* Add the next range info, + {idx of current range in ranges[], Next index - in vtmp[] - of element in this range}, + for next element in this range to the priority queue + */ + range_idx_info.push(std::make_pair(current_range, r_idx)); + } + } + } + + /* K-way merge sort a vector with multiple sorted ranges, where ranges are in {start_idx, end_idx + 1} format + e.g. vec_kway_merge_sort({2, 3, 4, 6, 7}, {{0, 3}, {3, 5}}) + */ + template + void vec_kway_merge_sort(std::vector &v, const std::vector > &ranges) + { + return vec_kway_merge_sort(v, ranges, [](const T &a, const T &b) { return (a + 1) == b; }); + } +} // namespace SPIO_Util + +#endif // _SPIO_SORT_UTILS_HPP_ diff --git a/src/clib/spio_tracer.cpp b/src/clib/util/spio_tracer.cpp similarity index 98% rename from src/clib/spio_tracer.cpp rename to src/clib/util/spio_tracer.cpp index dceb67a7944..a848e35d2cc 100644 --- a/src/clib/spio_tracer.cpp +++ b/src/clib/util/spio_tracer.cpp @@ -278,7 +278,7 @@ SPIO_Util::Logger::MPI_logger &SPIO_Util::Tracer::get_iosys_trace assert(ret == MPI_SUCCESS); // FIXME: use unique_ptr - std::ofstream *fstr = new std::ofstream(); + std::shared_ptr fstr = std::make_shared(); const std::string DEV_NULL = "/dev/null"; std::string log_fname = get_trace_log_fname(iosysid, mpi_wrank); @@ -318,10 +318,9 @@ void SPIO_Util::Tracer::finalize_iosys_trace_logger(std::string iosys_key) std::map >::iterator iter = SPIO_Util::Tracer::GVars::trace_loggers_.find(iosys_key); if(iter != SPIO_Util::Tracer::GVars::trace_loggers_.end()){ (*iter).second.log(get_trace_log_footer()); - std::ofstream *fstr = (*iter).second.get_log_stream(); + std::shared_ptr fstr = (*iter).second.get_log_stream(); assert(fstr); fstr->close(); - delete fstr; SPIO_Util::Tracer::GVars::trace_loggers_.erase(iter); } diff --git a/src/clib/spio_tracer.hpp b/src/clib/util/spio_tracer.hpp similarity index 100% rename from src/clib/spio_tracer.hpp rename to src/clib/util/spio_tracer.hpp diff --git a/src/clib/spio_tracer_decomp.cpp b/src/clib/util/spio_tracer_decomp.cpp similarity index 100% rename from src/clib/spio_tracer_decomp.cpp rename to src/clib/util/spio_tracer_decomp.cpp diff --git a/src/clib/spio_tracer_decomp.hpp b/src/clib/util/spio_tracer_decomp.hpp similarity index 100% rename from src/clib/spio_tracer_decomp.hpp rename to src/clib/util/spio_tracer_decomp.hpp diff --git a/src/clib/spio_tracer_mdata.cpp b/src/clib/util/spio_tracer_mdata.cpp similarity index 97% rename from src/clib/spio_tracer_mdata.cpp rename to src/clib/util/spio_tracer_mdata.cpp index cb7b5839af4..b20bddeaf46 100644 --- a/src/clib/spio_tracer_mdata.cpp +++ b/src/clib/util/spio_tracer_mdata.cpp @@ -131,8 +131,7 @@ SPIO_Util::Logger::MPI_logger &SPIO_Util::Tracer::get_iosys_trace ret = MPI_Comm_rank(comm, &rank); assert(ret == MPI_SUCCESS); - // FIXME: use unique_ptr - std::ofstream *fstr = new std::ofstream(); + std::shared_ptr fstr = std::make_shared(); const std::string DEV_NULL = "/dev/null"; std::string mdata_fname = get_trace_mdata_fname(iosysid, mpi_wrank); @@ -156,10 +155,9 @@ void SPIO_Util::Tracer::finalize_iosys_trace_mdata_logger(std::string iosys_key) std::map >::iterator iter = SPIO_Util::Tracer::GVars::trace_mdata_loggers_.find(iosys_key); if(iter != SPIO_Util::Tracer::GVars::trace_mdata_loggers_.end()){ (*iter).second.log(get_trace_mdata_footer()); - std::ofstream *fstr = (*iter).second.get_log_stream(); + std::shared_ptr fstr = (*iter).second.get_log_stream(); assert(fstr); fstr->close(); - delete fstr; SPIO_Util::Tracer::GVars::trace_mdata_loggers_.erase(iter); } diff --git a/src/clib/spio_tracer_mdata.hpp b/src/clib/util/spio_tracer_mdata.hpp similarity index 100% rename from src/clib/spio_tracer_mdata.hpp rename to src/clib/util/spio_tracer_mdata.hpp diff --git a/src/clib/spio_tree.hpp b/src/clib/util/spio_tree.hpp similarity index 100% rename from src/clib/spio_tree.hpp rename to src/clib/util/spio_tree.hpp diff --git a/src/clib/topology.cpp b/src/clib/util/topology.cpp similarity index 100% rename from src/clib/topology.cpp rename to src/clib/util/topology.cpp diff --git a/src/flib/CMakeLists.txt b/src/flib/CMakeLists.txt index 65cc5cfc2a9..cf91b94dd30 100644 --- a/src/flib/CMakeLists.txt +++ b/src/flib/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.7) +cmake_minimum_required (VERSION 3.7...4.0.1) project (PIOF Fortran) include (CheckFunctionExists) include (ExternalProject) diff --git a/src/flib/pio.F90 b/src/flib/pio.F90 index 85d2b3a8a13..a05ab502977 100644 --- a/src/flib/pio.F90 +++ b/src/flib/pio.F90 @@ -31,7 +31,8 @@ module pio pio_iotype_pnetcdf, pio_iotype_netcdf, pio_iotype_adios, pio_iotype_adiosc, & pio_iotype_hdf5, pio_iotype_hdf5c, & pio_global, pio_char, pio_write, pio_nowrite, pio_clobber, pio_noclobber, & - pio_max_name, pio_max_var_dims, pio_rearr_subset, pio_rearr_box, pio_rearr_any,& + pio_max_name, pio_max_var_dims,& + pio_rearr_subset, pio_rearr_box, pio_rearr_any, pio_rearr_contig,& #if defined(_NETCDF) || defined(_PNETCDF) pio_fill, pio_nofill, pio_unlimited, pio_fill_char, pio_fill_int, pio_fill_double, pio_fill_float, & #endif diff --git a/src/flib/pio_types.F90 b/src/flib/pio_types.F90 index 499f453eb64..c8bfe05b04e 100644 --- a/src/flib/pio_types.F90 +++ b/src/flib/pio_types.F90 @@ -155,6 +155,7 @@ module pio_types integer(i4), public, parameter :: PIO_rearr_box = 1 integer(i4), public, parameter :: PIO_rearr_subset = 2 integer(i4), public, parameter :: PIO_rearr_any = 3 + integer(i4), public, parameter :: PIO_rearr_contig = 4 !> !! @public diff --git a/src/flib_legacy/CMakeLists.txt b/src/flib_legacy/CMakeLists.txt index ba408d86f11..0366ca45a56 100644 --- a/src/flib_legacy/CMakeLists.txt +++ b/src/flib_legacy/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.7) +cmake_minimum_required (VERSION 3.7...4.0.1) project (PIOF Fortran) include (CheckFunctionExists) include (ExternalProject) diff --git a/src/gptl/CMakeLists.txt b/src/gptl/CMakeLists.txt index 9d8f9568341..d62d77153c4 100644 --- a/src/gptl/CMakeLists.txt +++ b/src/gptl/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.7) +cmake_minimum_required (VERSION 3.7...4.0.1) project (GPTL C Fortran) include (CheckFunctionExists) include(CheckSymbolExists) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3196ced72e6..5f9097a32a0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.7) +cmake_minimum_required (VERSION 3.7...4.0.1) project (PIOTests C Fortran) #============================================================================== @@ -23,3 +23,7 @@ if (PIO_ENABLE_FORTRAN) endif() add_subdirectory (cunit) + +if (WITH_PNETCDF) + add_subdirectory (pnetcdf) +endif () diff --git a/tests/cunit/CMakeLists.txt b/tests/cunit/CMakeLists.txt index ff6c39d6b54..61b73b19289 100644 --- a/tests/cunit/CMakeLists.txt +++ b/tests/cunit/CMakeLists.txt @@ -9,7 +9,8 @@ include(SPIOUtils) #============================================================================== # SET THE COMPILER OPTIONS #============================================================================== -include_directories("${SCORPIO_SOURCE_DIR}/tests/cunit" "${SCORPIO_SOURCE_DIR}/src/clib" "${SCORPIO_BINARY_DIR}/src/clib") +# FIXME: Use an interface lib to track these include deps +include_directories("${SCORPIO_SOURCE_DIR}/tests/cunit" "${SCORPIO_SOURCE_DIR}/util" "${SCORPIO_SOURCE_DIR}/src/clib" "${SCORPIO_BINARY_DIR}/src/clib" "${SCORPIO_SOURCE_DIR}/src/clib/core" "${SCORPIO_SOURCE_DIR}/src/clib/core/util" "${SCORPIO_SOURCE_DIR}/src/clib/core/rearr" "${SCORPIO_SOURCE_DIR}/src/clib/core/progress_engine" "${SCORPIO_SOURCE_DIR}/src/clib/core/iolib/hdf5" "${SCORPIO_SOURCE_DIR}/src/clib/util") # Compiler-specific compiler options string (TOUPPER "${CMAKE_C_COMPILER_ID}" CMAKE_C_COMPILER_NAME) @@ -62,52 +63,77 @@ endif () # DEFINE THE TARGETS AND TESTS #============================================================================== +add_library (test_common_obj OBJECT test_common.cpp ${SCORPIO_SOURCE_DIR}/util/argparser.cxx) +target_link_libraries (test_common_obj PRIVATE pioc) + # Exclude tests that require more than 1 procs when using the MPI serial library if (NOT PIO_USE_MPISERIAL) - add_spio_executable (test_intercomm2 TRUE "" test_intercomm2.c test_common.c) - add_spio_executable (test_async_simple TRUE "" test_async_simple.c test_common.c) - add_spio_executable (test_async_3proc TRUE "" test_async_3proc.c test_common.c) - add_spio_executable (test_async_4proc TRUE "" test_async_4proc.c test_common.c) - add_spio_executable (test_iosystem2_simple TRUE "" test_iosystem2_simple.c test_common.c) - add_spio_executable (test_iosystem2_simple2 TRUE "" test_iosystem2_simple2.c test_common.c) - add_spio_executable (test_iosystem2 TRUE "" test_iosystem2.c test_common.c) - add_spio_executable (test_iosystem3_simple TRUE "" test_iosystem3_simple.c test_common.c) - add_spio_executable (test_iosystem3_simple2 TRUE "" test_iosystem3_simple2.c test_common.c) - add_spio_executable (test_iosystem3 TRUE "" test_iosystem3.c test_common.c) - add_spio_executable (test_pioc TRUE "" test_pioc.c test_common.c test_shared.c) - add_spio_executable (test_pioc_unlim TRUE "" test_pioc_unlim.c test_common.c test_shared.c) - add_spio_executable (test_pioc_putget TRUE "" test_pioc_putget.c test_common.c test_shared.c) - add_spio_executable (test_pioc_fill TRUE "" test_pioc_fill.c test_common.c test_shared.c) - add_spio_executable (test_darray TRUE "" test_darray.c test_common.c) - add_spio_executable (test_darray_multi TRUE "" test_darray_multi.c test_common.c) - add_spio_executable (test_darray_multivar TRUE "" test_darray_multivar.c test_common.c) - add_spio_executable (test_darray_multivar2 TRUE "" test_darray_multivar2.c test_common.c) - add_spio_executable (test_darray_1d TRUE "" test_darray_1d.c test_common.c) - add_spio_executable (test_darray_3d TRUE "" test_darray_3d.c test_common.c) - add_spio_executable (test_decomp_uneven TRUE "" test_decomp_uneven.c test_common.c) - add_spio_executable (test_decomps TRUE "" test_decomps.c test_common.c) - add_spio_executable (test_rearr TRUE "" test_rearr.c test_common.c) + add_spio_executable (test_intercomm2 TRUE "" test_intercomm2.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_async_simple TRUE "" test_async_simple.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_async_3proc TRUE "" test_async_3proc.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_async_4proc TRUE "" test_async_4proc.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_iosystem2_simple TRUE "" test_iosystem2_simple.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_iosystem2_simple2 TRUE "" test_iosystem2_simple2.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_iosystem2 TRUE "" test_iosystem2.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_iosystem3_simple TRUE "" test_iosystem3_simple.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_iosystem3_simple2 TRUE "" test_iosystem3_simple2.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_iosystem3 TRUE "" test_iosystem3.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_pioc TRUE "" test_pioc.cpp test_shared.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_pioc_unlim TRUE "" test_pioc_unlim.cpp test_shared.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_pioc_putget TRUE "" test_pioc_putget.cpp test_shared.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_pioc_fill TRUE "" test_pioc_fill.cpp test_shared.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray TRUE "" test_darray.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray_multi TRUE "" test_darray_multi.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray_multivar TRUE "" test_darray_multivar.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray_multivar2 TRUE "" test_darray_multivar2.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray_1d TRUE "" test_darray_1d.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray_3d TRUE "" test_darray_3d.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_decomp_uneven TRUE "" test_decomp_uneven.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_decomps TRUE "" test_decomps.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_rearr TRUE "" test_rearr.cpp OBJ_LIBS test_common_obj) add_dependencies (tests test_intercomm2 test_async_simple test_async_3proc test_async_4proc test_iosystem2_simple test_iosystem2_simple2 test_iosystem2 test_iosystem3_simple test_iosystem3_simple2 test_iosystem3 test_pioc test_pioc_unlim test_pioc_putget test_pioc_fill test_darray test_darray_multi test_darray_multivar test_darray_multivar2 test_darray_1d test_darray_3d test_decomp_uneven test_decomps test_rearr) if (PIO_USE_MALLOC) - add_spio_executable (test_darray_async_simple TRUE "" test_darray_async_simple.c test_common.c) - add_spio_executable (test_darray_async TRUE "" test_darray_async.c test_common.c) - add_spio_executable (test_darray_async_many TRUE "" test_darray_async_many.c test_common.c) + add_spio_executable (test_darray_async_simple TRUE "" test_darray_async_simple.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray_async TRUE "" test_darray_async.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_darray_async_many TRUE "" test_darray_async_many.cpp OBJ_LIBS test_common_obj) add_dependencies (tests test_darray_async_simple test_darray_async test_darray_async_many) endif () endif () -add_spio_executable (test_spmd TRUE "" test_spmd.c test_common.c) +add_spio_executable (test_spmd TRUE "" test_spmd.cpp OBJ_LIBS test_common_obj) add_spio_executable (test_spio_ltimer TRUE "" test_spio_ltimer.cpp) add_spio_executable (test_spio_serializer TRUE "" test_spio_serializer.cpp) add_spio_executable (test_spio_tree TRUE "" test_spio_tree.cpp) add_spio_executable (test_spio_file_mvcache TRUE "" test_spio_file_mvcache.cpp) -add_spio_executable (test_sdecomp_regex TRUE "" test_sdecomp_regex.cpp test_common.c) -add_spio_executable(test_req_block_wait TRUE "" test_req_block_wait.c test_common.c) +add_spio_executable (test_spio_sort_utils TRUE "" test_spio_sort_utils.cpp) +add_spio_executable (test_spio_rearr_utils_gather TRUE "" test_spio_rearr_utils_gather.cpp) +add_spio_executable (test_spio_rearr_utils_scatter TRUE "" test_spio_rearr_utils_scatter.cpp) +add_spio_executable (test_spio_rearr_utils_alltoall TRUE "" test_spio_rearr_utils_alltoall.cpp) +add_spio_executable (test_spio_rearr_contig TRUE "" test_spio_rearr_contig.cpp OBJ_LIBS test_common_obj) +add_spio_executable (test_spio_rearr_contig_nvars TRUE "" test_spio_rearr_contig_nvars.cpp) +add_spio_executable (test_spio_rearr_contig_fillval TRUE "" test_spio_rearr_contig_fillval.cpp) +add_spio_executable (test_spio_decomp_logger TRUE "" test_spio_decomp_logger.cpp) +add_spio_executable (test_sdecomp_regex TRUE "" test_sdecomp_regex.cpp OBJ_LIBS test_common_obj) +add_spio_executable(test_req_block_wait TRUE "" test_req_block_wait.cpp OBJ_LIBS test_common_obj) +add_spio_executable(test_spio_dt_converter TRUE "" test_spio_dt_converter.cpp) add_dependencies (tests test_spmd test_spio_ltimer test_spio_serializer test_spio_tree - test_spio_file_mvcache test_sdecomp_regex test_req_block_wait) + test_spio_file_mvcache test_spio_sort_utils test_spio_rearr_utils_gather + test_spio_rearr_utils_scatter test_spio_rearr_utils_alltoall + test_spio_rearr_contig test_spio_rearr_contig_nvars test_spio_rearr_contig_fillval + test_spio_decomp_logger test_sdecomp_regex test_req_block_wait + test_spio_dt_converter) + +if(PIO_USE_ASYNC_WR_THREAD) + add_spio_executable (test_mtq TRUE "" test_async_mtq.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_mtq_signal TRUE "" test_async_mtq_signal.cpp OBJ_LIBS test_common_obj) + add_spio_executable (test_mtq_pool TRUE "" test_async_tpool.cpp OBJ_LIBS test_common_obj) + add_dependencies(tests test_mtq test_mtq_signal test_mtq_pool) +endif() + +set (SPIO_TEST_MAX_NPROCS 4) # Test Timeout in seconds. if (PIO_VALGRIND_CHECK) @@ -127,7 +153,15 @@ add_test(NAME test_spio_ltimer COMMAND test_spio_ltimer) add_test(NAME test_spio_serializer COMMAND test_spio_serializer) add_test(NAME test_spio_tree COMMAND test_spio_tree) add_test(NAME test_spio_file_mvcache COMMAND test_spio_file_mvcache) +add_test(NAME test_spio_dt_converter COMMAND test_spio_dt_converter) add_test(NAME test_sdecomp_regex COMMAND test_sdecomp_regex) +add_test(NAME test_spio_sort_utils COMMAND test_spio_sort_utils) + +if(PIO_USE_ASYNC_WR_THREAD) + add_test(NAME test_mtq COMMAND test_mtq) + add_test(NAME test_mtq_signal COMMAND test_mtq_signal) + add_test(NAME test_mtq_pool COMMAND test_mtq_pool) +endif() if (PIO_USE_MPISERIAL) add_test(NAME test_pioc @@ -149,6 +183,48 @@ else () EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_req_block_wait NUMPROCS 4 TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_spio_rearr_utils_gather${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spio_rearr_utils_gather + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_spio_rearr_utils_scatter${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spio_rearr_utils_scatter + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_spio_rearr_utils_alltoall${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spio_rearr_utils_alltoall + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_spio_rearr_contig${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spio_rearr_contig + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_spio_rearr_contig_nvars${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spio_rearr_contig_nvars + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_spio_rearr_contig_fillval${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spio_rearr_contig_fillval + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_spio_decomp_logger${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spio_decomp_logger + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() add_mpi_test(test_spmd EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_spmd NUMPROCS ${AT_LEAST_FOUR_TASKS} diff --git a/tests/cunit/pio_tests.h b/tests/cunit/pio_tests.h index 4e9d22c2933..6d8909fcca2 100644 --- a/tests/cunit/pio_tests.h +++ b/tests/cunit/pio_tests.h @@ -1,8 +1,6 @@ /** * @file * Include file for tests for the Parallel IO library. - * @author Ed Hartnett - * @date 9/13/2016 */ #ifndef _PIO_TESTS_H diff --git a/tests/cunit/spio_test_framework.hpp b/tests/cunit/spio_test_framework.hpp new file mode 100644 index 00000000000..5cb425aabd1 --- /dev/null +++ b/tests/cunit/spio_test_framework.hpp @@ -0,0 +1,215 @@ +#ifndef __SPIO_TEST_FRAMEWORK__ +#define __SPIO_TEST_FRAMEWORK__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "argparser.h" +#include "spio_logger.hpp" + +namespace SPIO_TF{ + +class Test_framework{ + public: + Test_framework(const std::string &name, MPI_Comm comm, std::vector > > &run_funcs) : + name_(name), log_fname_(tf_name_to_log_fname(name)), + comm_(comm), comm_rank_(0), comm_root_(DEFAULT_ROOT), comm_sz_(0), + run_funcs_(run_funcs) {} + + void init(int argc, char *argv[]){ + int ret = MPI_SUCCESS; + + ret = MPI_Comm_rank(comm_, &comm_rank_); assert(ret == MPI_SUCCESS); + ret = MPI_Comm_size(comm_, &comm_sz_); assert(ret == MPI_SUCCESS); + + /* Get command line user options */ + spio_tool_utils::ArgParser ap(comm_); + init_user_options(ap); + + std::string log_fname; + SPIO_Util::Logger::Log_level log_lvl = SPIO_Util::Logger::Log_level::INVALID; + bool verbose = false; + + ret = get_user_options(ap, argc, argv, comm_rank_, comm_root_, + log_fname, log_lvl, verbose); + if(ret == PIO_NOERR){ + if(verbose) { log_lvl = SPIO_Util::Logger::Log_level::TRACE; } + } + else{ + std::cerr << "ERROR: Getting user options failed\n"; + } + + /* Set up logger - only log from comm_root_ */ + if(!log_fname.empty()){ + logger_fstr_ = std::make_shared(); + const std::string DEV_NULL = "/dev/null"; + logger_fstr_->open((comm_rank_ == comm_root_) ? log_fname_.c_str() : DEV_NULL.c_str(), + std::ofstream::out | std::ofstream::trunc); + flogger_ = SPIO_Util::Logger::MPI_logger(comm_, logger_fstr_); + } + if(comm_rank_ == comm_root_) { flogger_.enable_logging(); } + + + get_logger().log(SPIO_Util::Logger::Log_level::DEBUG, "Testing framework initialized"); +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + get_logger().log(SPIO_Util::Logger::Log_level::ERROR, std::string("GPTLinitialize() FAILED, ret = ") + std::to_string(ret) + ")"); + return; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + } + + void register_run_funcs(const std::vector > > &run_funcs){ + run_funcs_.insert(run_funcs.end(), run_funcs.cbegin(), run_funcs.cend()); + } + + int run(void ){ + int nerrs = 0; + + std::for_each(run_funcs_.begin(), run_funcs_.end(), + [this, &nerrs](std::pair > &f){ + int ret = PIO_NOERR; + + ret = run(f.first, f.second); + if(ret != 0) { nerrs++; } + + }); + + if(nerrs == 0){ + get_logger().log(SPIO_Util::Logger::Log_level::INFO, "All Tests PASSED"); + } + else{ + const std::string fail_msg = std::string("[") + std::to_string(nerrs) + "] Tests FAILED"; + get_logger().log(SPIO_Util::Logger::Log_level::INFO, fail_msg); + } + get_logger().flush(); + + return nerrs; + } + + template + int run(const std::string &name, std::function &f, Args&&... args){ + int ret = PIO_NOERR, mpierr = MPI_SUCCESS; + try{ + ret = f(*this, std::forward(args)...); + } catch(...){ + ret = PIO_EINTERNAL; + } + + /* Reduce the number of fails/errors across all processes */ + int num_lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce((comm_rank_ != comm_root_) ? &num_lfail : MPI_IN_PLACE, &num_lfail, 1, MPI_INT, MPI_SUM, comm_root_, comm_); + assert(mpierr == MPI_SUCCESS); + + if(num_lfail == 0){ + get_logger().log(SPIO_Util::Logger::Log_level::INFO, name + " PASSED\n"); + } + else{ + const std::string all_proc_fail_msg("failed on all processes"); + const std::string some_proc_including_root_fail_msg = std::string("failed on ") + std::to_string(num_lfail) + " processes, including root"; + const std::string some_proc_excluding_root_fail_msg = std::string("failed on ") + std::to_string(num_lfail) + " non-root processes, passed on root"; + + + get_logger().log(SPIO_Util::Logger::Log_level::INFO, name + " FAILED (ret = " + std::to_string(ret) + ((num_lfail == comm_sz_) ? all_proc_fail_msg : ((ret != PIO_NOERR) ? some_proc_including_root_fail_msg : some_proc_excluding_root_fail_msg) + ")")); + + } + get_logger().flush(); + + return (num_lfail == 0) ? 0 : 1; + } + + SPIO_Util::Logger::MPI_logger get_logger(void ) const { + return flogger_; + } + + MPI_Comm get_comm(void ) const { return comm_; } + int get_comm_rank(void ) const { return comm_rank_; } + int get_comm_size(void ) const { return comm_sz_; } + + void finalize(void ){ + get_logger().log(SPIO_Util::Logger::Log_level::DEBUG, "Testing framework finalizing..."); +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + int ret = GPTLfinalize(); + if(ret != 0){ + get_logger().log(SPIO_Util::Logger::Log_level::ERROR, std::string("GPTLfinalize() FAILED, ret = ") + std::to_string(ret) + ")"); + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(logger_fstr_) { logger_fstr_->close(); } + } + + ~Test_framework() = default; + + private: + + static inline std::string tf_name_to_log_fname(const std::string &tf_name){ + return tf_name + "_log.txt"; + } + + inline bool is_comm_root(void ) const { return comm_rank_ == comm_root_; } + + static void init_user_options(spio_tool_utils::ArgParser &ap){ + ap.add_opt("log-file", "Output log file (default output is stdout)") + .add_opt("log-level", "Output log level, Valid values = " + SPIO_Util::Logger::get_available_log_levels()) + .add_opt("verbose", "Turn on verbose messages (log level = TRACE)"); + } + + static int get_user_options(spio_tool_utils::ArgParser &ap, + int argc, char *argv[], + int comm_rank, int comm_root, + std::string &log_fname, SPIO_Util::Logger::Log_level &log_lvl, + bool &verbose){ +#ifdef SPIO_NO_CXX_REGEX + ap.no_regex_parse(argc, argv); +#else + ap.parse(argc, argv); +#endif + log_fname = ""; + if(ap.has_arg("log-file")){ + log_fname = ap.get_arg("log-file"); + } + + log_lvl = SPIO_Util::Logger::Log_level::INFO; + if(ap.has_arg("log-level")){ + log_lvl = SPIO_Util::Logger::string_to_log_level(ap.get_arg("log-level")); + } + + verbose = false; + if(ap.has_arg("verbose")){ + verbose = true; + } + + return PIO_NOERR; + } + + const int DEFAULT_ROOT = 0; + + const std::string name_; + const std::string log_fname_; + MPI_Comm comm_; + int comm_rank_; + int comm_root_; + int comm_sz_; + std::vector > > run_funcs_; + std::shared_ptr logger_fstr_; + SPIO_Util::Logger::MPI_logger flogger_; +}; + +} // namespace SPIO_TF + +#endif // __SPIO_TEST_FRAMEWORK__ diff --git a/tests/cunit/test_async_3proc.c b/tests/cunit/test_async_3proc.cpp similarity index 100% rename from tests/cunit/test_async_3proc.c rename to tests/cunit/test_async_3proc.cpp diff --git a/tests/cunit/test_async_4proc.c b/tests/cunit/test_async_4proc.cpp similarity index 100% rename from tests/cunit/test_async_4proc.c rename to tests/cunit/test_async_4proc.cpp diff --git a/tests/cunit/test_async_mtq.cpp b/tests/cunit/test_async_mtq.cpp new file mode 100644 index 00000000000..f711b45fb01 --- /dev/null +++ b/tests/cunit/test_async_mtq.cpp @@ -0,0 +1,639 @@ +#include +#include +#include +#include +#include +#include "spio_async_mtq.hpp" +extern "C"{ +#include +#include +#include +} + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +class UType{ + public: + int i; + float f; +}; + +/* Test creating a multi-threaded queue */ +int test_create_mtq(void ) +{ + PIO_Util::PIO_mtq qi; + PIO_Util::PIO_mtq qf; + + class TestClass{ + int i; + float f; + }; + + PIO_Util::PIO_mtq qt; + + return PIO_NOERR; +} + +/* Util function to enqueue data in elems to q */ +template +void thread_enq(PIO_Util::PIO_mtq &q, const std::vector &elems, + const std::chrono::seconds &delay) +{ + for(typename std::vector::const_iterator citer = elems.cbegin(); + citer != elems.cend(); ++citer){ + if(delay.count() != 0){ + std::this_thread::sleep_for(delay); + } + q.enqueue(*citer); + //std::cout << "Enqueued item : " << *citer << "\n"; + } +} + +/* Util function to dequeue nelems from q and store in elems */ +template +void thread_deq(PIO_Util::PIO_mtq &q, std::vector &elems, + const std::chrono::seconds &delay) +{ + int nelems = elems.size(); + for(int i=0; i 0) && (nthreads > 0)); + int nelems_per_thread = max_elems_in_q/nthreads; + int nelems_last_thread = max_elems_in_q - nelems_per_thread * (nthreads - 1); + + /* Enqueue multiple elems from different threads and dequeue to make + * sure that the elems are in the queue + */ + + /* Generate input vals */ + PIO_Util::PIO_mtq qi; + std::vector > ivals; + std::vector > ovals; + for(int i=0; i ivals_ithread; + std::vector ovals_ithread; + if(!is_last_thread){ + ivals_ithread.resize(nelems_per_thread); + ovals_ithread.resize(nelems_per_thread); + } + else{ + ivals_ithread.resize(nelems_last_thread); + ovals_ithread.resize(nelems_last_thread); + } + int sval = i * nelems_per_thread; + std::generate(ivals_ithread.begin(), ivals_ithread.end(), [&sval]{return sval++; }); + ivals.push_back(ivals_ithread); + std::fill(ovals_ithread.begin(), ovals_ithread.end(), -1); + ovals.push_back(ovals_ithread); + } + + /* Enqueue */ + std::vector tpool_enq, tpool_deq; + for(int i=0; i, std::ref(qi), std::ref(ivals[i]), enqueue_delay)); + } + + /* Wait for enqueue operations to complete */ + if(!concurrent_enq_deq){ + for(int i=0; i, std::ref(qi), std::ref(ovals[i]), dequeue_delay)); + } + + /* Wait for enqueue operations to complete */ + for(int i=0; i elem_in_q(max_elems_in_q, false); + for(int i=0; i= max_elems_in_q)){ + /* Invalid element in queue */ + LOG_RANK0(wrank, "Invalid element (%d) in queue\n", idx); + return PIO_EINTERNAL; + } + /* Mark that the element was in the queue */ + elem_in_q[idx] = true; + } + } + + for(int i=0; ii << "," << putype->f << "), "; + return ostr; +} + +/* Test enqueue and dequeue operations in a multi-threaded queue containing + * a user defined type */ +int test_enq_deq_utype(int wrank, const int max_elems_in_q, const int nthreads) +{ + assert((max_elems_in_q > 0) && (nthreads > 0)); + int nelems_per_thread = max_elems_in_q/nthreads; + int nelems_last_thread = max_elems_in_q - nelems_per_thread * (nthreads - 1); + + /* Enqueue multiple elems from different threads and dequeue to make + * sure that the elems are in the queue + */ + + /* Generate input vals */ + PIO_Util::PIO_mtq qu; + std::vector > ivals; + std::vector > ovals; + for(int i=0; i ivals_ithread; + std::vector ovals_ithread; + if(!is_last_thread){ + ivals_ithread.resize(nelems_per_thread); + ovals_ithread.resize(nelems_per_thread); + } + else{ + ivals_ithread.resize(nelems_last_thread); + ovals_ithread.resize(nelems_last_thread); + } + int sval = i * nelems_per_thread; + std::generate(ivals_ithread.begin(), ivals_ithread.end(), + [&sval]{ UType t; + t.f = static_cast(sval); + t.i = sval++; + return t; }); + ivals.push_back(ivals_ithread); + UType invalid_val; + invalid_val.i = -1; + invalid_val.f = -1.0; + std::fill(ovals_ithread.begin(), ovals_ithread.end(), invalid_val); + ovals.push_back(ovals_ithread); + } + + /* Enqueue */ + std::vector tpool; + for(int i=0; i, std::ref(qu), std::ref(ivals[i]), std::chrono::seconds(0))); + } + + /* Wait for enqueue operations to complete */ + for(int i=0; i, std::ref(qu), std::ref(ovals[i]), std::chrono::seconds(0))); + } + + /* Wait for enqueue operations to complete */ + for(int i=0; i elem_in_q(max_elems_in_q, false); + for(int i=0; i= max_elems_in_q)){ + /* Invalid element in queue */ + LOG_RANK0(wrank, "Invalid element (%d) in queue\n", idx); + return PIO_EINTERNAL; + } + if(static_cast(foval) != ioval){ + /* Invalid element in queue */ + LOG_RANK0(wrank, "Invalid element (%d) in queue\n", idx); + return PIO_EINTERNAL; + } + /* Mark that the element was in the queue */ + elem_in_q[idx] = true; + } + } + + for(int i=0; i 0) && (nthreads > 0)); + int nelems_per_thread = max_elems_in_q/nthreads; + int nelems_last_thread = max_elems_in_q - nelems_per_thread * (nthreads - 1); + + /* Enqueue multiple elems from different threads and dequeue to make + * sure that the elems are in the queue + */ + + /* Generate input vals */ + PIO_Util::PIO_mtq qu; + std::vector > ivals; + std::vector > ovals; + for(int i=0; i ivals_ithread; + std::vector ovals_ithread; + if(!is_last_thread){ + ivals_ithread.resize(nelems_per_thread); + ovals_ithread.resize(nelems_per_thread); + } + else{ + ivals_ithread.resize(nelems_last_thread); + ovals_ithread.resize(nelems_last_thread); + } + int sval = i * nelems_per_thread; + std::generate(ivals_ithread.begin(), ivals_ithread.end(), + [&sval]{ UType *pt = new UType(); + pt->f = static_cast(sval); + pt->i = sval++; + return pt; }); + ivals.push_back(ivals_ithread); + UType invalid_val; + invalid_val.i = -1; + invalid_val.f = -1.0; + std::fill(ovals_ithread.begin(), ovals_ithread.end(), &invalid_val); + ovals.push_back(ovals_ithread); + } + + /* Enqueue */ + std::vector tpool; + for(int i=0; i, std::ref(qu), std::ref(ivals[i]),std::chrono::seconds(0))); + } + + /* Wait for enqueue operations to complete */ + for(int i=0; i, std::ref(qu), std::ref(ovals[i]), std::chrono::seconds(0))); + } + + /* Wait for enqueue operations to complete */ + for(int i=0; i elem_in_q(max_elems_in_q, false); + for(int i=0; ii; + int idx = ioval; + float foval = ((ovals[i])[j])->f; + if((idx < 0) || (idx >= max_elems_in_q)){ + /* Invalid element in queue */ + LOG_RANK0(wrank, "Invalid element (%d) in queue\n", idx); + return PIO_EINTERNAL; + } + if(static_cast(foval) != ioval){ + /* Invalid element in queue */ + LOG_RANK0(wrank, "Invalid element (%d) in queue\n", idx); + return PIO_EINTERNAL; + } + delete((ovals[i])[j]); + /* Mark that the element was in the queue */ + elem_in_q[idx] = true; + } + } + + for(int i=0; i= 0) && (wsz > 0) && num_errors); + + /* Test creating a multi threaded queue - the queue is empty */ + try{ + ret = test_create_mtq(); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_create_mtq() FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_create_mtq() PASSED\n"); + } + + /* Test enqueueing 10 ints into a queue using 2 threads, the ints are + * removed from the queue and the results are verified + */ + try{ + ret = test_enq_deq_int(wrank, 10, 2, + std::chrono::seconds(0), + std::chrono::seconds(0), false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_int(10 elems, 2 threads, no delay, deq after enq) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_int(10 elems, 2 threads, no delay, deq after enq) PASSED\n"); + } + + /* Test enqueueing 100 ints into a queue using 4 threads, the ints are + * removed from the queue and the results are verified + */ + try{ + ret = test_enq_deq_int(wrank, 100, 4, + std::chrono::seconds(0), + std::chrono::seconds(0), false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_int(100 elems, 4 threads, no delay, deq after enq) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_int(100 elems, 4 threads, no delay, deq after enq) PASSED\n"); + } + + /* Test enqueueing 10 user defined elems into a queue using 2 threads, + * the elems are removed from the queue and the results are verified + */ + try{ + ret = test_enq_deq_utype(wrank, 10, 2); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_utype(10 elems, 2 threads) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_utype(10 elems, 2 threads) PASSED\n"); + } + + /* Test enqueueing 100 user defined elems into a queue using 4 threads, + * the elems are removed from the queue and the results are verified + */ + try{ + ret = test_enq_deq_utype(wrank, 100, 4); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_utype(100 elems, 4 threads) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_utype(100 elems, 4 threads) PASSED\n"); + } + + /* Test enqueueing 10 ptr to user defined elems into a queue using 2 threads, + * the elems are removed from the queue and the results are verified + */ + try{ + ret = test_enq_deq_putype(wrank, 10, 2); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_putype(10 elems, 2 threads) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_putype(10 elems, 2 threads) PASSED\n"); + } + + /* Test enqueueing 100 ptr to user defined elems into a queue using 4 threads, + * the elems are removed from the queue and the results are verified + */ + try{ + ret = test_enq_deq_putype(wrank, 100, 4); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_putype(100 elems, 4 threads) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_putype(100 elems, 4 threads) PASSED\n"); + } + + /* Test enqueueing 20 ints into a queue using 2 threads, the ints are + * removed from the queue and the results are verified + * The enqueue and dequeue operations happen on separate threads (2 threads + * enqueue data - with a delay of 1s btw queuing elements and 2 threads + * dequeue data - no delay) + * Slow producer, fast consumer + */ + try{ + ret = test_enq_deq_int(wrank, 20, 2, + std::chrono::seconds(1), + std::chrono::seconds(0), true); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_int(20 elems, 2 threads, 1s delay for queueing data, concurrent deq enq) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_int(20 elems, 2 threads, 1s delay for queueing data, concurrent deq enq) PASSED\n"); + } + + /* Test enqueueing 20 ints into a queue using 4 threads, the ints are + * removed from the queue and the results are verified + * The enqueue and dequeue operations happen on separate threads (4 threads + * enqueue data - with a delay of 1s btw queuing elements and 4 threads + * dequeue data - no delay) + * Slow producer, fast consumer + */ + try{ + ret = test_enq_deq_int(wrank, 20, 4, + std::chrono::seconds(1), + std::chrono::seconds(0), true); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_enq_deq_int(20 elems, 4 threads, 1s delay for queueing data, concurrent deq enq) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_enq_deq_int(20 elems, 4 threads, 1s delay for queueing data, concurrent deq enq) PASSED\n"); + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef TIMING +#ifndef TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0) + { + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0) + { + LOG_RANK0(wrank, "Test driver FAILED\n"); + return FAIL; + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef TIMING +#ifndef TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0) + { + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0) + { + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_async_mtq_signal.cpp b/tests/cunit/test_async_mtq_signal.cpp new file mode 100644 index 00000000000..3a17f4bb719 --- /dev/null +++ b/tests/cunit/test_async_mtq_signal.cpp @@ -0,0 +1,484 @@ +#include +#include +#include +#include +#include +#include "spio_async_mtq.hpp" +extern "C"{ +#include +#include +#include +} + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +class UType{ + public: + int i; + float f; +}; + +/* Util function to enqueue data in elems to q */ +template +void thread_enq(PIO_Util::PIO_mtq &q, const std::vector &elems, + const std::chrono::seconds &delay) +{ + for(typename std::vector::const_iterator citer = elems.cbegin(); + citer != elems.cend(); ++citer){ + if(delay.count() != 0){ + std::this_thread::sleep_for(delay); + } + q.enqueue(*citer); + //std::cout << "Enqueued : " << *citer << "\n"; + } +} + +/* Util function to dequeue nelems from q and store in elems */ +template +void thread_deq(PIO_Util::PIO_mtq &q, std::vector &elems, + const std::chrono::seconds &delay) +{ + int nelems = elems.size(); + for(int i=0; i +void thread_signal(PIO_Util::PIO_mtq &q, + typename PIO_Util::PIO_mtq::SigTypes_t sig, + const std::chrono::seconds &delay) +{ + if(delay.count() != 0){ + std::this_thread::sleep_for(delay); + } + q.signal(sig); + if(sig == PIO_Util::PIO_mtq::PIO_MTQ_SIG_COMPLETE){ + q.signal(PIO_Util::PIO_mtq::PIO_MTQ_SIG_STOP); + } +} + +std::ostream &operator<<(std::ostream &ostr, const UType &utype) +{ + ostr << "(" << utype.i << "," << utype.f << "), "; + return ostr; +} + +std::ostream &operator<<(std::ostream &ostr, const UType *putype) +{ + ostr << "(" << putype->i << "," << putype->f << "), "; + return ostr; +} + +/* Test sending signals to waiting dequeue threads in a multi-threaded queue containing + * ints */ +int test_signal_mtq(int wrank, const int max_elems_in_q, const int nthreads, + PIO_Util::PIO_mtq::SigTypes_t sig, + const std::chrono::seconds &enqueue_delay, + const std::chrono::seconds &dequeue_delay, + bool use_signal_thread) +{ + assert((max_elems_in_q > 0) && (nthreads > 0)); + int nelems_per_thread = max_elems_in_q/nthreads; + int nelems_last_thread = max_elems_in_q - nelems_per_thread * (nthreads - 1); + + /* Enqueue multiple elems from different threads and dequeue to make + * sure that the elems are in the queue + */ + + /* Generate input vals */ + PIO_Util::PIO_mtq qi; + std::vector > ivals; + std::vector > ovals; + for(int i=0; i ivals_ithread; + std::vector ovals_ithread; + if(!is_last_thread){ + ivals_ithread.resize(nelems_per_thread); + /* The size is +1 so that we wait for more elements than in the queue */ + ovals_ithread.resize(nelems_per_thread + 1); + } + else{ + ivals_ithread.resize(nelems_last_thread); + /* The size is +1 so that we wait for more elements than in the queue */ + ovals_ithread.resize(nelems_last_thread + 1); + } + int sval = i * nelems_per_thread; + std::generate(ivals_ithread.begin(), ivals_ithread.end(), [&sval]{return sval++; }); + ivals.push_back(ivals_ithread); + std::fill(ovals_ithread.begin(), ovals_ithread.end(), -1); + ovals.push_back(ovals_ithread); + } + + /* Enqueue */ + std::vector tpool; + for(int i=0; i, std::ref(qi), std::ref(ivals[i]), enqueue_delay)); + } + + /* Wait for enqueue operations to complete */ + for(int i=0; i, std::ref(qi), std::ref(ovals[i]), dequeue_delay)); + } + + /* Wait for enqueue operations to complete */ + std::thread *sig_thread = NULL; + if(!use_signal_thread){ + /* Signal all threads so that they exit once the queue is empty */ + qi.signal(sig); + if(sig == PIO_Util::PIO_mtq::PIO_MTQ_SIG_COMPLETE){ + qi.signal(PIO_Util::PIO_mtq::PIO_MTQ_SIG_STOP); + } + } + else{ + /* If there is delay specified during dequeueing data, set the delay so + * that approx half of the data is dequeued by each thread before sending + * the signal + */ + std::chrono::seconds sig_thread_delay = dequeue_delay * ovals[0].size() / 2; + sig_thread = new std::thread(thread_signal, std::ref(qi), sig, sig_thread_delay); + } + for(int i=0; ijoin(); + delete sig_thread; + } + tpool.clear(); + + /* Verify that we dequeued all elements in the queue */ + std::vector elem_in_q(max_elems_in_q, false); + for(int i=0; i= max_elems_in_q)){ + /* Invalid element in queue */ + LOG_RANK0(wrank, "Invalid element (%d) in queue\n", idx); + return PIO_EINTERNAL; + } + /* Note that some threads may not be able to get ovals[i].size() + * elements from the queue. These entries will be -1 on that + * thread. Ignore these entries + */ + if(idx != -1){ + /* Mark that the element was in the queue */ + elem_in_q[idx] = true; + } + } + } + + /* A SIG_STOP abnormally terminates the threads, the queue is not + * completely used up, so we should not be verifying the contents + * here. + */ + if(sig != PIO_Util::PIO_mtq::PIO_MTQ_SIG_STOP){ + for(int i=0; i= 0) && (wsz > 0) && num_errors); + + /* Test signalling waiting threads (the threads wait for 11 elements but we + * only queue 10) - both threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_COMPLETE + */ + try{ + ret = test_signal_mtq(wrank, 10, 2, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_COMPLETE, + std::chrono::seconds(0), + std::chrono::seconds(0), + false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(10 elems, 2 threads, SIG_COMPLETE, no delays, no signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(10 elems, 2 threads, SIG_COMPLETE, no delays, no signalthread) PASSED\n"); + } + + /* Test signalling waiting threads (the threads wait for 101 elements but we + * only queue 100) - all 4 threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_COMPLETE + */ + try{ + ret = test_signal_mtq(wrank, 100, 4, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_COMPLETE, + std::chrono::seconds(0), + std::chrono::seconds(0), + false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(100 elems, 4 threads, SIG_COMPLETE, no delays, no signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(100 elems, 4 threads, SIG_COMPLETE, no delays, no signalthread) PASSED\n"); + } + + /* Test signalling waiting threads (the threads wait for 11 elements but we + * only queue 10) - both threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_STOP + */ + try{ + ret = test_signal_mtq(wrank, 10, 2, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_STOP, + std::chrono::seconds(0), + std::chrono::seconds(0), + false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(10 elems, 2 threads, SIG_STOP, no delays, no signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(10 elems, 2 threads, SIG_STOP, no delays, no signalthread) PASSED\n"); + } + + /* Test signalling waiting threads (the threads wait for 101 elements but we + * only queue 100) - all 4 threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_STOP + */ + try{ + ret = test_signal_mtq(wrank, 100, 4, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_STOP, + std::chrono::seconds(0), + std::chrono::seconds(0), + false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(100 elems, 4 threads, SIG_STOP, no delays, no signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(100 elems, 4 threads, SIG_STOP, no delays, no signalthread) PASSED\n"); + } + + /* Test signalling waiting threads (the threads wait for 21 elements but we + * only queue 20) - all 4 threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_COMPLETE, with a delay of 1 sec when dequeueing data + */ + try{ + ret = test_signal_mtq(wrank, 20, 4, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_COMPLETE, + std::chrono::seconds(0), + std::chrono::seconds(1), + false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_COMPLETE, 1s delay deq, no signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_COMPLETE, 1s delay deq, no signalthread) PASSED\n"); + } + + /* Test signalling waiting threads (the threads wait for 21 elements but we + * only queue 20) - all 4 threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_STOP, with a delay of 1 sec when dequeueing data + */ + try{ + ret = test_signal_mtq(wrank, 20, 4, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_STOP, + std::chrono::seconds(0), + std::chrono::seconds(1), + false); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_STOP, 1s delay deq, no signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_STOP, 1s delay deq, no signalthread) PASSED\n"); + } + + /* Test signalling waiting threads (the threads wait for 21 elements but we + * only queue 20) - all 4 threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_COMPLETE, with a delay of 1 sec when dequeueing data + * The signal is sent from a separate thread + */ + try{ + ret = test_signal_mtq(wrank, 20, 4, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_COMPLETE, + std::chrono::seconds(0), + std::chrono::seconds(1), + true); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_COMPLETE, 1s delay deq, using signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_COMPLETE, 1s delay deq, using signalthread) PASSED\n"); + } + + /* Test signalling waiting threads (the threads wait for 21 elements but we + * only queue 20) - all 4 threads are waiting and the main thread signals + * all the threads to quit + * Test SIG_STOP, with a delay of 1 sec when dequeueing data + * The signal is sent from a separate thread + */ + try{ + ret = test_signal_mtq(wrank, 20, 4, + PIO_Util::PIO_mtq::PIO_MTQ_SIG_STOP, + std::chrono::seconds(0), + std::chrono::seconds(1), + true); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_STOP, 1s delay deq, using signalthread) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_signal_mtq(20 elems, 4 threads, SIG_STOP, 1s delay deq, using signalthread) PASSED\n"); + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef TIMING +#ifndef TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0) + { + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0) + { + LOG_RANK0(wrank, "Test driver FAILED\n"); + return FAIL; + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef TIMING +#ifndef TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0) + { + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0) + { + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_async_simple.c b/tests/cunit/test_async_simple.cpp similarity index 100% rename from tests/cunit/test_async_simple.c rename to tests/cunit/test_async_simple.cpp diff --git a/tests/cunit/test_async_tpool.cpp b/tests/cunit/test_async_tpool.cpp new file mode 100644 index 00000000000..9a891ed8906 --- /dev/null +++ b/tests/cunit/test_async_tpool.cpp @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include "spio_async_op.hpp" +#include "spio_async_mtq.hpp" +#include "spio_async_tpool.hpp" +extern "C"{ +#include +#include +#include +} + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +class UType{ + public: + int nwaits; +}; + +std::ostream &operator<<(std::ostream &ostr, const UType &utype) +{ + ostr << "(" << utype.nwaits << "), "; + return ostr; +} + +std::ostream &operator<<(std::ostream &ostr, const UType *putype) +{ + ostr << "(" << putype->nwaits << "), "; + return ostr; +} + +int pio_utype_wait(void *pdata) +{ + UType *ut = static_cast(pdata); + assert(ut); + + ut->nwaits++; + return PIO_NOERR; +} + +int pio_poke_func_unavail(void *pdata, bool &flag) +{ + assert(0); + return PIO_NOERR; +} + +void pio_noop_free(void *pdata) +{ +} + +/* Enqueue op */ +int tpool_enq_putype(int wrank, const int max_elems_in_q, std::vector &udata) +{ + assert((wrank >= 0) && (max_elems_in_q > 0)); + + PIO_Util::PIO_async_tpool_manager tpool_mgr; + PIO_Util::PIO_async_tpool *tpool = tpool_mgr.get_tpool_instance(); + + for(int i=0; i(&(udata[i])), + pio_utype_wait, pio_poke_func_unavail, pio_noop_free}; + tpool->enqueue(op); + } + + /* tpool is managed by the tpool manager - no delete/free reqd */ + + return PIO_NOERR; +} + +/* Test enqueue and dequeue operations in a multi-threaded queue containing + * pointers to user defined types */ +int test_tpool_putype(int wrank, const int max_elems_in_q) +{ + int ret = PIO_NOERR; + assert((wrank >= 0) && (max_elems_in_q > 0)); + + std::vector udata; + for(int i=0; i= 0) && (wsz > 0) && num_errors); + + /* Test enqueueing 10 ptr to user defined elems into the thread pool, + */ + try{ + ret = test_tpool_putype(wrank, 10); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR) + { + LOG_RANK0(wrank, "test_tpool_putype(10 elems) FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_tpool_putype(10 elems) PASSED\n"); + } + + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef TIMING +#ifndef TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0) + { + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS) + { + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0) + { + LOG_RANK0(wrank, "Test driver FAILED\n"); + return FAIL; + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef TIMING +#ifndef TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0) + { + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0) + { + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_common.c b/tests/cunit/test_common.cpp similarity index 99% rename from tests/cunit/test_common.c rename to tests/cunit/test_common.cpp index 3465a8d9f9a..3069a6185c6 100644 --- a/tests/cunit/test_common.c +++ b/tests/cunit/test_common.cpp @@ -1149,7 +1149,7 @@ int create_decomposition_2d(int ntasks, int my_rank, int iosysid, int *dim_len_2 elements_per_pe = dim_len_2d[0] * dim_len_2d[1] / ntasks; /* Allocate space for the decomposition array. */ - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *) malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; /* Describe the decomposition. This is a 1-based array, so add 1! */ diff --git a/tests/cunit/test_darray.c b/tests/cunit/test_darray.cpp similarity index 94% rename from tests/cunit/test_darray.c rename to tests/cunit/test_darray.cpp index 6ee57790523..e98e1bbca67 100644 --- a/tests/cunit/test_darray.c +++ b/tests/cunit/test_darray.cpp @@ -180,27 +180,27 @@ int test_darray(int iosysid, int ioid, int num_flavors, int *flavor, int my_rank /* These will not work. */ if (PIOc_write_darray_multi(ncid + TEST_VAL_42, &varid, ioid, 1, arraylen, test_data, &frame, - fillvalue, flushtodisk) != PIO_EBADID) + (const void **) &fillvalue, flushtodisk) != PIO_EBADID) ERR(ERR_WRONG); if (PIOc_write_darray_multi(ncid, NULL, ioid, 1, arraylen, test_data, &frame, - fillvalue, flushtodisk) != PIO_EINVAL) + (const void **) &fillvalue, flushtodisk) != PIO_EINVAL) ERR(ERR_WRONG); if (PIOc_write_darray_multi(ncid, &varid, ioid + TEST_VAL_42, 1, arraylen, test_data, &frame, - fillvalue, flushtodisk) != PIO_EBADID) + (const void **) &fillvalue, flushtodisk) != PIO_EBADID) ERR(ERR_WRONG); if (PIOc_write_darray_multi(ncid, &varid, ioid, -1, arraylen, test_data, &frame, - fillvalue, flushtodisk) != PIO_EINVAL) + (const void **) &fillvalue, flushtodisk) != PIO_EINVAL) ERR(ERR_WRONG); /* if (PIOc_write_darray_multi(ncid, &varid, ioid, 1, arraylen, test_data, NULL, */ - /* fillvalue, flushtodisk) != PIO_EINVAL) */ + /* &fillvalue, flushtodisk) != PIO_EINVAL) */ /* ERR(ERR_WRONG); */ if (PIOc_write_darray_multi(ncid, &varid_big, ioid, 1, arraylen, test_data, &frame, - fillvalue, flushtodisk) != PIO_EINVAL) + (const void **) &fillvalue, flushtodisk) != PIO_EINVAL) ERR(ERR_WRONG); /* Write the data with the _multi function. */ if ((ret = PIOc_write_darray_multi(ncid, &varid, ioid, 1, arraylen, test_data, &frame, - fillvalue, flushtodisk))) + (const void **) &fillvalue, flushtodisk))) ERR(ret); } @@ -259,7 +259,7 @@ int test_darray(int iosysid, int ioid, int num_flavors, int *flavor, int my_rank else { if (PIOc_write_darray_multi(ncid2, &varid, ioid, 1, arraylen, test_data, &frame, - fillvalue, flushtodisk) != PIO_EPERM) + (const void **) &fillvalue, flushtodisk) != PIO_EPERM) ERR(ERR_WRONG); } diff --git a/tests/cunit/test_darray_1d.c b/tests/cunit/test_darray_1d.cpp similarity index 93% rename from tests/cunit/test_darray_1d.c rename to tests/cunit/test_darray_1d.cpp index 304b7292fb7..aabb6a0a786 100644 --- a/tests/cunit/test_darray_1d.c +++ b/tests/cunit/test_darray_1d.cpp @@ -186,18 +186,18 @@ int test_darray_fill(int iosysid, int ioid, int pio_type, int num_flavors, int * return ret; /* Initialize some data. */ - signed char byte_test_data[2] = {my_rank, my_rank}; - char char_test_data[2] = {my_rank, my_rank}; - short short_test_data[2] = {my_rank, my_rank}; + signed char byte_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + char char_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + short short_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; int int_test_data[2] = {my_rank, my_rank}; - float float_test_data[2] = {my_rank, my_rank}; - double double_test_data[2] = {my_rank, my_rank}; + float float_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + double double_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; #ifdef _NETCDF4 - unsigned char ubyte_test_data[2] = {my_rank, my_rank}; - unsigned short ushort_test_data[2] = {my_rank, my_rank}; - unsigned int uint_test_data[2] = {my_rank, my_rank}; - long long int64_test_data[2] = {my_rank, my_rank}; - unsigned long long uint64_test_data[2] = {my_rank, my_rank}; + unsigned char ubyte_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + unsigned short ushort_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + unsigned int uint_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + long long int64_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + unsigned long long uint64_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; #endif /* _NETCDF4 */ switch (pio_type) @@ -480,18 +480,18 @@ int test_darray_fill_unlim(int iosysid, int ioid, int pio_type, int num_flavors, return ret; /* Initialize some data. */ - signed char byte_test_data[2] = {my_rank, my_rank}; - char char_test_data[2] = {my_rank, my_rank}; - short short_test_data[2] = {my_rank, my_rank}; + signed char byte_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + char char_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + short short_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; int int_test_data[2] = {my_rank, my_rank}; - float float_test_data[2] = {my_rank, my_rank}; - double double_test_data[2] = {my_rank, my_rank}; + float float_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + double double_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; #ifdef _NETCDF4 - unsigned char ubyte_test_data[2] = {my_rank, my_rank}; - unsigned short ushort_test_data[2] = {my_rank, my_rank}; - unsigned int uint_test_data[2] = {my_rank, my_rank}; - long long int64_test_data[2] = {my_rank, my_rank}; - unsigned long long uint64_test_data[2] = {my_rank, my_rank}; + unsigned char ubyte_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + unsigned short ushort_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + unsigned int uint_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + long long int64_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; + unsigned long long uint64_test_data[2] = {static_cast(my_rank), static_cast(my_rank)}; #endif /* _NETCDF4 */ switch (pio_type) { diff --git a/tests/cunit/test_darray_3d.c b/tests/cunit/test_darray_3d.cpp similarity index 99% rename from tests/cunit/test_darray_3d.c rename to tests/cunit/test_darray_3d.cpp index 578f8fbd5db..69d90f5f9b0 100644 --- a/tests/cunit/test_darray_3d.c +++ b/tests/cunit/test_darray_3d.cpp @@ -82,7 +82,7 @@ int create_decomposition_3d(int ntasks, int my_rank, int iosysid, int *ioid) elements_per_pe = X_DIM_LEN * Y_DIM_LEN * Z_DIM_LEN / ntasks; /* Allocate space for the decomposition array. */ - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *) malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; /* Describe the decomposition. */ diff --git a/tests/cunit/test_darray_async.c b/tests/cunit/test_darray_async.cpp similarity index 100% rename from tests/cunit/test_darray_async.c rename to tests/cunit/test_darray_async.cpp diff --git a/tests/cunit/test_darray_async_many.c b/tests/cunit/test_darray_async_many.cpp similarity index 100% rename from tests/cunit/test_darray_async_many.c rename to tests/cunit/test_darray_async_many.cpp diff --git a/tests/cunit/test_darray_async_simple.c b/tests/cunit/test_darray_async_simple.cpp similarity index 100% rename from tests/cunit/test_darray_async_simple.c rename to tests/cunit/test_darray_async_simple.cpp diff --git a/tests/cunit/test_darray_multi.c b/tests/cunit/test_darray_multi.cpp similarity index 99% rename from tests/cunit/test_darray_multi.c rename to tests/cunit/test_darray_multi.cpp index 5cf94dc3d1b..d78b0d7a833 100644 --- a/tests/cunit/test_darray_multi.c +++ b/tests/cunit/test_darray_multi.cpp @@ -275,7 +275,7 @@ int test_darray(int iosysid, int ioid, int num_flavors, int *flavor, int my_rank /* Write the data with the _multi function. */ if ((ret = PIOc_write_darray_multi(ncid, varid, ioid, NVAR, arraylen, test_data, frame, - fillvalue, flushtodisk))) + (const void **)&fillvalue, flushtodisk))) ERR(ret); /* Close the netCDF file. */ diff --git a/tests/cunit/test_darray_multivar.c b/tests/cunit/test_darray_multivar.cpp similarity index 100% rename from tests/cunit/test_darray_multivar.c rename to tests/cunit/test_darray_multivar.cpp diff --git a/tests/cunit/test_darray_multivar2.c b/tests/cunit/test_darray_multivar2.cpp similarity index 99% rename from tests/cunit/test_darray_multivar2.c rename to tests/cunit/test_darray_multivar2.cpp index c4d4c7f3115..d4f9adc750c 100644 --- a/tests/cunit/test_darray_multivar2.c +++ b/tests/cunit/test_darray_multivar2.cpp @@ -181,7 +181,7 @@ int create_decomposition_2d_2(int ntasks, int my_rank, int iosysid, int *dim_len elements_per_pe = dim_len_2d[0] * dim_len_2d[1] / ntasks; /* Allocate space for the decomposition array. */ - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *) malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; /* Describe the decomposition. This is a 1-based array, so add 1! */ diff --git a/tests/cunit/test_decomp_uneven.c b/tests/cunit/test_decomp_uneven.cpp similarity index 99% rename from tests/cunit/test_decomp_uneven.c rename to tests/cunit/test_decomp_uneven.cpp index 655c6b91b70..6354ce148a8 100644 --- a/tests/cunit/test_decomp_uneven.c +++ b/tests/cunit/test_decomp_uneven.cpp @@ -61,7 +61,7 @@ int create_decomposition_3d(int ntasks, int my_rank, int iosysid, int *dim_len, printf("%d elements_per_pe = %lld remainder = %lld\n", my_rank, elements_per_pe, remainder); /* Allocate space for the decomposition array. */ - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *)malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; /* Describe the decomposition. */ diff --git a/tests/cunit/test_decomps.c b/tests/cunit/test_decomps.cpp similarity index 99% rename from tests/cunit/test_decomps.c rename to tests/cunit/test_decomps.cpp index 92f07ae7813..c233de3565c 100644 --- a/tests/cunit/test_decomps.c +++ b/tests/cunit/test_decomps.cpp @@ -72,7 +72,7 @@ int test_decomp1(int iosysid, int my_rank, MPI_Comm test_comm) /* The compdof array contains a mapping for this task into the * global data array. */ - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *) malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; for (int i = 0; i < elements_per_pe; i++) compdof[i] = my_rank * elements_per_pe + i + 1; diff --git a/tests/cunit/test_intercomm2.c b/tests/cunit/test_intercomm2.cpp similarity index 100% rename from tests/cunit/test_intercomm2.c rename to tests/cunit/test_intercomm2.cpp diff --git a/tests/cunit/test_iosystem2.c b/tests/cunit/test_iosystem2.cpp similarity index 100% rename from tests/cunit/test_iosystem2.c rename to tests/cunit/test_iosystem2.cpp diff --git a/tests/cunit/test_iosystem2_simple.c b/tests/cunit/test_iosystem2_simple.cpp similarity index 100% rename from tests/cunit/test_iosystem2_simple.c rename to tests/cunit/test_iosystem2_simple.cpp diff --git a/tests/cunit/test_iosystem2_simple2.c b/tests/cunit/test_iosystem2_simple2.cpp similarity index 100% rename from tests/cunit/test_iosystem2_simple2.c rename to tests/cunit/test_iosystem2_simple2.cpp diff --git a/tests/cunit/test_iosystem3.c b/tests/cunit/test_iosystem3.cpp similarity index 99% rename from tests/cunit/test_iosystem3.c rename to tests/cunit/test_iosystem3.cpp index 53b0c63b67c..738c93a861f 100644 --- a/tests/cunit/test_iosystem3.c +++ b/tests/cunit/test_iosystem3.cpp @@ -100,7 +100,7 @@ int check_file(MPI_Comm comm, int iosysid, int format, int ncid, char *filename, /* Check the attribute. Null terminating byte deliberately ignored * to match fortran code. */ - if (!(att_data = malloc(strlen(filename) * sizeof(char)))) + if (!(att_data = (char *) malloc(strlen(filename) * sizeof(char)))) return PIO_ENOMEM; if ((ret = PIOc_get_att(ncid, varid, attname, att_data))) return ret; diff --git a/tests/cunit/test_iosystem3_simple.c b/tests/cunit/test_iosystem3_simple.cpp similarity index 100% rename from tests/cunit/test_iosystem3_simple.c rename to tests/cunit/test_iosystem3_simple.cpp diff --git a/tests/cunit/test_iosystem3_simple2.c b/tests/cunit/test_iosystem3_simple2.cpp similarity index 100% rename from tests/cunit/test_iosystem3_simple2.c rename to tests/cunit/test_iosystem3_simple2.cpp diff --git a/tests/cunit/test_pioc.c b/tests/cunit/test_pioc.cpp similarity index 99% rename from tests/cunit/test_pioc.c rename to tests/cunit/test_pioc.cpp index b66637ef30b..09bb5d70e3d 100644 --- a/tests/cunit/test_pioc.c +++ b/tests/cunit/test_pioc.cpp @@ -97,7 +97,7 @@ int create_decomposition(int ntasks, int my_rank, int iosysid, int dim1_len, int elements_per_pe = dim1_len / ntasks; /* Allocate space for the decomposition array. */ - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *) malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; /* Describe the decomposition. The new init_decomp uses a 0-based @@ -464,24 +464,24 @@ int check_error_strings(int my_rank, int num_tries, int *errcode, int ret; /* Try each test code. */ - for (int try = 0; try < num_tries; try++) + for (int i = 0; i < num_tries; i++) { char errstr[PIO_MAX_NAME + 1]; /* Get the error string for this errcode. */ - if ((ret = PIOc_strerror(errcode[try], errstr, PIO_MAX_NAME))) + if ((ret = PIOc_strerror(errcode[i], errstr, PIO_MAX_NAME))) return ret; - printf("%d for errcode = %d message = %s\n", my_rank, errcode[try], errstr); + printf("%d for errcode = %d message = %s\n", my_rank, errcode[i], errstr); /* Check that it was as expected. */ - if (strncmp(errstr, expected[try], strlen(expected[try]))) + if (strncmp(errstr, expected[i], strlen(expected[i]))) { - printf("%d expected %s got %s\n", my_rank, expected[try], errstr); + printf("%d expected %s got %s\n", my_rank, expected[i], errstr); return ERR_AWFUL; } if (!my_rank) - printf("%d errcode = %d passed\n", my_rank, errcode[try]); + printf("%d errcode = %d passed\n", my_rank, errcode[i]); } return PIO_NOERR; @@ -1639,7 +1639,7 @@ int test_malloc_iodesc2(int iosysid, int my_rank) for (int t = 0; t < num_types; t++) { - if ((ret = malloc_iodesc(ios, test_type[t], 1, &iodesc))) + if ((ret = malloc_iodesc(ios, test_type[t], 1, 1, &iodesc))) return ret; if (iodesc->mpitype != mpi_type[t]) return ERR_WRONG; @@ -1648,6 +1648,8 @@ int test_malloc_iodesc2(int iosysid, int my_rank) ioid = pio_add_to_iodesc_list(iodesc, MPI_COMM_NULL); if (iodesc->firstregion) free_region_list(iodesc->firstregion); + free(iodesc->map); + free(iodesc->dimlen); if ((ret = pio_delete_iodesc_from_list(ioid))) return ret; } diff --git a/tests/cunit/test_pioc_fill.c b/tests/cunit/test_pioc_fill.cpp similarity index 100% rename from tests/cunit/test_pioc_fill.c rename to tests/cunit/test_pioc_fill.cpp diff --git a/tests/cunit/test_pioc_putget.c b/tests/cunit/test_pioc_putget.cpp similarity index 100% rename from tests/cunit/test_pioc_putget.c rename to tests/cunit/test_pioc_putget.cpp diff --git a/tests/cunit/test_pioc_unlim.c b/tests/cunit/test_pioc_unlim.cpp similarity index 99% rename from tests/cunit/test_pioc_unlim.c rename to tests/cunit/test_pioc_unlim.cpp index da4cbeb5fdf..f96a51a69e5 100644 --- a/tests/cunit/test_pioc_unlim.c +++ b/tests/cunit/test_pioc_unlim.cpp @@ -72,7 +72,7 @@ int create_decomposition(int ntasks, int my_rank, int iosysid, int dim1_len, elements_per_pe = X_DIM_LEN * Y_DIM_LEN / ntasks; /* Allocate space for the decomposition array. */ - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *) malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; /* Describe the decomposition. This is a 1-based array, so add 1! */ diff --git a/tests/cunit/test_rearr.c b/tests/cunit/test_rearr.cpp similarity index 94% rename from tests/cunit/test_rearr.c rename to tests/cunit/test_rearr.cpp index 26b2267a9cb..4dbd37caf14 100644 --- a/tests/cunit/test_rearr.c +++ b/tests/cunit/test_rearr.cpp @@ -415,12 +415,12 @@ int test_determine_fill(MPI_Comm test_comm) int ret; /* Initialize ios. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; ios->union_comm = test_comm; /* Set up iodesc for test. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) return PIO_ENOMEM; iodesc->ndims = 1; iodesc->rearranger = PIO_REARR_SUBSET; @@ -548,11 +548,11 @@ int test_define_iodesc_datatypes() /* Allocate space for arrays in iodesc that will be filled in * define_iodesc_datatypes(). */ - if (!(iodesc.rcount = malloc(iodesc.nrecvs * sizeof(int)))) + if (!(iodesc.rcount = (int *) malloc(iodesc.nrecvs * sizeof(int)))) return PIO_ENOMEM; - if (!(iodesc.rfrom = malloc(iodesc.nrecvs * sizeof(int)))) + if (!(iodesc.rfrom = (int *) malloc(iodesc.nrecvs * sizeof(int)))) return PIO_ENOMEM; - if (!(iodesc.rindex = malloc(1 * sizeof(PIO_Offset)))) + if (!(iodesc.rindex = (PIO_Offset *) malloc(1 * sizeof(PIO_Offset)))) return PIO_ENOMEM; iodesc.rindex[0] = 0; iodesc.rcount[0] = 1; @@ -562,9 +562,9 @@ int test_define_iodesc_datatypes() /* The two rearrangers create a different number of send types. */ int num_send_types = iodesc.rearranger == PIO_REARR_BOX ? ios.num_iotasks : 1; - if (!(iodesc.sindex = malloc(num_send_types * sizeof(PIO_Offset)))) + if (!(iodesc.sindex = (PIO_Offset *) malloc(num_send_types * sizeof(PIO_Offset)))) return PIO_ENOMEM; - if (!(iodesc.scount = malloc(num_send_types * sizeof(int)))) + if (!(iodesc.scount = (int *) malloc(num_send_types * sizeof(int)))) return PIO_ENOMEM; for (int st = 0; st < num_send_types; st++) { @@ -608,7 +608,7 @@ int test_compute_counts(MPI_Comm test_comm, int my_rank) int ret; /* Initialize ios. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; ios->num_iotasks = TARGET_NTASKS; @@ -617,17 +617,17 @@ int test_compute_counts(MPI_Comm test_comm, int my_rank) ios->ioproc = 1; ios->compproc = 1; ios->union_comm = test_comm; - if (!(ios->ioranks = malloc(TARGET_NTASKS * sizeof(int)))) + if (!(ios->ioranks = (int *) malloc(TARGET_NTASKS * sizeof(int)))) return PIO_ENOMEM; for (int t = 0; t < TARGET_NTASKS; t++) ios->ioranks[t] = t; - if (!(ios->compranks = calloc(ios->num_comptasks, sizeof(int)))) + if (!(ios->compranks = (int *) calloc(ios->num_comptasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for comp ranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->compranks[i] = i; /* Initialize iodesc. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) return PIO_ENOMEM; iodesc->rearranger = PIO_REARR_BOX; iodesc->ndof = TARGET_NTASKS; @@ -700,11 +700,11 @@ int test_box_rearrange_create(MPI_Comm test_comm, int my_rank) int ret; /* Allocate IO system info struct for this test. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; /* Allocate IO desc struct for this test. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) return PIO_ENOMEM; /* Default rearranger options. */ @@ -724,11 +724,11 @@ int test_box_rearrange_create(MPI_Comm test_comm, int my_rank) ios->num_iotasks = 4; ios->num_comptasks = 4; ios->num_uniontasks = 4; - if (!(ios->ioranks = calloc(ios->num_iotasks, sizeof(int)))) + if (!(ios->ioranks = (int *) calloc(ios->num_iotasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for ioranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->ioranks[i] = i; - if (!(ios->compranks = calloc(ios->num_comptasks, sizeof(int)))) + if (!(ios->compranks = (int *) calloc(ios->num_comptasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for compranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->compranks[i] = i; @@ -815,11 +815,11 @@ int test_box_rearrange_create_2(MPI_Comm test_comm, int my_rank) int ret; /* Allocate IO system info struct for this test. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; /* Allocate IO desc struct for this test. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) return PIO_ENOMEM; /* Default rearranger options. */ @@ -842,11 +842,11 @@ int test_box_rearrange_create_2(MPI_Comm test_comm, int my_rank) ios->num_iotasks = 4; ios->num_comptasks = 4; ios->num_uniontasks = 4; - if (!(ios->ioranks = calloc(ios->num_iotasks, sizeof(int)))) + if (!(ios->ioranks = (int *) calloc(ios->num_iotasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for ioranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->ioranks[i] = i; - if (!(ios->compranks = calloc(ios->num_comptasks, sizeof(int)))) + if (!(ios->compranks = (int *) calloc(ios->num_comptasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for compranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->compranks[i] = i; @@ -931,11 +931,11 @@ int test_box_rearrange_create_3(MPI_Comm test_comm, int my_rank) int ret; /* Allocate IO system info struct for this test. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; /* Allocate IO desc struct for this test. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) return PIO_ENOMEM; /* Default rearranger options. */ @@ -958,11 +958,11 @@ int test_box_rearrange_create_3(MPI_Comm test_comm, int my_rank) ios->num_iotasks = TARGET_NTASKS; ios->num_comptasks = TARGET_NTASKS; ios->num_uniontasks = TARGET_NTASKS; - if (!(ios->ioranks = calloc(ios->num_iotasks, sizeof(int)))) + if (!(ios->ioranks = (int *) calloc(ios->num_iotasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for ioranks"); for (int i = 0; i < ios->num_iotasks; i++) ios->ioranks[i] = i; - if (!(ios->compranks = calloc(ios->num_comptasks, sizeof(int)))) + if (!(ios->compranks = (int *) calloc(ios->num_comptasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for compranks"); for (int i = 0; i < ios->num_comptasks; i++) ios->compranks[i] = i; @@ -1017,11 +1017,11 @@ int test_default_subset_partition(MPI_Comm test_comm, int my_rank) int ret; /* Allocate IO system info struct for this test. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; /* Allocate IO desc struct for this test. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) return PIO_ENOMEM; ios->ioproc = 1; @@ -1052,6 +1052,7 @@ int test_rearrange_comp2io(MPI_Comm test_comm, int my_rank) { iosystem_desc_t *ios; io_desc_t *iodesc; + file_desc_t *file; void *sbuf = NULL; void *rbuf = NULL; int nvars = 1; @@ -1064,17 +1065,20 @@ int test_rearrange_comp2io(MPI_Comm test_comm, int my_rank) int ret; /* Allocate some space for data. */ - if (!(sbuf = calloc(4, sizeof(int)))) + if (!(sbuf = (int *) calloc(4, sizeof(int)))) return PIO_ENOMEM; - if (!(rbuf = calloc(4, sizeof(int)))) + if (!(rbuf = (int *) calloc(4, sizeof(int)))) return PIO_ENOMEM; /* Allocate IO system info struct for this test. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; /* Allocate IO desc struct for this test. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) + return PIO_ENOMEM; + + if (!(file = (file_desc_t *) calloc(1, sizeof(file_desc_t)))) return PIO_ENOMEM; ios->ioproc = 1; @@ -1092,6 +1096,9 @@ int test_rearrange_comp2io(MPI_Comm test_comm, int my_rank) iodesc->mpitype = MPI_INT; iodesc->stype = NULL; /* Array of MPI types will be created here. */ + file->iosystem = ios; + file->fh = -1; + /* The two rearrangers create a different number of send types. */ int num_send_types = iodesc->rearranger == PIO_REARR_BOX ? ios->num_iotasks : 1; @@ -1110,11 +1117,11 @@ int test_rearrange_comp2io(MPI_Comm test_comm, int my_rank) /* Set up the IO task info for the test. */ ios->union_rank = my_rank; ios->num_comptasks = 4; - if (!(ios->ioranks = calloc(ios->num_iotasks, sizeof(int)))) + if (!(ios->ioranks = (int *) calloc(ios->num_iotasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for ioranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->ioranks[i] = i; - if (!(ios->compranks = calloc(ios->num_comptasks, sizeof(int)))) + if (!(ios->compranks = (int *) calloc(ios->num_comptasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for compranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->compranks[i] = i; @@ -1133,7 +1140,7 @@ int test_rearrange_comp2io(MPI_Comm test_comm, int my_rank) return ret; /* Run the function to test. */ - if ((ret = rearrange_comp2io(ios, iodesc, sbuf, rbuf, nvars))) + if ((ret = rearrange_comp2io(ios, iodesc, file, sbuf, rbuf, nvars))) return ret; printf("returned from rearrange_comp2io\n"); @@ -1165,6 +1172,7 @@ int test_rearrange_comp2io(MPI_Comm test_comm, int my_rank) free(ior1); free(ios->ioranks); free(ios->compranks); + free(file); free(iodesc); free(ios); free(sbuf); @@ -1189,17 +1197,17 @@ int test_rearrange_io2comp(MPI_Comm test_comm, int my_rank) int ret; /* Allocate some space for data. */ - if (!(sbuf = calloc(4, sizeof(int)))) + if (!(sbuf = (int *) calloc(4, sizeof(int)))) return PIO_ENOMEM; - if (!(rbuf = calloc(4, sizeof(int)))) + if (!(rbuf = (int *) calloc(4, sizeof(int)))) return PIO_ENOMEM; /* Allocate IO system info struct for this test. */ - if (!(ios = calloc(1, sizeof(iosystem_desc_t)))) + if (!(ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)))) return PIO_ENOMEM; /* Allocate IO desc struct for this test. */ - if (!(iodesc = calloc(1, sizeof(io_desc_t)))) + if (!(iodesc = (io_desc_t *) calloc(1, sizeof(io_desc_t)))) return PIO_ENOMEM; ios->ioproc = 1; @@ -1239,11 +1247,11 @@ int test_rearrange_io2comp(MPI_Comm test_comm, int my_rank) ios->num_iotasks = 4; ios->num_comptasks = 4; ios->num_uniontasks = 4; - if (!(ios->ioranks = calloc(ios->num_iotasks, sizeof(int)))) + if (!(ios->ioranks = (int *) calloc(ios->num_iotasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for ioranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->ioranks[i] = i; - if (!(ios->compranks = calloc(ios->num_comptasks, sizeof(int)))) + if (!(ios->compranks = (int *) calloc(ios->num_comptasks, sizeof(int)))) return pio_err(ios, NULL, PIO_ENOMEM, __FILE__, __LINE__, "Error allocating memory for compranks"); for (int i = 0; i < TARGET_NTASKS; i++) ios->compranks[i] = i; diff --git a/tests/cunit/test_req_block_wait.c b/tests/cunit/test_req_block_wait.cpp similarity index 99% rename from tests/cunit/test_req_block_wait.c rename to tests/cunit/test_req_block_wait.cpp index a9c8e800dc1..c68e7592c44 100644 --- a/tests/cunit/test_req_block_wait.c +++ b/tests/cunit/test_req_block_wait.cpp @@ -129,8 +129,8 @@ int test_setup(MPI_Comm comm, int rank, int sz, assert((comm != MPI_COMM_NULL) && (rank >= 0) && (sz > 0) && pios && pfile); - *pios = calloc(1, sizeof(iosystem_desc_t)); - *pfile = calloc(1, sizeof(file_desc_t)); + *pios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)); + *pfile = (file_desc_t *) calloc(1, sizeof(file_desc_t)); iosystem_desc_t *ios = *pios; file_desc_t *file = *pfile; diff --git a/tests/cunit/test_shared.c b/tests/cunit/test_shared.cpp similarity index 98% rename from tests/cunit/test_shared.c rename to tests/cunit/test_shared.cpp index 0926e0be9fd..36bc007d049 100644 --- a/tests/cunit/test_shared.c +++ b/tests/cunit/test_shared.cpp @@ -104,7 +104,7 @@ int test_no_async2(int my_rank, int num_flavors, int *flavor, MPI_Comm test_comm /* Describe the decomposition. This is a 0-based array, so don't add 1! */ elements_per_pe = x_dim_len * y_dim_len / target_ntasks; - if (!(compdof = malloc(elements_per_pe * sizeof(PIO_Offset)))) + if (!(compdof = (PIO_Offset *) malloc(elements_per_pe * sizeof(PIO_Offset)))) return PIO_ENOMEM; for (int i = 0; i < elements_per_pe; i++) compdof[i] = my_rank * elements_per_pe + i; diff --git a/tests/cunit/test_spio_decomp_logger.cpp b/tests/cunit/test_spio_decomp_logger.cpp new file mode 100644 index 00000000000..377910841ac --- /dev/null +++ b/tests/cunit/test_spio_decomp_logger.cpp @@ -0,0 +1,653 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "spio_decomp_logger.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0){ \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +template +bool cmp_result(int wrank, const std::vector &res, const std::vector &exp) +{ + + if(res.size() != exp.size()){ + LOG_RANK0(wrank, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + std::ostringstream oss; + oss << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")"; + LOG_RANK0(wrank, "ERROR: %s\n", oss.str().c_str()); + return false; + } + } + + return true; +} + +iosystem_desc_t *get_iosystem(MPI_Comm comm, int wrank, int wsz, int nio_procs) +{ + int ret = PIO_NOERR; + static int iosysid = 1; + iosystem_desc_t *ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)); + if(!ios){ + LOG_RANK0(wrank, "Unable to allocate memory for I/O system\n"); + return NULL; + } + + ios->iosysid = iosysid++; + ios->union_comm = comm; + ios->num_uniontasks = wsz; + ios->union_rank = wrank; + /* Every proc is a compute proc */ + ios->comp_comm = comm; + ios->num_comptasks = wsz; + ios->comp_rank = wrank; + ios->compproc = true; + + assert(nio_procs <= wsz); + + /* Assign first nio_procs procs as I/O processes */ + int color = (wrank/nio_procs == 0) ? 0 : 1; + + ret = MPI_Comm_split(comm, color, 0, &(ios->io_comm)); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Unable to split comm for creating I/O system\n"); + free(ios); + return NULL; + } + + ios->num_iotasks = nio_procs; + ios->ioproc = (color == 0) ? true : false; + if(ios->ioproc){ + ret = MPI_Comm_rank(ios->io_comm, &(ios->io_rank)); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Unable to get rank of I/O process\n"); + free(ios); + return NULL; + } + } + else{ + ios->io_rank = -1; + } + + return ios; +} + +void free_iosystem(iosystem_desc_t *ios){ + if(!ios){ + return; + } + + MPI_Comm_free(&(ios->io_comm)); + free(ios); +} + +io_desc_t *get_iodesc(int wrank, iosystem_desc_t *ios, const std::vector &compmap, const std::vector &gdimlen) +{ + io_desc_t *iodesc = NULL; + int ret = PIO_NOERR; + + ret = malloc_iodesc(ios, PIO_DOUBLE, static_cast(gdimlen.size()), static_cast(compmap.size()), &iodesc); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Unable to alloc mem for I/O desc\n"); + return iodesc; + } + + assert(iodesc->dimlen); + std::copy(gdimlen.cbegin(), gdimlen.cend(), iodesc->dimlen); + + assert(iodesc->map); + std::copy(compmap.cbegin(), compmap.cend(), iodesc->map); + + return iodesc; +} + +void free_iodesc(io_desc_t *iodesc) +{ + if(!iodesc){ + return; + } + + free(iodesc->firstregion->start); + free(iodesc->firstregion->count); + free(iodesc->firstregion); + free(iodesc->map); + free(iodesc->dimlen); + free(iodesc); +} + +int test_create_decomp_logger(MPI_Comm comm, int wrank, int wsz) +{ + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system\n"); + return PIO_EINTERNAL; + } + + try{ + std::string log_fname("piodecomplogger_test_01.nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + return PIO_EINTERNAL; + } + + delete logger; + } + catch(...){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_simple_decomp_logger(MPI_Comm comm, int wrank, int wsz) +{ + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system\n"); + return PIO_EINTERNAL; + } + + try{ + std::string log_fname("piodecomplogger_test_simple_01.nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + const int LOCAL_COMPMAP_SZ = 4; + std::vector gdimlen = {LOCAL_COMPMAP_SZ * wsz}; + std::vector compmap(LOCAL_COMPMAP_SZ); + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + + io_desc_t *iodesc = get_iodesc(wrank, ios, compmap, gdimlen); + if(iodesc == NULL){ + LOG_RANK0(wrank, "Create I/O descriptor failed\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + (*logger).write_only().open().put(iodesc).close(); + free_iodesc(iodesc); + delete logger; + } + catch(...){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_simple_decomp_logger_wr_cached_rd(MPI_Comm comm, int wrank, int wsz) +{ + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system\n"); + return PIO_EINTERNAL; + } + + try{ + std::string log_fname("piodecomplogger_test_simple_01.nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + const int LOCAL_COMPMAP_SZ = 4; + std::vector gdimlen = {LOCAL_COMPMAP_SZ * wsz}; + std::vector compmap(LOCAL_COMPMAP_SZ); + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + + io_desc_t *iodesc = get_iodesc(wrank, ios, compmap, gdimlen); + if(iodesc == NULL){ + LOG_RANK0(wrank, "Create I/O descriptor failed\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + (*logger).write_only().open().put(iodesc).close(); + free_iodesc(iodesc); + + std::string version; + int rd_nprocs = -1; + std::vector rd_gdims; + std::vector rd_compmap; + + (*logger).get(version, rd_nprocs, rd_gdims, rd_compmap); + if(rd_nprocs != wsz){ + LOG_RANK0(wrank, "Read invalid nprocs (%d) from log file, expected = %d\n", rd_nprocs, wsz); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_gdims, gdimlen)){ + LOG_RANK0(wrank, "Read invalid gdims from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_compmap, compmap)){ + LOG_RANK0(wrank, "Read invalid map (local compmap) from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + delete logger; + } + catch(...){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_simple_decomp_logger_wr_rd(MPI_Comm comm, int wrank, int wsz) +{ + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system\n"); + return PIO_EINTERNAL; + } + + try{ + std::string log_fname("piodecomplogger_test_simple_01.nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + const int LOCAL_COMPMAP_SZ = 4; + std::vector gdimlen = {LOCAL_COMPMAP_SZ * wsz}; + std::vector compmap(LOCAL_COMPMAP_SZ); + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + + io_desc_t *iodesc = get_iodesc(wrank, ios, compmap, gdimlen); + if(iodesc == NULL){ + LOG_RANK0(wrank, "Create I/O descriptor failed\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + (*logger).write_only().open().put(iodesc).close(); + free_iodesc(iodesc); + delete logger; + + logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger (for read) failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + std::string version; + int rd_nprocs = -1; + std::vector rd_gdims; + std::vector rd_compmap; + + (*logger).read_only().open().get(version, rd_nprocs, rd_gdims, rd_compmap).close(); + if(rd_nprocs != wsz){ + LOG_RANK0(wrank, "Read invalid nprocs (%d) from log file, expected = %d\n", rd_nprocs, wsz); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_gdims, gdimlen)){ + LOG_RANK0(wrank, "Read invalid gdims from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_compmap, compmap)){ + LOG_RANK0(wrank, "Read invalid map (local compmap) from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + delete logger; + } + catch(std::runtime_error &e){ + LOG_RANK0(wrank, "Creating decomp logger failedi (%s)\n", e.what()); + free_iosystem(ios); + return PIO_EINTERNAL; + } + catch(...){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_vlen_decomp_logger(MPI_Comm comm, int wrank, int wsz) +{ + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system\n"); + return PIO_EINTERNAL; + } + + try{ + std::string log_fname("piodecomplogger_test_vlen_01.nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + const int LOCAL_COMPMAP_SZ = wrank + 1; + std::vector gdimlen = {(wsz * (wsz + 1))/2}; + std::vector compmap(LOCAL_COMPMAP_SZ); + + std::iota(compmap.begin(), compmap.end(), (wrank * (wrank + 1))/2); + + io_desc_t *iodesc = get_iodesc(wrank, ios, compmap, gdimlen); + if(iodesc == NULL){ + LOG_RANK0(wrank, "Create I/O descriptor failed\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + (*logger).write_only().open().put(iodesc).close(); + free_iodesc(iodesc); + + delete logger; + + logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger (for read) failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + std::string version; + int rd_nprocs = -1; + std::vector rd_gdims; + std::vector rd_compmap; + + (*logger).read_only().open().get(version, rd_nprocs, rd_gdims, rd_compmap).close(); + if(rd_nprocs != wsz){ + LOG_RANK0(wrank, "Read invalid nprocs (%d) from log file, expected = %d\n", rd_nprocs, wsz); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_gdims, gdimlen)){ + LOG_RANK0(wrank, "Read invalid gdims from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_compmap, compmap)){ + LOG_RANK0(wrank, "Read invalid map (local compmap) from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + delete logger; + } + catch(...){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_rvlen_decomp_logger(MPI_Comm comm, int wrank, int wsz) +{ + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system\n"); + return PIO_EINTERNAL; + } + + try{ + std::string log_fname("piodecomplogger_test_rvlen_01.nc"); + SPIO_Util::Decomp_Util::Decomp_logger *logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + const int LOCAL_COMPMAP_SZ = (wsz - 1 - wrank) + 1; + std::vector gdimlen = {(wsz * (wsz + 1))/2}; + std::vector compmap(LOCAL_COMPMAP_SZ); + + std::iota(compmap.rbegin(), compmap.rend(), ((wsz - 1 - wrank) * (wsz - wrank))/2); + + io_desc_t *iodesc = get_iodesc(wrank, ios, compmap, gdimlen); + if(iodesc == NULL){ + LOG_RANK0(wrank, "Create I/O descriptor failed\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + (*logger).write_only().open().put(iodesc).close(); + free_iodesc(iodesc); + + delete logger; + + logger = + SPIO_Util::Decomp_Util::create_decomp_logger(ios->comp_comm, log_fname); + if(logger == NULL){ + LOG_RANK0(wrank, "Creating decomp logger (for read) failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + std::string version; + int rd_nprocs = -1; + std::vector rd_gdims; + std::vector rd_compmap; + + (*logger).read_only().open().get(version, rd_nprocs, rd_gdims, rd_compmap).close(); + if(rd_nprocs != wsz){ + LOG_RANK0(wrank, "Read invalid nprocs (%d) from log file, expected = %d\n", rd_nprocs, wsz); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_gdims, gdimlen)){ + LOG_RANK0(wrank, "Read invalid gdims from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rd_compmap, compmap)){ + LOG_RANK0(wrank, "Read invalid map (local compmap) from log file\n"); + delete logger; + free_iosystem(ios); + return PIO_EINTERNAL; + } + + delete logger; + } + catch(...){ + LOG_RANK0(wrank, "Creating decomp logger failed\n"); + free_iosystem(ios); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR, mpierr = MPI_SUCCESS; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + std::vector > > test_funcs = { + {"test_create_decomp_logger", test_create_decomp_logger}, + {"test_simple_decomp_logger", test_simple_decomp_logger}, + {"test_simple_decomp_logger_wr_cached_rd", test_simple_decomp_logger_wr_cached_rd}, + {"test_simple_decomp_logger_wr_rd", test_simple_decomp_logger_wr_rd}, + {"test_vlen_decomp_logger", test_vlen_decomp_logger}, + {"test_rvlen_decomp_logger", test_rvlen_decomp_logger} + }; + + for(std::size_t tid = 0; tid < test_funcs.size(); tid++){ + try{ + ret = test_funcs[tid].second(comm, wrank, wsz); + } + catch(...){ + ret = PIO_EINTERNAL; + nerrs++; + } + int lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce(&lfail, &ret, 1, MPI_INT, MPI_SUM, 0, comm); + if(mpierr != MPI_SUCCESS){ + LOG_RANK0(wrank, "Test Driver failed: Unable to calculate total num errors\n"); + } + if(ret != 0){ + std::string non_root_fail_msg = std::string(", failed on ") + std::to_string(ret) + std::string(" non-root processes"); + LOG_RANK0(wrank, "%s() FAILED (ret = %d%s)\n", test_funcs[tid].first.c_str(), ret, (lfail) ? "" : non_root_fail_msg.c_str()); + nerrs++; + } + else{ + LOG_RANK0(wrank, "%s() PASSED\n", test_funcs[tid].first.c_str()); + } + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + return FAIL; + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spio_dt_converter.cpp b/tests/cunit/test_spio_dt_converter.cpp new file mode 100644 index 00000000000..5996f30ea89 --- /dev/null +++ b/tests/cunit/test_spio_dt_converter.cpp @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "spio_dt_converter.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +int test_double_to_float(int wrank) +{ + const int DUMMY_NCID = 1; + std::vector dval = {1.0, 2.0, 3.0, 4.0, 5.0}; + + SPIO_Util::File_Util::DTConverter dt; + + void *val = dt.convert(DUMMY_NCID, static_cast(dval.data()), dval.size() * sizeof(double), + PIO_DOUBLE, PIO_FLOAT); + float *fval = static_cast(val); + if(fval == NULL){ + LOG_RANK0(wrank, "Data type converter failed : Returned null buffer\n"); + return PIO_EINTERNAL; + } + + for(std::size_t i = 9; i < dval.size(); i++){ + if(static_cast(dval[i]) != fval[i]){ + LOG_RANK0(wrank, "Data type converter returned wrong value, val[%zu] = %f (expected %f)\n", + i, fval[i], dval[i]); + dt.clear(); + return PIO_EINTERNAL; + } + } + + dt.clear(); + return PIO_NOERR; +} + +int test_float_to_double(int wrank) +{ + const int DUMMY_NCID = 1; + std::vector fval = {1.0, 2.0, 3.0, 4.0, 5.0}; + + SPIO_Util::File_Util::DTConverter dt; + + void *val = dt.convert(DUMMY_NCID, static_cast(fval.data()), fval.size() * sizeof(float), + PIO_FLOAT, PIO_DOUBLE); + double *dval = static_cast(val); + if(dval == NULL){ + LOG_RANK0(wrank, "Data type converter failed : Returned null buffer\n"); + return PIO_EINTERNAL; + } + + for(std::size_t i = 9; i < fval.size(); i++){ + if(static_cast(fval[i]) != dval[i]){ + LOG_RANK0(wrank, "Data type converter returned wrong value, val[%zu] = %f (expected %f)\n", + i, dval[i], fval[i]); + dt.clear(); + return PIO_EINTERNAL; + } + } + + dt.clear(); + return PIO_NOERR; +} + +int test_int_to_double(int wrank) +{ + const int DUMMY_NCID = 1; + std::vector ival = {1, 2, 3, 4, 5}; + + SPIO_Util::File_Util::DTConverter dt; + + void *val = dt.convert(DUMMY_NCID, static_cast(ival.data()), ival.size() * sizeof(int), + PIO_INT, PIO_DOUBLE); + double *dval = static_cast(val); + if(dval == NULL){ + LOG_RANK0(wrank, "Data type converter failed : Returned null buffer\n"); + return PIO_EINTERNAL; + } + + for(std::size_t i = 9; i < ival.size(); i++){ + if(static_cast(ival[i]) != dval[i]){ + LOG_RANK0(wrank, "Data type converter returned wrong value, val[%zu] = %f (expected %f)\n", + i, dval[i], static_cast(ival[i])); + dt.clear(); + return PIO_EINTERNAL; + } + } + + dt.clear(); + return PIO_NOERR; +} + +int test_multi_convert(int wrank) +{ + const int DUMMY_NCID = 1; + std::vector fval = {1.1, 2.12, 3.123, 4.1234, 5.12345}; + + SPIO_Util::File_Util::DTConverter dt; + + void *val = dt.convert(DUMMY_NCID, static_cast(fval.data()), fval.size() * sizeof(float), + PIO_FLOAT, PIO_DOUBLE); + double *dval = static_cast(val); + if(dval == NULL){ + LOG_RANK0(wrank, "Data type converter failed : Returned null buffer\n"); + return PIO_EINTERNAL; + } + + for(std::size_t i = 9; i < fval.size(); i++){ + if(static_cast(fval[i]) != dval[i]){ + LOG_RANK0(wrank, "Data type converter returned wrong value, val[%zu] = %f (expected %f)\n", + i, dval[i], static_cast(fval[i])); + dt.clear(); + return PIO_EINTERNAL; + } + } + + val = dt.convert(DUMMY_NCID, static_cast(fval.data()), fval.size() * sizeof(float), + PIO_FLOAT, PIO_INT); + int *ival = static_cast(val); + if(ival == NULL){ + LOG_RANK0(wrank, "Data type converter failed : Returned null buffer\n"); + return PIO_EINTERNAL; + } + + for(std::size_t i = 9; i < fval.size(); i++){ + if(static_cast(fval[i]) != ival[i]){ + LOG_RANK0(wrank, "Data type converter returned wrong value, val[%zu] = %d (expected %d)\n", + i, ival[i], static_cast(fval[i])); + dt.clear(); + return PIO_EINTERNAL; + } + } + + dt.clear(); + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + try{ + ret = test_double_to_float(wrank); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "test_double_to_float() FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_double_to_float() PASSED\n"); + } + + try{ + ret = test_float_to_double(wrank); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "test_float_to_double() FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_float_to_double() PASSED\n"); + } + + try{ + ret = test_int_to_double(wrank); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "test_int_to_double() FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_int_to_double() PASSED\n"); + } + + try{ + ret = test_multi_convert(wrank); + } + catch(...){ + ret = PIO_EINTERNAL; + } + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "test_multi_convert() FAILED, ret = %d\n", ret); + nerrs++; + } + else{ + LOG_RANK0(wrank, "test_multi_convert() PASSED\n"); + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + return FAIL; + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spio_file_mvcache.cpp b/tests/cunit/test_spio_file_mvcache.cpp index 55be72eecde..21c2ef9bebf 100644 --- a/tests/cunit/test_spio_file_mvcache.cpp +++ b/tests/cunit/test_spio_file_mvcache.cpp @@ -199,8 +199,6 @@ int test_cint_mvcache(int wrank) { file_desc_t file; - std::memset(&file, 0, sizeof(file_desc_t)); - /* Initialize the mvcache associated with this dummy file */ spio_file_mvcache_init(&file); diff --git a/tests/cunit/test_spio_rearr_contig.cpp b/tests/cunit/test_spio_rearr_contig.cpp new file mode 100644 index 00000000000..e2ff6735e91 --- /dev/null +++ b/tests/cunit/test_spio_rearr_contig.cpp @@ -0,0 +1,630 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "pio_rearr_contig.hpp" +#include "spio_test_framework.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +static const int FAIL = -1; + +template +bool cmp_result(SPIO_TF::Test_framework &tf, int wrank, const std::vector &res, const std::vector &exp) +{ + if(res.size() != exp.size()){ + tf.get_logger().log(SPIO_Util::Logger::Log_level::INFO, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + std::ostringstream oss; + oss << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")\n"; + tf.get_logger().log(SPIO_Util::Logger::Log_level::INFO, "ERROR:" + oss.str()); + return false; + } + } + + return true; +} + +iosystem_desc_t *get_iosystem(SPIO_TF::Test_framework &tf, MPI_Comm comm, int wrank, int wsz, int nio_procs) +{ + int ret = PIO_NOERR; + static int iosysid = 1; + iosystem_desc_t *ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)); + if(!ios){ + tf.get_logger().log(SPIO_Util::Logger::Log_level::INFO, "Unable to allocate memory for I/O system"); + return NULL; + } + + ios->iosysid = iosysid++; + ios->union_comm = comm; + ios->num_uniontasks = wsz; + ios->union_rank = wrank; + /* Every proc is a compute proc */ + ios->comp_comm = comm; + ios->num_comptasks = wsz; + ios->comp_rank = wrank; + ios->compproc = true; + + assert(nio_procs <= wsz); + + /* Assign first nio_procs procs as I/O processes */ + int color = (wrank/nio_procs == 0) ? 0 : 1; + + ret = MPI_Comm_split(comm, color, 0, &(ios->io_comm)); + if(ret != MPI_SUCCESS){ + tf.get_logger().log(SPIO_Util::Logger::Log_level::INFO, "Unable to split comm for creating I/O system"); + free(ios); + return NULL; + } + + ios->num_iotasks = nio_procs; + ios->ioproc = (color == 0) ? true : false; + if(ios->ioproc){ + ret = MPI_Comm_rank(ios->io_comm, &(ios->io_rank)); + if(ret != MPI_SUCCESS){ + tf.get_logger().log(SPIO_Util::Logger::Log_level::INFO, "Unable to get rank of I/O process"); + free(ios); + return NULL; + } + } + else{ + ios->io_rank = -1; + } + + return ios; +} + +void free_iosystem(iosystem_desc_t *ios){ + if(!ios){ + return; + } + + MPI_Comm_free(&(ios->io_comm)); + free(ios); +} + +int test_create_block_rearr(SPIO_TF::Test_framework &tf) +{ + MPI_Comm comm = tf.get_comm(); + int wrank = tf.get_comm_rank(); + int wsz = tf.get_comm_size(); + SPIO_Util::Logger::MPI_logger logger = tf.get_logger(); + + int ret = PIO_NOERR; + + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(tf, comm, wrank, wsz, nio_procs); + if(!ios){ + logger.log(SPIO_Util::Logger::Log_level::ERROR, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Creating contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_c2i_block_data_rearr(SPIO_TF::Test_framework &tf) +{ + MPI_Comm comm = tf.get_comm(); + int wrank = tf.get_comm_rank(); + int wsz = tf.get_comm_size(); + SPIO_Util::Logger::MPI_logger logger = tf.get_logger(); + int ret = PIO_NOERR; + + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(tf, comm, wrank, wsz, nio_procs); + if(!ios){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, rdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_block_data_rearr(SPIO_TF::Test_framework &tf) +{ + MPI_Comm comm = tf.get_comm(); + int wrank = tf.get_comm_rank(); + int wsz = tf.get_comm_size(); + SPIO_Util::Logger::MPI_logger logger = tf.get_logger(); + int ret = PIO_NOERR; + + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(tf, comm, wrank, wsz, nio_procs); + if(!ios){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + + /* Rearrange data from compute processes to I/O processes */ + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, rdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement (compute to I/O procs)\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, sdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_rev_block_data_rearr(SPIO_TF::Test_framework &tf) +{ + MPI_Comm comm = tf.get_comm(); + int wrank = tf.get_comm_rank(); + int wsz = tf.get_comm_size(); + SPIO_Util::Logger::MPI_logger logger = tf.get_logger(); + + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(tf, comm, wrank, wsz, nio_procs); + if(!ios){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + //std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + PIO_Offset compmap_val = wrank * LOCAL_COMPMAP_SZ; + std::generate(compmap.begin(), compmap.end(), + [&compmap_val, gdimlen]() mutable { return gdimlen - 1 - compmap_val++; }); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, rdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, sdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_mrange_block_data_rearr(SPIO_TF::Test_framework &tf) +{ + MPI_Comm comm = tf.get_comm(); + int wrank = tf.get_comm_rank(); + int wsz = tf.get_comm_size(); + SPIO_Util::Logger::MPI_logger logger = tf.get_logger(); + + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(tf, comm, wrank, wsz, nio_procs); + if(!ios){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int FIRST_RANGE_SZ = 3; + const int SECOND_RANGE_SZ = 1; + const int THIRD_RANGE_SZ = 2; + const int LOCAL_COMPMAP_SZ = FIRST_RANGE_SZ + SECOND_RANGE_SZ + THIRD_RANGE_SZ; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + std::iota(compmap.begin(), compmap.begin() + FIRST_RANGE_SZ, wrank * FIRST_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ, compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, wsz * FIRST_RANGE_SZ + wrank * SECOND_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, compmap.end(), wsz * (FIRST_RANGE_SZ + SECOND_RANGE_SZ) + wrank * THIRD_RANGE_SZ); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, rdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, sdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_mrange_oddz_data_rearr(SPIO_TF::Test_framework &tf) +{ + MPI_Comm comm = tf.get_comm(); + int wrank = tf.get_comm_rank(); + int wsz = tf.get_comm_size(); + SPIO_Util::Logger::MPI_logger logger = tf.get_logger(); + + bool is_odd_proc = ((wrank % 2) != 0) ? true : false; + int nodd_procs = wsz/2; + int neven_procs = wsz - nodd_procs; + int ret = PIO_NOERR; + + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(tf, comm, wrank, wsz, nio_procs); + if(!ios){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int FIRST_RANGE_SZ = 3; + const int SECOND_RANGE_SZ = 1; + const int THIRD_RANGE_SZ = 2; + const int LOCAL_COMPMAP_SZ = FIRST_RANGE_SZ + SECOND_RANGE_SZ + THIRD_RANGE_SZ; + /* Only even procs have data */ + int gdimlen = LOCAL_COMPMAP_SZ * neven_procs; + std::vector compmap; + std::vector sdata; + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + if(!is_odd_proc){ + compmap.resize(LOCAL_COMPMAP_SZ); + sdata.resize(compmap.size()); + + int neven_procs_before_wrank = wrank - (wrank / 2); + std::iota(compmap.begin(), compmap.begin() + FIRST_RANGE_SZ, neven_procs_before_wrank * FIRST_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ, compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, neven_procs * FIRST_RANGE_SZ + neven_procs_before_wrank * SECOND_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, compmap.end(), neven_procs * (FIRST_RANGE_SZ + SECOND_RANGE_SZ) + neven_procs_before_wrank * THIRD_RANGE_SZ); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + } + + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, rdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + if(!is_odd_proc){ + exp_data.resize(LOCAL_COMPMAP_SZ); + } + else{ + exp_data.clear(); + } + + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(tf, wrank, sdata, exp_data)){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + logger.log(SPIO_Util::Logger::Log_level::INFO, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int main(int argc, char *argv[]) +{ + int ret; + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + std::cerr << "MPI_Init() FAILED, ret = " << ret << "\n"; + return ret; + } + + std::vector > > rfuncs = { + {"test_create_block_rearr", test_create_block_rearr}, + {"test_c2i_block_data_rearr", test_c2i_block_data_rearr}, + {"test_block_data_rearr", test_block_data_rearr}, + {"test_rev_block_data_rearr", test_rev_block_data_rearr}, + {"test_mrange_block_data_rearr", test_mrange_block_data_rearr}, + {"test_mrange_oddz_data_rearr", test_mrange_oddz_data_rearr} + }; + + SPIO_TF::Test_framework tf("test_spio_rearr_contig", MPI_COMM_WORLD, rfuncs); + tf.init(argc, argv); + ret = tf.run(); + tf.finalize(); + + MPI_Finalize(); + + return ret; +} diff --git a/tests/cunit/test_spio_rearr_contig_fillval.cpp b/tests/cunit/test_spio_rearr_contig_fillval.cpp new file mode 100644 index 00000000000..2c631c3f640 --- /dev/null +++ b/tests/cunit/test_spio_rearr_contig_fillval.cpp @@ -0,0 +1,839 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "pio_rearr_contig.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0){ \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +template +bool cmp_result(int wrank, const std::vector &res, const std::vector &exp) +{ + + if(res.size() != exp.size()){ + LOG_RANK0(wrank, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + std::ostringstream oss; + oss << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")"; + LOG_RANK0(wrank, "ERROR: %s\n", oss.str().c_str()); + return false; + } + } + + return true; +} + +iosystem_desc_t *get_iosystem(MPI_Comm comm, int wrank, int wsz, int nio_procs) +{ + int ret = PIO_NOERR; + static int iosysid = 1; + iosystem_desc_t *ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)); + if(!ios){ + LOG_RANK0(wrank, "Unable to allocate memory for I/O system"); + return NULL; + } + + ios->iosysid = iosysid++; + ios->union_comm = comm; + ios->num_uniontasks = wsz; + ios->union_rank = wrank; + /* Every proc is a compute proc */ + ios->comp_comm = comm; + ios->num_comptasks = wsz; + ios->comp_rank = wrank; + ios->compproc = true; + + assert(nio_procs <= wsz); + + /* Assign first nio_procs procs as I/O processes */ + int color = (wrank/nio_procs == 0) ? 0 : 1; + + ret = MPI_Comm_split(comm, color, 0, &(ios->io_comm)); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Unable to split comm for creating I/O system"); + free(ios); + return NULL; + } + + ios->num_iotasks = nio_procs; + ios->ioproc = (color == 0) ? true : false; + if(ios->ioproc){ + ret = MPI_Comm_rank(ios->io_comm, &(ios->io_rank)); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Unable to get rank of I/O process"); + free(ios); + return NULL; + } + } + else{ + ios->io_rank = -1; + } + + return ios; +} + +void free_iosystem(iosystem_desc_t *ios){ + if(!ios){ + return; + } + + MPI_Comm_free(&(ios->io_comm)); + free(ios); +} + +int test_c2i_block_data_exp_fillval(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 10; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + const std::size_t FILLVAL_MASK_OFFSET = 3; + const double FILLVAL = 123.456; + const PIO_Offset PIO_COMPMAP_FILLVAL = -1; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), FILLVAL); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + + for(std::size_t i = 0, gstart = ios->io_rank * rearr_iochunk_sz; i < exp_data.size(); i++){ + if((gstart + i) % FILLVAL_MASK_OFFSET == 0){ + exp_data[i] = FILLVAL; + } + } + } + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + std::transform(compmap.cbegin(), compmap.cend(), compmap.begin(), + [FILLVAL_MASK_OFFSET](const PIO_Offset i){ return (i % FILLVAL_MASK_OFFSET == 0) ? PIO_COMPMAP_FILLVAL : i; }); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [PIO_COMPMAP_FILLVAL, FILLVAL](const PIO_Offset i){ return static_cast((i != PIO_COMPMAP_FILLVAL) ? i : FILLVAL); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_c2i_block_data_imp_fillval(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 10; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap; + std::vector sdata; + + std::vector rdata, exp_data; + const std::size_t FILLVAL_MASK_OFFSET = 3; + const double FILLVAL = 123.456; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), FILLVAL); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + + for(std::size_t i = 0, gstart = ios->io_rank * rearr_iochunk_sz; i < exp_data.size(); i++){ + if((gstart + i) % FILLVAL_MASK_OFFSET == 0){ + exp_data[i] = FILLVAL; + } + } + } + + const std::size_t wrank_compmap_end_idx = static_cast(wrank * LOCAL_COMPMAP_SZ + LOCAL_COMPMAP_SZ); + for(std::size_t i = wrank * LOCAL_COMPMAP_SZ; i < wrank_compmap_end_idx; i++){ + if(i % FILLVAL_MASK_OFFSET != 0){ + compmap.push_back(i); + sdata.push_back(static_cast(i)); + } + } + + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_block_data_exp_fillval(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 10; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + const std::size_t FILLVAL_MASK_OFFSET = 3; + const double FILLVAL = 123.456; + const PIO_Offset PIO_COMPMAP_FILLVAL = -1; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), FILLVAL); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + + for(std::size_t i = 0, gstart = ios->io_rank * rearr_iochunk_sz; i < exp_data.size(); i++){ + if((gstart + i) % FILLVAL_MASK_OFFSET == 0){ + exp_data[i] = FILLVAL; + } + } + } + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + std::transform(compmap.cbegin(), compmap.cend(), compmap.begin(), + [FILLVAL_MASK_OFFSET](const PIO_Offset i){ return (i % FILLVAL_MASK_OFFSET == 0) ? PIO_COMPMAP_FILLVAL : i; }); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [PIO_COMPMAP_FILLVAL, FILLVAL](const PIO_Offset i){ return static_cast((i != PIO_COMPMAP_FILLVAL) ? i : FILLVAL); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + + /* Rearrange data from compute processes to I/O processes */ + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (compute to I/O procs)\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), FILLVAL); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_block_data_imp_fillval(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 10; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap; + std::vector sdata; + + std::vector rdata, exp_data; + const std::size_t FILLVAL_MASK_OFFSET = 3; + const double FILLVAL = 123.456; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), FILLVAL); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + + for(std::size_t i = 0, gstart = ios->io_rank * rearr_iochunk_sz; i < exp_data.size(); i++){ + if((gstart + i) % FILLVAL_MASK_OFFSET == 0){ + exp_data[i] = FILLVAL; + } + } + } + + const std::size_t wrank_compmap_end_idx = static_cast(wrank * LOCAL_COMPMAP_SZ + LOCAL_COMPMAP_SZ); + for(std::size_t i = wrank * LOCAL_COMPMAP_SZ; i < wrank_compmap_end_idx; i++){ + if(i % FILLVAL_MASK_OFFSET != 0){ + compmap.push_back(i); + sdata.push_back(static_cast(i)); + } + } + + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + + /* Rearrange data from compute processes to I/O processes */ + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (compute to I/O procs)\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_rev_block_data_rearr(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + //std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + PIO_Offset compmap_val = wrank * LOCAL_COMPMAP_SZ; + std::generate(compmap.begin(), compmap.end(), + [&compmap_val, gdimlen]() mutable { return gdimlen - 1 - compmap_val++; }); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_mrange_block_data_rearr(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int FIRST_RANGE_SZ = 3; + const int SECOND_RANGE_SZ = 1; + const int THIRD_RANGE_SZ = 2; + const int LOCAL_COMPMAP_SZ = FIRST_RANGE_SZ + SECOND_RANGE_SZ + THIRD_RANGE_SZ; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size()); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + std::iota(compmap.begin(), compmap.begin() + FIRST_RANGE_SZ, wrank * FIRST_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ, compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, wsz * FIRST_RANGE_SZ + wrank * SECOND_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, compmap.end(), wsz * (FIRST_RANGE_SZ + SECOND_RANGE_SZ) + wrank * THIRD_RANGE_SZ); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_mrange_oddz_data_rearr(MPI_Comm comm, int wrank, int wsz) +{ + bool is_odd_proc = ((wrank % 2) != 0) ? true : false; + int nodd_procs = wsz/2; + int neven_procs = wsz - nodd_procs; + int ret = PIO_NOERR; + + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int FIRST_RANGE_SZ = 3; + const int SECOND_RANGE_SZ = 1; + const int THIRD_RANGE_SZ = 2; + const int LOCAL_COMPMAP_SZ = FIRST_RANGE_SZ + SECOND_RANGE_SZ + THIRD_RANGE_SZ; + /* Only even procs have data */ + int gdimlen = LOCAL_COMPMAP_SZ * neven_procs; + std::vector compmap; + std::vector sdata; + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + std::iota(exp_data.begin(), exp_data.end(), ios->io_rank * rearr_iochunk_sz); + } + + if(!is_odd_proc){ + compmap.resize(LOCAL_COMPMAP_SZ); + sdata.resize(compmap.size()); + + int neven_procs_before_wrank = wrank - (wrank / 2); + std::iota(compmap.begin(), compmap.begin() + FIRST_RANGE_SZ, neven_procs_before_wrank * FIRST_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ, compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, neven_procs * FIRST_RANGE_SZ + neven_procs_before_wrank * SECOND_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, compmap.end(), neven_procs * (FIRST_RANGE_SZ + SECOND_RANGE_SZ) + neven_procs_before_wrank * THIRD_RANGE_SZ); + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin(), + [](const PIO_Offset i){ return static_cast(i); }); + } + + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + if(!is_odd_proc){ + exp_data.resize(LOCAL_COMPMAP_SZ); + } + else{ + exp_data.clear(); + } + + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), 1); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR, mpierr = MPI_SUCCESS; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + std::vector > > test_funcs = { + {"test_c2i_block_data_exp_fillval", test_c2i_block_data_exp_fillval}, + {"test_c2i_block_data_imp_fillval", test_c2i_block_data_imp_fillval}, + {"test_block_data_exp_fillval", test_block_data_exp_fillval}, + {"test_block_data_imp_fillval", test_block_data_imp_fillval}, + {"test_rev_block_data_rearr", test_rev_block_data_rearr}, + {"test_mrange_block_data_rearr", test_mrange_block_data_rearr}, + {"test_mrange_oddz_data_rearr", test_mrange_oddz_data_rearr} + }; + + for(std::size_t tid = 0; tid < test_funcs.size(); tid++){ + try{ + ret = test_funcs[tid].second(comm, wrank, wsz); + } + catch(...){ + ret = PIO_EINTERNAL; + nerrs++; + } + int lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce(&ret, &lfail, 1, MPI_INT, MPI_SUM, 0, comm); + if(mpierr != MPI_SUCCESS){ + LOG_RANK0(wrank, "Test Driver failed: Unable to calculate total num errors\n"); + } + if(ret != 0){ + std::string non_root_fail_msg = std::string(", failed on ") + std::to_string(ret) + std::string(" non-root processes"); + LOG_RANK0(wrank, "%s() FAILED (ret = %d%s)\n", test_funcs[tid].first.c_str(), ret, (lfail) ? "" : non_root_fail_msg.c_str()); + nerrs++; + } + else{ + LOG_RANK0(wrank, "%s() PASSED\n", test_funcs[tid].first.c_str()); + } + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + return FAIL; + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spio_rearr_contig_nvars.cpp b/tests/cunit/test_spio_rearr_contig_nvars.cpp new file mode 100644 index 00000000000..2002b78dfb7 --- /dev/null +++ b/tests/cunit/test_spio_rearr_contig_nvars.cpp @@ -0,0 +1,671 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "pio_rearr_contig.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0){ \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +template +bool cmp_result(int wrank, const std::vector &res, const std::vector &exp) +{ + + if(res.size() != exp.size()){ + LOG_RANK0(wrank, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + std::ostringstream oss; + oss << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")"; + LOG_RANK0(wrank, "ERROR: %s\n", oss.str().c_str()); + return false; + } + } + + return true; +} + +iosystem_desc_t *get_iosystem(MPI_Comm comm, int wrank, int wsz, int nio_procs) +{ + int ret = PIO_NOERR; + static int iosysid = 1; + iosystem_desc_t *ios = (iosystem_desc_t *) calloc(1, sizeof(iosystem_desc_t)); + if(!ios){ + LOG_RANK0(wrank, "Unable to allocate memory for I/O system"); + return NULL; + } + + ios->iosysid = iosysid++; + ios->union_comm = comm; + ios->num_uniontasks = wsz; + ios->union_rank = wrank; + /* Every proc is a compute proc */ + ios->comp_comm = comm; + ios->num_comptasks = wsz; + ios->comp_rank = wrank; + ios->compproc = true; + + assert(nio_procs <= wsz); + + /* Assign first nio_procs procs as I/O processes */ + int color = (wrank/nio_procs == 0) ? 0 : 1; + + ret = MPI_Comm_split(comm, color, 0, &(ios->io_comm)); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Unable to split comm for creating I/O system"); + free(ios); + return NULL; + } + + ios->num_iotasks = nio_procs; + ios->ioproc = (color == 0) ? true : false; + if(ios->ioproc){ + ret = MPI_Comm_rank(ios->io_comm, &(ios->io_rank)); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Unable to get rank of I/O process"); + free(ios); + return NULL; + } + } + else{ + ios->io_rank = -1; + } + + return ios; +} + +void free_iosystem(iosystem_desc_t *ios){ + if(!ios){ + return; + } + + MPI_Comm_free(&(ios->io_comm)); + free(ios); +} + +int test_c2i_block_data_rearr_nvars(MPI_Comm comm, int wrank, int wsz, int nvars) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size() * nvars); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t one_var_rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + std::size_t rdata_sz = one_var_rdata_sz * nvars; + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + for(int i = 0; i < nvars; i++){ + std::iota(exp_data.begin() + i * one_var_rdata_sz, exp_data.begin() + (i + 1) * one_var_rdata_sz, ios->io_rank * rearr_iochunk_sz); + } + } + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + + for(int i = 0; i < nvars; i++){ + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin() + i * LOCAL_COMPMAP_SZ, + [](const PIO_Offset i){ return static_cast(i); }); + } + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_block_data_rearr_nvars(MPI_Comm comm, int wrank, int wsz, int nvars) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size() * nvars); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t one_var_rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + std::size_t rdata_sz = one_var_rdata_sz * nvars; + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + for(int i = 0; i < nvars; i++){ + std::iota(exp_data.begin() + i * one_var_rdata_sz, exp_data.begin() + (i + 1) * one_var_rdata_sz, ios->io_rank * rearr_iochunk_sz); + } + } + + std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + + for(int i = 0; i < nvars; i++){ + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin() + i * LOCAL_COMPMAP_SZ, + [](const PIO_Offset i){ return static_cast(i); }); + } + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ * nvars); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_rev_block_data_rearr_nvars(MPI_Comm comm, int wrank, int wsz, int nvars) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int LOCAL_COMPMAP_SZ = 4; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size() * nvars); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t one_var_rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + std::size_t rdata_sz = one_var_rdata_sz * nvars; + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + for(int i = 0; i < nvars; i++){ + std::iota(exp_data.begin() + i * one_var_rdata_sz, exp_data.begin() + (i + 1) * one_var_rdata_sz, ios->io_rank * rearr_iochunk_sz); + } + } + + //std::iota(compmap.begin(), compmap.end(), wrank * LOCAL_COMPMAP_SZ); + PIO_Offset compmap_val = wrank * LOCAL_COMPMAP_SZ; + std::generate(compmap.begin(), compmap.end(), + [&compmap_val, gdimlen]() mutable { return gdimlen - 1 - compmap_val++; }); + + for(int i = 0; i < nvars; i++){ + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin() + i * LOCAL_COMPMAP_SZ, + [](const PIO_Offset i){ return static_cast(i); }); + } + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ * nvars); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_mrange_block_data_rearr_nvars(MPI_Comm comm, int wrank, int wsz, int nvars) +{ + int ret = PIO_NOERR; + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int FIRST_RANGE_SZ = 3; + const int SECOND_RANGE_SZ = 1; + const int THIRD_RANGE_SZ = 2; + const int LOCAL_COMPMAP_SZ = FIRST_RANGE_SZ + SECOND_RANGE_SZ + THIRD_RANGE_SZ; + int gdimlen = LOCAL_COMPMAP_SZ * wsz; + std::vector compmap(LOCAL_COMPMAP_SZ); + std::vector sdata(compmap.size() * nvars); + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t one_var_rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + std::size_t rdata_sz = one_var_rdata_sz * nvars; + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + for(int i = 0; i < nvars; i++){ + std::iota(exp_data.begin() + i * one_var_rdata_sz, exp_data.begin() + (i + 1) * one_var_rdata_sz, ios->io_rank * rearr_iochunk_sz); + } + } + + std::iota(compmap.begin(), compmap.begin() + FIRST_RANGE_SZ, wrank * FIRST_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ, compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, wsz * FIRST_RANGE_SZ + wrank * SECOND_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, compmap.end(), wsz * (FIRST_RANGE_SZ + SECOND_RANGE_SZ) + wrank * THIRD_RANGE_SZ); + + for(int i = 0; i < nvars; i++){ + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin() + i * LOCAL_COMPMAP_SZ, + [](const PIO_Offset i){ return static_cast(i); }); + } + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + exp_data.resize(LOCAL_COMPMAP_SZ * nvars); + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_mrange_oddz_data_rearr_nvars(MPI_Comm comm, int wrank, int wsz, int nvars) +{ + bool is_odd_proc = ((wrank % 2) != 0) ? true : false; + int nodd_procs = wsz/2; + int neven_procs = wsz - nodd_procs; + int ret = PIO_NOERR; + + int nio_procs = (wsz/2 > 0) ? wsz/2 : wsz; + iosystem_desc_t *ios = get_iosystem(comm, wrank, wsz, nio_procs); + if(!ios){ + LOG_RANK0(wrank, "Unable to get I/O system"); + return PIO_EINTERNAL; + } + + try{ + SPIO::DataRearr::Contig_rearr rearr(ios); + const int FIRST_RANGE_SZ = 3; + const int SECOND_RANGE_SZ = 1; + const int THIRD_RANGE_SZ = 2; + const int LOCAL_COMPMAP_SZ = FIRST_RANGE_SZ + SECOND_RANGE_SZ + THIRD_RANGE_SZ; + /* Only even procs have data */ + int gdimlen = LOCAL_COMPMAP_SZ * neven_procs; + std::vector compmap; + std::vector sdata; + + std::vector rdata, exp_data; + + if(ios->ioproc){ + std::size_t rearr_iochunk_sz = gdimlen/nio_procs; + std::size_t one_var_rdata_sz = (ios->io_rank != (nio_procs - 1)) ? rearr_iochunk_sz : (gdimlen - (ios->io_rank * rearr_iochunk_sz)); + std::size_t rdata_sz = one_var_rdata_sz * nvars; + + rdata.resize(rdata_sz); + std::fill(rdata.begin(), rdata.end(), PIO_FILL_DOUBLE); + + exp_data.resize(rdata_sz); + for(int i = 0; i < nvars; i++){ + std::iota(exp_data.begin() + i * one_var_rdata_sz, exp_data.begin() + (i + 1) * one_var_rdata_sz, ios->io_rank * rearr_iochunk_sz); + } + } + + if(!is_odd_proc){ + compmap.resize(LOCAL_COMPMAP_SZ); + sdata.resize(compmap.size() * nvars); + + int neven_procs_before_wrank = wrank - (wrank / 2); + std::iota(compmap.begin(), compmap.begin() + FIRST_RANGE_SZ, neven_procs_before_wrank * FIRST_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ, compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, neven_procs * FIRST_RANGE_SZ + neven_procs_before_wrank * SECOND_RANGE_SZ); + std::iota(compmap.begin() + FIRST_RANGE_SZ + SECOND_RANGE_SZ, compmap.end(), neven_procs * (FIRST_RANGE_SZ + SECOND_RANGE_SZ) + neven_procs_before_wrank * THIRD_RANGE_SZ); + + for(int i = 0; i < nvars; i++){ + std::transform(compmap.cbegin(), compmap.cend(), sdata.begin() + i * LOCAL_COMPMAP_SZ, + [](const PIO_Offset i){ return static_cast(i); }); + } + } + + ret = rearr.init(PIO_DOUBLE, compmap.data(), compmap.size(), &gdimlen, 1, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Initializing contig rearranger failed"); + return PIO_EINTERNAL; + } + ret = rearr.rearrange_comp2io(sdata.data(), sdata.size() * sizeof(double), + rdata.data(), rdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement\n"); + return PIO_EINTERNAL; + } + + /* Rearrange data from I/O processes to compute processes */ + if(!is_odd_proc){ + exp_data.resize(LOCAL_COMPMAP_SZ * nvars); + } + else{ + exp_data.clear(); + } + + assert(exp_data.size() == sdata.size()); + std::copy(sdata.begin(), sdata.end(), exp_data.begin()); + std::fill(sdata.begin(), sdata.end(), PIO_FILL_DOUBLE); + + ret = rearr.rearrange_io2comp(rdata.data(), rdata.size() * sizeof(double), + sdata.data(), sdata.size() * sizeof(double), nvars); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed (I/O to compute procs)"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, sdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/invalid data received after data rearrangement (I/O to compute procs)\n"); + return PIO_EINTERNAL; + } + + ret = rearr.finalize(); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Finalizing contig rearranger failed"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Rearranging data using contig rearranger failed"); + return PIO_EINTERNAL; + } + + free_iosystem(ios); + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR, mpierr = MPI_SUCCESS; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + std::vector > > test_funcs = { + {"test_c2i_block_data_rearr_nvars", test_c2i_block_data_rearr_nvars}, + {"test_block_data_rearr_nvars", test_block_data_rearr_nvars}, + {"test_rev_block_data_rearr_nvars", test_rev_block_data_rearr_nvars}, + {"test_mrange_block_data_rearr_nvars", test_mrange_block_data_rearr_nvars}, + {"test_mrange_oddz_data_rearr_nvars", test_mrange_oddz_data_rearr_nvars} + }; + + const int MAX_NVARS = 6; + for(std::size_t tid = 0; tid < test_funcs.size(); tid++){ + for(int i = 1; i < MAX_NVARS; i++){ + try{ + ret = test_funcs[tid].second(comm, wrank, wsz, i); + } + catch(...){ + ret = PIO_EINTERNAL; + nerrs++; + } + int lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce(&ret, &lfail, 1, MPI_INT, MPI_SUM, 0, comm); + if(mpierr != MPI_SUCCESS){ + LOG_RANK0(wrank, "Test Driver failed: Unable to calculate total num errors\n"); + } + if(ret != 0){ + std::string non_root_fail_msg = std::string(", failed on ") + std::to_string(ret) + std::string(" non-root processes"); + LOG_RANK0(wrank, "%s(nvars=%d) FAILED (ret = %d%s)\n", test_funcs[tid].first.c_str(), i, ret, (lfail) ? "" : non_root_fail_msg.c_str()); + nerrs++; + } + else{ + LOG_RANK0(wrank, "%s(nvars=%d) PASSED\n", test_funcs[tid].first.c_str(), i); + } + } + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + return FAIL; + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spio_rearr_utils_alltoall.cpp b/tests/cunit/test_spio_rearr_utils_alltoall.cpp new file mode 100644 index 00000000000..a815e7492e6 --- /dev/null +++ b/tests/cunit/test_spio_rearr_utils_alltoall.cpp @@ -0,0 +1,453 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "pio_rearr_utils.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0){ \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +template +bool cmp_result(int wrank, const std::vector &res, const std::vector &exp) +{ + + if(res.size() != exp.size()){ + LOG_RANK0(wrank, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + std::ostringstream oss; + oss << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")"; + LOG_RANK0(wrank, "ERROR: %s\n", oss.str().c_str()); + return false; + } + } + + return true; +} + +/* Test alltoall() with each process sending the same sized block of data */ +int test_alltoall_eq_sized_type_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + std::vector sdata(LOCAL_SZ * wsz); + std::vector rdata(LOCAL_SZ * wsz, FILLVAL); + std::vector exp_data(LOCAL_SZ * wsz, FILLVAL); + rearr_comm_fc_opt_t ropts = {true, true, -1}; + + const double ldata_start = wrank * LOCAL_SZ; + double ldata = ldata_start; + int ldata_idx = 0; + std::generate(sdata.begin(), sdata.end(), + [&ldata, &ldata_idx, ldata_start] () mutable { + if(ldata_idx % LOCAL_SZ == 0){ + ldata = ldata_start; + } + ldata_idx++; + return ldata++; + } + ); + + std::iota(exp_data.begin(), exp_data.end(), 0); + + try{ + ret = SPIO_Util::Rearr_Util::alltoall(sdata.data(), LOCAL_SZ, MPI_DOUBLE, + rdata.data(), LOCAL_SZ, MPI_DOUBLE, comm, &ropts); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Alltoall failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data received in alltoall()\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Alltoall failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +/* Test alltoall() with each process sending a contig block of data */ +int test_alltoall_contig_to_type_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + std::vector sdata(LOCAL_SZ * wsz); + std::vector rdata(LOCAL_SZ * wsz, FILLVAL); + std::vector exp_data(LOCAL_SZ * wsz, FILLVAL); + + MPI_Datatype stype = MPI_DATATYPE_NULL; + rearr_comm_fc_opt_t ropts = {true, true, -1}; + + const double ldata_start = wrank * LOCAL_SZ; + double ldata = ldata_start; + int ldata_idx = 0; + std::generate(sdata.begin(), sdata.end(), + [&ldata, &ldata_idx, ldata_start] () mutable { + if(ldata_idx % LOCAL_SZ == 0){ + ldata = ldata_start; + } + ldata_idx++; + return ldata++; + } + ); + + ret = MPI_Type_contiguous(LOCAL_SZ, MPI_DOUBLE, &stype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&stype); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Creating contig MPI type to send data failed\n"); + return PIO_EINTERNAL; + } + + std::iota(exp_data.begin(), exp_data.end(), 0); + + try{ + ret = SPIO_Util::Rearr_Util::alltoall(sdata.data(), 1, stype, + rdata.data(), LOCAL_SZ, MPI_DOUBLE, comm, &ropts); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Alltoall failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data received in alltoall()\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Alltoall failed\n"); + return PIO_EINTERNAL; + } + + MPI_Type_free(&stype); + + return PIO_NOERR; +} + +/* Test alltoall() with each process receives a contig block of data */ +int test_alltoall_type_to_contig_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + std::vector sdata(LOCAL_SZ * wsz); + std::vector rdata(LOCAL_SZ * wsz, FILLVAL); + std::vector exp_data(LOCAL_SZ * wsz, FILLVAL); + + MPI_Datatype rtype = MPI_DATATYPE_NULL; + rearr_comm_fc_opt_t ropts = {true, true, -1}; + + const double ldata_start = wrank * LOCAL_SZ; + double ldata = ldata_start; + int ldata_idx = 0; + std::generate(sdata.begin(), sdata.end(), + [&ldata, &ldata_idx, ldata_start] () mutable { + if(ldata_idx % LOCAL_SZ == 0){ + ldata = ldata_start; + } + ldata_idx++; + return ldata++; + } + ); + + ret = MPI_Type_contiguous(LOCAL_SZ, MPI_DOUBLE, &rtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&rtype); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "Creating contig MPI type to send data failed\n"); + return PIO_EINTERNAL; + } + + std::iota(exp_data.begin(), exp_data.end(), 0); + + try{ + ret = SPIO_Util::Rearr_Util::alltoall(sdata.data(), LOCAL_SZ, MPI_DOUBLE, + rdata.data(), 1, rtype, comm, &ropts); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Alltoall failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data received in alltoall()\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Alltoall failed\n"); + return PIO_EINTERNAL; + } + + MPI_Type_free(&rtype); + + return PIO_NOERR; +} + +/* Test alltoallw() with each process sending the same sized block of data */ +int test_alltoallw_eq_sized_type_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + std::vector sdata(LOCAL_SZ * wsz); + std::vector rdata(LOCAL_SZ * wsz, FILLVAL); + std::vector exp_data(LOCAL_SZ * wsz, FILLVAL); + rearr_comm_fc_opt_t ropts = {true, true, -1}; + + std::vector srcount(wsz, LOCAL_SZ); + std::vector srdispls(wsz, 0); + std::vector srtypes(wsz, MPI_DOUBLE); + int type_sz = 0; + + ret = MPI_Type_size(MPI_DOUBLE, &type_sz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to query size of MPI_DOUBLE\n"); + return PIO_EINTERNAL; + } + + const double ldata_start = wrank * LOCAL_SZ; + double ldata = ldata_start; + int ldata_idx = 0; + std::generate(sdata.begin(), sdata.end(), + [&ldata, &ldata_idx, ldata_start] () mutable { + if(ldata_idx % LOCAL_SZ == 0){ + ldata = ldata_start; + } + ldata_idx++; + return ldata++; + } + ); + + int displ = -LOCAL_SZ; + std::generate(srdispls.begin(), srdispls.end(), + [&displ, type_sz] () mutable { + displ += LOCAL_SZ; + return displ * type_sz; + } + ); + + std::iota(exp_data.begin(), exp_data.end(), 0); + + try{ + ret = SPIO_Util::Rearr_Util::alltoallw( + sdata.data(), srcount.data(), srdispls.data(), srtypes.data(), + rdata.data(), srcount.data(), srdispls.data(), srtypes.data(), + comm, &ropts); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Alltoallw failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data received in alltoallw()\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Alltoallw failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +/* Test alltoallw() with each process sending uneven sized block of data */ +int test_alltoallw_uneven_block_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const double FILLVAL = -1.0; + const int LOCAL_SZ = wrank + 1; + std::vector sdata(LOCAL_SZ); + std::vector rdata((wsz * (wsz + 1))/2, FILLVAL); + std::vector exp_data((wsz * (wsz + 1))/2, FILLVAL); + + std::vector scounts(wsz), sdispls(wsz), rcounts(wsz), rdispls(wsz); + std::vector types(wsz, MPI_DOUBLE); + int type_sz = 0; + rearr_comm_fc_opt_t ropts = {true, true, -1}; + + ret = MPI_Type_size(MPI_DOUBLE, &type_sz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to query size of MPI_DOUBLE\n"); + return PIO_EINTERNAL; + } + + /* sdata[] in proc i contains i * wsz consecutive numbers, + * e.g.: wsz = 3 + * sdata[] at rank 0 is {0} => rank 0 sends {0} to all procs + * sdata[] at rank 1 is {1,2} => rank 1 sends {1,2} to all procs + * sdata[] at rank 2 is {3,4,5} => rank 2 sends {3,4,5} to all procs + * Number of elements in all procs <= wrank, = (wrank + 1) * (wrank + 2)/2 + * Number of elements in proc wrank = (wrank + 1) + * => Proc wrank starts at index (wrank * (wrank + 1)) / 2) + * sdata[] at rank wrank is {(wrank * (wrank + 1)) / 2), + * (wrank * (wrank + 1)) / 2) + 1, ..., + * (wrank * (wrank + 1)) / 2) + wrank - 1} + */ + std::iota(sdata.begin(), sdata.end(), (wrank * (wrank + 1)) / 2); + std::iota(exp_data.begin(), exp_data.end(), 0); + + for(int i = 0; i < wsz; i++){ + scounts[i] = wrank + 1; + sdispls[i] = 0; + + rcounts[i] = i + 1; + rdispls[i] = (i * (i + 1))/2 * type_sz; + } + + try{ + ret = SPIO_Util::Rearr_Util::alltoallw(sdata.data(), scounts.data(), sdispls.data(), types.data(), + rdata.data(), rcounts.data(), rdispls.data(), types.data(), comm, &ropts); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Alltoallw failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, rdata, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in alltoallw buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Alltoallw failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR, mpierr = MPI_SUCCESS; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + std::vector > > test_funcs = { + {"test_alltoall_eq_sized_type_decomp", test_alltoall_eq_sized_type_decomp}, + {"test_alltoall_contig_to_type_decomp", test_alltoall_contig_to_type_decomp}, + {"test_alltoall_type_to_contig_decomp", test_alltoall_type_to_contig_decomp}, + {"test_alltoallw_eq_sized_type_decomp", test_alltoallw_eq_sized_type_decomp}, + {"test_alltoallw_uneven_block_decomp", test_alltoallw_uneven_block_decomp} + }; + + for(std::size_t tid = 0; tid < test_funcs.size(); tid++){ + try{ + ret = test_funcs[tid].second(comm, wrank, wsz); + } + catch(...){ + ret = PIO_EINTERNAL; + nerrs++; + } + int lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce(&ret, &lfail, 1, MPI_INT, MPI_SUM, 0, comm); + if(mpierr != MPI_SUCCESS){ + LOG_RANK0(wrank, "Test Driver failed: Unable to calculate total num errors\n"); + } + if(ret != 0){ + std::string non_root_fail_msg = std::string(", failed on ") + std::to_string(ret) + std::string(" non-root processes"); + LOG_RANK0(wrank, "%s() FAILED (ret = %d%s)\n", test_funcs[tid].first.c_str(), ret, (lfail) ? "" : non_root_fail_msg.c_str()); + nerrs++; + } + else{ + LOG_RANK0(wrank, "%s() PASSED\n", test_funcs[tid].first.c_str()); + } + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spio_rearr_utils_gather.cpp b/tests/cunit/test_spio_rearr_utils_gather.cpp new file mode 100644 index 00000000000..13e5d204039 --- /dev/null +++ b/tests/cunit/test_spio_rearr_utils_gather.cpp @@ -0,0 +1,468 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "pio_rearr_utils.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +template +bool cmp_result(int wrank, const std::vector &res, const std::vector &exp) +{ + + if(res.size() != exp.size()){ + LOG_RANK0(wrank, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + if(wrank == 0){ + std::cerr << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")\n"; + } + return false; + } + } + + return true; +} + +/* Test gatherw() with each process sending the same sized block of data */ +int test_gatherw_double_block_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + std::vector local_data(LOCAL_SZ); + std::vector gather_data; + + std::vector recvcounts, rdispls; + std::vector recvtypes; + + std::iota(local_data.begin(), local_data.end(), wrank * LOCAL_SZ); + + if(wrank == ROOT_RANK){ + gather_data.resize(wsz * LOCAL_SZ); + recvcounts.resize(wsz); + rdispls.resize(wsz); + recvtypes.resize(wsz); + for(std::size_t i = 0; i < gather_data.size(); i++){ + gather_data[i] = FILLVAL; + } + for(int i = 0; i < wsz; i++){ + recvcounts[i] = LOCAL_SZ; + rdispls[i] = i * LOCAL_SZ * sizeof(double); + recvtypes[i] = MPI_DOUBLE; + } + } + + try{ + ret = SPIO_Util::Rearr_Util::gatherw(local_data.data(), LOCAL_SZ, MPI_DOUBLE, + gather_data.data(), recvcounts, rdispls, recvtypes, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + std::vector expected_gather_data(wsz * LOCAL_SZ); + std::iota(expected_gather_data.begin(), expected_gather_data.end(), 0); + if(!cmp_result(wrank, gather_data, expected_gather_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in gather buffer\n"); + return PIO_EINTERNAL; + } + } + } + catch(...){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +/* Test gatherw() with each process sending uneven sized block of data */ +int test_gatherw_double_uneven_block_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + const int LOCAL_SZ = wrank; + std::vector local_data(LOCAL_SZ); + std::vector gather_data; + + std::vector recvcounts, rdispls; + std::vector recvtypes; + + /* local_data[] in proc i contains i consecutive numbers, + * e.g.: wsz = 5 + * local_data[] at rank 0 is empty + * local_data[] at rank 1 is {0} + * local_data[] at rank 2 is {1,2} + * local_data[] at rank 3 is {3,4,5} + * local_data[] at rank 4 is {6,7,8,9} + * local_data[] at rank 5 is {10,11,12,13,14} + * Number of elements in all procs <= wrank, = wrank * (wrank + 1)/2 + * Number of elements in proc wrank = wrank + * => Proc wrank starts at index (wrank * (wrank + 1)) / 2 - wrank) + * local_data[] at rank wrank is {(wrank * (wrank + 1)) / 2 - wrank), + * (wrank * (wrank + 1)) / 2 - wrank) + 1, ..., + * (wrank * (wrank + 1)) / 2 - wrank) + wrank - 1} + */ + std::iota(local_data.begin(), local_data.end(), (wrank * (wrank + 1)) / 2 - wrank); + + if(wrank == ROOT_RANK){ + gather_data.resize((wsz * (wsz - 1))/2); + recvcounts.resize(wsz); + rdispls.resize(wsz); + recvtypes.resize(wsz); + for(std::size_t i = 0; i < gather_data.size(); i++){ + gather_data[i] = FILLVAL; + } + for(int i = 0; i < wsz; i++){ + recvcounts[i] = i; + /* Number of elements in all procs <= i, = i * (i + 1)/2 + * Number of elements in proc i = i + * => Proc i starts at index (i * (i + 1)) / 2 - i) + */ + rdispls[i] = ((i * (i + 1)) / 2 - i) * sizeof(double); + recvtypes[i] = MPI_DOUBLE; + } + } + + try{ + ret = SPIO_Util::Rearr_Util::gatherw(local_data.data(), LOCAL_SZ, MPI_DOUBLE, + gather_data.data(), recvcounts, rdispls, recvtypes, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + std::vector expected_gather_data((wsz * (wsz - 1))/2); + std::iota(expected_gather_data.begin(), expected_gather_data.end(), 0); + if(!cmp_result(wrank, gather_data, expected_gather_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in gather buffer\n"); + return PIO_EINTERNAL; + } + } + } + catch(...){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +/* Test gatherw() with each process sending the same sized block of data using contig type */ +int test_gatherw_contig_block_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + std::vector local_data(LOCAL_SZ); + std::vector gather_data; + + std::vector recvcounts, rdispls; + MPI_Datatype sendtype = MPI_DATATYPE_NULL; + std::vector recvtypes; + + std::iota(local_data.begin(), local_data.end(), wrank * LOCAL_SZ); + + ret = MPI_Type_contiguous(LOCAL_SZ, MPI_DOUBLE, &sendtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&sendtype); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to create MPI contig type to send double data (sz = %d)\n", LOCAL_SZ); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + gather_data.resize(wsz * LOCAL_SZ); + recvcounts.resize(wsz); + rdispls.resize(wsz); + recvtypes.resize(wsz); + for(std::size_t i = 0; i < gather_data.size(); i++){ + gather_data[i] = FILLVAL; + } + for(int i = 0; i < wsz; i++){ + recvcounts[i] = 1; + rdispls[i] = i * LOCAL_SZ * sizeof(double); + + ret = MPI_Type_dup(sendtype, &(recvtypes[i])); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&(recvtypes[i])); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to create MPI dup of send type to recv doubles\n"); + return PIO_EINTERNAL; + } + } + } + + try{ + ret = SPIO_Util::Rearr_Util::gatherw(local_data.data(), 1, sendtype, + gather_data.data(), recvcounts, rdispls, recvtypes, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + std::vector expected_gather_data(wsz * LOCAL_SZ); + std::iota(expected_gather_data.begin(), expected_gather_data.end(), 0); + if(!cmp_result(wrank, gather_data, expected_gather_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in gather buffer\n"); + return PIO_EINTERNAL; + } + } + } + catch(...){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + if(sendtype != MPI_DATATYPE_NULL){ + MPI_Type_free(&sendtype); + } + for(std::size_t i = 0; i < recvtypes.size(); i++){ + if(recvtypes[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(recvtypes[i])); + } + } + + return PIO_NOERR; +} + +/* Test gatherw() with each process sending contig block of data and gathered using an indexed type */ +int test_gatherw_contig_indexed(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + std::vector local_data(LOCAL_SZ); + std::vector gather_data; + + std::vector recvcounts, rdispls; + MPI_Datatype sendtype = MPI_DATATYPE_NULL; + std::vector recvtypes; + + /* Each process contains LOCAL_SZ doubles. + * rank0 contains {0, 0+wsz, 0+2*wsz,...} + * rank1 contains {1, 1+wsz, 1+2*wsz,...} + * ... + * ranki contains {i, i+wsz, i+2*wsz,...} + */ + int i = 0; + std::generate(local_data.begin(), local_data.end(), + [wrank, wsz, &i](){ return static_cast(i++ * wsz + wrank); } ); + + ret = MPI_Type_contiguous(LOCAL_SZ, MPI_DOUBLE, &sendtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&sendtype); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to create MPI contig type to send double data (sz = %d)\n", LOCAL_SZ); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + gather_data.resize(wsz * LOCAL_SZ); + recvcounts.resize(wsz); + rdispls.resize(wsz); + recvtypes.resize(wsz); + for(std::size_t i = 0; i < gather_data.size(); i++){ + gather_data[i] = FILLVAL; + } + for(int i = 0; i < wsz; i++){ + recvcounts[i] = 1; + rdispls[i] = i * sizeof(double); + + /* Receive the data such that the elements are in a sorted order - ascending */ + /* Receive data in rank0 {0, 0+wsz, 0+2*wz,...} at indices 0, 0+wsz, 0+2*wsz,... */ + std::vector elem_counts(LOCAL_SZ, 1); + std::vector elem_rdispls(LOCAL_SZ); + + int j = 0; + std::generate(elem_rdispls.begin(), elem_rdispls.end(), + [&j, wsz](){ return static_cast(j++ * wsz); } ); + + ret = MPI_Type_indexed(LOCAL_SZ, elem_counts.data(), elem_rdispls.data(), + MPI_DOUBLE, &(recvtypes[i])); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&(recvtypes[i])); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to create MPI dup of send type to recv doubles\n"); + return PIO_EINTERNAL; + } + } + } + + try{ + ret = SPIO_Util::Rearr_Util::gatherw(local_data.data(), 1, sendtype, + gather_data.data(), recvcounts, rdispls, recvtypes, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + std::vector expected_gather_data(wsz * LOCAL_SZ); + std::iota(expected_gather_data.begin(), expected_gather_data.end(), 0); + if(!cmp_result(wrank, gather_data, expected_gather_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in gather buffer\n"); + return PIO_EINTERNAL; + } + } + } + catch(...){ + LOG_RANK0(wrank, "Gathering data failed\n"); + return PIO_EINTERNAL; + } + + if(sendtype != MPI_DATATYPE_NULL){ + MPI_Type_free(&sendtype); + } + for(std::size_t i = 0; i < recvtypes.size(); i++){ + if(recvtypes[i] != MPI_DATATYPE_NULL){ + MPI_Type_free(&(recvtypes[i])); + } + } + + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR, mpierr = MPI_SUCCESS; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + std::vector > > test_funcs = { + {"test_gatherw_double_block_decomp", test_gatherw_double_block_decomp}, + {"test_gatherw_double_uneven_block_decomp", test_gatherw_double_uneven_block_decomp}, + {"test_gatherw_contig_block_decomp", test_gatherw_contig_block_decomp}, + {"test_gatherw_contig_indexed", test_gatherw_contig_indexed} + }; + + for(std::size_t tid = 0; tid < test_funcs.size(); tid++){ + try{ + ret = test_funcs[tid].second(comm, wrank, wsz); + } + catch(...){ + ret = PIO_EINTERNAL; + nerrs++; + } + int lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce(&ret, &lfail, 1, MPI_INT, MPI_SUM, 0, comm); + if(mpierr != MPI_SUCCESS){ + LOG_RANK0(wrank, "Test Driver failed: Unable to calculate total num errors\n"); + } + if(ret != 0){ + std::string non_root_fail_msg = std::string(", failed on ") + std::to_string(ret) + std::string(" non-root processes"); + LOG_RANK0(wrank, "%s() FAILED (ret = %d%s)\n", test_funcs[tid].first.c_str(), ret, (lfail) ? "" : non_root_fail_msg.c_str()); + nerrs++; + } + else{ + LOG_RANK0(wrank, "%s() PASSED\n", test_funcs[tid].first.c_str()); + } + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spio_rearr_utils_scatter.cpp b/tests/cunit/test_spio_rearr_utils_scatter.cpp new file mode 100644 index 00000000000..fad5ce875bf --- /dev/null +++ b/tests/cunit/test_spio_rearr_utils_scatter.cpp @@ -0,0 +1,457 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "pio_rearr_utils.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +template +bool cmp_result(int wrank, const std::vector &res, const std::vector &exp) +{ + + if(res.size() != exp.size()){ + LOG_RANK0(wrank, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + if(wrank == 0){ + std::cerr << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")\n"; + } + return false; + } + } + + return true; +} + +/* Test scatterw() with each process receiving the same sized block of data */ +int test_scatterw_double_block_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + std::vector local_data(LOCAL_SZ); + std::vector scatter_data; + std::vector exp_data(LOCAL_SZ); + + int recvcount = LOCAL_SZ; + MPI_Datatype recvtype = MPI_DOUBLE; + + std::vector sendcounts, sdispls; + std::vector sendtypes; + + std::fill(local_data.begin(), local_data.end(), FILLVAL); + std::iota(exp_data.begin(), exp_data.end(), wrank * LOCAL_SZ); + + if(wrank == ROOT_RANK){ + scatter_data.resize(wsz * LOCAL_SZ); + std::iota(scatter_data.begin(), scatter_data.end(), 0); + + sendcounts.resize(wsz); + sdispls.resize(wsz); + sendtypes.resize(wsz); + + for(int i = 0; i < wsz; i++){ + sendcounts[i] = LOCAL_SZ; + sdispls[i] = i * LOCAL_SZ * sizeof(double); + sendtypes[i] = MPI_DOUBLE; + } + } + + try{ + ret = SPIO_Util::Rearr_Util::scatterw(scatter_data.data(), sendcounts, + sdispls, sendtypes, + local_data.data(), recvcount, recvtype, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, local_data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in scatter buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +/* Test scatterw() with each process sending uneven sized block of data */ +int test_scatterw_double_uneven_block_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + const int LOCAL_SZ = wrank; + std::vector local_data(LOCAL_SZ); + std::vector scatter_data; + std::vector exp_data(LOCAL_SZ); + + int recvcount = LOCAL_SZ; + MPI_Datatype recvtype = MPI_DOUBLE; + + std::vector sendcounts, sdispls; + std::vector sendtypes; + + /* local_data[] in proc i contains i consecutive numbers, + * e.g.: wsz = 5, after scatter, + * local_data[] at rank 0 is empty + * local_data[] at rank 1 is {0} + * local_data[] at rank 2 is {1,2} + * local_data[] at rank 3 is {3,4,5} + * local_data[] at rank 4 is {6,7,8,9} + * local_data[] at rank 5 is {10,11,12,13,14} + * Number of elements in all procs <= wrank, = wrank * (wrank + 1)/2 + * Number of elements in proc wrank = wrank + * => Proc wrank starts at index (wrank * (wrank + 1)) / 2 - wrank) + * local_data[] at rank wrank is {(wrank * (wrank + 1)) / 2 - wrank), + * (wrank * (wrank + 1)) / 2 - wrank) + 1, ..., + * (wrank * (wrank + 1)) / 2 - wrank) + wrank - 1} + */ + std::fill(local_data.begin(), local_data.end(), FILLVAL); + std::iota(exp_data.begin(), exp_data.end(), (wrank * (wrank + 1)) / 2 - wrank); + + if(wrank == ROOT_RANK){ + scatter_data.resize((wsz * (wsz - 1))/2); + std::iota(scatter_data.begin(), scatter_data.end(), 0); + + sendcounts.resize(wsz); + sdispls.resize(wsz); + sendtypes.resize(wsz); + + for(int i = 0; i < wsz; i++){ + sendcounts[i] = i; + /* Number of elements in all procs <= i, = i * (i + 1)/2 + * Number of elements in proc i = i + * => Proc i starts at index (i * (i + 1)) / 2 - i) + */ + sdispls[i] = ((i * (i + 1)) / 2 - i) * sizeof(double); + sendtypes[i] = MPI_DOUBLE; + } + } + + try{ + ret = SPIO_Util::Rearr_Util::scatterw(scatter_data.data(), sendcounts, + sdispls, sendtypes, + local_data.data(), recvcount, recvtype, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, local_data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in scatter buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +/* Test scatterw() with each process receiving the same sized block of data using contig type */ +int test_scatterw_contig_block_decomp(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + std::vector local_data(LOCAL_SZ); + std::vector scatter_data; + std::vector exp_data(LOCAL_SZ); + + int recvcount = 1; + MPI_Datatype srtype = MPI_DATATYPE_NULL; + + std::vector sendcounts, sdispls; + std::vector sendtypes; + + std::fill(local_data.begin(), local_data.end(), FILLVAL); + std::iota(exp_data.begin(), exp_data.end(), wrank * LOCAL_SZ); + + ret = MPI_Type_contiguous(LOCAL_SZ, MPI_DOUBLE, &srtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&srtype); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to create MPI contig type to receive double data (sz = %d)\n", LOCAL_SZ); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + scatter_data.resize(wsz * LOCAL_SZ); + std::iota(scatter_data.begin(), scatter_data.end(), 0); + + sendcounts.resize(wsz); + sdispls.resize(wsz); + sendtypes.resize(wsz); + + for(int i = 0; i < wsz; i++){ + sendcounts[i] = 1; + sdispls[i] = i * LOCAL_SZ * sizeof(double); + sendtypes[i] = srtype; + } + } + + try{ + ret = SPIO_Util::Rearr_Util::scatterw(scatter_data.data(), sendcounts, + sdispls, sendtypes, + local_data.data(), recvcount, srtype, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, local_data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in scatter buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + MPI_Type_free(&srtype); + + return PIO_NOERR; +} + +/* Test scatterw() with each process sending contig block of data and scattered using an indexed type */ +int test_scatterw_contig_indexed(MPI_Comm comm, int wrank, int wsz) +{ + int ret = PIO_NOERR; + const int LOCAL_SZ = 4; + const double FILLVAL = -1.0; + const int ROOT_RANK = 0; + std::vector local_data(LOCAL_SZ); + std::vector scatter_data; + std::vector exp_data(LOCAL_SZ); + + int recvcount = 1; + MPI_Datatype recvtype = MPI_DATATYPE_NULL; + + std::vector sendcounts, sdispls; + MPI_Datatype sendtype = MPI_DATATYPE_NULL; + std::vector sendtypes; + + /* Each process contains LOCAL_SZ doubles. After scatter, + * rank0 contains {0, 0+wsz, 0+2*wsz,...} + * rank1 contains {1, 1+wsz, 1+2*wsz,...} + * ... + * ranki contains {i, i+wsz, i+2*wsz,...} + */ + std::fill(local_data.begin(), local_data.end(), FILLVAL); + int i = 0; + std::generate(exp_data.begin(), exp_data.end(), + [wrank, wsz, &i](){ return static_cast(i++ * wsz + wrank); } ); + + ret = MPI_Type_contiguous(LOCAL_SZ, MPI_DOUBLE, &recvtype); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&recvtype); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to create MPI contig type to recv double data (sz = %d)\n", LOCAL_SZ); + return PIO_EINTERNAL; + } + + if(wrank == ROOT_RANK){ + scatter_data.resize(wsz * LOCAL_SZ); + std::iota(scatter_data.begin(), scatter_data.end(), 0); + + sendcounts.resize(wsz); + sdispls.resize(wsz); + sendtypes.resize(wsz); + + /* Receive the data such that the elements are in a sorted order - ascending */ + /* Receive data in rank0 {0, 0+wsz, 0+2*wz,...} at indices 0, 0+wsz, 0+2*wsz,... */ + std::vector elem_counts(LOCAL_SZ, 1); + std::vector elem_rdispls(LOCAL_SZ); + + int j = 0; + std::generate(elem_rdispls.begin(), elem_rdispls.end(), + [&j, wsz](){ return static_cast(j++ * wsz); } ); + + ret = MPI_Type_indexed(LOCAL_SZ, elem_counts.data(), elem_rdispls.data(), + MPI_DOUBLE, &(sendtype)); + if(ret == MPI_SUCCESS){ + ret = MPI_Type_commit(&(sendtype)); + } + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "ERROR: Unable to create MPI indexed type to send doubles\n"); + return PIO_EINTERNAL; + } + + for(int i = 0; i < wsz; i++){ + sendcounts[i] = 1; + sdispls[i] = i * sizeof(double); + sendtypes[i] = sendtype; + } + } + + try{ + ret = SPIO_Util::Rearr_Util::scatterw(scatter_data.data(), sendcounts, + sdispls, sendtypes, + local_data.data(), recvcount, recvtype, + ROOT_RANK, comm, NULL); + if(ret != PIO_NOERR){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + if(!cmp_result(wrank, local_data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in scatter buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Scattering data failed\n"); + return PIO_EINTERNAL; + } + + MPI_Type_free(&recvtype); + if(sendtype != MPI_DATATYPE_NULL){ + MPI_Type_free(&(sendtype)); + } + + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR, mpierr = MPI_SUCCESS; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + std::vector > > test_funcs = { + {"test_scatterw_double_block_decomp", test_scatterw_double_block_decomp}, + {"test_scatterw_double_uneven_block_decomp", test_scatterw_double_uneven_block_decomp}, + {"test_scatterw_contig_block_decomp", test_scatterw_contig_block_decomp}, + {"test_scatterw_contig_indexed", test_scatterw_contig_indexed} + }; + + for(std::size_t tid = 0; tid < test_funcs.size(); tid++){ + try{ + ret = test_funcs[tid].second(comm, wrank, wsz); + } + catch(...){ + ret = PIO_EINTERNAL; + nerrs++; + } + int lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce(&ret, &lfail, 1, MPI_INT, MPI_SUM, 0, comm); + if(mpierr != MPI_SUCCESS){ + LOG_RANK0(wrank, "Test Driver failed: Unable to calculate total num errors\n"); + } + if(ret != 0){ + std::string non_root_fail_msg = std::string(", failed on ") + std::to_string(ret) + std::string(" non-root processes"); + LOG_RANK0(wrank, "%s() FAILED (ret = %d%s)\n", test_funcs[tid].first.c_str(), ret, (lfail) ? "" : non_root_fail_msg.c_str()); + nerrs++; + } + else{ + LOG_RANK0(wrank, "%s() PASSED\n", test_funcs[tid].first.c_str()); + } + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spio_sort_utils.cpp b/tests/cunit/test_spio_sort_utils.cpp new file mode 100644 index 00000000000..d779c204c1b --- /dev/null +++ b/tests/cunit/test_spio_sort_utils.cpp @@ -0,0 +1,392 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pio_config.h" +#include "pio.h" +#include "pio_tests.h" +#include "spio_sort_utils.hpp" + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL +#include "gptl.h" +#endif +#endif + +#define LOG_RANK0(rank, ...) \ + do{ \ + if(rank == 0) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + }while(0); + +static const int FAIL = -1; + +template +bool cmp_result(int wrank, const std::vector &res, const std::vector &exp) +{ + + if(res.size() != exp.size()){ + LOG_RANK0(wrank, "ERROR: The result and expected vectors are of different sizes\n"); + return false; + } + + for(std::size_t i = 0; i < res.size(); i++){ + if(res[i] != exp[i]){ + if(wrank == 0){ + std::cerr << "ERROR: Invalid/Unexpected value, array[ " << i << "] = " << res[i] + << " (Expected array[" << i << "] = " << exp[i] << ")\n"; + } + return false; + } + } + + return true; +} + +int test_simple_kway_merge_sort(MPI_Comm comm, int wrank, int wsz) +{ + std::vector data_range1 = {12, 28, 32, 36, 37, 68, 69, 70}; + std::vector data_range2 = {2, 3, 4, 6, 9, 10, 13, 14, 30, 81}; + + std::vector data; + data.insert(data.end(), data_range1.begin(), data_range1.end()); + data.insert(data.end(), data_range2.begin(), data_range2.end()); + + std::vector > ranges = + { {0, data_range1.size()}, {data_range1.size(), data_range1.size() + data_range2.size()} }; + + std::vector exp_data(data); + std::sort(exp_data.begin(), exp_data.end()); + + try{ + SPIO_Util::vec_kway_merge_sort(data, ranges); + + if(!cmp_result(wrank, data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in sorted buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Kway merge sort of Vector failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +int test_odd_even_kway_merge_sort(MPI_Comm comm, int wrank, int wsz) +{ + const std::size_t NELEMS = 32; + std::vector data_range1; + std::vector data_range2; + + for(std::size_t i = 0; i < NELEMS; i+=2){ + data_range1.push_back(i); + data_range2.push_back(i + 1); + } + + std::vector data; + data.insert(data.end(), data_range1.begin(), data_range1.end()); + data.insert(data.end(), data_range2.begin(), data_range2.end()); + + std::vector > ranges = + { {0, data_range1.size()}, {data_range1.size(), data_range1.size() + data_range2.size()} }; + + std::vector exp_data(data); + std::sort(exp_data.begin(), exp_data.end()); + + try{ + SPIO_Util::vec_kway_merge_sort(data, ranges); + + if(!cmp_result(wrank, data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in sorted buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Kway merge sort of Vector failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +int test_rev_kway_merge_sort(MPI_Comm comm, int wrank, int wsz) +{ + std::vector data_range1 = {11, 12, 13, 14}; + std::vector data_range2 = {5, 6, 7, 8, 9}; + std::vector data_range3 = {2, 4}; + + std::vector data; + data.insert(data.end(), data_range1.begin(), data_range1.end()); + data.insert(data.end(), data_range2.begin(), data_range2.end()); + data.insert(data.end(), data_range3.begin(), data_range3.end()); + + std::vector > ranges = + { {0, data_range1.size()}, + {data_range1.size(), data_range1.size() + data_range2.size()}, + {data_range1.size() + data_range2.size(), + data_range1.size() + data_range2.size() + data_range3.size()} + }; + + std::vector exp_data(data); + std::sort(exp_data.begin(), exp_data.end()); + + try{ + SPIO_Util::vec_kway_merge_sort(data, ranges); + + if(!cmp_result(wrank, data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in sorted buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Kway merge sort of Vector failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +int test_indirect_simple_kway_merge_sort(MPI_Comm comm, int wrank, int wsz) +{ + std::vector data_range1 = {12, 28, 32, 36, 37, 68, 69, 70}; + std::vector data_range2 = {2, 3, 4, 6, 9, 10, 13, 14, 30, 81}; + + std::vector data; + data.insert(data.end(), data_range1.begin(), data_range1.end()); + data.insert(data.end(), data_range2.begin(), data_range2.end()); + + std::vector data_idx(data.size()); + std::iota(data_idx.begin(), data_idx.end(), 0); + + std::vector > ranges = + { {0, data_range1.size()}, {data_range1.size(), data_range1.size() + data_range2.size()} }; + + std::vector exp_data(data); + std::sort(exp_data.begin(), exp_data.end()); + + try{ + SPIO_Util::vec_kway_merge_sort(data_idx, ranges, + [&data](const std::size_t a, const std::size_t b){ return (data[a] + 1 == data[b]); }, + [&data](const std::size_t a, const std::size_t b){ return data[a] < data[b]; }); + + std::vector sorted_data(data.size()); + std::transform(data_idx.cbegin(), data_idx.cend(), + sorted_data.begin(), [&data](const std::size_t idx){ return data[idx]; }); + + if(!cmp_result(wrank, sorted_data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in sorted buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Kway merge sort of Vector failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +int test_indirect_odd_even_kway_merge_sort(MPI_Comm comm, int wrank, int wsz) +{ + const std::size_t NELEMS = 32; + std::vector data_range1; + std::vector data_range2; + + for(std::size_t i = 0; i < NELEMS; i+=2){ + data_range1.push_back(i); + data_range2.push_back(i + 1); + } + + std::vector data; + data.insert(data.end(), data_range1.begin(), data_range1.end()); + data.insert(data.end(), data_range2.begin(), data_range2.end()); + + std::vector data_idx(data.size()); + std::iota(data_idx.begin(), data_idx.end(), 0); + + std::vector > ranges = + { {0, data_range1.size()}, {data_range1.size(), data_range1.size() + data_range2.size()} }; + + std::vector exp_data(data); + std::sort(exp_data.begin(), exp_data.end()); + + try{ + SPIO_Util::vec_kway_merge_sort(data_idx, ranges, + [&data](const std::size_t a, const std::size_t b){ return (data[a] + 1 == data[b]); }, + [&data](const std::size_t a, const std::size_t b){ return data[a] < data[b]; }); + + std::vector sorted_data(data.size()); + std::transform(data_idx.cbegin(), data_idx.cend(), + sorted_data.begin(), [&data](const std::size_t idx){ return data[idx]; }); + + if(!cmp_result(wrank, sorted_data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in sorted buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Kway merge sort of Vector failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +int test_indirect_rev_kway_merge_sort(MPI_Comm comm, int wrank, int wsz) +{ + std::vector data_range1 = {11, 12, 13, 14}; + std::vector data_range2 = {5, 6, 7, 8, 9}; + std::vector data_range3 = {2, 4}; + + std::vector data; + data.insert(data.end(), data_range1.begin(), data_range1.end()); + data.insert(data.end(), data_range2.begin(), data_range2.end()); + data.insert(data.end(), data_range3.begin(), data_range3.end()); + + std::vector data_idx(data.size()); + std::iota(data_idx.begin(), data_idx.end(), 0); + + std::vector > ranges = + { {0, data_range1.size()}, + {data_range1.size(), data_range1.size() + data_range2.size()}, + {data_range1.size() + data_range2.size(), + data_range1.size() + data_range2.size() + data_range3.size()} + }; + + std::vector exp_data(data); + std::sort(exp_data.begin(), exp_data.end()); + + try{ + SPIO_Util::vec_kway_merge_sort(data_idx, ranges, + [&data](const std::size_t a, const std::size_t b){ return (data[a] + 1 == data[b]); }, + [&data](const std::size_t a, const std::size_t b){ return data[a] < data[b]; }); + + std::vector sorted_data(data.size()); + std::transform(data_idx.cbegin(), data_idx.cend(), + sorted_data.begin(), [&data](const std::size_t idx){ return data[idx]; }); + + if(!cmp_result(wrank, sorted_data, exp_data)){ + LOG_RANK0(wrank, "ERROR: Unexpected/Invalid data in sorted buffer\n"); + return PIO_EINTERNAL; + } + } + catch(...){ + LOG_RANK0(wrank, "Kway merge sort of Vector failed\n"); + return PIO_EINTERNAL; + } + + return PIO_NOERR; +} + +int test_driver(MPI_Comm comm, int wrank, int wsz, int *num_errors) +{ + int nerrs = 0, ret = PIO_NOERR, mpierr = MPI_SUCCESS; + assert((comm != MPI_COMM_NULL) && (wrank >= 0) && (wsz > 0) && num_errors); + + std::vector > > test_funcs = { + {"test_simple_kway_merge_sort", test_simple_kway_merge_sort}, + {"test_odd_even_kway_merge_sort", test_odd_even_kway_merge_sort}, + {"test_rev_kway_merge_sort", test_rev_kway_merge_sort}, + {"test_indirect_simple_kway_merge_sort", test_indirect_simple_kway_merge_sort}, + {"test_indirect_odd_even_kway_merge_sort", test_indirect_odd_even_kway_merge_sort}, + {"test_indirect_rev_kway_merge_sort", test_indirect_rev_kway_merge_sort} + }; + + for(std::size_t tid = 0; tid < test_funcs.size(); tid++){ + try{ + ret = test_funcs[tid].second(comm, wrank, wsz); + } + catch(...){ + ret = PIO_EINTERNAL; + nerrs++; + } + int lfail = (ret == PIO_NOERR) ? 0 : 1; + mpierr = MPI_Reduce(&ret, &lfail, 1, MPI_INT, MPI_SUM, 0, comm); + if(mpierr != MPI_SUCCESS){ + LOG_RANK0(wrank, "Test Driver failed: Unable to calculate total num errors\n"); + } + if(ret != 0){ + std::string non_root_fail_msg = std::string(", failed on ") + std::to_string(ret) + std::string(" non-root processes"); + LOG_RANK0(wrank, "%s() FAILED (ret = %d%s)\n", test_funcs[tid].first.c_str(), ret, (lfail) ? "" : non_root_fail_msg.c_str()); + nerrs++; + } + else{ + LOG_RANK0(wrank, "%s() PASSED\n", test_funcs[tid].first.c_str()); + } + } + + *num_errors += nerrs; + return nerrs; +} + +int main(int argc, char *argv[]) +{ + int ret; + int wrank, wsz; + int num_errors; +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLinitialize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + ret = MPI_Init(&argc, &argv); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Init() FAILED, ret = %d\n", ret); + return ret; + } + + ret = MPI_Comm_rank(MPI_COMM_WORLD, &wrank); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + ret = MPI_Comm_size(MPI_COMM_WORLD, &wsz); + if(ret != MPI_SUCCESS){ + LOG_RANK0(wrank, "MPI_Comm_rank() FAILED, ret = %d\n", ret); + return ret; + } + + num_errors = 0; + ret = test_driver(MPI_COMM_WORLD, wrank, wsz, &num_errors); + if(ret != 0){ + LOG_RANK0(wrank, "Test driver FAILED\n"); + } + else{ + LOG_RANK0(wrank, "All tests PASSED\n"); + } + + MPI_Finalize(); + +#ifdef SPIO_ENABLE_GPTL_TIMING +#ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL + ret = GPTLfinalize(); + if(ret != 0){ + LOG_RANK0(wrank, "GPTLinitialize() FAILED, ret = %d\n", ret); + return ret; + } +#endif /* TIMING_INTERNAL */ +#endif /* TIMING */ + + if(num_errors != 0){ + LOG_RANK0(wrank, "Total errors = %d\n", num_errors); + return FAIL; + } + return 0; +} diff --git a/tests/cunit/test_spmd.c b/tests/cunit/test_spmd.cpp similarity index 98% rename from tests/cunit/test_spmd.c rename to tests/cunit/test_spmd.cpp index d8ef5967e35..aa3bb1c9ca2 100644 --- a/tests/cunit/test_spmd.c +++ b/tests/cunit/test_spmd.cpp @@ -202,15 +202,15 @@ int test_lists() file_desc_t *fdesc; /* Test that bad input is correctly rejected. */ - if (pio_delete_iodesc_from_list(42) != PIO_EBADID) + if (pio_delete_iodesc_from_list(42) == PIO_NOERR) return ERR_WRONG; - if (pio_delete_iosystem_from_list(42) != PIO_EBADID) + if (pio_delete_iosystem_from_list(42) == PIO_NOERR) return ERR_WRONG; - if (pio_delete_file_from_list(42) != PIO_EBADID) + if (pio_delete_file_from_list(42) == PIO_NOERR) return ERR_WRONG; - if (pio_get_file(42, NULL) != PIO_EINVAL) + if (pio_get_file(42, NULL) == PIO_NOERR) return ERR_WRONG; - if (pio_get_file(42, &fdesc) != PIO_EBADID) + if (pio_get_file(42, &fdesc) == PIO_NOERR) return ERR_WRONG; return 0; } diff --git a/tests/general/CMakeLists.txt b/tests/general/CMakeLists.txt index fbaf4eb3103..a6763628437 100644 --- a/tests/general/CMakeLists.txt +++ b/tests/general/CMakeLists.txt @@ -55,13 +55,24 @@ SET(GENERATED_SRCS pio_file_simple_tests.F90 pio_iosystem_tests3.F90) foreach (SRC_FILE IN LISTS GENERATED_SRCS) - add_custom_command ( - OUTPUT ${SRC_FILE} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/util/pio_tf_f90gen.pl - --annotate-source - --out=${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE} - ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in) + if(PIO_USE_ASYNC_WR_THREAD) + add_custom_command ( + OUTPUT ${SRC_FILE} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/util/pio_tf_f90gen.pl + --annotate-source + --threaded + --out=${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE} + ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in) + else() + add_custom_command ( + OUTPUT ${SRC_FILE} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/util/pio_tf_f90gen.pl + --annotate-source + --out=${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE} + ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILE}.in) + endif() endforeach () set(DEF_SPIO_TIMING_DIR ${CMAKE_CURRENT_BINARY_DIR}/spio_stats) @@ -140,7 +151,7 @@ endif () #============================================================================== # Test Timeout (16 min = 960 sec) -set (DEFAULT_TEST_TIMEOUT 960) +set (DEFAULT_TEST_TIMEOUT 1800) set (DEFAULT_TEST_MAXNUMPROCS 4) # Use environment variable PIO_TF_MAXNUMPROCS to set the maximum number diff --git a/tests/general/ncdf_get_put.F90.in b/tests/general/ncdf_get_put.F90.in index 64443161996..8bc0663bb66 100644 --- a/tests/general/ncdf_get_put.F90.in +++ b/tests/general/ncdf_get_put.F90.in @@ -39,6 +39,9 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_1datt ret = PIO_put_att(pio_file, pio_cvar, 'dummy_att_put_cval', cval); PIO_TF_CHECK_ERR(ret, "Failed to put char attribute:" // trim(filename)) + ret = PIO_put_att(pio_file, pio_cvar, 'empty_str_att', ""); + PIO_TF_CHECK_ERR(ret, "Failed to put char attribute:" // trim(filename)) + ret = PIO_enddef(pio_file) PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(filename)) @@ -297,13 +300,161 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1dvar PIO_TF_AUTO_TEST_SUB_END test_put_get_1dvar +! Put and get a single coordinate variable (dimension and variable with the same name e.g. lon[lon]) +PIO_TF_TEMPLATE +PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1d_coord_var + Implicit none + type(file_desc_t) :: pio_file + character(len=PIO_TF_MAX_STR_LEN) :: filename + type(var_desc_t) :: pio_var + integer :: pio_dim + integer, parameter :: DIM_LEN = 100 + PIO_TF_FC_DATA_TYPE, dimension(DIM_LEN) :: pval, gval + integer, dimension(:), allocatable :: iotypes + character(len=PIO_TF_MAX_STR_LEN), dimension(:), allocatable :: iotype_descs + character(len=*), parameter :: PIO_VAR_NAME = 'dummy_coord_var' + integer :: num_iotypes + integer :: i, ret + + pval = pio_tf_world_sz_ + num_iotypes = 0 + call PIO_TF_Get_nc_iotypes(iotypes, iotype_descs, num_iotypes) + filename = "test_pio_ncdf_get_put.testfile" + do i=1,num_iotypes + PIO_TF_LOG(0,*) "Testing type :", iotype_descs(i) + ret = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_CLOBBER) + PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(filename)) + + ! Since file is just created no need to enter redef + ret = PIO_def_dim(pio_file, PIO_VAR_NAME, DIM_LEN, pio_dim) + PIO_TF_CHECK_ERR(ret, "Failed to define dim:" // trim(filename)) + + ret = PIO_def_var(pio_file, PIO_VAR_NAME, PIO_TF_DATA_TYPE, (/pio_dim/), pio_var) + PIO_TF_CHECK_ERR(ret, "Failed to define coord var:" // trim(filename)) + + ret = PIO_enddef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(filename)) + + ret = PIO_put_var(pio_file, pio_var, pval); + PIO_TF_CHECK_ERR(ret, "Failed to put coord var:" // trim(filename)) + +#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC + call PIO_closefile(pio_file) + + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) + PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) + + ret = PIO_inq_varid(pio_file, PIO_VAR_NAME, pio_var) + PIO_TF_CHECK_ERR(ret, "Failed to get coord var:" // trim(filename)) +#else + call PIO_syncfile(pio_file) +#endif + + gval = 0 + ret = PIO_get_var(pio_file, pio_var, gval); + PIO_TF_CHECK_ERR(ret, "Failed to get coord var:" // trim(filename)) + + PIO_TF_CHECK_VAL((gval, pval), "Got wrong value") + + call PIO_closefile(pio_file) + call PIO_deletefile(pio_tf_iosystem_, filename); + end do + if(allocated(iotypes)) then + deallocate(iotypes) + deallocate(iotype_descs) + end if + +PIO_TF_AUTO_TEST_SUB_END test_put_get_1d_coord_var + +! Put and get multiple variables and coordinate variables +PIO_TF_TEMPLATE +PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1d_coord_mvars + Implicit none + type(file_desc_t) :: pio_file + character(len=PIO_TF_MAX_STR_LEN) :: filename + type(var_desc_t) :: pio_var, pio_var2 + integer :: pio_dim + integer, parameter :: DIM_LEN = 100 + PIO_TF_FC_DATA_TYPE, dimension(DIM_LEN) :: pval, gval + integer, dimension(:), allocatable :: iotypes + character(len=PIO_TF_MAX_STR_LEN), dimension(:), allocatable :: iotype_descs + character(len=*), parameter :: PIO_CVAR_NAME = 'dummy_coord_var' + character(len=*), parameter :: PIO_VAR_NAME = 'dummy_var' + integer :: num_iotypes + integer :: i, ret + + pval = pio_tf_world_sz_ + num_iotypes = 0 + call PIO_TF_Get_nc_iotypes(iotypes, iotype_descs, num_iotypes) + filename = "test_pio_ncdf_get_put.testfile" + do i=1,num_iotypes + PIO_TF_LOG(0,*) "Testing type :", iotype_descs(i) + ret = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_CLOBBER) + PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(filename)) + + ! Since file is just created no need to enter redef + ret = PIO_def_dim(pio_file, PIO_CVAR_NAME, DIM_LEN, pio_dim) + PIO_TF_CHECK_ERR(ret, "Failed to define dim:" // trim(filename)) + + ret = PIO_def_var(pio_file, PIO_VAR_NAME, PIO_TF_DATA_TYPE, (/pio_dim/), pio_var2) + PIO_TF_CHECK_ERR(ret, "Failed to define var:" // trim(filename)) + + ret = PIO_def_var(pio_file, PIO_CVAR_NAME, PIO_TF_DATA_TYPE, (/pio_dim/), pio_var) + PIO_TF_CHECK_ERR(ret, "Failed to define coord var:" // trim(filename)) + + ret = PIO_enddef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(filename)) + + ret = PIO_put_var(pio_file, pio_var, pval); + PIO_TF_CHECK_ERR(ret, "Failed to put coord var:" // trim(filename)) + + ret = PIO_put_var(pio_file, pio_var2, pval); + PIO_TF_CHECK_ERR(ret, "Failed to put var:" // trim(filename)) + +#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC + call PIO_closefile(pio_file) + + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) + PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) + + ret = PIO_inq_varid(pio_file, PIO_CVAR_NAME, pio_var) + PIO_TF_CHECK_ERR(ret, "Failed to get coord var:" // trim(filename)) + + ret = PIO_inq_varid(pio_file, PIO_VAR_NAME, pio_var2) + PIO_TF_CHECK_ERR(ret, "Failed to get var:" // trim(filename)) +#else + call PIO_syncfile(pio_file) +#endif + + gval = 0 + ret = PIO_get_var(pio_file, pio_var, gval); + PIO_TF_CHECK_ERR(ret, "Failed to get coord var:" // trim(filename)) + + PIO_TF_CHECK_VAL((gval, pval), "Got wrong value") + + gval = 0 + ret = PIO_get_var(pio_file, pio_var2, gval); + PIO_TF_CHECK_ERR(ret, "Failed to get var:" // trim(filename)) + + PIO_TF_CHECK_VAL((gval, pval), "Got wrong value") + + call PIO_closefile(pio_file) + call PIO_deletefile(pio_tf_iosystem_, filename); + end do + if(allocated(iotypes)) then + deallocate(iotypes) + deallocate(iotype_descs) + end if + +PIO_TF_AUTO_TEST_SUB_END test_put_get_1d_coord_mvars + ! Put and get variables with large/small (compared to the variable size) user buffers PIO_TF_TEMPLATE PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1dvar_ls_buf Implicit none type(file_desc_t) :: pio_file character(len=PIO_TF_MAX_STR_LEN) :: filename - type(var_desc_t) :: pio_var, pio_cvar + type(var_desc_t) :: pio_svar, pio_lvar, pio_scvar, pio_lcvar integer :: pio_dim integer, parameter :: DIM_LEN = 200 @@ -318,8 +469,10 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1dvar_ls_buf integer, dimension(:), allocatable :: iotypes character(len=PIO_TF_MAX_STR_LEN), dimension(:), allocatable :: iotype_descs - character(len=*), parameter :: PIO_VAR_NAME = 'dummy_var_put_val' - character(len=*), parameter :: PIO_CVAR_NAME = 'dummy_var_put_cval' + character(len=*), parameter :: PIO_SVAR_NAME = 'dummy_svar_put_val' + character(len=*), parameter :: PIO_LVAR_NAME = 'dummy_lvar_put_val' + character(len=*), parameter :: PIO_SCVAR_NAME = 'dummy_svar_put_cval' + character(len=*), parameter :: PIO_LCVAR_NAME = 'dummy_lvar_put_cval' integer :: num_iotypes integer :: i, j, ret @@ -358,34 +511,54 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1dvar_ls_buf ret = PIO_def_dim(pio_file, 'dummy_dim_put_val', DIM_LEN, pio_dim) PIO_TF_CHECK_ERR(ret, "Failed to define dim:" // trim(filename)) - ret = PIO_def_var(pio_file, PIO_VAR_NAME, PIO_TF_DATA_TYPE, (/pio_dim/), pio_var) - PIO_TF_CHECK_ERR(ret, "Failed to define var:" // trim(filename)) + ret = PIO_def_var(pio_file, PIO_SVAR_NAME, PIO_TF_DATA_TYPE, (/pio_dim/), pio_svar) + PIO_TF_CHECK_ERR(ret, "Failed to define var (smaller than user buffer):" // trim(filename)) - ret = PIO_def_var(pio_file, PIO_CVAR_NAME, PIO_char, (/pio_dim/), pio_cvar) - PIO_TF_CHECK_ERR(ret, "Failed to define char var:" // trim(filename)) + ret = PIO_def_var(pio_file, PIO_LVAR_NAME, PIO_TF_DATA_TYPE, (/pio_dim/), pio_lvar) + PIO_TF_CHECK_ERR(ret, "Failed to define var (larger than user buffer):" // trim(filename)) + + ret = PIO_def_var(pio_file, PIO_SCVAR_NAME, PIO_char, (/pio_dim/), pio_scvar) + PIO_TF_CHECK_ERR(ret, "Failed to define char var (smaller than user buffer):" // trim(filename)) + + ret = PIO_def_var(pio_file, PIO_LCVAR_NAME, PIO_char, (/pio_dim/), pio_lcvar) + PIO_TF_CHECK_ERR(ret, "Failed to define char var (larger than user buffer):" // trim(filename)) ret = PIO_enddef(pio_file) PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(filename)) ! Put and get the values using a user buffer that is larger than the ! variable - ret = PIO_put_var(pio_file, pio_var, lpval); - PIO_TF_CHECK_ERR(ret, "Failed to put var:" // trim(filename)) + ret = PIO_put_var(pio_file, pio_svar, lpval); + PIO_TF_CHECK_ERR(ret, "Failed to put var (smaller than user buffer)):" // trim(filename)) - ret = PIO_put_var(pio_file, pio_cvar, lpcval); - PIO_TF_CHECK_ERR(ret, "Failed to put char var:" // trim(filename)) + ret = PIO_put_var(pio_file, pio_scvar, lpcval); + PIO_TF_CHECK_ERR(ret, "Failed to put char var (smaller than user buffer):" // trim(filename)) + + ! Put the values using a user buffer that is smaller than the + ! variable and get it with a larger buffer + ret = PIO_put_var(pio_file, pio_lvar, spval); + PIO_TF_CHECK_ERR(ret, "Failed to put var (larger than user buffer):" // trim(filename)) + + ret = PIO_put_var(pio_file, pio_lcvar, spcval); + PIO_TF_CHECK_ERR(ret, "Failed to put char var (larger than user buffer):" // trim(filename)) #ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC call PIO_closefile(pio_file) - ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_write) + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) - ret = PIO_inq_varid(pio_file, PIO_VAR_NAME, pio_var) - PIO_TF_CHECK_ERR(ret, "Failed to get scalar var:" // trim(filename)) + ret = PIO_inq_varid(pio_file, PIO_SVAR_NAME, pio_svar) + PIO_TF_CHECK_ERR(ret, "Failed to get var (smaller than user buffer):" // trim(filename)) - ret = PIO_inq_varid(pio_file, PIO_CVAR_NAME, pio_cvar) - PIO_TF_CHECK_ERR(ret, "Failed to get scalar char var:" // trim(filename)) + ret = PIO_inq_varid(pio_file, PIO_LVAR_NAME, pio_lvar) + PIO_TF_CHECK_ERR(ret, "Failed to get var (larger than user buffer):" // trim(filename)) + + ret = PIO_inq_varid(pio_file, PIO_SCVAR_NAME, pio_scvar) + PIO_TF_CHECK_ERR(ret, "Failed to get char var (smaller than user buffer):" // trim(filename)) + + ret = PIO_inq_varid(pio_file, PIO_LCVAR_NAME, pio_lcvar) + PIO_TF_CHECK_ERR(ret, "Failed to get char var (larger than user buffer):" // trim(filename)) #else call PIO_syncfile(pio_file) #endif @@ -395,38 +568,15 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1dvar_ls_buf lgcval(j:j) = '' end do - ret = PIO_get_var(pio_file, pio_var, lgval); - PIO_TF_CHECK_ERR(ret, "Failed to get var:" // trim(filename)) - - PIO_TF_CHECK_VAL((lgval, lpval), "Get with large user buffer : Got wrong value") - - ret = PIO_get_var(pio_file, pio_cvar, lgcval); - PIO_TF_CHECK_ERR(ret, "Failed to get char var:" // trim(filename)) + ret = PIO_get_var(pio_file, pio_svar, lgval); + PIO_TF_CHECK_ERR(ret, "Failed to get var (smaller than user buffer):" // trim(filename)) - PIO_TF_CHECK_VAL((lgcval, lpcval), "Get with large user buffer : Got wrong value") + PIO_TF_CHECK_VAL((lgval, lpval), "Get variable written with large user buffer : Got wrong value") - ! Put the values using a user buffer that is smaller than the - ! variable and get it with a larger buffer - ret = PIO_put_var(pio_file, pio_var, spval); - PIO_TF_CHECK_ERR(ret, "Failed to put var:" // trim(filename)) + ret = PIO_get_var(pio_file, pio_scvar, lgcval); + PIO_TF_CHECK_ERR(ret, "Failed to get char var (smaller than user buffer):" // trim(filename)) - ret = PIO_put_var(pio_file, pio_cvar, spcval); - PIO_TF_CHECK_ERR(ret, "Failed to put char var:" // trim(filename)) - -#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC - call PIO_closefile(pio_file) - - ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) - PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) - - ret = PIO_inq_varid(pio_file, PIO_VAR_NAME, pio_var) - PIO_TF_CHECK_ERR(ret, "Failed to get scalar var:" // trim(filename)) - - ret = PIO_inq_varid(pio_file, PIO_CVAR_NAME, pio_cvar) - PIO_TF_CHECK_ERR(ret, "Failed to get scalar char var:" // trim(filename)) -#else - call PIO_syncfile(pio_file) -#endif + PIO_TF_CHECK_VAL((lgcval, lpcval), "Get variable written with large user buffer : Got wrong value") do j=1,DIM_SMALL_LEN lpcval(j:j) = spcval(j:j) @@ -449,13 +599,13 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1dvar_ls_buf ! Note: Getting a variable with a buffer smaller than the size of the variable ! is an error, so use a large buffer for reading this data - ret = PIO_get_var(pio_file, pio_var, lgval); - PIO_TF_CHECK_ERR(ret, "Failed to get var:" // trim(filename)) + ret = PIO_get_var(pio_file, pio_lvar, lgval); + PIO_TF_CHECK_ERR(ret, "Failed to get var (larger than user buffer):" // trim(filename)) - PIO_TF_CHECK_VAL((lgval, lpval), "Get with large user buffer : Got wrong value") + PIO_TF_CHECK_VAL((lgval, lpval), "Get variable written with small user buffer : Got wrong value") - ret = PIO_get_var(pio_file, pio_cvar, lgcval); - PIO_TF_CHECK_ERR(ret, "Failed to get char var:" // trim(filename)) + ret = PIO_get_var(pio_file, pio_lcvar, lgcval); + PIO_TF_CHECK_ERR(ret, "Failed to get char var (larger than user buffer):" // trim(filename)) ! Since the variable is written out with a smaller buffer (than the size of the ! variable), the rest of the variable might contain invalid/uninitialized @@ -463,7 +613,7 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_1dvar_ls_buf ! results lgcval(DIM_SMALL_LEN+1:DIM_LARGE_LEN) = '' - PIO_TF_CHECK_VAL((lgcval, lpcval), "Get with large user buffer : Got wrong value") + PIO_TF_CHECK_VAL((lgcval, lpcval), "Get variable written with small user buffer : Got wrong value") call PIO_closefile(pio_file) call PIO_deletefile(pio_tf_iosystem_, filename); @@ -789,9 +939,13 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_md2mdplus1_var start(4) = tstep count(4) = 1 pval_start = (tstep - 1) * (MAX_ROWS * MAX_COLS * MAX_LEVS) - ret = PIO_put_var(pio_file, pio_4dvar, start, count,& - pval_3d(:,:,:)+pval_start) - PIO_TF_CHECK_ERR(ret, "Failed to put 4d var:" // trim(filename)) + if((iotypes(i) == PIO_IOTYPE_ADIOS) .or. (iotypes(i) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping write/put of 4d var (NOT SUPPORTED) :", iotype_descs(i) + else + ret = PIO_put_var(pio_file, pio_4dvar, start, count,& + pval_3d(:,:,:)+pval_start) + PIO_TF_CHECK_ERR(ret, "Failed to put 4d var:" // trim(filename)) + end if end do #ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC @@ -824,25 +978,185 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_md2mdplus1_var PIO_TF_CHECK_VAL((gval_3d, exp_val_3d), "Got wrong value (3d var)") - gval_4d = 0 - ret = PIO_get_var(pio_file, pio_4dvar, gval_4d) - PIO_TF_CHECK_ERR(ret, "Failed to get 4d var:" // trim(filename)) + if((iotypes(i) == PIO_IOTYPE_ADIOS) .or. (iotypes(i) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping read/get of 4d var (WRITE/PUT NOT SUPPORTED) :", iotype_descs(i) + else + gval_4d = 0 + ret = PIO_get_var(pio_file, pio_4dvar, gval_4d) + PIO_TF_CHECK_ERR(ret, "Failed to get 4d var:" // trim(filename)) + + ! Special code to handle 4d vals is required since the framework + ! currently does not support comparing 4d arrays + do tstep=1,MAX_TIMES + PIO_TF_CHECK_VAL((gval_4d(:,:,:,tstep), exp_val_4d(:,:,:,tstep)), "Got wrong value (4d var)") + end do + end if - ! Special code to handle 4d vals is required since the framework - ! currently does not support comparing 4d arrays + call PIO_closefile(pio_file) + call PIO_deletefile(pio_tf_iosystem_, filename); + end do + if(allocated(iotypes)) then + deallocate(iotypes) + deallocate(iotype_descs) + end if + +PIO_TF_AUTO_TEST_SUB_END test_put_get_md2mdplus1_var + +! Write out a 6d var, one time slice at a time +! (ne4pg2 + EAMXX writes out similar 6d vars) +PIO_TF_TEMPLATE +PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_6d_var + Implicit none + type(file_desc_t) :: pio_file + character(len=PIO_TF_MAX_STR_LEN) :: filename + character(len=*), parameter :: PIO_6DVAR_NAME = '6d_val' + ! There are 6 dims in the variable, but only 5 unique dims + integer, parameter :: MAX_DIMS = 6 + integer, parameter :: MAX_LEVS = 9 + integer, parameter :: MAX_GP = 4 + integer, parameter :: MAX_DIM2 = 2 + integer, parameter :: MAX_ELEMS = 6 + integer, parameter :: MAX_TIMES = 3 + integer, dimension(MAX_DIMS) :: pio_dims + type(var_desc_t) :: pio_6dvar + PIO_TF_FC_DATA_TYPE, dimension(MAX_LEVS, MAX_GP, MAX_GP, MAX_DIM2, MAX_ELEMS, MAX_TIMES) ::& + gval_6d, exp_val_6d + integer, dimension(:) :: start(MAX_DIMS), count(MAX_DIMS) + integer, dimension(:), allocatable :: iotypes + character(len=PIO_TF_MAX_STR_LEN), dimension(:), allocatable :: iotype_descs + integer :: num_iotypes + integer :: i, j, k, l, m, tstep, iotype_idx, ret + + num_iotypes = 0 + call PIO_TF_Get_nc_iotypes(iotypes, iotype_descs, num_iotypes) + filename = "test_pio_ncdf_get_put_6d_slice.testfile" + do iotype_idx=1,num_iotypes + PIO_TF_LOG(0,*) "Testing type :", iotype_descs(iotype_idx) + if((iotypes(iotype_idx) == PIO_IOTYPE_ADIOS) .or. (iotypes(iotype_idx) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping (Put/Write of 6D vars NOT SUPPORTED) :", iotype_descs(iotype_idx) + cycle + end if + ret = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(iotype_idx), filename, PIO_CLOBBER) + PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(filename)) + + ! Since file is just created no need to enter redef + ret = PIO_def_dim(pio_file, 'nlevs', MAX_LEVS, pio_dims(1)) + PIO_TF_CHECK_ERR(ret, "Failed to define nlevs dim:" // trim(filename)) + + ret = PIO_def_dim(pio_file, 'gp', MAX_GP, pio_dims(2)) + PIO_TF_CHECK_ERR(ret, "Failed to define gp dim:" // trim(filename)) + + pio_dims(3) = pio_dims(2) + + ret = PIO_def_dim(pio_file, 'dim2', MAX_DIM2, pio_dims(4)) + PIO_TF_CHECK_ERR(ret, "Failed to define dim2 dim:" // trim(filename)) + + ret = PIO_def_dim(pio_file, 'elems', MAX_ELEMS, pio_dims(5)) + PIO_TF_CHECK_ERR(ret, "Failed to define elems dim:" // trim(filename)) + + ret = PIO_def_dim(pio_file, 'time', PIO_UNLIMITED, pio_dims(6)) + PIO_TF_CHECK_ERR(ret, "Failed to define time dim:" // trim(filename)) + + ret = PIO_def_var(pio_file, PIO_6DVAR_NAME, PIO_TF_DATA_TYPE,& + pio_dims, pio_6dvar) + PIO_TF_CHECK_ERR(ret, "Failed to define 6d var:" // trim(filename)) + + ret = PIO_enddef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(filename)) + + ! Put vals are for each timestep & + ! expected vals are combined for all timesteps do tstep=1,MAX_TIMES - PIO_TF_CHECK_VAL((gval_4d(:,:,:,tstep), exp_val_4d(:,:,:,tstep)), "Got wrong value (4d var)") + do m=1,MAX_ELEMS + do l=1,MAX_DIM2 + do k=1,MAX_GP + do j=1,MAX_GP + do i=1,MAX_LEVS + exp_val_6d(:,:,:,:,:,tstep) = i + j * MAX_LEVS + k * (MAX_GP * MAX_LEVS) +& + l * (MAX_GP * MAX_GP * MAX_LEVS) + m * (MAX_DIM2 * MAX_GP * MAX_GP * MAX_LEVS) +& + tstep * (MAX_ELEMS * MAX_DIM2 * MAX_GP * MAX_GP * MAX_LEVS) + end do + end do + end do + end do + end do + end do + + ! Put 6d val, one timestep at a time + do tstep=1,MAX_TIMES + start = 0 + count = 0 + + start(1) = 1 + count(1) = MAX_LEVS + start(2) = 1 + count(2) = MAX_GP + start(3) = 1 + count(3) = MAX_GP + start(4) = 1 + count(4) = MAX_DIM2 + start(5) = 1 + count(5) = MAX_ELEMS + start(6) = tstep + count(6) = 1 + ret = PIO_put_var(pio_file, pio_6dvar, start, count,& + exp_val_6d(:,:,:,:,:,tstep)) + PIO_TF_CHECK_ERR(ret, "Failed to put 6d var:" // trim(filename)) + end do + +#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC + call PIO_closefile(pio_file) + + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(iotype_idx), filename, PIO_nowrite) + PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) + + ret = PIO_inq_varid(pio_file, PIO_6DVAR_NAME, pio_6dvar) + PIO_TF_CHECK_ERR(ret, "Failed to inq 6d var" // trim(filename)) +#else + call PIO_syncfile(pio_file) +#endif + + gval_6d = 0 + do tstep=1,MAX_TIMES + start = 0 + count = 0 + + start(1) = 1 + count(1) = MAX_LEVS + start(2) = 1 + count(2) = MAX_GP + start(3) = 1 + count(3) = MAX_GP + start(4) = 1 + count(4) = MAX_DIM2 + start(5) = 1 + count(5) = MAX_ELEMS + start(6) = tstep + count(6) = 1 + ret = PIO_get_var(pio_file, pio_6dvar, start, count, gval_6d(:,:,:,:,:,tstep)) + PIO_TF_CHECK_ERR(ret, "Failed to get 6d var time slice:" // trim(filename)) + end do + + ! Special code to handle 6d vals is required since the framework + ! currently does not support comparing 6d arrays + do tstep=1,MAX_TIMES + do m=1,MAX_ELEMS + do l=1,MAX_DIM2 + PIO_TF_CHECK_VAL((gval_6d(:,:,:,l,m,tstep), exp_val_6d(:,:,:,l,m,tstep)), "Got wrong value (6d var)") + end do + end do end do call PIO_closefile(pio_file) call PIO_deletefile(pio_tf_iosystem_, filename); end do + if(allocated(iotypes)) then deallocate(iotypes) deallocate(iotype_descs) end if -PIO_TF_AUTO_TEST_SUB_END test_put_get_md2mdplus1_var +PIO_TF_AUTO_TEST_SUB_END test_put_get_6d_var ! Similar to test_put_get_md2mdplus1_var, but uses an unlimited time dimension instead PIO_TF_TEMPLATE @@ -1499,10 +1813,22 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str type(file_desc_t) :: pio_file character(len=*), parameter :: filename = "test_pio_ncdf_get_put_misc_str.testfile" type(var_desc_t) :: var0d, var1d, var2d, var3d + type(var_desc_t) :: svar0d, svar1d, svar2d, svar3d + type(var_desc_t) :: hsvar0d, hsvar1d, hsvar2d, hsvar3d character(len=*), parameter :: var0d_name = "var0d" character(len=*), parameter :: var1d_name = "var1d" character(len=*), parameter :: var2d_name = "var2d" character(len=*), parameter :: var3d_name = "var3d" + ! Small variables : Puts with buffers smaller than the variable, Gets with larger buf + character(len=*), parameter :: svar0d_name = "svr0d" + character(len=*), parameter :: svar1d_name = "svr1d" + character(len=*), parameter :: svar2d_name = "svr2d" + character(len=*), parameter :: svar3d_name = "svr3d" + ! Variables with data written to it in hyperslabs + character(len=*), parameter :: hsvar0d_name = "hvr0d" + character(len=*), parameter :: hsvar1d_name = "hvr1d" + character(len=*), parameter :: hsvar2d_name = "hvr2d" + character(len=*), parameter :: hsvar3d_name = "hvr3d" integer, parameter :: SHORT_STR_LEN = 32 integer, parameter :: LONG_STR_LEN = 128 @@ -1521,12 +1847,19 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str character(len=STR_LEN) :: exp_str0d, exp_str1d(NUM_ROWS) character(len=STR_LEN) :: exp_str2d(NUM_ROWS, NUM_COLS) character(len=STR_LEN) :: exp_str3d(NUM_ROWS, NUM_COLS, NUM_STEPS) + character(len=STR_LEN) :: exp_hstr1d(NUM_ROWS) + character(len=STR_LEN) :: exp_hstr2d(NUM_ROWS, NUM_COLS) + character(len=STR_LEN) :: exp_hstr3d(NUM_ROWS, NUM_COLS, NUM_STEPS) + character(len=STR_LEN) :: gstr0d, gstr1d(NUM_ROWS) character(len=STR_LEN) :: gstr2d(NUM_ROWS, NUM_COLS) character(len=STR_LEN) :: gstr3d(NUM_ROWS, NUM_COLS, NUM_STEPS) character(len=SHORT_STR_LEN) :: exp_sstr0d, exp_sstr1d(NUM_ROWS) character(len=SHORT_STR_LEN) :: exp_sstr2d(NUM_ROWS, NUM_COLS) + character(len=SHORT_STR_LEN) :: exp_hsstr1d(NUM_ROWS) + character(len=SHORT_STR_LEN) :: exp_hsstr2d(NUM_ROWS, NUM_COLS) + character(len=LONG_STR_LEN) :: glstr0d, glstr1d(NUM_ROWS) character(len=LONG_STR_LEN) :: glstr2d(NUM_ROWS, NUM_COLS) @@ -1554,23 +1887,44 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str do i=1,num_iotypes ! Initialize the buffers written out to the variable exp_str0d = var0d_name - exp_sstr0d = var0d_name do row=1,NUM_ROWS write(exp_str1d(row), VNAME_WITH_1D_IDX_FMT1) var1d_name, row - write(exp_sstr1d(row), VNAME_WITH_1D_IDX_FMT1) var1d_name, row do col=1,NUM_COLS write(exp_str2d(row,col), VNAME_WITH_2D_IDX_FMT1) var2d_name, row, col - write(exp_sstr2d(row,col), VNAME_WITH_2D_IDX_FMT1) var2d_name, row, col do step=1,NUM_STEPS write(exp_str3d(row,col,step), VNAME_WITH_3D_IDX_FMT1) var2d_name, row, col, step end do end do end do + ! Write the variables in the file with a smaller (than the variable size) + ! buffer + exp_sstr0d = svar0d_name // "_" + do row=1,NUM_ROWS + write(exp_sstr1d(row), VNAME_WITH_1D_IDX_FMT2) svar1d_name, row + do col=1,NUM_COLS + write(exp_sstr2d(row,col), VNAME_WITH_2D_IDX_FMT2) svar2d_name, row, col + end do + end do + + ! Write the variables in the file to test hyperslab puts + do row=1,NUM_ROWS + write(exp_hstr1d(row), VNAME_WITH_1D_IDX_FMT1) hsvar1d_name, row + write(exp_hsstr1d(row), VNAME_WITH_1D_IDX_FMT1) hsvar1d_name, row + do col=1,NUM_COLS + write(exp_hstr2d(row,col), VNAME_WITH_2D_IDX_FMT1) hsvar2d_name, row, col + write(exp_hsstr2d(row,col), VNAME_WITH_2D_IDX_FMT1) hsvar2d_name, row, col + do step=1,NUM_STEPS + write(exp_hstr3d(row,col,step), VNAME_WITH_3D_IDX_FMT1) hsvar3d_name, row, col, step + end do + end do + end do + PIO_TF_LOG(0,*) "Testing type :", iotype_descs(i) ret = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_CLOBBER) PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(filename)) + ! ========== DIMENSIONS ============== ret = PIO_def_dim(pio_file, dim0_name, STR_LEN, pio_dims(1)) PIO_TF_CHECK_ERR(ret, "Failed to define dim 0:" // trim(filename)) @@ -1583,6 +1937,7 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str ret = PIO_def_dim(pio_file, dim3_name, NUM_STEPS, pio_dims(4)) PIO_TF_CHECK_ERR(ret, "Failed to define dim 3:" // trim(filename)) + ! ========== VARIABLES ============== ret = PIO_def_var(pio_file, var0d_name, PIO_char, (/pio_dims(1)/), var0d) PIO_TF_CHECK_ERR(ret, "Failed to define 0 dim char array or string in file " // trim(filename)) @@ -1595,9 +1950,36 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str ret = PIO_def_var(pio_file, var3d_name, PIO_char, pio_dims, var3d) PIO_TF_CHECK_ERR(ret, "Failed to define 3 dim char array or 3d array of strings in file " // trim(filename)) + ! ========== SMALL VARIABLES (Buffers used are smaller than variable) ============== + ret = PIO_def_var(pio_file, svar0d_name, PIO_char, (/pio_dims(1)/), svar0d) + PIO_TF_CHECK_ERR(ret, "Failed to define 0 dim char array or string in file (small var) " // trim(filename)) + + ret = PIO_def_var(pio_file, svar1d_name, PIO_char, (/pio_dims(1), pio_dims(2)/), svar1d) + PIO_TF_CHECK_ERR(ret, "Failed to define 1 dim char array or 1d array of strings in file (small var)" // trim(filename)) + + ret = PIO_def_var(pio_file, svar2d_name, PIO_char, (/pio_dims(1), pio_dims(2), pio_dims(3)/), svar2d) + PIO_TF_CHECK_ERR(ret, "Failed to define 2 dim char array or 2d array of strings in file (small var)" // trim(filename)) + + ret = PIO_def_var(pio_file, svar3d_name, PIO_char, pio_dims, svar3d) + PIO_TF_CHECK_ERR(ret, "Failed to define 3 dim char array or 3d array of strings in file (small var)" // trim(filename)) + + ! ========== HYPERSLAB VARIABLES (Operations are on hyperslabs of this variable) ============== + ret = PIO_def_var(pio_file, hsvar0d_name, PIO_char, (/pio_dims(1)/), hsvar0d) + PIO_TF_CHECK_ERR(ret, "Failed to define 0 dim char array or string in file (hyperslab var) " // trim(filename)) + + ret = PIO_def_var(pio_file, hsvar1d_name, PIO_char, (/pio_dims(1), pio_dims(2)/), hsvar1d) + PIO_TF_CHECK_ERR(ret, "Failed to define 1 dim char array or 1d array of strings in file (hyperslab var)" // trim(filename)) + + ret = PIO_def_var(pio_file, hsvar2d_name, PIO_char, (/pio_dims(1), pio_dims(2), pio_dims(3)/), hsvar2d) + PIO_TF_CHECK_ERR(ret, "Failed to define 2 dim char array or 2d array of strings in file (hyperslab var)" // trim(filename)) + + ret = PIO_def_var(pio_file, hsvar3d_name, PIO_char, pio_dims, hsvar3d) + PIO_TF_CHECK_ERR(ret, "Failed to define 3 dim char array or 3d array of strings in file (hyperslab var)" // trim(filename)) + ret = PIO_enddef(pio_file) PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(filename)) + ! ========== WRITE VARIABLES ================= ! This calls put_var_0d_text() ret = PIO_put_var(pio_file, var0d, exp_str0d); PIO_TF_CHECK_ERR(ret, "Failed to put 0 dim char array or string in file " // trim(filename)) @@ -1611,14 +1993,69 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str PIO_TF_CHECK_ERR(ret, "Failed to put 2d array of strings in file " // trim(filename)) ! This calls put_var_3d_text() - ret = PIO_put_var(pio_file, var3d, exp_str3d); - PIO_TF_CHECK_ERR(ret, "Failed to put 3d array of strings in file " // trim(filename)) + if((iotypes(i) == PIO_IOTYPE_ADIOS) .or. (iotypes(i) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping put/write of 3d string (NOT SUPPORTED for 4d char vars) :", iotype_descs(i) + else + ret = PIO_put_var(pio_file, var3d, exp_str3d); + PIO_TF_CHECK_ERR(ret, "Failed to put 3d array of strings in file " // trim(filename)) + end if + + ! ========== WRITE VARIABLES WITH SMALL BUFFERS ================= + ! This calls put_var_0d_text() + ret = PIO_put_var(pio_file, svar0d, exp_sstr0d); + PIO_TF_CHECK_ERR(ret, "Failed to put 0 dim char array or string with small buf in file (small var)" // trim(filename)) + + ! This calls put_var_1d_text() + ret = PIO_put_var(pio_file, svar1d, exp_sstr1d); + PIO_TF_CHECK_ERR(ret, "Failed to put 1d array of strings with small buf in file (small var)" // trim(filename)) + + ! This calls put_var_2d_text() + ret = PIO_put_var(pio_file, svar2d, exp_sstr2d); + PIO_TF_CHECK_ERR(ret, "Failed to put 2d array of strings with small buf in file (small var)" // trim(filename)) + + ! ========== WRITE VARIABLES IN HYPERSLABS ================= + strt = 1 + cnt = 0 + cnt(1) = STR_LEN + cnt(2) = NUM_ROWS + + ! This calls put_vara_1d_text() + ret = PIO_put_var(pio_file, hsvar1d, strt, cnt, exp_hstr1d); + PIO_TF_CHECK_ERR(ret, "Failed to put hslab of 1d array of strings in file (hyperslab var)" // trim(filename)) + strt = 1 + cnt = 0 + cnt(1) = STR_LEN + cnt(2) = NUM_ROWS + cnt(3) = NUM_COLS + + ! This calls put_vara_2d_text() + ret = PIO_put_var(pio_file, hsvar2d, strt, cnt, exp_hstr2d); + PIO_TF_CHECK_ERR(ret, "Failed to put hslab of 2d array of strings in file (hyperslab var)" // trim(filename)) + + strt = 1 + cnt = 0 + cnt(1) = STR_LEN + cnt(2) = NUM_ROWS + cnt(3) = NUM_COLS + cnt(4) = NUM_STEPS + + ! This calls put_vara_3d_text() + ! Skip for ADIOS since we currently don't support put/write of + ! 4D char variables with ADIOS + if((iotypes(i) == PIO_IOTYPE_ADIOS) .or. (iotypes(i) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping put/write of 3d string (NOT SUPPORTED for 4d char vars) :", iotype_descs(i) + else + ret = PIO_put_var(pio_file, hsvar3d, strt, cnt, exp_hstr3d); + PIO_TF_CHECK_ERR(ret, "Failed to put hslab of 3d array of strings in file (hyperslab var)" // trim(filename)) + end if + + ! ========== SYNC Data ================= ! Sync data to disk #ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC call PIO_closefile(pio_file) - ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_write) + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) ret = PIO_inq_varid(pio_file, var0d_name, var0d) @@ -1632,10 +2069,35 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str ret = PIO_inq_varid(pio_file, var3d_name, var3d) PIO_TF_CHECK_ERR(ret, "Failed to inquire 3d array of strings var in file " // trim(filename)) + + ret = PIO_inq_varid(pio_file, svar0d_name, svar0d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 0d char or string var in file (small var)" // trim(filename)) + + ret = PIO_inq_varid(pio_file, svar1d_name, svar1d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file (small var)" // trim(filename)) + + ret = PIO_inq_varid(pio_file, svar2d_name, svar2d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 2d array of strings var in file (small var)" // trim(filename)) + + ret = PIO_inq_varid(pio_file, svar3d_name, svar3d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 3d array of strings var in file (small var)" // trim(filename)) + + ret = PIO_inq_varid(pio_file, hsvar0d_name, hsvar0d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 0d char or string var in file (hyperslab var)" // trim(filename)) + + ret = PIO_inq_varid(pio_file, hsvar1d_name, hsvar1d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file (hyperslab var)" // trim(filename)) + + ret = PIO_inq_varid(pio_file, hsvar2d_name, hsvar2d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 2d array of strings var in file (hyperslab var)" // trim(filename)) + + ret = PIO_inq_varid(pio_file, hsvar3d_name, hsvar3d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 3d array of strings var in file (hyperslab var)" // trim(filename)) #else call PIO_syncfile(pio_file) #endif + ! ========== READ & VERIFY : VARIABLES ================= ! This calls get_var_0d_text() gstr0d = '' ret = PIO_get_var(pio_file, var0d, gstr0d); @@ -1658,161 +2120,64 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_misc_str PIO_TF_CHECK_VAL((gstr2d, exp_str2d), "Got wrong value for 2d char var") ! This calls get_var_3d_text() - gstr3d = '' - ret = PIO_get_var(pio_file, var3d, gstr3d); - PIO_TF_CHECK_ERR(ret, "Failed to get 3d char var in file " // trim(filename)) - - PIO_TF_CHECK_VAL((gstr3d, exp_str3d), "Got wrong value for 3d char var") - - ! Re-write the variables in the file with a smaller (than the variable size) - ! buffer - exp_sstr0d = var0d_name // "_" - do row=1,NUM_ROWS - write(exp_sstr1d(row), VNAME_WITH_1D_IDX_FMT2) var1d_name, row - do col=1,NUM_COLS - write(exp_sstr2d(row,col), VNAME_WITH_2D_IDX_FMT2) var2d_name, row, col - end do - end do - - ! This calls put_var_0d_text() - ret = PIO_put_var(pio_file, var0d, exp_sstr0d); - PIO_TF_CHECK_ERR(ret, "Failed to put 0 dim char array or string with small buf in file " // trim(filename)) - - ! This calls put_var_1d_text() - ret = PIO_put_var(pio_file, var1d, exp_sstr1d); - PIO_TF_CHECK_ERR(ret, "Failed to put 1d array of strings with small buf in file " // trim(filename)) - - ! This calls put_var_2d_text() - ret = PIO_put_var(pio_file, var2d, exp_sstr2d); - PIO_TF_CHECK_ERR(ret, "Failed to put 2d array of strings with small buf in file " // trim(filename)) - - ! Sync data to disk -#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC - call PIO_closefile(pio_file) - - ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_write) - PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) - - ret = PIO_inq_varid(pio_file, var0d_name, var0d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 0d char or string var in file " // trim(filename)) - - ret = PIO_inq_varid(pio_file, var1d_name, var1d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) - - ret = PIO_inq_varid(pio_file, var2d_name, var2d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 2d array of strings var in file " // trim(filename)) + ! Skip for ADIOS since we currently don't support put/write of + ! 4D char variables with ADIOS + if((iotypes(i) /= PIO_IOTYPE_ADIOS) .and. (iotypes(i) /= PIO_IOTYPE_ADIOSC)) then + gstr3d = '' + ret = PIO_get_var(pio_file, var3d, gstr3d); + PIO_TF_CHECK_ERR(ret, "Failed to get 3d char var in file " // trim(filename)) - ret = PIO_inq_varid(pio_file, var3d_name, var3d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 3d array of strings var in file " // trim(filename)) - -#else - call PIO_syncfile(pio_file) -#endif + PIO_TF_CHECK_VAL((gstr3d, exp_str3d), "Got wrong value for 3d char var") + end if + ! ========== READ & VERIFY : SMALL (puts with small buffer) VARIABLES ================= ! Read data back with a larger (than the size of the variable) buffer ! This calls get_var_0d_text() glstr0d = '' - ret = PIO_get_var(pio_file, var0d, glstr0d); - PIO_TF_CHECK_ERR(ret, "Failed to get 0d char var with large buf in file " // trim(filename)) + ret = PIO_get_var(pio_file, svar0d, glstr0d); + PIO_TF_CHECK_ERR(ret, "Failed to get 0d char var with large buf in file (small var)" // trim(filename)) PIO_TF_CHECK_VAL((glstr0d, exp_sstr0d), "Got wrong value for 0d char var with large buf") ! This calls get_var_1d_text() glstr1d = '' - ret = PIO_get_var(pio_file, var1d, glstr1d); - PIO_TF_CHECK_ERR(ret, "Failed to get 1d char var with large buf in file " // trim(filename)) + ret = PIO_get_var(pio_file, svar1d, glstr1d); + PIO_TF_CHECK_ERR(ret, "Failed to get 1d char var with large buf in file (small var)" // trim(filename)) PIO_TF_CHECK_VAL((glstr1d, exp_sstr1d), "Got wrong value for 1d char var with large buf") ! This calls get_var_2d_text() glstr2d = '' - ret = PIO_get_var(pio_file, var2d, glstr2d); - PIO_TF_CHECK_ERR(ret, "Failed to get 2d char var with large buf in file " // trim(filename)) + ret = PIO_get_var(pio_file, svar2d, glstr2d); + PIO_TF_CHECK_ERR(ret, "Failed to get 2d char var with large buf in file (small var)" // trim(filename)) PIO_TF_CHECK_VAL((glstr2d, exp_sstr2d), "Got wrong value for 2d char var with large buf") - ! Re-write the variables in the file to test hyperslab puts - do row=1,NUM_ROWS - write(exp_str1d(row), VNAME_WITH_1D_IDX_FMT1) var1d_name, row - write(exp_sstr1d(row), VNAME_WITH_1D_IDX_FMT1) var1d_name, row - do col=1,NUM_COLS - write(exp_str2d(row,col), VNAME_WITH_2D_IDX_FMT1) var2d_name, row, col - write(exp_sstr2d(row,col), VNAME_WITH_2D_IDX_FMT1) var2d_name, row, col - do step=1,NUM_STEPS - write(exp_str3d(row,col,step), VNAME_WITH_3D_IDX_FMT1) var3d_name, row, col, step - end do - end do - end do - - strt = 1 - cnt = 0 - cnt(1) = STR_LEN - cnt(2) = NUM_ROWS - - ! This calls put_vara_1d_text() - ret = PIO_put_var(pio_file, var1d, strt, cnt, exp_str1d); - PIO_TF_CHECK_ERR(ret, "Failed to put hslab of 1d array of strings in file " // trim(filename)) - - strt = 1 - cnt = 0 - cnt(1) = STR_LEN - cnt(2) = NUM_ROWS - cnt(3) = NUM_COLS - - ! This calls put_vara_2d_text() - ret = PIO_put_var(pio_file, var2d, strt, cnt, exp_str2d); - PIO_TF_CHECK_ERR(ret, "Failed to put hslab of 2d array of strings in file " // trim(filename)) - - strt = 1 - cnt = 0 - cnt(1) = STR_LEN - cnt(2) = NUM_ROWS - cnt(3) = NUM_COLS - cnt(4) = NUM_STEPS - - ! This calls put_vara_3d_text() - ret = PIO_put_var(pio_file, var3d, strt, cnt, exp_str3d); - PIO_TF_CHECK_ERR(ret, "Failed to put hslab of 3d array of strings in file " // trim(filename)) - - ! Sync data to disk -#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC - call PIO_closefile(pio_file) - - ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) - PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) - - ret = PIO_inq_varid(pio_file, var1d_name, var1d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) - - ret = PIO_inq_varid(pio_file, var2d_name, var2d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 2d array of strings var in file " // trim(filename)) - - ret = PIO_inq_varid(pio_file, var3d_name, var3d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 3d array of strings var in file " // trim(filename)) -#else - call PIO_syncfile(pio_file) -#endif - + ! ========== READ & VERIFY : HYPERSLAB (puts in hyperslabs) VARIABLES ================= ! This calls get_var_1d_text() gstr1d = '' - ret = PIO_get_var(pio_file, var1d, gstr1d); - PIO_TF_CHECK_ERR(ret, "Failed to get 1d char var in file " // trim(filename)) + ret = PIO_get_var(pio_file, hsvar1d, gstr1d); + PIO_TF_CHECK_ERR(ret, "Failed to get 1d char var in file (hyperslab var)" // trim(filename)) - PIO_TF_CHECK_VAL((gstr1d, exp_str1d), "Got wrong value for 1d char var written with hslab put") + PIO_TF_CHECK_VAL((gstr1d, exp_hstr1d), "Got wrong value for 1d char var written with hslab put") ! This calls get_var_2d_text() gstr2d = '' - ret = PIO_get_var(pio_file, var2d, gstr2d); - PIO_TF_CHECK_ERR(ret, "Failed to get 2d char var in file " // trim(filename)) + ret = PIO_get_var(pio_file, hsvar2d, gstr2d); + PIO_TF_CHECK_ERR(ret, "Failed to get 2d char var in file (hyperslab var)" // trim(filename)) - PIO_TF_CHECK_VAL((gstr2d, exp_str2d), "Got wrong value for 2d char var written with hslab put") + PIO_TF_CHECK_VAL((gstr2d, exp_hstr2d), "Got wrong value for 2d char var written with hslab put") ! This calls get_var_3d_text() - gstr3d = '' - ret = PIO_get_var(pio_file, var3d, gstr3d); - PIO_TF_CHECK_ERR(ret, "Failed to get 3d char var in file " // trim(filename)) + ! Skip for ADIOS since we currently don't support put/write of + ! 4D char variables with ADIOS + if((iotypes(i) /= PIO_IOTYPE_ADIOS) .and. (iotypes(i) /= PIO_IOTYPE_ADIOSC)) then + gstr3d = '' + ret = PIO_get_var(pio_file, hsvar3d, gstr3d); + PIO_TF_CHECK_ERR(ret, "Failed to get 3d char var in file (hyperslab var)" // trim(filename)) - PIO_TF_CHECK_VAL((gstr3d, exp_str3d), "Got wrong value for 3d char var written with hslab put") + PIO_TF_CHECK_VAL((gstr3d, exp_hstr3d), "Got wrong value for 3d char var written with hslab put") + end if call PIO_closefile(pio_file) call PIO_deletefile(pio_tf_iosystem_, filename); @@ -1957,23 +2322,27 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_str_frames end do ! Write the 3d text variable, 1 2d array (row x col) of strings at a time - do step=1,NUM_STEPS - strt = 1 - cnt = 0 - strt(1) = 1 - strt(2) = 1 - strt(3) = 1 - strt(4) = step - cnt(1) = STR_LEN - cnt(2) = NUM_ROWS - cnt(3) = NUM_COLS - cnt(4) = 1 - - frame_str3d(:,:,1) = exp_str3d(:,:,step) - ! This calls put_vara_3d_text() - ret = PIO_put_var(pio_file, var3d, strt, cnt, frame_str3d); - PIO_TF_CHECK_ERR(ret, "Failed to put 3d array of strings 1 frame at a time in file " // trim(filename)) - end do + if((iotypes(i) /= PIO_IOTYPE_ADIOS) .and. (iotypes(i) /= PIO_IOTYPE_ADIOSC)) then + do step=1,NUM_STEPS + strt = 1 + cnt = 0 + strt(1) = 1 + strt(2) = 1 + strt(3) = 1 + strt(4) = step + cnt(1) = STR_LEN + cnt(2) = NUM_ROWS + cnt(3) = NUM_COLS + cnt(4) = 1 + + frame_str3d(:,:,1) = exp_str3d(:,:,step) + ! This calls put_vara_3d_text() + ret = PIO_put_var(pio_file, var3d, strt, cnt, frame_str3d); + PIO_TF_CHECK_ERR(ret, "Failed to put 3d array of strings 1 frame at a time in file " // trim(filename)) + end do + else + PIO_TF_LOG(0,*) "Skipping put/write of 4d char vars (NOT SUPPORTED), type :", iotype_descs(i) + end if ! Sync data to disk #ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC @@ -2009,11 +2378,14 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_str_frames PIO_TF_CHECK_VAL((gstr2d, exp_str2d), "Got wrong value for 2d char var") ! This calls get_var_3d_text() - gstr3d = '' - ret = PIO_get_var(pio_file, var3d, gstr3d); - PIO_TF_CHECK_ERR(ret, "Failed to get 3d char var in file " // trim(filename)) + ! Skip for ADIOS since put/write of 4D vars is not supported right now + if((iotypes(i) /= PIO_IOTYPE_ADIOS) .and. (iotypes(i) /= PIO_IOTYPE_ADIOSC)) then + gstr3d = '' + ret = PIO_get_var(pio_file, var3d, gstr3d); + PIO_TF_CHECK_ERR(ret, "Failed to get 3d char var in file " // trim(filename)) - PIO_TF_CHECK_VAL((gstr3d, exp_str3d), "Got wrong value for 3d char var") + PIO_TF_CHECK_VAL((gstr3d, exp_str3d), "Got wrong value for 3d char var") + end if call PIO_closefile(pio_file) call PIO_deletefile(pio_tf_iosystem_, filename); @@ -2218,7 +2590,13 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_str_idx #ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC call PIO_closefile(pio_file) - ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_write) + if((iotypes(i) /= PIO_IOTYPE_ADIOS) .and. (iotypes(i) /= PIO_IOTYPE_ADIOSC)) then + ! Re-open in write mode for modifying "var1d_midx" var + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_write) + else + ! ADIOS does not support appending data + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) + end if PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) ret = PIO_inq_varid(pio_file, var1d_name, var1d) @@ -2239,42 +2617,46 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_str_idx call PIO_syncfile(pio_file) #endif - do row=1,NUM_ROWS - ! Change the variable name to "vname_mdix" by writing at the index for the variable name - ! vname_midx(i) = "Index is: vname_midx(i)" - strt = 1 - strt(1) = VNAME_IDX_IN_VNAME_WITH_1D_IDX_FMT2 - strt(2) = row - - ! This calls put_vara_1d_text() - ret = PIO_put_var(pio_file, var1d_midx, strt, var1d_midx_name); - PIO_TF_CHECK_ERR(ret, "Failed to put 1d array of long strings 1 string at a time to file " // trim(filename)) - end do + ! Skip overwriting variables (varid_midx) for ADIOS iotypes. ADIOS does not support + ! appending/modifying data + if((iotypes(i) /= PIO_IOTYPE_ADIOS) .and. (iotypes(i) /= PIO_IOTYPE_ADIOSC)) then + do row=1,NUM_ROWS + ! Change the variable name to "vname_mdix" by writing at the index for the variable name + ! vname_midx(i) = "Index is: vname_midx(i)" + strt = 1 + strt(1) = VNAME_IDX_IN_VNAME_WITH_1D_IDX_FMT2 + strt(2) = row + + ! This calls put_vara_1d_text() + ret = PIO_put_var(pio_file, var1d_midx, strt, var1d_midx_name); + PIO_TF_CHECK_ERR(ret, "Failed to put 1d array of long strings 1 string at a time to file " // trim(filename)) + end do - ! Sync data to disk + ! Sync data to disk #ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC - call PIO_closefile(pio_file) + call PIO_closefile(pio_file) - ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) - PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) + ret = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) + PIO_TF_CHECK_ERR(ret, "Failed to reopen:" // trim(filename)) - ret = PIO_inq_varid(pio_file, var1d_name, var1d) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) + ret = PIO_inq_varid(pio_file, var1d_name, var1d) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) - ret = PIO_inq_varid(pio_file, var1d_shrt_name, var1d_shrt) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) + ret = PIO_inq_varid(pio_file, var1d_shrt_name, var1d_shrt) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) - ret = PIO_inq_varid(pio_file, var1d_long_name, var1d_long) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) + ret = PIO_inq_varid(pio_file, var1d_long_name, var1d_long) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) - ret = PIO_inq_varid(pio_file, var1d_trnc_name, var1d_trnc) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) + ret = PIO_inq_varid(pio_file, var1d_trnc_name, var1d_trnc) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) - ret = PIO_inq_varid(pio_file, var1d_midx_name, var1d_midx) - PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) + ret = PIO_inq_varid(pio_file, var1d_midx_name, var1d_midx) + PIO_TF_CHECK_ERR(ret, "Failed to inquire 1d array of strings var in file " // trim(filename)) #else - call PIO_syncfile(pio_file) + call PIO_syncfile(pio_file) #endif + end if ! This calls get_var_1d_text() gstr1d = '' @@ -2301,11 +2683,14 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_str_idx PIO_TF_CHECK_VAL((gstr1d, exp_trnc_str1d), "Got wrong value for truncatable 1d char var") - gstr1d = '' - ret = PIO_get_var(pio_file, var1d_midx, gstr1d); - PIO_TF_CHECK_ERR(ret, "Failed to get 1d char var in file " // trim(filename)) + ! Skip for ADIOS - ADIOS does not support appending/modifying data + if((iotypes(i) /= PIO_IOTYPE_ADIOS) .and. (iotypes(i) /= PIO_IOTYPE_ADIOSC)) then + gstr1d = '' + ret = PIO_get_var(pio_file, var1d_midx, gstr1d); + PIO_TF_CHECK_ERR(ret, "Failed to get 1d char var in file " // trim(filename)) - PIO_TF_CHECK_VAL((gstr1d, exp_midx_str1d), "Got wrong value for index write of 1d char var") + PIO_TF_CHECK_VAL((gstr1d, exp_midx_str1d), "Got wrong value for index write of 1d char var") + end if call PIO_closefile(pio_file) call PIO_deletefile(pio_tf_iosystem_, filename); @@ -2408,6 +2793,10 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_put_get_str_hslabs end do PIO_TF_LOG(0,*) "Testing type :", iotype_descs(i) + if((iotypes(i) == PIO_IOTYPE_ADIOS) .or. (iotypes(i) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping writing hyperslabs of strings in phases (Appending to files is not SUPPORTED), type :", iotype_descs(i) + cycle + end if ret = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_CLOBBER) PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(filename)) diff --git a/tests/general/ncdf_simple_tests.F90.in b/tests/general/ncdf_simple_tests.F90.in index 176ad9a66ec..aa6a920b9a8 100644 --- a/tests/general/ncdf_simple_tests.F90.in +++ b/tests/general/ncdf_simple_tests.F90.in @@ -73,6 +73,51 @@ PIO_TF_AUTO_TEST_SUB_BEGIN test_redef_enddef PIO_TF_AUTO_TEST_SUB_END test_redef_enddef +PIO_TF_AUTO_TEST_SUB_BEGIN test_redef_twice + use ncdf_simple_tests_tgv + Implicit none + character(len=PIO_TF_MAX_STR_LEN), parameter :: tmp_fname = "pio_test_create_redef_twice.nc" + type(file_desc_t) :: pio_file + type(var_desc_t) :: pio_var1, pio_var2 + integer :: pio_dim1, pio_dim2 + integer :: ret + + ret = PIO_createfile(pio_tf_iosystem_, pio_file, tgv_iotype, tmp_fname, PIO_CLOBBER) + PIO_TF_CHECK_ERR(ret, "Failed to create:" // trim(tgv_fname)) + + ret = PIO_enddef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(tgv_fname)) + + ! A simple redef and then enddef + ret = PIO_redef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to redef:" // trim(tgv_fname)) + + ret = PIO_def_dim(pio_file, 'dummy_dim_def_var_redef1', 100, pio_dim1) + PIO_TF_CHECK_ERR(ret, "Failed to define dim (1st redef):" // trim(tgv_fname)) + + ret = PIO_def_var(pio_file, 'dummy_var_def_var_redef1', PIO_int, (/pio_dim1/), pio_var1) + PIO_TF_CHECK_ERR(ret, "Failed to define var (1st redef):" // trim(tgv_fname)) + + ret = PIO_enddef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(tgv_fname)) + + ! A second redef and then enddef + ret = PIO_redef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to redef:" // trim(tgv_fname)) + + ret = PIO_def_dim(pio_file, 'dummy_dim_def_var_redef2', 100, pio_dim2) + PIO_TF_CHECK_ERR(ret, "Failed to define dim (2nd redef):" // trim(tgv_fname)) + + ret = PIO_def_var(pio_file, 'dummy_var_def_var_redef2', PIO_int, (/pio_dim2/), pio_var2) + PIO_TF_CHECK_ERR(ret, "Failed to define var (2nd redef):" // trim(tgv_fname)) + + ret = PIO_enddef(pio_file) + PIO_TF_CHECK_ERR(ret, "Failed to enddef (2nd redef):" // trim(tgv_fname)) + + call PIO_closefile(pio_file) + +PIO_TF_AUTO_TEST_SUB_END test_redef_twice + PIO_TF_AUTO_TEST_SUB_BEGIN test_def_dim use ncdf_simple_tests_tgv Implicit none diff --git a/tests/general/pio_decomp_frame_tests.F90.in b/tests/general/pio_decomp_frame_tests.F90.in index 6db7964a273..090fecb122b 100644 --- a/tests/general/pio_decomp_frame_tests.F90.in +++ b/tests/general/pio_decomp_frame_tests.F90.in @@ -1,3 +1,96 @@ +! Write multiple frames of a 1d variable with an unlimited dimension +PIO_TF_TEMPLATE +PIO_TF_AUTO_TEST_SUB_BEGIN nc_wr_rd_1d_unlim_dim + implicit none + integer, parameter :: NDIMS = 1 + integer, parameter :: NFRAMES = 6 + integer, parameter :: VEC_LOCAL_SZ = 7 + type(var_desc_t) :: pio_var + type(file_desc_t) :: pio_file + character(len=PIO_TF_MAX_STR_LEN) :: filename + character(len=*), parameter :: PIO_VAR_NAME = 'PIO_TF_test_var_1d_unlim' + PIO_TF_FC_DATA_TYPE, dimension(:), allocatable :: rbuf, wbuf, exp_val + integer, dimension(NDIMS) :: start, count + integer, dimension(NDIMS) :: pio_dims + integer :: i, ierr + integer(kind=pio_offset_kind) :: f + ! iotypes = valid io types + integer, dimension(:), allocatable :: iotypes + character(len=PIO_TF_MAX_STR_LEN), dimension(:), allocatable :: iotype_descs + integer :: num_iotypes + + start = 0 + count = 1 + + allocate(wbuf(1)) + allocate(rbuf(1)) + wbuf = 0 + rbuf = 0 + allocate(exp_val(NFRAMES)) + do f=1,NFRAMES + exp_val(f) = int(f) + end do + + num_iotypes = 0 + call PIO_TF_Get_nc_iotypes(iotypes, iotype_descs, num_iotypes) + filename = "test_pio_decomp_frame_tests.testfile" + do i=1,num_iotypes + PIO_TF_LOG(0,*) "Testing : PIO_TF_DATA_TYPE : ", iotype_descs(i) + ierr = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_CLOBBER) + PIO_TF_CHECK_ERR(ierr, "Could not create file " // trim(filename)) + + ierr = PIO_def_dim(pio_file, 'PIO_TF_test_dim_time', pio_unlimited, pio_dims(1)) + PIO_TF_CHECK_ERR(ierr, "Failed to define a dim : " // trim(filename)) + + ierr = PIO_def_var(pio_file, PIO_VAR_NAME, PIO_TF_DATA_TYPE, pio_dims, pio_var) + PIO_TF_CHECK_ERR(ierr, "Failed to define a var : " // trim(filename)) + + ierr = PIO_enddef(pio_file) + PIO_TF_CHECK_ERR(ierr, "Failed to end redef mode : " // trim(filename)) + + do f=1,NFRAMES + start = int(f) + wbuf = int(f) + ! Write the current frame + ierr = PIO_put_var(pio_file, pio_var, start, count, wbuf) + PIO_TF_CHECK_ERR(ierr, "Failed to write darray : " // trim(filename)) + end do + +#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC + call PIO_closefile(pio_file) + + ierr = PIO_openfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_nowrite) + PIO_TF_CHECK_ERR(ierr, "Could not reopen file " // trim(filename)) + + ierr = PIO_inq_varid(pio_file, PIO_VAR_NAME, pio_var) + PIO_TF_CHECK_ERR(ierr, "Could not inq var : " // trim(filename)) +#else + call PIO_syncfile(pio_file) +#endif + + do f=1,NFRAMES + start = int(f) + rbuf = 0 + ierr = PIO_get_var(pio_file, pio_var, start, count, rbuf) + PIO_TF_CHECK_ERR(ierr, "Failed to read darray : " // trim(filename)) + PIO_TF_CHECK_VAL((rbuf, exp_val(f)), "Got wrong val, frame = ", f) + end do + + call PIO_closefile(pio_file) + + call PIO_deletefile(pio_tf_iosystem_, filename); + end do + + if(allocated(iotypes)) then + deallocate(iotypes) + deallocate(iotype_descs) + end if + + deallocate(exp_val) + deallocate(rbuf) + deallocate(wbuf) +PIO_TF_AUTO_TEST_SUB_END nc_wr_rd_1d_unlim_dim + ! Write multiple frames of a 2d variable with an unlimited dimension PIO_TF_TEMPLATE PIO_TF_AUTO_TEST_SUB_BEGIN nc_wr_rd_2d_unlim_dim diff --git a/tests/general/pio_decomp_tests2_1d.F90.in b/tests/general/pio_decomp_tests2_1d.F90.in index 2a384717d19..e836a816240 100644 --- a/tests/general/pio_decomp_tests2_1d.F90.in +++ b/tests/general/pio_decomp_tests2_1d.F90.in @@ -173,6 +173,10 @@ PIO_TF_AUTO_TEST_SUB_BEGIN nc_wr_rd_a2a filename = "test_pio_decomp_simple_tests.testfile" do i=1,num_iotypes PIO_TF_LOG(0,*) "Testing : PIO_TF_DATA_TYPE : ", iotype_descs(i) + if((iotypes(i) == PIO_IOTYPE_ADIOS) .OR. (iotypes(i) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping (Read/Writes with different I/O decomps is not supported) : ", iotype_descs(i) + cycle + end if ierr = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_CLOBBER) PIO_TF_CHECK_ERR(ierr, "Could not create file " // trim(filename)) @@ -294,6 +298,10 @@ PIO_TF_AUTO_TEST_SUB_BEGIN nc_wr_rev_rd filename = "test_pio_decomp_simple_tests.testfile" do i=1,num_iotypes PIO_TF_LOG(0,*) "Testing : PIO_TF_DATA_TYPE : ", iotype_descs(i) + if((iotypes(i) == PIO_IOTYPE_ADIOS) .OR. (iotypes(i) == PIO_IOTYPE_ADIOSC)) then + PIO_TF_LOG(0,*) "Skipping (Read/Writes with different I/O decomps is not supported) : ", iotype_descs(i) + cycle + end if ierr = PIO_createfile(pio_tf_iosystem_, pio_file, iotypes(i), filename, PIO_CLOBBER) PIO_TF_CHECK_ERR(ierr, "Could not create file " // trim(filename)) diff --git a/tests/general/pio_iosystem_tests.F90.in b/tests/general/pio_iosystem_tests.F90.in index 4d4da46f27e..a98ccd4a0ac 100644 --- a/tests/general/pio_iosystem_tests.F90.in +++ b/tests/general/pio_iosystem_tests.F90.in @@ -153,7 +153,7 @@ SUBROUTINE open_and_check_file(comm, iosys, iotype, pio_file, fname, & logical, intent(in) :: disable_fclose integer, intent(inout) :: ret - ret = PIO_openfile(iosys, pio_file, iotype, fname, PIO_write) + ret = PIO_openfile(iosys, pio_file, iotype, fname, PIO_nowrite) PIO_TF_CHECK_ERR(ret, comm, "Failed to open:" // fname) call check_file(comm, pio_file, fname, attname, dimname, ret) diff --git a/tests/general/util/pio_tf_f90gen.pl b/tests/general/util/pio_tf_f90gen.pl index d6f3cb0d9e2..a7a99e42db5 100755 --- a/tests/general/util/pio_tf_f90gen.pl +++ b/tests/general/util/pio_tf_f90gen.pl @@ -8,6 +8,7 @@ use Getopt::Long; my($verbose); +my($threaded); my($nargs); my($template_fname, $output_fname); # If the template source has PIO_TF_AUTO_TEST* and the test @@ -38,6 +39,7 @@ # Initializing global vars $verbose = 0; +$threaded = 0; $nargs = 0; $template_fname = ''; $output_fname = ''; @@ -602,19 +604,26 @@ sub update_auto_func_list_with_gen_templ # Returns the default test main code sub get_default_test_main { + my($threaded) = @_; my($out_line); $out_line = "\n\n"; $out_line = $out_line . " PROGRAM PIO_TF_Test_main_\n"; $out_line = $out_line . " USE pio_tutil\n"; + $out_line = $out_line . " USE mpi\n"; $out_line = $out_line . " IMPLICIT NONE\n"; - $out_line = $out_line . " INTEGER, PARAMETER :: NREARRS = 2\n"; - $out_line = $out_line . " INTEGER :: rearrs(NREARRS) = (/pio_rearr_subset,pio_rearr_box/)\n"; - $out_line = $out_line . " CHARACTER(LEN=PIO_TF_MAX_STR_LEN) :: rearrs_info(NREARRS) = (/\"PIO_REARR_SUBSET\",\"PIO_REARR_BOX \"/)\n"; - $out_line = $out_line . " INTEGER i, ierr\n"; + $out_line = $out_line . " INTEGER, PARAMETER :: NREARRS = 3\n"; + $out_line = $out_line . " INTEGER :: rearrs(NREARRS) = (/pio_rearr_contig,pio_rearr_box,pio_rearr_subset/)\n"; + $out_line = $out_line . " CHARACTER(LEN=PIO_TF_MAX_STR_LEN) :: rearrs_info(NREARRS) = (/\"PIO_REARR_CONTIG\",\"PIO_REARR_BOX \",\"PIO_REARR_SUBSET\"/)\n"; + $out_line = $out_line . " INTEGER i, ierr, provided\n"; $out_line = $out_line . "\n"; $out_line = $out_line . " pio_tf_nerrs_total_=0\n"; $out_line = $out_line . " pio_tf_retval_utest_=0\n"; - $out_line = $out_line . " CALL MPI_Init(ierr)\n"; + if($threaded){ + $out_line = $out_line . " CALL MPI_Init_thread(MPI_THREAD_MULTIPLE, provided, ierr)\n"; + } + else{ + $out_line = $out_line . " CALL MPI_Init(ierr)\n"; + } $out_line = $out_line . " DO i=1,SIZE(rearrs)\n"; $out_line = $out_line . " CALL PIO_TF_Init_(rearrs(i))\n"; $out_line = $out_line . " IF (pio_tf_world_rank_ == 0) THEN\n"; @@ -705,14 +714,14 @@ sub get_header # been appended the driver sub get_footer { - my($ref_auto_funcs_list) = @_; + my($ref_auto_funcs_list, $threaded) = @_; my($out_line); if($template_has_test_driver == 0){ # Add default test driver $out_line = &get_default_test_driver($ref_auto_funcs_list); } - $out_line = $out_line . &get_default_test_main(); + $out_line = $out_line . &get_default_test_main($threaded); return $out_line; } @@ -721,7 +730,7 @@ sub get_footer # * Also does some basic processing of inputs sub process_template_file { - my($ifname, $ofname) = @_; + my($ifname, $ofname, $threaded) = @_; # The var below keeps track of input file line number my($ifline_num); my($ifline); @@ -856,13 +865,13 @@ sub process_template_file $ifline_num += 1; } - $footer = &get_footer(\@auto_funcs_list); + $footer = &get_footer(\@auto_funcs_list, $threaded); print OUTPUT_FILE $footer; } sub print_usage_and_exit() { - print "\n\nUsage: ./pio_tf_f90gen.pl --annotate-source --out= \n\n"; + print "\n\nUsage: ./pio_tf_f90gen.pl --annotate-source --threaded --out= \n\n"; print "eg: ./pio_tf_f90gen.pl --annotate-source --out=pio_init_finalize.F90 pio_init_finalize.F90.in\n"; exit; } @@ -874,6 +883,7 @@ () # Annotate generated source with template line numbers etc "annotate-source" => \$annotate_source, "out=s" => \$output_fname, + "threaded" => \$threaded, "verbose" => \$verbose ); @@ -895,4 +905,4 @@ () &init_predef_types(); if($verbose){ print "Reading input args complete\n" } -&process_template_file($template_fname, $output_fname); +&process_template_file($template_fname, $output_fname, $threaded); diff --git a/tests/performance/README_parse_decomp_info_py.txt b/tests/performance/README_parse_decomp_info_py.txt new file mode 100644 index 00000000000..12cb4343d8f --- /dev/null +++ b/tests/performance/README_parse_decomp_info_py.txt @@ -0,0 +1,32 @@ +Parse the pio_decomp_map_info.txt file (that contains the mapping between file/variable names and dumped decomposition file names) and filter out the decomposition files used by specified set of components. + +================ EXAMPLE ==================================== +$ ./parse_decomp_info.py --help +usage: parse_decomp_info.py [-h] [--filter-components= filter_component [filter_component ...]] [--decomp-map-info-file DECOMP_MAP_INFO_FNAME] [--log-level LOG_LEVEL] + +Parse decomp info file : pio_decomp_map_info.txt + +options: + -h, --help show this help message and exit + --filter-components= filter_component [filter_component ...] + The name of components to filter (Supported component names = ['eam', 'cpl', 'elm', 'mosart', 'mpaso', 'mpassi']) + --decomp-map-info-file DECOMP_MAP_INFO_FNAME + The complete path of pio_decomp_map_info.txt (or the file containing the decomp map info) + --log-level LOG_LEVEL + Specify the log level (Default is INFO, Available log levels are : dict_keys(['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'])) + + +================ EXAMPLE ==================================== + +$ ./parse_decomp_info.py --filter-components eam cpl --decomp-map-info-file ./pio_decomp_map_info.txt --log-level=INFO |& tee out.log +INFO:Decomposition map info parser tool +===================================================== + +{'piodecomp002049id16tasks04io01dims0242.dat', 'piodecomp002048id16tasks04io02dims0269.dat', 'piodecomp002049id16tasks04io01dims15.dat', 'piodecomp002048id16tasks04io02dims0267.dat', 'piodecomp002048id16tasks04io02dims0263.dat', 'piodecomp002049id16tasks04io01dims0256.dat', 'piodecomp002049id16tasks04io02dims0248.dat', 'piodecomp002048id16tasks04io02dims0268.dat', 'piodecomp002049id16tasks04io01dims0245.dat', 'piodecomp002048id16tasks04io02dims0259.dat', 'piodecomp002049id16tasks04io02dims0247.dat', 'piodecomp002048id16tasks04io02dims0270.dat', 'piodecomp002049id16tasks04io01dims0244.dat', 'piodecomp002049id16tasks04io01dims0252.dat', 'piodecomp002049id16tasks04io01dims0253.dat', 'piodecomp002049id16tasks04io01dims0254.dat', 'piodecomp002048id16tasks04io02dims0262.dat', 'piodecomp002049id16tasks04io02dims0249.dat', 'piodecomp002048id16tasks04io02dims0265.dat', 'piodecomp002048id16tasks04io02dims0261.dat', 'piodecomp002049id16tasks04io01dims0250.dat', 'piodecomp002049id16tasks04io02dims0243.dat', 'piodecomp002048id16tasks04io02dims0264.dat', 'piodecomp002048id16tasks04io02dims0260.dat', 'piodecomp002048id16tasks04io02dims0258.dat', 'piodecomp002049id16tasks04io01dims0246.dat', 'piodecomp002048id16tasks04io02dims0266.dat'} + +================ EXAMPLE ==================================== +./parse_decomp_info.py --filter-components mosart --decomp-map-info-file ./pio_decomp_map_info.txt --log-level=INFO | sed -e "s/'//g" +INFO:Decomposition map info parser tool +===================================================== + +{piodecomp002054id16tasks04io02dims0202.dat, piodecomp002054id16tasks04io02dims0201.dat} diff --git a/tests/performance/README_pioperformance_rearr.txt b/tests/performance/README_pioperformance_rearr.txt new file mode 100644 index 00000000000..31f633d4954 --- /dev/null +++ b/tests/performance/README_pioperformance_rearr.txt @@ -0,0 +1,9 @@ +============ EXAMPLE ================== + +#!/bin/bash +for nioprocs in 2 4 8 12 16 +do + echo "Running with $nioprocs I/O procs" + mpiexec -n 16 ../scorpio_build/tests/performance/pioperf_rearr --pio-decompfiles='piodecomp002049id16tasks04io01dims0253.dat, piodecomp002049id16tasks04io02dims0247.dat, piodecomp002049id16tasks04io01dims0252.dat, piodecomp002049id16tasks04io02dims0248.dat' --pio-types='pnetcdf' --pio-rearrangers='1,2,3,4' --pio-nvars=1 --pio-niotasks=$nioprocs +done + diff --git a/tests/performance/parse_decomp_info.py b/tests/performance/parse_decomp_info.py new file mode 100755 index 00000000000..6fc35e3d8d5 --- /dev/null +++ b/tests/performance/parse_decomp_info.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +import sys +import argparse +import re +import logging + +#logger = logging.getLogger(__name__) + +component_names = ["eam", "cpl", "elm", "mosart", "mpaso", "mpassi"] +log_levels = {"DEBUG" : logging.DEBUG, "INFO" : logging.INFO, + "WARNING" : logging.WARNING, "ERROR" : logging.ERROR, + "CRITICAL" : logging.CRITICAL} + +tool_hdr_str = "Decomposition map info parser tool\n=====================================================\n" + +def set_log_level(log_level): + logging.basicConfig(format='%(levelname)s:%(message)s', level=log_levels[log_level]) + +def parse_command_line(argv): + parser = argparse.ArgumentParser(description ='Parse decomp info file : pio_decomp_map_info.txt') + parser.add_argument('--filter-components=', dest = 'filter_components', + default ='eam', + metavar ='filter_component', + nargs ='+', action ='store', + help ='The name of components to filter (Supported component names = ' + str(component_names) + ')') + parser.add_argument('--decomp-map-info-file', dest ='decomp_map_info_fname', + default ='./pio_decomp_map_info.txt', + action ='store', + help ='The complete path of pio_decomp_map_info.txt (or the file containing the decomp map info)') + parser.add_argument('--log-level', dest ='log_level', + default ='INFO', + action ='store', + help ='Specify the log level (Default is INFO, Available log levels are : ' + str(log_levels.keys()) + ')') + args = parser.parse_args() + return args.filter_components, args.decomp_map_info_fname, args.log_level + +def parse_decomp_map_info(fname, filter_components): + decomp_map_fnames = set() + with open(fname, 'r') as file: + component_begin_fname_rgx_str = r'^[{].*\sfname\s[=].*(' + r'|'.join(filter_components) + r').*$' + logging.debug("Using regex str \"{}\" to match files to filter".format(component_begin_fname_rgx_str)) + component_begin_fname_rgx = re.compile(component_begin_fname_rgx_str) + component_end_fname_rgx = re.compile(r'^[}]$') + collect_decomp_map_fnames = False + for line in file: + line = line.strip() + if component_begin_fname_rgx.match(line): + collect_decomp_map_fnames = True + logging.debug("Enabling collection of decomp map file names : Parsed string : {}".format(line)) + elif component_end_fname_rgx.match(line): + collect_decomp_map_fnames = False + logging.debug("Disabling collection of decomp map file names : Parsed string : {}".format(line)) + elif collect_decomp_map_fnames: + decomp_map_info = line.split(',') + decomp_map_fname = decomp_map_info[2].split('=')[1].strip() + decomp_map_fnames.add(decomp_map_fname) + return decomp_map_fnames + +def _main(argv): + #set_log_level('DEBUG') + filter_components, decomp_map_info_fname, log_level = parse_command_line(argv) + set_log_level(log_level) + logging.info(tool_hdr_str) + logging.debug("Parsing decomp info file : {}".format(decomp_map_info_fname)) + logging.debug("Filtering decomp file info of components : {}".format(str(filter_components))) + filtered_decomp_map_fnames = parse_decomp_map_info(decomp_map_info_fname, filter_components) + print(filtered_decomp_map_fnames) + +if __name__ == "__main__": + _main(sys.argv) + + diff --git a/tests/performance/pioperformance_rearr.F90 b/tests/performance/pioperformance_rearr.F90 index 5b1d9b44946..5e106924db1 100644 --- a/tests/performance/pioperformance_rearr.F90 +++ b/tests/performance/pioperformance_rearr.F90 @@ -7,7 +7,8 @@ program pioperformance_rearr use mpi #endif use pio, only : pio_iotype_netcdf, pio_iotype_pnetcdf, pio_iotype_netcdf4p, & - pio_iotype_netcdf4c, pio_iotype_adios, pio_iotype_hdf5, pio_rearr_subset, pio_rearr_box, PIO_MAX_NAME,& + pio_iotype_netcdf4c, pio_iotype_adios, pio_iotype_hdf5,& + pio_rearr_subset, pio_rearr_box, pio_rearr_any, pio_rearr_contig, PIO_MAX_NAME,& pio_rearr_opt_t, pio_rearr_comm_p2p, pio_rearr_comm_coll,& pio_rearr_comm_fc_2d_disable, pio_rearr_comm_fc_1d_comp2io,& pio_rearr_comm_fc_1d_io2comp, pio_rearr_comm_fc_2d_enable,& @@ -19,7 +20,7 @@ program pioperformance_rearr integer, parameter :: MAX_IO_TASK_ARRAY_SIZE=256, MAX_DECOMP_FILES=256 integer, parameter :: MAX_FNAME_LEN = 1024 integer, parameter :: MAX_PIO_TYPENAME_LEN = 8 - integer, parameter :: MAX_PIO_TYPES = 4, MAX_PIO_REARRS = 2 + integer, parameter :: MAX_PIO_TYPES = 4, MAX_PIO_REARRS = 4 integer, parameter :: MAX_NVARS = 12 character(len=*), parameter :: PIO_NML_FNAME = 'pioperf_rearr.nl' @@ -34,6 +35,7 @@ program pioperformance_rearr integer :: vs, varsize(MAX_NVARS) ! Local size of array for idealized decomps logical :: unlimdimindof type(pio_rearr_opt_t) :: rearr_opts + integer :: log_level #ifdef BGQTRY external :: print_memusage #endif @@ -60,6 +62,10 @@ program pioperformance_rearr call CheckMPIreturn(__LINE__,ierr) if(mype==0) then Mastertask=.true. + if(mype == 0) then + print *, 'SCORPIO REPLAY TOOL' + print *, '=======================' + endif else Mastertask=.false. endif @@ -75,9 +81,13 @@ program pioperformance_rearr varsize = 0 varsize(1) = 1 unlimdimindof=.false. + log_level = 0 + if(mype == 0) then + print *, "Reading user input..." + endif call read_user_input(mype, decompfile, piotypes, rearrangers,& niotasks, nframes, unlimdimindof, nvars, varsize,& - rearr_opts, ierr) + rearr_opts, log_level, ierr) #ifdef SPIO_ENABLE_GPTL_TIMING #ifndef SPIO_ENABLE_GPTL_TIMING_INTERNAL @@ -92,6 +102,8 @@ program pioperformance_rearr if(rearrangers(1)==0) then rearrangers(1)=1 rearrangers(2)=2 + rearrangers(3)=3 + rearrangers(4)=4 endif do i=1,MAX_DECOMP_FILES @@ -103,7 +115,7 @@ program pioperformance_rearr if(nvars(nv)>0) then call pioperformance_rearrtest(decompfile(i), piotypes(1:niotypes),& mype, npe, rearrangers, rearr_opts, niotasks, nframes,& - nvars(nv), varsize(vs),unlimdimindof) + nvars(nv), varsize(vs),unlimdimindof, log_level) endif enddo endif @@ -263,7 +275,7 @@ end subroutine pio_rearr_str2opt ! Parse a single command line arg subroutine parse_and_process_input(argv, decompfiles, piotypes,& rearrangers, niotasks, nframes, unlimdimindof, nvars, varsize,& - rearr_opts, ierr) + rearr_opts, log_level, ierr) character(len=*), intent(in) :: argv character(len=MAX_FNAME_LEN), intent(out) :: decompfiles(MAX_DECOMP_FILES) integer, intent(out) :: piotypes(MAX_PIO_TYPES) @@ -274,6 +286,7 @@ subroutine parse_and_process_input(argv, decompfiles, piotypes,& integer, intent(out) :: nvars(MAX_NVARS) integer, intent(out) :: varsize(MAX_NVARS) type(pio_rearr_opt_t), intent(out) :: rearr_opts + integer, intent(out) :: log_level integer, intent(out) :: ierr integer, parameter :: MAX_PIO_REARR_OPT_LEN = 128 @@ -351,6 +364,9 @@ subroutine parse_and_process_input(argv, decompfiles, piotypes,& else if (argv(:pos) == "--pio-varsize=") then call init_arr_from_list(argv(pos+1:), iarr=varsize, ierr=ierr) !print *, "Read varsize : ", varsize + else if (argv(:pos) == "--pio-log-level=") then + read(argv(pos+1:), *) log_level + !print *, "Read varsize : ", varsize end if end if @@ -359,7 +375,7 @@ end subroutine parse_and_process_input ! Parse command line user options subroutine read_cmd_line_input(decompfile, piotypes, rearrangers,& niotasks, nframes, unlimdimindof, nvars, varsize,& - rearr_opts, ierr) + rearr_opts, log_level, ierr) character(len=MAX_FNAME_LEN), intent(out) :: decompfile(MAX_DECOMP_FILES) integer, intent(out) :: piotypes(MAX_PIO_TYPES) integer, intent(out) :: rearrangers(MAX_PIO_REARRS) @@ -369,6 +385,7 @@ subroutine read_cmd_line_input(decompfile, piotypes, rearrangers,& integer, intent(out) :: nvars(MAX_NVARS) integer, intent(out) :: varsize(MAX_NVARS) type(pio_rearr_opt_t), intent(out) :: rearr_opts + integer, intent(out) :: log_level integer, intent(out) :: ierr integer, parameter :: MAX_STDIN_ARG_LEN = 8192 @@ -380,7 +397,7 @@ subroutine read_cmd_line_input(decompfile, piotypes, rearrangers,& call get_command_argument(i, argv) call parse_and_process_input(argv, decompfile, piotypes, rearrangers,& niotasks, nframes, unlimdimindof, nvars, varsize,& - rearr_opts, ierr) + rearr_opts, log_level, ierr) end do end subroutine read_cmd_line_input @@ -536,7 +553,7 @@ end subroutine bcast_rearr_opts ! read (and override) the command line options subroutine read_user_input(mype, decompfile, piotypes, rearrangers,& niotasks, nframes, unlimdimindof, nvars, varsize,& - rearr_opts, ierr) + rearr_opts, log_level, ierr) integer, intent(in) :: mype character(len=MAX_FNAME_LEN), intent(out) :: decompfile(MAX_DECOMP_FILES) integer, intent(out) :: piotypes(MAX_PIO_TYPES) @@ -547,11 +564,13 @@ subroutine read_user_input(mype, decompfile, piotypes, rearrangers,& integer, intent(out) :: nvars(MAX_NVARS) integer, intent(out) :: varsize(MAX_NVARS) type(pio_rearr_opt_t), intent(out) :: rearr_opts + integer, intent(out) :: log_level integer, intent(out) :: ierr character(len=MAX_PIO_TYPENAME_LEN) :: pio_typenames(MAX_PIO_TYPES) pio_typenames = ' ' + log_level = 0 if(mype == 0) then ! Read namelist file @@ -561,7 +580,7 @@ subroutine read_user_input(mype, decompfile, piotypes, rearrangers,& ! Allow user to override the values via command line call read_cmd_line_input(decompfile, piotypes, rearrangers,& niotasks, nframes, unlimdimindof, nvars, varsize,& - rearr_opts, ierr) + rearr_opts, log_level, ierr) end if call MPI_Bcast(decompfile,MAX_FNAME_LEN*MAX_DECOMP_FILES,MPI_CHARACTER,0, MPI_COMM_WORLD,ierr) @@ -572,6 +591,7 @@ subroutine read_user_input(mype, decompfile, piotypes, rearrangers,& call MPI_Bcast(unlimdimindof, 1, MPI_INTEGER, 0, MPI_COMM_WORLD,ierr) call MPI_Bcast(nvars, MAX_NVARS, MPI_INTEGER, 0, MPI_COMM_WORLD,ierr) call MPI_Bcast(varsize, MAX_NVARS, MPI_INTEGER, 0, MPI_COMM_WORLD,ierr) + call MPI_Bcast(log_level, 1, MPI_INTEGER, 0, MPI_COMM_WORLD,ierr) call bcast_rearr_opts(rearr_opts) @@ -579,7 +599,7 @@ end subroutine read_user_input subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & rearrangers, rearr_opts, niotasks,nframes, nvars, varsize,& - unlimdimindof) + unlimdimindof, log_level) use pio character(len=*), intent(in) :: filename integer, intent(in) :: mype, npe_base @@ -591,6 +611,7 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & integer, intent(in) :: nvars integer, intent(in) :: varsize logical, intent(in) :: unlimdimindof + integer, intent(in) :: log_level integer(kind=PIO_Offset_kind), pointer :: compmap(:) integer :: ntasks integer :: comm @@ -622,7 +643,7 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & integer, parameter :: c0 = -1 double precision, parameter :: cd0 = 1.0e30 integer :: nvarmult - character(len=*), parameter :: rearr_name(MAX_PIO_REARRS) = (/' BOX','SUBSET'/) + character(len=*), parameter :: rearr_name(MAX_PIO_REARRS) = (/' BOX','SUBSET',' ANY','CONTIG'/) nullify(compmap) @@ -635,16 +656,10 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & #else call pio_readdof(filename, ndims, gdims, compmap, MPI_COMM_WORLD) #endif - -! print *,__FILE__,__LINE__,' gdims=',ndims endif maplen = size(compmap) -! color = 0 -! if(maplen>0) then - color = 1 -! endif - call MPI_Comm_split(MPI_COMM_WORLD, color, mype, comm, ierr) + comm = MPI_COMM_WORLD call MPI_Comm_size(comm, npe, ierr) call CheckMPIreturn(__LINE__,ierr) @@ -664,24 +679,36 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & gmaplen = product(gdims) endif +#ifdef VARINT allocate(ifld(maplen,nvars)) allocate(ifld_in(maplen,nvars,nframes)) + ifld = PIO_FILL_INT +#endif +#ifdef VARREAL allocate(rfld(maplen,nvars)) allocate(rfld_in(maplen,nvars,nframes)) + rfld = PIO_FILL_FLOAT +#endif +#ifdef VARDOUBLE allocate(dfld(maplen,nvars)) allocate(dfld_in(maplen,nvars,nframes)) - - ifld = PIO_FILL_INT - rfld = PIO_FILL_FLOAT dfld = PIO_FILL_DOUBLE +#endif + do nv=1,nvars do j=1,maplen if(compmap(j) > 0) then +#ifdef VARINT ifld(j,nv) = int(compmap(j)) - dfld(j,nv) = ifld(j,nv)/1000000.0 - rfld(j,nv) = 1.0E5*ifld(j,nv) +#endif +#ifdef VARREAL + rfld(j,nv) = 1.0E5*int(compmap(j)) +#endif +#ifdef VARDOUBLE + dfld(j,nv) = int(compmap(j))/1000000.0 +#endif endif enddo enddo @@ -693,17 +720,14 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & do k=1,size(piotypes) iotype = piotypes(k) call MPI_Barrier(comm,ierr) - if(mype==0) then - !print *,'iotype=',piotypes(k) - endif if(iotype==PIO_IOTYPE_PNETCDF) then mode = PIO_64BIT_DATA else mode = 0 endif - do rearrtype=1,2 + do rearrtype=1,4 rearr = rearrangers(rearrtype) - if(rearr /= PIO_REARR_SUBSET .and. rearr /= PIO_REARR_BOX) exit + if(rearr /= PIO_REARR_SUBSET .and. rearr /= PIO_REARR_BOX .and. rearr /= PIO_REARR_ANY .and. rearr /= PIO_REARR_CONTIG) exit do n=niomin,niomax ntasks = niotasks(n) @@ -713,11 +737,13 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & call pio_init(mype, comm, ntasks, 0, stride, PIO_REARR_SUBSET,& iosystem, rearr_opts=rearr_opts) + + ierr = PIO_set_log_level(log_level) write(fname, '(a,i1,a,i5.5,a,i5.5,a,i5.5,a,i1,a,i5.5,a,i5.5,a)') 'pioperf-rearr-', rearr, & '-ncomptasks-', npe, '-niotasks-', ntasks, '-stride-', stride, '-iotype-', iotype, & '-nframes-',nframes,'-nvars-',nvars,'.nc' - + ierr = PIO_CreateFile(iosystem, File, iotype, trim(fname), mode) call WriteMetadata(File, gdims, vari, varr, vard, unlimdimindof) @@ -738,14 +764,11 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & endif wall(1) = MPI_Wtime() - ! print *,__FILE__,__LINE__,minval(dfld),maxval(dfld),minloc(dfld),maxloc(dfld) do frame=1,nframes recnum = frame if( unlimdimindof) then recnum = 1 + (frame-1)*gdims(ndims) -! compmap = compmap2 + (frame-1)*gdims(ndims) -! print *,__FILE__,__LINE__,compmap #ifdef VARINT call PIO_InitDecomp(iosystem, PIO_INT, gdims, compmap, iodesc_i4, rearr=rearr) #endif @@ -756,10 +779,8 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & call PIO_InitDecomp(iosystem, PIO_DOUBLE, gdims, compmap, iodesc_r8, rearr=rearr) #endif endif - !if(mype==0) print *,__FILE__,__LINE__,'Frame: ',recnum do nv=1,nvars - !if(mype==0) print *,__FILE__,__LINE__,'var: ',nv #ifdef VARINT call PIO_setframe(File, vari(nv), recnum) call pio_write_darray(File, vari(nv), iodesc_i4, ifld(:,nv) , ierr, fillval= PIO_FILL_INT) @@ -788,7 +809,6 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & wall_wr_darr(2) = MPI_Wtime() call pio_closefile(File) - call MPI_Barrier(comm,ierr) wall(2) = MPI_Wtime() @@ -892,15 +912,12 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & #ifdef VARINT #ifdef DEBUG write(*,'(a11,i2,a9,i11,a9,i11,a9,i2)') & - ' Int PE=',mype,'ifld=',ifld(j,nv),' ifld_in=',ifld_in(j,nv,frame),' compmap=',compmap(j) + ' Int PE=',mype,'ifld=',ifld(j,nv),' ifld_in=',ifld_in(j,nv,frame),' compmap=',compmap(j) #endif if(ifld(j,nv) /= ifld_in(j,nv,frame)) then - !if(errorcnt < 10) then - ! print *,__LINE__,'Int: ',mype,j,nv,ifld(j,nv),ifld_in(j,nv,frame),compmap(j) - !endif write(*,*) '***ERROR:Mismatch!***' write(*,'(a11,i2,a9,i11,a9,i11,a9,i2)') & - ' Int PE=',mype,'ifld=',ifld(j,nv),' ifld_in=',ifld_in(j,nv,frame),' compmap=',compmap(j) + ' Int PE=',mype,'ifld=',ifld(j,nv),' ifld_in=',ifld_in(j,nv,frame),' compmap=',compmap(j) errorcnt = errorcnt+1 endif @@ -908,16 +925,13 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & #ifdef VARREAL #ifdef DEBUG write(*,'(a11,i2,a9,f11.2,a9,f11.2,a9,i2)') & - ' Real PE=',mype,'rfld=',rfld(j,nv),' rfld_in=',rfld_in(j,nv,frame),' compmap=',compmap(j) + ' Real PE=',mype,'rfld=',rfld(j,nv),' rfld_in=',rfld_in(j,nv,frame),' compmap=',compmap(j) #endif if(rfld(j,nv) /= rfld_in(j,nv,frame) ) then - !if(errorcnt < 10) then - ! print *,__LINE__,'Real:', mype,j,nv,rfld(j,nv),rfld_in(j,nv,frame),compmap(j) - !endif write(*,*) '***ERROR:Mismatch!***' write(*,'(a11,i2,a9,f11.2,a9,f11.2,a9,i2)') & - ' Real PE=',mype,'rfld=',rfld(j,nv),' rfld_in=',rfld_in(j,nv,frame),' compmap=',compmap(j) + ' Real PE=',mype,'rfld=',rfld(j,nv),' rfld_in=',rfld_in(j,nv,frame),' compmap=',compmap(j) errorcnt = errorcnt+1 endif @@ -925,15 +939,12 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & #ifdef VARDOUBLE #ifdef DEBUG write(*,'(a11,i2,a9,d11.4,a9,d11.4,a9,i2)') & - 'Double PE=',mype,'dfld=',dfld(j,nv),'dfld_in=',dfld_in(j,nv,frame),'compmap=',compmap(j) + 'Double PE=',mype,'dfld=',dfld(j,nv),'dfld_in=',dfld_in(j,nv,frame),'compmap=',compmap(j) #endif if(dfld(j,nv) /= dfld_in(j,nv,frame) ) then - !if(errorcnt < 10) then - ! print *,__LINE__,'Dbl:',mype,j,nv,dfld(j,nv),dfld_in(j,nv,frame),compmap(j) - !endif write(*,*) '***ERROR:Mismatch!***' write(*,'(a11,i2,a9,d11.4,a9,d11.4,a9,i2)') & - 'Double PE=',mype,'dfld=',dfld(j,nv),'dfld_in=',dfld_in(j,nv,frame),'compmap=',compmap(j) + 'Double PE=',mype,'dfld=',dfld(j,nv),'dfld_in=',dfld_in(j,nv,frame),'compmap=',compmap(j) errorcnt = errorcnt+1 endif @@ -964,7 +975,7 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & rearr_name(rearr), piotypes(k), ntasks, nvars, & nvarmult*nvars*nframes*gmaplen*4.0D0/(1048576.0*wall(2)) #ifdef BGQTRY - call print_memusage() + call print_memusage() #endif end if #ifdef VARREAL @@ -982,13 +993,12 @@ subroutine pioperformance_rearrtest(filename, piotypes, mype, npe_base, & enddo deallocate(compmap) deallocate(gdims) - deallocate(ifld) - deallocate(ifld_in) - deallocate(rfld) - deallocate(dfld) - deallocate(dfld_in) - deallocate(rfld_in) - call MPI_Comm_free(comm, ierr) + if(allocated(ifld)) deallocate(ifld) + if(allocated(ifld_in)) deallocate(ifld_in) + if(allocated(rfld)) deallocate(rfld) + if(allocated(rfld_in)) deallocate(rfld_in) + if(allocated(dfld)) deallocate(dfld) + if(allocated(dfld_in)) deallocate(dfld_in) endif end subroutine pioperformance_rearrtest diff --git a/tests/pnetcdf/CMakeLists.txt b/tests/pnetcdf/CMakeLists.txt new file mode 100644 index 00000000000..d9e74295517 --- /dev/null +++ b/tests/pnetcdf/CMakeLists.txt @@ -0,0 +1,109 @@ +include (LibMPI) + +message(STATUS "===== Configuring SCORPIO PnetCDF tests... =====") +#============================================================================== +# HELPER MACROS +#============================================================================== +include(SPIOUtils) + +#============================================================================== +# SET THE COMPILER OPTIONS +#============================================================================== +include_directories("${SCORPIO_SOURCE_DIR}/tests/cunit" "${SCORPIO_SOURCE_DIR}/src/clib" "${SCORPIO_BINARY_DIR}/src/clib") +# Required for time.h to include decl of asctime_r() +add_compile_definitions(_POSIX_C_SOURCE=200112L) + +# Compiler-specific compiler options +string (TOUPPER "${CMAKE_C_COMPILER_ID}" CMAKE_C_COMPILER_NAME) +if (CMAKE_C_COMPILER_NAME STREQUAL "CRAY") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -h std=c99") +elseif (CMAKE_C_COMPILER_NAME STREQUAL "PGI") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -c99") +elseif (CMAKE_C_COMPILER_NAME STREQUAL "NVHPC") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -c99") +else () + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") +endif () + +string (TOUPPER "${CMAKE_CXX_COMPILER_ID}" CMAKE_CXX_COMPILER_NAME) +if (CMAKE_CXX_COMPILER_NAME STREQUAL "CRAY") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -h std=c++11") +else () + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif () + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") + +#============================================================================== +# FIND EXTERNAL LIBRARIES/DEPENDENCIES +#============================================================================== +#===== MPI ===== +if (PIO_USE_MPISERIAL) + find_package (MPISERIAL COMPONENTS C REQUIRED) + if (MPISERIAL_C_FOUND) + set (CMAKE_REQUIRED_INCLUDES ${MPISERIAL_C_INCLUDE_DIRS}) + endif () +else () + find_package (MPI REQUIRED) + set (CMAKE_REQUIRED_INCLUDES ${MPI_INCLUDE_PATH}) +endif () + +#===== GPTL ===== +if (PIO_ENABLE_TIMING) + find_package (GPTL COMPONENTS C QUIET) + if (GPTL_C_FOUND) + message (STATUS "Found GPTL C: ${GPTL_C_LIBRARIES}") + else () + include_directories(${PROJECT_SOURCE_DIR}/../src/gptl) + message (STATUS "Using internal GPTL C library for timing") + endif () +endif () + +#===== PnetCDF-C ===== +if (WITH_PNETCDF) + find_package (PnetCDF ${PNETCDF_MIN_VER_REQD} COMPONENTS C) + if (PnetCDF_FOUND) + message(STATUS "PnetCDF C library dependencies: ${PnetCDF_C_LIBRARY}") + include_directories (${PnetCDF_C_INCLUDE_DIRS}) + link_libraries (${PnetCDF_C_LIBRARIES}) + else () + message(STATUS "Could not find PnetCDF library, disable PnetCDF tests") + endif () +else () + message(STATUS "Disabling support for PnetCDF tests") +endif () + +#============================================================================== +# DEFINE THE TARGETS AND TESTS +#============================================================================== + +# Exclude tests that require more than 1 procs when using the MPI serial library +if (WITH_PNETCDF AND PnetCDF_FOUND) + add_spio_executable (test_pnetcdf TRUE "" test_pnetcdf.c) + add_spio_executable (test_pnetcdf_4d TRUE "" test_pnetcdf_4d.cpp) + + add_dependencies (tests test_pnetcdf test_pnetcdf_4d) +endif () + +set (SPIO_TEST_MAX_NPROCS 4) + +# Test Timeout in seconds. +if (PIO_VALGRIND_CHECK) + set (DEFAULT_TEST_TIMEOUT 480) +else () + set (DEFAULT_TEST_TIMEOUT 240) +endif () + +if (WITH_PNETCDF) + foreach(nproc RANGE 1 ${SPIO_TEST_MAX_NPROCS}) + add_mpi_test(test_pnetcdf${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_pnetcdf + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + add_mpi_test(test_pnetcdf_4d${nproc} + EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/test_pnetcdf_4d + NUMPROCS ${nproc} + TIMEOUT ${DEFAULT_TEST_TIMEOUT}) + endforeach() +endif () diff --git a/tests/pnetcdf/test_pnetcdf.c b/tests/pnetcdf/test_pnetcdf.c new file mode 100644 index 00000000000..a172c70e845 --- /dev/null +++ b/tests/pnetcdf/test_pnetcdf.c @@ -0,0 +1,272 @@ +/********************************************************************* + * + * Copyright (C) 2013, Northwestern University and Argonne National Laboratory + * See COPYRIGHT notice in top-level directory. + * + *********************************************************************/ +/* $Id$ */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * This example shows how to use ncmpi_put_vara_int_all() to write a 2D 4-byte + * integer array in parallel. It first defines a netCDF variable of size + * global_ny * global_nx where + * global_ny == NY and + * global_nx == (NX * number of MPI processes). + * The data partitioning pattern is a column-wise partitioning across all + * processes. Each process writes a subarray of size ny * nx. + * + * To compile: + * mpicc -O2 put_vara.c -o put_vara -lpnetcdf + * + * Example commands for MPI run and outputs from running ncmpidump on the + * output netCDF file produced by this example program: + * + * % mpiexec -n 4 ./put_vara /pvfs2/wkliao/testfile.nc + * + * % ncmpidump /pvfs2/wkliao/testfile.nc + * netcdf testfile { + * // file format: CDF-5 (big variables) + * dimensions: + * y = 10 ; + * x = 16 ; + * variables: + * int var(y, x) ; + * var:str_att_name = "example attribute of type text." ; + * var:float_att_name = 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f ; + * var:short_att_name = 1000s ; + * // global attributes: + * :history = "Mon Aug 13 21:27:48 2018" ; + * "" ; + * data: + * + * var = + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 ; + * } + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include /* strcpy(), strncpy() */ +#include /* getopt() */ +#include /* time() localtime(), asctime() */ +#include +#include + +#define NZ 2 +#define NY 10 +#define NX 4 + +static int verbose; + +#define ERR {if(err!=NC_NOERR){printf("Error at %s:%d : %s\n", __FILE__,__LINE__, ncmpi_strerror(err));nerrs++;}} + +static void +usage(char *argv0) +{ + char *help = + "Usage: %s [-h] | [-q] [-k format] [file_name]\n" + " [-h] Print help\n" + " [-q] Quiet mode (reports when fail)\n" + " [-k format] file format: 1 for CDF-1, 2 for CDF-2, 3 for NetCDF4,\n" + " 4 for NetCDF4 classic model, 5 for CDF-5\n" + " [filename] output netCDF file name\n"; + fprintf(stderr, help, argv0); +} + +/*----< pnetcdf_check_mem_usage() >------------------------------------------*/ +/* check PnetCDF library internal memory usage */ +static int +pnetcdf_check_mem_usage(MPI_Comm comm) +{ + int err, nerrs=0, rank; + MPI_Offset malloc_size, sum_size; + + MPI_Comm_rank(comm, &rank); + + /* print info about PnetCDF internal malloc usage */ + err = ncmpi_inq_malloc_max_size(&malloc_size); + if (err == NC_NOERR) { + MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD); + if (rank == 0 && verbose) + printf("maximum heap memory allocated by PnetCDF internally is %lld bytes\n", + sum_size); + + /* check if there is any PnetCDF internal malloc residue */ + err = ncmpi_inq_malloc_size(&malloc_size); + MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD); + if (rank == 0 && sum_size > 0) + printf("heap memory allocated by PnetCDF internally has %lld bytes yet to be freed\n", + sum_size); + } + else if (err != NC_ENOTENABLED) { + printf("Error at %s:%d: %s\n", __FILE__,__LINE__,ncmpi_strerror(err)); + nerrs++; + } + return nerrs; +} + +/*----< pnetcdf_io() >-------------------------------------------------------*/ +static int +pnetcdf_io(MPI_Comm comm, char *filename, int cmode) +{ + int i, j, z, rank, nprocs, err, nerrs=0; + int ncid, var1did, var2did, var3did, dimid[3], buf1d[NX], buf2d[NY][NX], buf3d[NZ][NY][NX]; + char str_att[128]; + float float_att[100]; + MPI_Offset global_nz, global_ny, global_nx; + MPI_Offset start[3], count[3]; + + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &nprocs); + + /* create a new file for writing ----------------------------------------*/ + cmode |= NC_CLOBBER; + err = ncmpi_create(comm, filename, cmode, MPI_INFO_NULL, &ncid); ERR + + for (i=0; i 0); +} + diff --git a/tests/pnetcdf/test_pnetcdf_4d.cpp b/tests/pnetcdf/test_pnetcdf_4d.cpp new file mode 100644 index 00000000000..a12a9c5f7d7 --- /dev/null +++ b/tests/pnetcdf/test_pnetcdf_4d.cpp @@ -0,0 +1,611 @@ +/********************************************************************* + * + * Copyright (C) 2013, Northwestern University and Argonne National Laboratory + * See COPYRIGHT notice in top-level directory. + * + *********************************************************************/ +/* $Id$ */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * This example shows how to use ncmpi_put_vara_int_all() to write a 2D 4-byte + * integer array in parallel. It first defines a netCDF variable of size + * global_ny * global_nx where + * global_ny == NY and + * global_nx == (NX * number of MPI processes). + * The data partitioning pattern is a column-wise partitioning across all + * processes. Each process writes a subarray of size ny * nx. + * + * To compile: + * mpicc -O2 put_vara.c -o put_vara -lpnetcdf + * + * Example commands for MPI run and outputs from running ncmpidump on the + * output netCDF file produced by this example program: + * + * % mpiexec -n 4 ./put_vara /pvfs2/wkliao/testfile.nc + * + * % ncmpidump /pvfs2/wkliao/testfile.nc + * netcdf testfile { + * // file format: CDF-5 (big variables) + * dimensions: + * y = 10 ; + * x = 16 ; + * variables: + * int var(y, x) ; + * var:str_att_name = "example attribute of type text." ; + * var:float_att_name = 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f ; + * var:short_att_name = 1000s ; + * // global attributes: + * :history = "Mon Aug 13 21:27:48 2018" ; + * "" ; + * data: + * + * var = + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + * 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 ; + * } + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* strcpy(), strncpy() */ +#include /* getopt() */ +#include /* time() localtime(), asctime() */ +#include +#include +#include +#include + +#define NZ 2 +#define NY 7 +#define NX 2 + +static int verbose; + +#define ERR {if(err!=NC_NOERR){printf("Error at %s:%d : %s\n", __FILE__,__LINE__, ncmpi_strerror(err));nerrs++;}} + +static void +usage(char *argv0) +{ + const char *help = + "Usage: %s [-h] | [-q] [-k format] [file_name]\n" + " [-h] Print help\n" + " [-q] Quiet mode (reports when fail)\n" + " [-k format] file format: 1 for CDF-1, 2 for CDF-2, 3 for NetCDF4,\n" + " 4 for NetCDF4 classic model, 5 for CDF-5\n" + " [filename] output netCDF file name\n"; + fprintf(stderr, help, argv0); +} + +/*----< pnetcdf_check_mem_usage() >------------------------------------------*/ +/* check PnetCDF library internal memory usage */ +static int +pnetcdf_check_mem_usage(MPI_Comm comm) +{ + int err, nerrs=0, rank; + MPI_Offset malloc_size, sum_size; + + MPI_Comm_rank(comm, &rank); + + /* print info about PnetCDF internal malloc usage */ + err = ncmpi_inq_malloc_max_size(&malloc_size); + if (err == NC_NOERR) { + MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD); + if (rank == 0 && verbose) + printf("maximum heap memory allocated by PnetCDF internally is %lld bytes\n", + sum_size); + + /* check if there is any PnetCDF internal malloc residue */ + err = ncmpi_inq_malloc_size(&malloc_size); + MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD); + if (rank == 0 && sum_size > 0) + printf("heap memory allocated by PnetCDF internally has %lld bytes yet to be freed\n", + sum_size); + } + else if (err != NC_ENOTENABLED) { + printf("Error at %s:%d: %s\n", __FILE__,__LINE__,ncmpi_strerror(err)); + nerrs++; + } + return nerrs; +} + +void convert_gidx_to_dim_idx(int gidx, int ndims, const MPI_Offset *gdimlen, std::vector &dim_idx) +{ + int dim_chunk_sz[ndims]; + + assert(ndims > 0); + assert(gdimlen && (static_cast(dim_idx.size()) == ndims)); + + dim_chunk_sz[ndims - 1] = 1; + for(int i = ndims - 2; i >= 0; i--){ + dim_chunk_sz[i] = dim_chunk_sz[i + 1] * gdimlen[i + 1]; + } + + for(int i = 0; i < ndims; i++){ + dim_idx[i] = gidx / dim_chunk_sz[i]; + + gidx -= dim_idx[i] * dim_chunk_sz[i]; + } + + assert(gidx == 0); +} + +/* Debug print a vector */ +template +static void print_1dvec(const std::vector &v) +{ + std::ostringstream ostr; + ostr << "["; + std::copy(v.cbegin(), v.cend(), std::ostream_iterator(ostr, ",")); + ostr << "]"; + std::cout << ostr.str().c_str() << "\n" << std::flush; +} + + +void partition_range_to_contig_dim_ranges(int ndims, const MPI_Offset *gdimlen, + MPI_Offset start_off, MPI_Offset end_off, + std::vector &start, std::vector &count) +{ + assert(ndims > 0); + assert(gdimlen && (start_off >= 0) && (end_off >= start_off)); + + start.clear(); count.clear(); + + MPI_Offset cur_tot_count = end_off - start_off; + + if(cur_tot_count == 0) return; + + // First get start_off to a multiple of last dim len + // e.g. v[z][y][x], get range to get to multiple of x + MPI_Offset cur_range_start = start_off; + MPI_Offset cur_range_count = 0; + MPI_Offset off_from_last_dim_mult = cur_range_start % gdimlen[ndims - 1]; + if(off_from_last_dim_mult != 0){ + cur_range_count = gdimlen[ndims - 1] - off_from_last_dim_mult; + if(start_off + cur_range_count > end_off){ + cur_range_count = end_off - start_off; + } + start.push_back(cur_range_start); + count.push_back(cur_range_count); + + cur_range_start += cur_range_count; + cur_tot_count -= cur_range_count; + } + + if(cur_tot_count == 0) return; + + // Break/partition the cur_range_start to end_off to contiguous dim chunks + // e.g. v[z][y][x], break up remaining range to ranges of sizes y*x, x, 1 + std::vector dim_chunk_sz(ndims, 1); + for(int i = ndims - 2; i >= 0; i--){ + dim_chunk_sz[i] = dim_chunk_sz[i + 1] * gdimlen[i + 1]; + } + + MPI_Offset cur_range_end = cur_range_start; + for(int i = 0; i < ndims; i++){ + cur_range_end = dim_chunk_sz[i] * (end_off / dim_chunk_sz[i]); + if(cur_range_end != 0){ + cur_range_count = cur_range_end - cur_range_start; + start.push_back(cur_range_start); + count.push_back(cur_range_count); + + cur_range_start += cur_range_count; + cur_tot_count -= cur_range_count; + } + if(cur_tot_count == 0) break; + } + + assert(cur_tot_count == 0); +} + +int validate_starts_counts(MPI_Offset start_off, MPI_Offset end_off, + const std::vector > &starts, + const std::vector > &counts) +{ + const int FAIL = 1, SUCCESS = 0; + + if(start_off > end_off){ + std::cerr << "ERROR: Start offset provided needs to be <= end offset\n"; + return FAIL; + } + + if(starts.size() != counts.size()){ + std::cerr << "ERROR: Mismatched starts/counts array sizes\n"; + return FAIL; + } + + if(starts.size() == 0) return SUCCESS; + + if(starts[0].size() == 0){ + std::cerr << "ERROR: No start arrays in the starts vector\n"; + return FAIL; + } + + if(starts[0][0] != start_off){ + std::cerr << "ERROR: Invalid start offset, starts[0][0] = " << starts[0][0] << ", expected = " << start_off << "\n" << std::flush; + return FAIL; + } + + MPI_Offset next_exp_off = start_off; + for(std::size_t i = 0; i < starts.size(); i++){ + if(starts[i].size() != counts[i].size()){ + std::cerr << "ERROR: Mismatch in start/count array sizes: starts[" << i << "].size() = " << starts[i].size() << ", counts[" << i << "].size() = " << counts[i].size() << "\n" << std::flush; + return FAIL; + } + for(std::size_t j = 0; j < starts[i].size(); j++){ + if(starts[i][j] != next_exp_off){ + std::cout << "ERROR : Mismatch in start offset, starts[" << i << "][" << j << "] = " << starts[i][j] << ", expected = " << next_exp_off << "\n" << std::flush; + return FAIL; + } + next_exp_off += counts[i][j]; + } + } + + return SUCCESS; +} + +void test_partition_ranges(void ) +{ + const MPI_Offset ZMAX = 10; + const MPI_Offset YMAX = 10; + const MPI_Offset XMAX = 10; + const int MAX_NPROCS = 6; + + for(MPI_Offset z = 1; z < ZMAX; z++){ + for(MPI_Offset y = 1; y < YMAX; y++){ + for(MPI_Offset x = 1; x < XMAX; x++){ + std::vector gdimlen = {z, y, x}; + MPI_Offset tot_gdimlen = 1; + std::vector start, count; + std::vector > starts, counts; + + for(std::size_t d = 0; d < gdimlen.size(); d++){ + tot_gdimlen *= gdimlen[d]; + } + std::cout << "Testing gdimlen[] : "; print_1dvec(gdimlen); + for(int nprocs = 1; nprocs < std::min(MAX_NPROCS, static_cast(tot_gdimlen)); nprocs++){ + + MPI_Offset proc_chunk_sz = tot_gdimlen / nprocs; + for(int rank = 0; rank < nprocs; rank++){ + MPI_Offset start_off = rank * proc_chunk_sz; + if(rank == nprocs - 1) proc_chunk_sz = tot_gdimlen - (nprocs - 1) * proc_chunk_sz; + MPI_Offset end_off = start_off + proc_chunk_sz; + + partition_range_to_contig_dim_ranges(gdimlen.size(), gdimlen.data(), start_off, end_off, start, count); + std::cout << "[" << rank << "/" << nprocs << "]: partition_range start/count [" << start_off << "," << end_off <<") : "; print_1dvec(start); print_1dvec(count); + starts.push_back(start); counts.push_back(count); + } + validate_starts_counts(0, tot_gdimlen, starts, counts); + starts.clear(); counts.clear(); + } + } + } + } +} + +static inline void convert_off_to_start_dim_idx(const std::vector &dim_chunk_sz, int start_off, std::vector &start_dim_idx) +{ + assert(dim_chunk_sz.size() == start_dim_idx.size()); + for(std::size_t i = 0; i < start_dim_idx.size(); i++){ + start_dim_idx[i] = 0; + } + for(std::size_t i = 0; i < dim_chunk_sz.size(); i++){ + start_dim_idx[i] = start_off / dim_chunk_sz[i]; + + start_off -= start_dim_idx[i] * dim_chunk_sz[i]; + if(start_off == 0) break; + } + assert(start_off == 0); +} + +static inline void convert_off_to_count_dim_idx(int ndims, const MPI_Offset *gdimlen, const std::vector &dim_chunk_sz, int count_off, std::vector &count_dim_idx) +{ + assert(dim_chunk_sz.size() == count_dim_idx.size()); + assert(static_cast(count_dim_idx.size()) == ndims); + + for(int i = 0; i < ndims; i++){ + count_dim_idx[i] = gdimlen[i]; + } + for(std::size_t i = 0; i < dim_chunk_sz.size(); i++){ + count_dim_idx[i] = count_off / dim_chunk_sz[i]; + + count_off -= count_dim_idx[i] * dim_chunk_sz[i]; + if(count_off == 0) break; + } + + assert(count_off == 0); + for(std::size_t i = 0; i < count_dim_idx.size(); i++){ + if(count_dim_idx[i] != 0){ + break; + } + else{ + count_dim_idx[i] = 1; + } + } +} + +void convert_start_count_off_to_dim_idx(int ndims, const MPI_Offset *gdimlen, + std::vector &start_off, std::vector &count_off, + std::vector > &starts, std::vector > &counts) +{ + assert((ndims > 0) && gdimlen); + assert(start_off.size() == count_off.size()); + + std::vector dim_chunk_sz(ndims, 1); + for(int i = ndims - 2; i >= 0; i--){ + dim_chunk_sz[i] = dim_chunk_sz[i + 1] * gdimlen[i + 1]; + } + std::vector start(ndims, 0), count(ndims, 1); + assert(start_off.size() == count_off.size()); + for(std::size_t i = 0; i < start_off.size(); i++){ + convert_off_to_start_dim_idx(dim_chunk_sz, start_off[i], start); + convert_off_to_count_dim_idx(ndims, gdimlen, dim_chunk_sz, count_off[i], count); + starts.push_back(start); counts.push_back(count); + } +} + +void partition_buf(int rank, int nprocs, int ndims, const MPI_Offset *gdimlen, + std::vector > &starts, std::vector > &counts) +{ + std::vector start_coff, count_coff; + + MPI_Offset tot_gdimlen = 1; + for(int i = 0; i < ndims; i++){ + tot_gdimlen *= gdimlen[i]; + } + + MPI_Offset iochunk_sz = tot_gdimlen / nprocs; + assert(iochunk_sz > 0); + + MPI_Offset start_off = rank * iochunk_sz; + if(rank == nprocs - 1){ + iochunk_sz = tot_gdimlen - (nprocs - 1) * iochunk_sz; + } + MPI_Offset end_off = start_off + iochunk_sz; + + //std::cout << "start/end off : " << start_off << ", " << end_off << "\n" << std::flush; + partition_range_to_contig_dim_ranges(ndims, gdimlen, start_off, end_off, start_coff, count_coff); + //usleep(rank * 1000000); + //std::cout << "start/end contig off : "; print_1dvec(start_coff); print_1dvec(count_coff); + convert_start_count_off_to_dim_idx(ndims, gdimlen, start_coff, count_coff, starts, counts); +} + +/* +// Hard-coded partition for var [2, 7, 2] with 3 procs +void hc_partition_buf(int rank, int nprocs, int ndims, const MPI_Offset *gdimlen, + std::vector > &starts, std::vector > &counts) +{ + std::vector hc_start(ndims, 0), hc_count(ndims, 1); + if(rank == 0){ + hc_start = {0, 0, 0}; + hc_count = {1, 4, 2}; + + starts.push_back(hc_start); + counts.push_back(hc_count); + } + else if(rank == 1){ + hc_start = {0, 4, 0}; + hc_count = {1, 3, 2}; + + starts.push_back(hc_start); + counts.push_back(hc_count); + + hc_start = {1, 0, 0}; + hc_count = {1, 1, 2}; + + starts.push_back(hc_start); + counts.push_back(hc_count); + } + else if(rank == 2){ + hc_start = {1, 1, 0}; + hc_count = {1, 6, 2}; + + starts.push_back(hc_start); + counts.push_back(hc_count); + } + else{ + assert(0); + } +} +*/ + +/*----< pnetcdf_io() >-------------------------------------------------------*/ +static int +pnetcdf_io(MPI_Comm comm, char *filename, int cmode) +{ + int i, j, z, rank, nprocs, err, nerrs=0; + int ncid, dimid[3]; + int dimid_x_1d2d; + int var1did, buf1d[NX]; + int var2did, buf2d[NY][NX]; + int var3did, buf3d[NZ][NY][NX], var3d_rankid, buf3d_rank[NZ][NY][NX]; + char str_att[128]; + float float_att[100]; + MPI_Offset global_nz, global_ny, global_nx, gdimlen[3]; + //MPI_Offset global_tot_sz; + MPI_Offset global_nx_1d2d; + MPI_Offset start[3], count[3]; + std::vector > starts, counts; + + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &nprocs); + + /* create a new file for writing ----------------------------------------*/ + cmode |= NC_CLOBBER; + err = ncmpi_create(comm, filename, cmode, MPI_INFO_NULL, &ncid); ERR + + for (i=0; i 0); +} + diff --git a/tools/adios2pio-nm/CMakeLists.txt b/tools/adios2pio-nm/CMakeLists.txt index b6b6b116e69..9d8edffdf1a 100644 --- a/tools/adios2pio-nm/CMakeLists.txt +++ b/tools/adios2pio-nm/CMakeLists.txt @@ -88,8 +88,16 @@ endif () #============================================================================== # DEFINE THE TARGET LIBRARY #============================================================================== +# FIXME: Remove this hack once we fix E3SM dependency for this lib +macro(create_dummy_adios_lib ) + set(spio_dummy_adios2pio_nm_lib_src "${CMAKE_CURRENT_BINARY_DIR}/spio_dummy_adios2pio_nm_lib.cxx") + file(WRITE "${spio_dummy_adios2pio_nm_lib_src}" "/* DUMMY FILE */") + add_library(adios2pio-nm-lib ${spio_dummy_adios2pio_nm_lib_src}) +endmacro() + SET(SRC ${SCORPIO_SOURCE_DIR}/util/argparser.cxx adios2pio-nm.cxx) -add_library(adios2pio-nm-lib ${SCORPIO_SOURCE_DIR}/util/spio_misc_tool_utils.cxx adios2pio-nm-lib.cxx) +add_library(adios2pio-nm-lib-objs OBJECT ${SCORPIO_SOURCE_DIR}/util/spio_misc_tool_utils.cxx adios2pio-nm-lib.cxx) +create_dummy_adios_lib() include_directories( "${PROJECT_SOURCE_DIR}" # to find foo/foo.h "${PROJECT_BINARY_DIR}") # to find foo/config.h @@ -115,8 +123,9 @@ else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif () -target_include_directories(adios2pio-nm-lib PRIVATE +target_include_directories(adios2pio-nm-lib-objs PRIVATE ${SCORPIO_SOURCE_DIR}/src/clib + ${SCORPIO_SOURCE_DIR}/src/clib/util ${SCORPIO_BINARY_DIR}/src/clib ${SCORPIO_SOURCE_DIR}/util ${PnetCDF_C_INCLUDE_DIRS} @@ -126,6 +135,7 @@ target_include_directories(adios2pio-nm-lib PRIVATE target_include_directories(adios2pio-nm.exe PRIVATE ${SCORPIO_SOURCE_DIR}/src/clib + ${SCORPIO_SOURCE_DIR}/src/clib/util ${SCORPIO_BINARY_DIR}/src/clib ${SCORPIO_SOURCE_DIR}/util ${PnetCDF_C_INCLUDE_DIRS} @@ -135,8 +145,12 @@ target_include_directories(adios2pio-nm.exe PRIVATE # Adding path to internal GPTL lib, if needed if (PIO_ENABLE_TIMING) - if (NOT GPTL_C_FOUND) - target_include_directories(adios2pio-nm-lib + if (GPTL_C_FOUND) + target_include_directories(adios2pio-nm-lib-objs + PRIVATE ${GPTL_C_INCLUDE_DIRS}) + else () + # Use internal GPTL lib + target_include_directories(adios2pio-nm-lib-objs PRIVATE ${PROJECT_SOURCE_DIR}/../src/gptl) target_include_directories(adios2pio-nm.exe PRIVATE ${PROJECT_SOURCE_DIR}/../src/gptl) @@ -144,75 +158,76 @@ if (PIO_ENABLE_TIMING) endif () # System and compiler CPP directives -target_compile_definitions (adios2pio-nm-lib +target_compile_definitions (adios2pio-nm-lib-objs PRIVATE ${CMAKE_SYSTEM_DIRECTIVE}) -target_compile_definitions (adios2pio-nm-lib +target_compile_definitions (adios2pio-nm-lib-objs PUBLIC ${CMAKE_C_COMPILER_DIRECTIVE}) # Skip MPI C++ headers/bindings for MPICH lib -target_compile_definitions (adios2pio-nm-lib +target_compile_definitions (adios2pio-nm-lib-objs PUBLIC MPICH_SKIP_MPICXX) # Skip MPI C++ headers/bindings for OpenMPI lib -target_compile_definitions (adios2pio-nm-lib +target_compile_definitions (adios2pio-nm-lib-objs PUBLIC OMPI_SKIP_MPICXX) # Skip MPI C++ headers/bindings for SGI MPT lib -target_compile_definitions (adios2pio-nm-lib +target_compile_definitions (adios2pio-nm-lib-objs PUBLIC MPI_NO_CPPBIND) # Set external lib compiler/link flags if (PnetCDF_C_FOUND) - target_include_directories (adios2pio-nm-lib + target_include_directories (adios2pio-nm-lib-objs PUBLIC ${PnetCDF_C_INCLUDE_DIRS}) - target_compile_definitions (adios2pio-nm-lib + target_compile_definitions (adios2pio-nm-lib-objs PUBLIC _PNETCDF) - target_link_libraries (adios2pio-nm-lib + target_link_libraries (adios2pio-nm-lib-objs PUBLIC ${PnetCDF_C_LIBRARIES}) else () - target_compile_definitions (adios2pio-nm-lib + target_compile_definitions (adios2pio-nm-lib-objs PUBLIC _NOPNETCDF) endif () if (NetCDF_C_FOUND) - target_include_directories (adios2pio-nm-lib + target_include_directories (adios2pio-nm-lib-objs PUBLIC ${NetCDF_C_INCLUDE_DIRS}) - target_compile_definitions (adios2pio-nm-lib + target_compile_definitions (adios2pio-nm-lib-objs PUBLIC _NETCDF) - target_link_libraries (adios2pio-nm-lib + target_link_libraries (adios2pio-nm-lib-objs PUBLIC ${NetCDF_C_LIBRARIES}) if (${NetCDF_C_HAS_PARALLEL}) - target_compile_definitions (adios2pio-nm-lib + target_compile_definitions (adios2pio-nm-lib-objs PUBLIC _NETCDF4) endif () else () - target_compile_definitions (adios2pio-nm-lib + target_compile_definitions (adios2pio-nm-lib-objs PUBLIC _NONETCDF) endif () if (ADIOS2_FOUND) - target_compile_definitions (adios2pio-nm-lib + target_compile_definitions (adios2pio-nm-lib-objs PRIVATE _ADIOS2) - target_link_libraries (adios2pio-nm-lib + target_link_libraries (adios2pio-nm-lib-objs PUBLIC adios2::adios2) else () - target_compile_definitions (adios2pio-nm-lib + target_compile_definitions (adios2pio-nm-lib-objs PUBLIC _NOADIOS) endif () # Add the extra (user-specified) compile/link options -target_include_directories (adios2pio-nm-lib +target_include_directories (adios2pio-nm-lib-objs PUBLIC ${PIO_C_EXTRA_INCLUDE_DIRS}) -target_link_libraries (adios2pio-nm-lib +target_link_libraries (adios2pio-nm-lib-objs PUBLIC ${PIO_C_EXTRA_LIBRARIES}) -target_compile_options (adios2pio-nm-lib +target_compile_options (adios2pio-nm-lib-objs PRIVATE ${PIO_C_EXTRA_COMPILE_OPTIONS}) -target_compile_definitions (adios2pio-nm-lib +target_compile_definitions (adios2pio-nm-lib-objs PUBLIC ${PIO_C_EXTRA_COMPILE_DEFINITIONS}) if (PIO_C_EXTRA_LINK_FLAGS) - set_target_properties(adios2pio-nm-lib PROPERTIES + set_target_properties(adios2pio-nm-lib-objs PROPERTIES LINK_FLAGS ${PIO_C_EXTRA_LINK_FLAGS}) endif () -TARGET_LINK_LIBRARIES(adios2pio-nm-lib PRIVATE pioc) -TARGET_LINK_LIBRARIES(adios2pio-nm.exe PRIVATE adios2pio-nm-lib) +#TARGET_LINK_LIBRARIES(adios2pio-nm-lib-objs PRIVATE pioc) +TARGET_LINK_LIBRARIES(adios2pio-nm-lib-objs PRIVATE spio_default_public_options) +TARGET_LINK_LIBRARIES(adios2pio-nm.exe PRIVATE adios2pio-nm-lib-objs) #============================================================================== # INSTALL diff --git a/tools/adios2pio-nm/adios2pio-nm.cxx b/tools/adios2pio-nm/adios2pio-nm.cxx index d8c30c9b2a3..d4cb4e19e7b 100644 --- a/tools/adios2pio-nm/adios2pio-nm.cxx +++ b/tools/adios2pio-nm/adios2pio-nm.cxx @@ -4,6 +4,7 @@ #endif #include #include +#include "pio_internal.h" #include "adios2pio-nm-lib.h" #include "argparser.h" diff --git a/util/spio_logger.hpp b/util/spio_logger.hpp new file mode 100644 index 00000000000..94ff87d3783 --- /dev/null +++ b/util/spio_logger.hpp @@ -0,0 +1,175 @@ +#ifndef __SPIO_LOGGER_HPP__ +#define __SPIO_LOGGER_HPP__ + +#include +#include +#include +#include +#include + +#include "mpi.h" + +namespace SPIO_Util{ + namespace Logger{ + enum class Log_level : int{ + INVALID = 0, + TRACE, + DEBUG, + INFO, + WARN, + ERROR, + FATAL + }; + + inline std::string log_level_to_string(const Log_level &lvl){ + switch(lvl){ + case Logger::Log_level::INVALID : return "INVALID"; + case Logger::Log_level::TRACE : return "TRACE"; + case Logger::Log_level::DEBUG : return "DEBUG"; + case Logger::Log_level::INFO : return "INFO"; + case Logger::Log_level::WARN : return "WARN"; + case Logger::Log_level::ERROR : return "ERROR"; + case Logger::Log_level::FATAL : return "FATAL"; + default : return "INVALID"; + } + } + + inline Log_level string_to_log_level(const std::string &lvl){ + std::string ulvl(lvl); + + std::transform(ulvl.begin(), ulvl.end(), ulvl.begin(), + [](unsigned char c) { return std::toupper(c); }); + + if(ulvl == "INVALID") { return Logger::Log_level::INVALID; } + if(ulvl == "TRACE") { return Logger::Log_level::TRACE; } + if(ulvl == "DEBUG") { return Logger::Log_level::DEBUG; } + if(ulvl == "INFO") { return Logger::Log_level::INFO; } + if(ulvl == "WARN") { return Logger::Log_level::WARN; } + if(ulvl == "ERROR") { return Logger::Log_level::ERROR; } + if(ulvl == "FATAL") { return Logger::Log_level::FATAL; } + + return Logger::Log_level::INVALID; + } + + inline std::string get_available_log_levels(void ){ + std::vector log_levels = {Log_level::INVALID, + Log_level::TRACE, Log_level::DEBUG, Log_level::INFO, + Log_level::WARN, Log_level::ERROR, Log_level::FATAL}; + + std::string str_lvls("["); + std::for_each(log_levels.cbegin(), log_levels.cend(), + [&str_lvls](const Log_level &lvl) { str_lvls += log_level_to_string(lvl) + ", "; }); + + return str_lvls + "]"; + } + + + /* FIXME: Add a generic logger class */ + /* FIXME : Allow logging from multiple classes */ + /* An MPI logger */ + template + class MPI_logger{ + public: + MPI_logger() : mpi_comm_(MPI_COMM_NULL), ostr_(NULL), is_io_proc_(false), slog_done_(false), log_lvl_(Log_level::INFO) {} + + MPI_logger(MPI_Comm comm, std::shared_ptr &ostr); + + MPI_logger(const MPI_logger &other) = default; + + MPI_logger &operator=(const MPI_logger &other) = default; + + /* Enable logging on this proc. By default logging is only enabled on rank 0 */ + MPI_logger &enable_logging(void ) { is_io_proc_ = true; return *this; } + + /* Log message */ + void log(const std::string &log_msg); + + /* Log message, with log level */ + void log(Log_level lvl, const std::string &log_msg); + + /* Singleton log : Logging is only done once - e.g. writing headers etc */ + void slog(const std::string &slog_msg); + + /* Singleton log with log level : Logging is only done once - e.g. writing headers etc */ + void slog(Log_level lvl, const std::string &slog_msg); + + /* Explicitly flush the contents of the logger */ + void flush(void ); + + /* Get associated stream */ + std::shared_ptr get_log_stream(void ) const; + + ~MPI_logger(){} + + private: + MPI_Comm mpi_comm_; + std::shared_ptr ostr_; + bool is_io_proc_; + bool slog_done_; + Log_level log_lvl_; + + static const int MPI_DEFAULT_ROOT = 0; + static const char LOG_SEP = '\n'; + }; + } // namespace Logger +} // namespace SPIO_Util + +template +SPIO_Util::Logger::MPI_logger::MPI_logger(MPI_Comm comm, std::shared_ptr &ostr): + mpi_comm_(comm), ostr_(ostr), is_io_proc_(false), slog_done_(false), log_lvl_(Log_level::INFO) +{ + int rank; + int ret; + + ret = MPI_Comm_rank(mpi_comm_, &rank); + assert(ret == MPI_SUCCESS); + if(rank == MPI_DEFAULT_ROOT){ + is_io_proc_ = true; + } +} + +template +void SPIO_Util::Logger::MPI_logger::log(const std::string &log_msg) +{ + log(Log_level::INFO, log_msg); +} + +template +void SPIO_Util::Logger::MPI_logger::log(Log_level lvl, const std::string &log_msg) +{ + if(is_io_proc_ && (log_lvl_ <= lvl)){ + ((ostr_) ? (*ostr_) : std::cout) << log_msg.c_str() << LOG_SEP; + } +} + +template +void SPIO_Util::Logger::MPI_logger::slog(const std::string &slog_msg) +{ + slog(Log_level::INFO, slog_msg); +} + +template +void SPIO_Util::Logger::MPI_logger::slog(Log_level lvl, const std::string &slog_msg) +{ + if(!slog_done_){ + if(is_io_proc_ && (log_lvl_ <= lvl)){ + log(slog_msg); + } + slog_done_ = true; + } +} + +template +void SPIO_Util::Logger::MPI_logger::flush() +{ + if(is_io_proc_){ + (ostr_) ? ostr_->flush() : std::cout.flush(); + } +} + +template +std::shared_ptr SPIO_Util::Logger::MPI_logger::get_log_stream(void ) const +{ + return ostr_; +} +#endif /* __SPIO_LOGGER_HPP__ */