diff --git a/release_docs/CHANGELOG.md b/release_docs/CHANGELOG.md index 9dca3397183..74ad9b50f2b 100644 --- a/release_docs/CHANGELOG.md +++ b/release_docs/CHANGELOG.md @@ -112,6 +112,18 @@ We would like to thank the many HDF5 community members who contributed to this r ## Library +### Fixed a performance issue with chunked dataset I/O + + When dataset chunks are unable to be placed in the dataset chunk cache (for example, if a chunk + is too large), the library falls back to an alternative approach for I/O on dataset chunks. An + issue with the logic in this approach prevented chunked dataset I/O from making use of the library's + data sieve buffer I/O optimization functionality. For chunk shapes that are non-contiguous with + the memory layout of a buffer, this could result in severely degraded I/O performance, with the + worst-case behavior causing I/O to be performed on a single data element at a time. + + The data sieve buffer functionality has been extended to cover the case of uncached chunks and + will be used as long as the underlying Virtual File Driver supports data sieving. + ## Java Library ## Configuration diff --git a/src/H5Dchunk.c b/src/H5Dchunk.c index f2a0e85c032..10dcfeb1b20 100644 --- a/src/H5Dchunk.c +++ b/src/H5Dchunk.c @@ -409,6 +409,9 @@ H5FL_BLK_DEFINE_STATIC(chunk); /* Declare extern free list to manage the H5S_sel_iter_t struct */ H5FL_EXTERN(H5S_sel_iter_t); +/* Declare the external PQ free list for the sieve buffer information */ +H5FL_BLK_EXTERN(sieve_buf); + /*------------------------------------------------------------------------- * Function: H5D__chunk_direct_write * @@ -3105,6 +3108,16 @@ H5D__chunk_read(H5D_io_info_t *io_info, H5D_dset_io_info_t *dset_info) ctg_io_info.dsets_info = &ctg_dset_info; ctg_io_info.count = 1; + /* Ensure dataset I/O sieve buffer is properly setup for when the contiguous + * dataset I/O routines are used for chunks. Force reset of sieve buffer here + * on each I/O until more advanced use cases like H5Dset_extent between writes + * and subsequent writes or reads can be supported. + */ + dset_info->dset->shared->cache.sieve.sieve_buf_size = H5F_SIEVE_BUF_SIZE(dset_info->dset->oloc.file); + dset_info->dset->shared->cache.sieve.sieve_loc = HADDR_UNDEF; + dset_info->dset->shared->cache.sieve.sieve_size = 0; + assert(!dset_info->dset->shared->cache.sieve.sieve_dirty); /* Any writes should have been flushed */ + /* Initialize temporary contiguous storage info */ ctg_store.contig.dset_size = dset_info->dset->shared->layout.u.chunk.size; @@ -3201,6 +3214,14 @@ H5D__chunk_read(H5D_io_info_t *io_info, H5D_dset_io_info_t *dset_info) } /* end else */ done: + /* Free dataset sieve buffer and reset cached fields */ + if (dset_info->dset->shared->cache.sieve.sieve_buf) { + dset_info->dset->shared->cache.sieve.sieve_loc = HADDR_UNDEF; + dset_info->dset->shared->cache.sieve.sieve_size = 0; + dset_info->dset->shared->cache.sieve.sieve_buf = + (unsigned char *)H5FL_BLK_FREE(sieve_buf, dset_info->dset->shared->cache.sieve.sieve_buf); + } + /* Cleanup on failure */ if (ret_value < 0) { if (chunk_mem_spaces != chunk_mem_spaces_local) @@ -3263,6 +3284,15 @@ H5D__chunk_write(H5D_io_info_t *io_info, H5D_dset_io_info_t *dset_info) ctg_io_info.dsets_info = &ctg_dset_info; ctg_io_info.count = 1; + /* Ensure dataset I/O sieve buffer is properly setup for when the contiguous + * dataset I/O routines are used for chunks. Force reset of sieve buffer here + * on each I/O until more advanced use cases like H5Dset_extent between writes + * and subsequent writes or reads can be supported. + */ + dset_info->dset->shared->cache.sieve.sieve_buf_size = H5F_SIEVE_BUF_SIZE(dset_info->dset->oloc.file); + dset_info->dset->shared->cache.sieve.sieve_loc = HADDR_UNDEF; + dset_info->dset->shared->cache.sieve.sieve_size = 0; + /* Initialize temporary contiguous storage info */ ctg_store.contig.dset_size = dset_info->dset->shared->layout.u.chunk.size; @@ -3599,9 +3629,30 @@ H5D__chunk_write(H5D_io_info_t *io_info, H5D_dset_io_info_t *dset_info) /* Advance to next chunk in list */ chunk_node = H5D_CHUNK_GET_NEXT_NODE(dset_info, chunk_node); } /* end while */ - } /* end else */ + + /* + * Flush any data in sieve buffer - for now, don't keep data cached + * in sieve buffer between writes / writes and reads. + */ + if (H5D__flush_sieve_buf(dset_info->dset) < 0) + HGOTO_ERROR(H5E_DATASET, H5E_CANTFLUSH, FAIL, "unable to flush sieve buffer"); + } /* end else */ done: + /* Free dataset sieve buffer and reset cached fields */ + if (dset_info->dset->shared->cache.sieve.sieve_buf) { + dset_info->dset->shared->cache.sieve.sieve_loc = HADDR_UNDEF; + dset_info->dset->shared->cache.sieve.sieve_size = 0; + + /* Drop any unflushed sieve buffer data on failure - reads expect + * that sieve buffer will be clean + */ + dset_info->dset->shared->cache.sieve.sieve_dirty = false; + + dset_info->dset->shared->cache.sieve.sieve_buf = + (unsigned char *)H5FL_BLK_FREE(sieve_buf, dset_info->dset->shared->cache.sieve.sieve_buf); + } + /* Cleanup on failure */ if (ret_value < 0) { if (chunk_mem_spaces != chunk_mem_spaces_local) @@ -3643,6 +3694,10 @@ H5D__chunk_flush(H5D_t *dset) /* Sanity check */ assert(dset); + /* Flush any data in sieve buffer */ + if (H5D__flush_sieve_buf(dset) < 0) + HGOTO_ERROR(H5E_DATASET, H5E_CANTFLUSH, FAIL, "unable to flush sieve buffer"); + /* Loop over all entries in the chunk cache */ for (ent = rdcc->head; ent; ent = next) { next = ent->next; diff --git a/src/H5Dcontig.c b/src/H5Dcontig.c index 067675915b2..080b9f481d7 100644 --- a/src/H5Dcontig.c +++ b/src/H5Dcontig.c @@ -51,7 +51,7 @@ /* Callback info for sieve buffer readvv operation */ typedef struct H5D_contig_readvv_sieve_ud_t { H5F_shared_t *f_sh; /* Shared file for dataset */ - H5D_rdcdc_t *dset_contig; /* Cached information about contiguous data */ + H5D_sieve_buf_t *sieve_info; /* Information about dataset sieve buffer */ const H5D_contig_storage_t *store_contig; /* Contiguous storage info for this I/O operation */ unsigned char *rbuf; /* Pointer to buffer to fill */ } H5D_contig_readvv_sieve_ud_t; @@ -66,7 +66,7 @@ typedef struct H5D_contig_readvv_ud_t { /* Callback info for sieve buffer writevv operation */ typedef struct H5D_contig_writevv_sieve_ud_t { H5F_shared_t *f_sh; /* Shared file for dataset */ - H5D_rdcdc_t *dset_contig; /* Cached information about contiguous data */ + H5D_sieve_buf_t *sieve_info; /* Information about dataset sieve buffer */ const H5D_contig_storage_t *store_contig; /* Contiguous storage info for this I/O operation */ const unsigned char *wbuf; /* Pointer to buffer to write */ } H5D_contig_writevv_sieve_ud_t; @@ -481,9 +481,9 @@ H5D__contig_construct(H5F_t *f, H5D_t *dset) /* Adjust the sieve buffer size to the smaller one between the dataset size and the buffer size * from the file access property. (SLU - 2012/3/30) */ if (tmp_size < tmp_sieve_buf_size) - dset->shared->cache.contig.sieve_buf_size = tmp_size; + dset->shared->cache.sieve.sieve_buf_size = tmp_size; else - dset->shared->cache.contig.sieve_buf_size = tmp_sieve_buf_size; + dset->shared->cache.sieve.sieve_buf_size = tmp_sieve_buf_size; /* If the layout is below version 3, upgrade to version 3 if allowed. Do not upgrade past version 3 since * there is no benefit. */ @@ -557,9 +557,9 @@ H5D__contig_init(H5F_t *f, H5D_t *dset, hid_t H5_ATTR_UNUSED dapl_id, bool H5_AT /* Adjust the sieve buffer size to the smaller one between the dataset size and the buffer size * from the file access property. (SLU - 2012/3/30) */ if (dset->shared->layout.storage.u.contig.size < tmp_sieve_buf_size) - dset->shared->cache.contig.sieve_buf_size = dset->shared->layout.storage.u.contig.size; + dset->shared->cache.sieve.sieve_buf_size = dset->shared->layout.storage.u.contig.size; else - dset->shared->cache.contig.sieve_buf_size = tmp_sieve_buf_size; + dset->shared->cache.sieve.sieve_buf_size = tmp_sieve_buf_size; done: FUNC_LEAVE_NOAPI(ret_value) @@ -607,7 +607,7 @@ H5D__contig_is_data_cached(const H5D_shared_t *shared_dset) /* Sanity checks */ assert(shared_dset); - FUNC_LEAVE_NOAPI(shared_dset->cache.contig.sieve_size > 0) + FUNC_LEAVE_NOAPI(shared_dset->cache.sieve.sieve_size > 0) } /* end H5D__contig_is_data_cached() */ /*------------------------------------------------------------------------- @@ -816,8 +816,8 @@ H5D__contig_may_use_select_io(H5D_io_info_t *io_info, const H5D_dset_io_info_t * io_info->use_select_io = H5D_SELECTION_IO_MODE_OFF; io_info->no_selection_io_cause |= H5D_SEL_IO_NOT_CONTIGUOUS_OR_CHUNKED_DATASET; } - else if ((op_type == H5D_IO_OP_READ && dataset->shared->cache.contig.sieve_dirty) || - (op_type == H5D_IO_OP_WRITE && dataset->shared->cache.contig.sieve_buf)) { + else if ((op_type == H5D_IO_OP_READ && dataset->shared->cache.sieve.sieve_dirty) || + (op_type == H5D_IO_OP_WRITE && dataset->shared->cache.sieve.sieve_buf)) { io_info->use_select_io = H5D_SELECTION_IO_MODE_OFF; io_info->no_selection_io_cause |= H5D_SEL_IO_CONTIGUOUS_SIEVE_BUFFER; } @@ -1034,9 +1034,9 @@ static herr_t H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void *_udata) { H5D_contig_readvv_sieve_ud_t *udata = - (H5D_contig_readvv_sieve_ud_t *)_udata; /* User data for H5VM_opvv() operator */ - H5F_shared_t *f_sh = udata->f_sh; /* Shared file for dataset */ - H5D_rdcdc_t *dset_contig = udata->dset_contig; /* Cached information about contiguous data */ + (H5D_contig_readvv_sieve_ud_t *)_udata; /* User data for H5VM_opvv() operator */ + H5F_shared_t *f_sh = udata->f_sh; /* Shared file for dataset */ + H5D_sieve_buf_t *sieve_info = udata->sieve_info; /* Information about dataset sieve buffer */ const H5D_contig_storage_t *store_contig = udata->store_contig; /* Contiguous storage info for this I/O operation */ unsigned char *buf; /* Pointer to buffer to fill */ @@ -1052,9 +1052,9 @@ H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * FUNC_ENTER_PACKAGE /* Stash local copies of these value */ - if (dset_contig->sieve_buf != NULL) { - sieve_start = dset_contig->sieve_loc; - sieve_size = dset_contig->sieve_size; + if (sieve_info->sieve_buf != NULL) { + sieve_start = sieve_info->sieve_loc; + sieve_size = sieve_info->sieve_size; sieve_end = sieve_start + sieve_size; } /* end if */ @@ -1065,19 +1065,19 @@ H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * buf = udata->rbuf + src_off; /* Check if the sieve buffer is allocated yet */ - if (NULL == dset_contig->sieve_buf) { + if (NULL == sieve_info->sieve_buf) { /* Check if we can actually hold the I/O request in the sieve buffer */ - if (len > dset_contig->sieve_buf_size) { + if (len > sieve_info->sieve_buf_size) { if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, addr, len, buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "block read failed"); } /* end if */ else { /* Allocate room for the data sieve buffer */ - if (NULL == (dset_contig->sieve_buf = H5FL_BLK_CALLOC(sieve_buf, dset_contig->sieve_buf_size))) + if (NULL == (sieve_info->sieve_buf = H5FL_BLK_CALLOC(sieve_buf, sieve_info->sieve_buf_size))) HGOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, "memory allocation failed"); /* Determine the new sieve buffer size & location */ - dset_contig->sieve_loc = addr; + sieve_info->sieve_loc = addr; /* Make certain we don't read off the end of the file */ if (HADDR_UNDEF == (rel_eoa = H5F_shared_get_eoa(f_sh, H5FD_MEM_DRAW))) @@ -1087,19 +1087,19 @@ H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * max_data = store_contig->dset_size - dst_off; /* Compute the size of the sieve buffer */ - min = MIN3(rel_eoa - dset_contig->sieve_loc, max_data, dset_contig->sieve_buf_size); - H5_CHECKED_ASSIGN(dset_contig->sieve_size, size_t, min, hsize_t); + min = MIN3(rel_eoa - sieve_info->sieve_loc, max_data, sieve_info->sieve_buf_size); + H5_CHECKED_ASSIGN(sieve_info->sieve_size, size_t, min, hsize_t); /* Read the new sieve buffer */ - if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, dset_contig->sieve_loc, dset_contig->sieve_size, - dset_contig->sieve_buf) < 0) + if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, sieve_info->sieve_loc, sieve_info->sieve_size, + sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "block read failed"); /* Grab the data out of the buffer (must be first piece of data in buffer ) */ - H5MM_memcpy(buf, dset_contig->sieve_buf, len); + H5MM_memcpy(buf, sieve_info->sieve_buf, len); /* Reset sieve buffer dirty flag */ - dset_contig->sieve_dirty = false; + sieve_info->sieve_dirty = false; } /* end else */ } /* end if */ else { @@ -1108,7 +1108,7 @@ H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * /* If entire read is within the sieve buffer, read it from the buffer */ if (addr >= sieve_start && contig_end < sieve_end) { - unsigned char *base_sieve_buf = dset_contig->sieve_buf + (addr - sieve_start); + unsigned char *base_sieve_buf = sieve_info->sieve_buf + (addr - sieve_start); /* Grab the data out of the buffer */ H5MM_memcpy(buf, base_sieve_buf, len); @@ -1116,19 +1116,19 @@ H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * /* Entire request is not within this data sieve buffer */ else { /* Check if we can actually hold the I/O request in the sieve buffer */ - if (len > dset_contig->sieve_buf_size) { + if (len > sieve_info->sieve_buf_size) { /* Check for any overlap with the current sieve buffer */ if ((sieve_start >= addr && sieve_start < (contig_end + 1)) || ((sieve_end - 1) >= addr && (sieve_end - 1) < (contig_end + 1))) { /* Flush the sieve buffer, if it's dirty */ - if (dset_contig->sieve_dirty) { + if (sieve_info->sieve_dirty) { /* Write to file */ if (H5F_shared_block_write(f_sh, H5FD_MEM_DRAW, sieve_start, sieve_size, - dset_contig->sieve_buf) < 0) + sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "block write failed"); /* Reset sieve buffer dirty flag */ - dset_contig->sieve_dirty = false; + sieve_info->sieve_dirty = false; } /* end if */ } /* end if */ @@ -1139,18 +1139,18 @@ H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * /* Element size fits within the buffer size */ else { /* Flush the sieve buffer if it's dirty */ - if (dset_contig->sieve_dirty) { + if (sieve_info->sieve_dirty) { /* Write to file */ if (H5F_shared_block_write(f_sh, H5FD_MEM_DRAW, sieve_start, sieve_size, - dset_contig->sieve_buf) < 0) + sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "block write failed"); /* Reset sieve buffer dirty flag */ - dset_contig->sieve_dirty = false; + sieve_info->sieve_dirty = false; } /* end if */ /* Determine the new sieve buffer size & location */ - dset_contig->sieve_loc = addr; + sieve_info->sieve_loc = addr; /* Make certain we don't read off the end of the file */ if (HADDR_UNDEF == (rel_eoa = H5F_shared_get_eoa(f_sh, H5FD_MEM_DRAW))) @@ -1164,19 +1164,19 @@ H5D__contig_readvv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * * the end of the data element, and don't read more than * the buffer size. */ - min = MIN3(rel_eoa - dset_contig->sieve_loc, max_data, dset_contig->sieve_buf_size); - H5_CHECKED_ASSIGN(dset_contig->sieve_size, size_t, min, hsize_t); + min = MIN3(rel_eoa - sieve_info->sieve_loc, max_data, sieve_info->sieve_buf_size); + H5_CHECKED_ASSIGN(sieve_info->sieve_size, size_t, min, hsize_t); /* Read the new sieve buffer */ - if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, dset_contig->sieve_loc, - dset_contig->sieve_size, dset_contig->sieve_buf) < 0) + if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, sieve_info->sieve_loc, sieve_info->sieve_size, + sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "block read failed"); /* Grab the data out of the buffer (must be first piece of data in buffer ) */ - H5MM_memcpy(buf, dset_contig->sieve_buf, len); + H5MM_memcpy(buf, sieve_info->sieve_buf, len); /* Reset sieve buffer dirty flag */ - dset_contig->sieve_dirty = false; + sieve_info->sieve_dirty = false; } /* end else */ } /* end else */ } /* end else */ @@ -1251,7 +1251,7 @@ H5D__contig_readvv(const H5D_io_info_t *io_info, const H5D_dset_io_info_t *dset_ /* Set up user data for H5VM_opvv() */ udata.f_sh = io_info->f_sh; - udata.dset_contig = &(dset_info->dset->shared->cache.contig); + udata.sieve_info = &(dset_info->dset->shared->cache.sieve); udata.store_contig = &(dset_info->store->contig); udata.rbuf = (unsigned char *)dset_info->buf.vp; @@ -1293,9 +1293,9 @@ static herr_t H5D__contig_writevv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void *_udata) { H5D_contig_writevv_sieve_ud_t *udata = - (H5D_contig_writevv_sieve_ud_t *)_udata; /* User data for H5VM_opvv() operator */ - H5F_shared_t *f_sh = udata->f_sh; /* Shared file for dataset */ - H5D_rdcdc_t *dset_contig = udata->dset_contig; /* Cached information about contiguous data */ + (H5D_contig_writevv_sieve_ud_t *)_udata; /* User data for H5VM_opvv() operator */ + H5F_shared_t *f_sh = udata->f_sh; /* Shared file for dataset */ + H5D_sieve_buf_t *sieve_info = udata->sieve_info; /* Information about dataset sieve buffer */ const H5D_contig_storage_t *store_contig = udata->store_contig; /* Contiguous storage info for this I/O operation */ const unsigned char *buf; /* Pointer to buffer to fill */ @@ -1311,9 +1311,9 @@ H5D__contig_writevv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void FUNC_ENTER_PACKAGE /* Stash local copies of these values */ - if (dset_contig->sieve_buf != NULL) { - sieve_start = dset_contig->sieve_loc; - sieve_size = dset_contig->sieve_size; + if (sieve_info->sieve_buf != NULL) { + sieve_start = sieve_info->sieve_loc; + sieve_size = sieve_info->sieve_size; sieve_end = sieve_start + sieve_size; } /* end if */ @@ -1324,23 +1324,23 @@ H5D__contig_writevv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void buf = udata->wbuf + src_off; /* No data sieve buffer yet, go allocate one */ - if (NULL == dset_contig->sieve_buf) { + if (NULL == sieve_info->sieve_buf) { /* Check if we can actually hold the I/O request in the sieve buffer */ - if (len > dset_contig->sieve_buf_size) { + if (len > sieve_info->sieve_buf_size) { if (H5F_shared_block_write(f_sh, H5FD_MEM_DRAW, addr, len, buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "block write failed"); } /* end if */ else { /* Allocate room for the data sieve buffer */ - if (NULL == (dset_contig->sieve_buf = H5FL_BLK_CALLOC(sieve_buf, dset_contig->sieve_buf_size))) + if (NULL == (sieve_info->sieve_buf = H5FL_BLK_CALLOC(sieve_buf, sieve_info->sieve_buf_size))) HGOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, "memory allocation failed"); /* Clear memory */ - if (dset_contig->sieve_size > len) - memset(dset_contig->sieve_buf + len, 0, (dset_contig->sieve_size - len)); + if (sieve_info->sieve_size > len) + memset(sieve_info->sieve_buf + len, 0, (sieve_info->sieve_size - len)); /* Determine the new sieve buffer size & location */ - dset_contig->sieve_loc = addr; + sieve_info->sieve_loc = addr; /* Make certain we don't read off the end of the file */ if (HADDR_UNDEF == (rel_eoa = H5F_shared_get_eoa(f_sh, H5FD_MEM_DRAW))) @@ -1350,26 +1350,26 @@ H5D__contig_writevv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void max_data = store_contig->dset_size - dst_off; /* Compute the size of the sieve buffer */ - min = MIN3(rel_eoa - dset_contig->sieve_loc, max_data, dset_contig->sieve_buf_size); - H5_CHECKED_ASSIGN(dset_contig->sieve_size, size_t, min, hsize_t); + min = MIN3(rel_eoa - sieve_info->sieve_loc, max_data, sieve_info->sieve_buf_size); + H5_CHECKED_ASSIGN(sieve_info->sieve_size, size_t, min, hsize_t); /* Check if there is any point in reading the data from the file */ - if (dset_contig->sieve_size > len) { + if (sieve_info->sieve_size > len) { /* Read the new sieve buffer */ - if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, dset_contig->sieve_loc, - dset_contig->sieve_size, dset_contig->sieve_buf) < 0) + if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, sieve_info->sieve_loc, sieve_info->sieve_size, + sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "block read failed"); } /* end if */ /* Grab the data out of the buffer (must be first piece of data in buffer ) */ - H5MM_memcpy(dset_contig->sieve_buf, buf, len); + H5MM_memcpy(sieve_info->sieve_buf, buf, len); /* Set sieve buffer dirty flag */ - dset_contig->sieve_dirty = true; + sieve_info->sieve_dirty = true; /* Stash local copies of these values */ - sieve_start = dset_contig->sieve_loc; - sieve_size = dset_contig->sieve_size; + sieve_start = sieve_info->sieve_loc; + sieve_size = sieve_info->sieve_size; sieve_end = sieve_start + sieve_size; } /* end else */ } /* end if */ @@ -1379,35 +1379,35 @@ H5D__contig_writevv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void /* If entire write is within the sieve buffer, write it to the buffer */ if (addr >= sieve_start && contig_end < sieve_end) { - unsigned char *base_sieve_buf = dset_contig->sieve_buf + (addr - sieve_start); + unsigned char *base_sieve_buf = sieve_info->sieve_buf + (addr - sieve_start); /* Put the data into the sieve buffer */ H5MM_memcpy(base_sieve_buf, buf, len); /* Set sieve buffer dirty flag */ - dset_contig->sieve_dirty = true; + sieve_info->sieve_dirty = true; } /* end if */ /* Entire request is not within this data sieve buffer */ else { /* Check if we can actually hold the I/O request in the sieve buffer */ - if (len > dset_contig->sieve_buf_size) { + if (len > sieve_info->sieve_buf_size) { /* Check for any overlap with the current sieve buffer */ if ((sieve_start >= addr && sieve_start < (contig_end + 1)) || ((sieve_end - 1) >= addr && (sieve_end - 1) < (contig_end + 1))) { /* Flush the sieve buffer, if it's dirty */ - if (dset_contig->sieve_dirty) { + if (sieve_info->sieve_dirty) { /* Write to file */ if (H5F_shared_block_write(f_sh, H5FD_MEM_DRAW, sieve_start, sieve_size, - dset_contig->sieve_buf) < 0) + sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "block write failed"); /* Reset sieve buffer dirty flag */ - dset_contig->sieve_dirty = false; + sieve_info->sieve_dirty = false; } /* end if */ /* Force the sieve buffer to be re-read the next time */ - dset_contig->sieve_loc = HADDR_UNDEF; - dset_contig->sieve_size = 0; + sieve_info->sieve_loc = HADDR_UNDEF; + sieve_info->sieve_size = 0; } /* end if */ /* Write directly from the user's buffer */ @@ -1418,44 +1418,43 @@ H5D__contig_writevv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void else { /* Check if it is possible to (exactly) prepend or append to existing (dirty) sieve buffer */ if (((addr + len) == sieve_start || addr == sieve_end) && - (len + sieve_size) <= dset_contig->sieve_buf_size && dset_contig->sieve_dirty) { + (len + sieve_size) <= sieve_info->sieve_buf_size && sieve_info->sieve_dirty) { /* Prepend to existing sieve buffer */ if ((addr + len) == sieve_start) { /* Move existing sieve information to correct location */ - memmove(dset_contig->sieve_buf + len, dset_contig->sieve_buf, - dset_contig->sieve_size); + memmove(sieve_info->sieve_buf + len, sieve_info->sieve_buf, sieve_info->sieve_size); /* Copy in new information (must be first in sieve buffer) */ - H5MM_memcpy(dset_contig->sieve_buf, buf, len); + H5MM_memcpy(sieve_info->sieve_buf, buf, len); /* Adjust sieve location */ - dset_contig->sieve_loc = addr; + sieve_info->sieve_loc = addr; } /* end if */ /* Append to existing sieve buffer */ else { /* Copy in new information */ - H5MM_memcpy(dset_contig->sieve_buf + sieve_size, buf, len); + H5MM_memcpy(sieve_info->sieve_buf + sieve_size, buf, len); } /* end else */ /* Adjust sieve size */ - dset_contig->sieve_size += len; + sieve_info->sieve_size += len; } /* end if */ /* Can't add the new data onto the existing sieve buffer */ else { /* Flush the sieve buffer if it's dirty */ - if (dset_contig->sieve_dirty) { + if (sieve_info->sieve_dirty) { /* Write to file */ if (H5F_shared_block_write(f_sh, H5FD_MEM_DRAW, sieve_start, sieve_size, - dset_contig->sieve_buf) < 0) + sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "block write failed"); /* Reset sieve buffer dirty flag */ - dset_contig->sieve_dirty = false; + sieve_info->sieve_dirty = false; } /* end if */ /* Determine the new sieve buffer size & location */ - dset_contig->sieve_loc = addr; + sieve_info->sieve_loc = addr; /* Make certain we don't read off the end of the file */ if (HADDR_UNDEF == (rel_eoa = H5F_shared_get_eoa(f_sh, H5FD_MEM_DRAW))) @@ -1469,22 +1468,22 @@ H5D__contig_writevv_sieve_cb(hsize_t dst_off, hsize_t src_off, size_t len, void * the end of the data element, and don't read more than * the buffer size. */ - min = MIN3(rel_eoa - dset_contig->sieve_loc, max_data, dset_contig->sieve_buf_size); - H5_CHECKED_ASSIGN(dset_contig->sieve_size, size_t, min, hsize_t); + min = MIN3(rel_eoa - sieve_info->sieve_loc, max_data, sieve_info->sieve_buf_size); + H5_CHECKED_ASSIGN(sieve_info->sieve_size, size_t, min, hsize_t); /* Check if there is any point in reading the data from the file */ - if (dset_contig->sieve_size > len) { + if (sieve_info->sieve_size > len) { /* Read the new sieve buffer */ - if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, dset_contig->sieve_loc, - dset_contig->sieve_size, dset_contig->sieve_buf) < 0) + if (H5F_shared_block_read(f_sh, H5FD_MEM_DRAW, sieve_info->sieve_loc, + sieve_info->sieve_size, sieve_info->sieve_buf) < 0) HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "block read failed"); } /* end if */ /* Grab the data out of the buffer (must be first piece of data in buffer ) */ - H5MM_memcpy(dset_contig->sieve_buf, buf, len); + H5MM_memcpy(sieve_info->sieve_buf, buf, len); /* Set sieve buffer dirty flag */ - dset_contig->sieve_dirty = true; + sieve_info->sieve_dirty = true; } /* end else */ } /* end else */ } /* end else */ @@ -1561,7 +1560,7 @@ H5D__contig_writevv(const H5D_io_info_t *io_info, const H5D_dset_io_info_t *dset /* Set up user data for H5VM_opvv() */ udata.f_sh = io_info->f_sh; - udata.dset_contig = &(dset_info->dset->shared->cache.contig); + udata.sieve_info = &(dset_info->dset->shared->cache.sieve); udata.store_contig = &(dset_info->store->contig); udata.wbuf = (const unsigned char *)dset_info->buf.cvp; @@ -1792,10 +1791,10 @@ H5D__contig_copy(H5F_t *f_src, const H5O_storage_contig_t *storage_src, H5F_t *f /* If data sieving is enabled and the dataset is open in the file, set up to copy data out of the sieve buffer if deemed possible later */ - if (H5F_HAS_FEATURE(f_src, H5FD_FEAT_DATA_SIEVE) && shared_fo && shared_fo->cache.contig.sieve_buf) { + if (H5F_HAS_FEATURE(f_src, H5FD_FEAT_DATA_SIEVE) && shared_fo && shared_fo->cache.sieve.sieve_buf) { try_sieve = true; - sieve_start = shared_fo->cache.contig.sieve_loc; - sieve_end = sieve_start + shared_fo->cache.contig.sieve_size; + sieve_start = shared_fo->cache.sieve.sieve_loc; + sieve_end = sieve_start + shared_fo->cache.sieve.sieve_size; } while (total_src_nbytes > 0) { @@ -1825,7 +1824,7 @@ H5D__contig_copy(H5F_t *f_src, const H5O_storage_contig_t *storage_src, H5F_t *f /* If the entire copy is within the sieve buffer, copy data from the sieve buffer */ if (try_sieve && (addr_src >= sieve_start) && ((addr_src + src_nbytes - 1) < sieve_end)) { - unsigned char *base_sieve_buf = shared_fo->cache.contig.sieve_buf + (addr_src - sieve_start); + unsigned char *base_sieve_buf = shared_fo->cache.sieve.sieve_buf + (addr_src - sieve_start); H5MM_memcpy(buf, base_sieve_buf, src_nbytes); } diff --git a/src/H5Defl.c b/src/H5Defl.c index c4314ef3e72..d3cd9fc6a2d 100644 --- a/src/H5Defl.c +++ b/src/H5Defl.c @@ -156,8 +156,11 @@ H5D__efl_construct(H5F_t *f, H5D_t *dset) tmp_size = (hsize_t)stmp_size * dt_size; H5_CHECKED_ASSIGN(dset->shared->layout.storage.u.contig.size, hsize_t, tmp_size, hssize_t); - /* Get the sieve buffer size for this dataset */ - dset->shared->cache.contig.sieve_buf_size = H5F_SIEVE_BUF_SIZE(f); + /* Get the sieve buffer size for this dataset - the smaller of the dataset size and + * the sieve buffer size from the FAPL is used + */ + dset->shared->cache.sieve.sieve_buf_size = + MIN(dset->shared->layout.storage.u.contig.size, H5F_SIEVE_BUF_SIZE(f)); done: FUNC_LEAVE_NOAPI(ret_value) diff --git a/src/H5Dint.c b/src/H5Dint.c index 6ab3de39037..854b94e0738 100644 --- a/src/H5Dint.c +++ b/src/H5Dint.c @@ -1962,13 +1962,15 @@ H5D_close(H5D_t *dataset) */ dataset->shared->closing = true; + /* Free the data sieve buffer, if it's been allocated */ + if (dataset->shared->cache.sieve.sieve_buf) + dataset->shared->cache.sieve.sieve_buf = + (unsigned char *)H5FL_BLK_FREE(sieve_buf, dataset->shared->cache.sieve.sieve_buf); + /* Free cached information for each kind of dataset */ switch (dataset->shared->layout.type) { case H5D_CONTIGUOUS: - /* Free the data sieve buffer, if it's been allocated */ - if (dataset->shared->cache.contig.sieve_buf) - dataset->shared->cache.contig.sieve_buf = - (unsigned char *)H5FL_BLK_FREE(sieve_buf, dataset->shared->cache.contig.sieve_buf); + /* Nothing special to do */ break; case H5D_CHUNKED: @@ -1991,6 +1993,7 @@ H5D_close(H5D_t *dataset) H5FL_FREE(H5D_piece_info_t, dataset->shared->cache.chunk.single_piece_info); dataset->shared->cache.chunk.single_piece_info = NULL; } /* end if */ + break; case H5D_COMPACT: @@ -2156,15 +2159,13 @@ H5D_mult_refresh_close(hid_t dset_id) assert(dataset->shared->fo_count > 0); if (dataset->shared->fo_count > 1) { + /* Free the data sieve buffer, if it's been allocated */ + if (dataset->shared->cache.sieve.sieve_buf) + dataset->shared->cache.sieve.sieve_buf = + (unsigned char *)H5FL_BLK_FREE(sieve_buf, dataset->shared->cache.sieve.sieve_buf); + /* Free cached information for each kind of dataset */ switch (dataset->shared->layout.type) { - case H5D_CONTIGUOUS: - /* Free the data sieve buffer, if it's been allocated */ - if (dataset->shared->cache.contig.sieve_buf) - dataset->shared->cache.contig.sieve_buf = - (unsigned char *)H5FL_BLK_FREE(sieve_buf, dataset->shared->cache.contig.sieve_buf); - break; - case H5D_CHUNKED: /* Check for skip list for iterating over chunks during I/O to close */ if (dataset->shared->cache.chunk.sel_chunks) { @@ -2187,6 +2188,7 @@ H5D_mult_refresh_close(hid_t dset_id) } /* end if */ break; + case H5D_CONTIGUOUS: case H5D_COMPACT: case H5D_VIRTUAL: /* Nothing special to do (info freed in the layout destroy) */ @@ -3250,18 +3252,18 @@ H5D__flush_sieve_buf(H5D_t *dataset) assert(dataset); /* Flush the raw data buffer, if we have a dirty one */ - if (dataset->shared->cache.contig.sieve_buf && dataset->shared->cache.contig.sieve_dirty) { + if (dataset->shared->cache.sieve.sieve_buf && dataset->shared->cache.sieve.sieve_dirty) { assert(dataset->shared->layout.type != H5D_COMPACT); /* We should never have a sieve buffer for compact storage */ /* Write dirty data sieve buffer to file */ if (H5F_shared_block_write( - H5F_SHARED(dataset->oloc.file), H5FD_MEM_DRAW, dataset->shared->cache.contig.sieve_loc, - dataset->shared->cache.contig.sieve_size, dataset->shared->cache.contig.sieve_buf) < 0) + H5F_SHARED(dataset->oloc.file), H5FD_MEM_DRAW, dataset->shared->cache.sieve.sieve_loc, + dataset->shared->cache.sieve.sieve_size, dataset->shared->cache.sieve.sieve_buf) < 0) HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "block write failed"); /* Reset sieve buffer dirty flag */ - dataset->shared->cache.contig.sieve_dirty = false; + dataset->shared->cache.sieve.sieve_dirty = false; } /* end if */ done: diff --git a/src/H5Dpkg.h b/src/H5Dpkg.h index e28199cfe48..1e153ed5232 100644 --- a/src/H5Dpkg.h +++ b/src/H5Dpkg.h @@ -562,14 +562,14 @@ typedef struct H5D_rdcc_t { unsigned scaled_encode_bits[H5S_MAX_RANK]; /* The number of bits needed to encode the scaled dim sizes */ } H5D_rdcc_t; -/* The raw data contiguous data cache */ -typedef struct H5D_rdcdc_t { +/* Information about the dataset sieve buffer */ +typedef struct H5D_sieve_buf_t { unsigned char *sieve_buf; /* Buffer to hold data sieve buffer */ haddr_t sieve_loc; /* File location (offset) of the data sieve buffer */ size_t sieve_size; /* Size of the data sieve buffer used (in bytes) */ size_t sieve_buf_size; /* Size of the data sieve buffer allocated (in bytes) */ bool sieve_dirty; /* Flag to indicate that the data sieve buffer is dirty */ -} H5D_rdcdc_t; +} H5D_sieve_buf_t; /* * A dataset is made of two layers, an H5D_t struct that is unique to @@ -598,12 +598,8 @@ struct H5D_shared_t { /* Buffered/cached information for types of raw data storage*/ struct { - H5D_rdcdc_t contig; /* Information about contiguous data */ - /* (Note that the "contig" cache - * information can be used by a chunked - * dataset in certain circumstances) - */ - H5D_rdcc_t chunk; /* Information about chunked data */ + H5D_sieve_buf_t sieve; /* Information about dataset sieve buffer */ + H5D_rdcc_t chunk; /* Information about chunked data */ } cache; H5D_append_flush_t append_flush; /* Append flush property information */ diff --git a/src/H5Ppublic.h b/src/H5Ppublic.h index 44ba2b786f9..dcc3eeb57d7 100644 --- a/src/H5Ppublic.h +++ b/src/H5Ppublic.h @@ -5405,8 +5405,8 @@ H5_DLL herr_t H5Pset_object_flush_cb(hid_t plist_id, H5F_flush_cb_t func, void * * the dataset being read in for hyperslab selections boosts * performance by quite a bit. * - * The default value is set to 64KB, indicating that file I/O for raw - * data reads and writes will occur in at least 64KB blocks. Setting + * The default value is set to 64KiB, indicating that file I/O for raw + * data reads and writes will occur in at least 64KiB blocks. Setting * the value to zero (\TText{0}) with this API function will turn off * the data sieving, even if the VFL driver attempts to use that * strategy. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 15aa6e5e5e5..7ee9dd28b5d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -375,6 +375,7 @@ set (H5_EXPRESS_TESTS rtree objcopy_ref objcopy + io_perf ) set (H5_TESTS diff --git a/test/io_perf.c b/test/io_perf.c new file mode 100644 index 00000000000..4e0e7aac729 --- /dev/null +++ b/test/io_perf.c @@ -0,0 +1,326 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the LICENSE file, which can be found at the root of the source code * + * distribution tree, or in https://www.hdfgroup.org/licenses. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * This file contains tests which attempt to catch I/O performance regressions + * by scaling up problem sizes according to the current "TestExpress" setting. + * While not easy to do reliably, tests should generally be designed to try + * causing a timeout by running for an extraordinarily long duration when the + * "TestExpress" value is set to level 0 and the performance problem in question + * has been regressed on. When the performance problem has NOT been regressed + * on, tests should run reasonably fast for a "TestExpress" level of 0 in order + * to facilitate CI testing. For higher "TestExpress" values, tests should + * generally run quickly, even when the performance problem in question has been + * regressed on, so that the cumulative runtime of this test program is minimal. + * The duration that may elapse before a timeout occurs is currently determined + * by the CMake build system's CTEST_TEST_TIMEOUT / DART_TESTING_TIMEOUT + * variables (currently 1200 seconds by default). + */ + +#include "testframe.h" + +/* + * Test I/O on a dataset with chunks that are non-contiguous with respect to + * memory layout. This test attempts to catch an I/O performance issue where + * the library was skipping use of a sieve buffer and performing I/O on chunks + * element by element, resulting in very bad performance. The number of chunks + * being written and read scales up with lower values of TestExpress. At a + * TestExpress setting of 0 the number of chunks should result in a very long + * time spent in I/O if a sieve buffer isn't being used. + */ +#define FILE_NAME "chunk_non_contig_mem_io.h5" +#define DATASET_NAME "chunked_dataset" +#define DATASET_TYPE int +#define CHUNK_DIM_0 4194304 +static void +chunk_non_contig_mem_io(void H5_ATTR_UNUSED *params) +{ + hsize_t dims[2] = {0}; + hsize_t mem_dims[2] = {0}; + hsize_t chunk_dims[2] = {0}; + hsize_t start[2] = {0}; + hsize_t count[2] = {0}; + hsize_t block[2] = {0}; + hid_t file_id = H5I_INVALID_HID; + hid_t dset_id = H5I_INVALID_HID; + hid_t fapl_id = H5I_INVALID_HID; + hid_t space_id = H5I_INVALID_HID; + hid_t mem_space_id = H5I_INVALID_HID; + hid_t dcpl_id = H5I_INVALID_HID; + size_t num_chunks = 0; + size_t data_size = 0; + void *write_buf = NULL; + void *read_buf = NULL; + int TestExpress = GetTestExpress(); + + switch (TestExpress) { + case H5_TEST_EXPRESS_EXHAUSTIVE: + num_chunks = 1024; + break; + case H5_TEST_EXPRESS_FULL: + num_chunks = 512; + break; + case H5_TEST_EXPRESS_QUICK: + num_chunks = 128; + break; + case H5_TEST_EXPRESS_SMOKE_TEST: + default: + num_chunks = 64; + break; + } + + MESSAGE(VERBO_NONE, ("Express test mode set to %d. Testing with %zu chunks\n", TestExpress, num_chunks)); + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) { + fprintf(stderr, "Failed to create FAPL\n"); + goto error; + } + + /* Disable dataset chunk caching */ + if (H5Pset_cache(fapl_id, 0, 0, 0, 0.0) < 0) { + fprintf(stderr, "Failed to disable dataset chunk caching\n"); + goto error; + } + + /* Set sieve buffer size to size of a single chunk */ + if (H5Pset_sieve_buf_size(fapl_id, (size_t)CHUNK_DIM_0 * sizeof(DATASET_TYPE)) < 0) { + fprintf(stderr, "Failed to set data sieve buffer size\n"); + goto error; + } + + if ((file_id = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id)) < 0) { + fprintf(stderr, "Failed to create file '%s'\n", FILE_NAME); + goto error; + } + + dims[0] = (hsize_t)CHUNK_DIM_0; + dims[1] = (hsize_t)num_chunks; + if ((space_id = H5Screate_simple(2, dims, NULL)) < 0) { + fprintf(stderr, "Failed to create dataspace for dataset\n"); + goto error; + } + + if ((dcpl_id = H5Pcreate(H5P_DATASET_CREATE)) < 0) { + fprintf(stderr, "Failed to create DCPL\n"); + goto error; + } + + if (H5Pset_fill_time(dcpl_id, H5D_FILL_TIME_NEVER) < 0) { + fprintf(stderr, "Failed to set fill value writing time on DCPL\n"); + goto error; + } + + chunk_dims[0] = (hsize_t)CHUNK_DIM_0; + chunk_dims[1] = 1; + if (H5Pset_chunk(dcpl_id, 2, chunk_dims) < 0) { + fprintf(stderr, "Failed to set chunking on DCPL\n"); + goto error; + } + + if ((dset_id = H5Dcreate2(file_id, DATASET_NAME, H5T_NATIVE_INT, space_id, H5P_DEFAULT, dcpl_id, + H5P_DEFAULT)) < 0) { + fprintf(stderr, "Failed to create dataset\n"); + goto error; + } + + data_size = (size_t)CHUNK_DIM_0 * sizeof(DATASET_TYPE); + if (NULL == (write_buf = malloc(data_size))) { + fprintf(stderr, "Failed to allocate write buffer\n"); + goto error; + } + + for (size_t i = 0; i < data_size / sizeof(DATASET_TYPE); i++) + ((DATASET_TYPE *)write_buf)[i] = (DATASET_TYPE)1; + + mem_dims[0] = 1; + mem_dims[1] = (hsize_t)CHUNK_DIM_0; + if ((mem_space_id = H5Screate_simple(2, mem_dims, NULL)) < 0) { + fprintf(stderr, "Failed to create memory dataspace\n"); + goto error; + } + + for (size_t chunk = 0; chunk < num_chunks; chunk++) { + MESSAGE(VERBO_DEF, ("Writing chunk %zu\n", chunk)); + + start[0] = 0; + start[1] = chunk; + count[0] = 1; + count[1] = 1; + block[0] = (hsize_t)CHUNK_DIM_0; + block[1] = 1; + if (H5Sselect_hyperslab(space_id, H5S_SELECT_SET, start, NULL, count, block) < 0) { + fprintf(stderr, "Failed to select hyperslab for chunk\n"); + goto error; + } + + if (H5Dwrite(dset_id, H5T_NATIVE_INT, mem_space_id, space_id, H5P_DEFAULT, write_buf) < 0) { + fprintf(stderr, "Failed to write to dataset\n"); + goto error; + } + } + + if (H5Sclose(mem_space_id) < 0) { + fprintf(stderr, "Failed to close dataspace\n"); + goto error; + } + + if (H5Dclose(dset_id) < 0) { + fprintf(stderr, "Failed to close dataset\n"); + goto error; + } + + free(write_buf); + write_buf = NULL; + + if ((dset_id = H5Dopen2(file_id, DATASET_NAME, H5P_DEFAULT)) < 0) { + fprintf(stderr, "Failed to open dataset\n"); + goto error; + } + + if (NULL == (read_buf = malloc(2 * data_size))) { + fprintf(stderr, "Failed to allocate read buffer\n"); + goto error; + } + + /* Use same shape selection in memory buffer as file selection to + * avoid iterating element by element when setting up chunk dataspaces + * for selections + */ + mem_dims[0] = (hsize_t)CHUNK_DIM_0; + mem_dims[1] = 2; + if ((mem_space_id = H5Screate_simple(2, mem_dims, NULL)) < 0) { + fprintf(stderr, "Failed to create memory dataspace\n"); + goto error; + } + + start[0] = 0; + start[1] = 0; + count[0] = 1; + count[1] = 1; + block[0] = (hsize_t)CHUNK_DIM_0; + block[1] = 1; + if (H5Sselect_hyperslab(mem_space_id, H5S_SELECT_SET, start, NULL, count, block) < 0) { + fprintf(stderr, "Failed to select hyperslab in memory dataspace\n"); + goto error; + } + + for (size_t chunk = 0; chunk < num_chunks; chunk++) { + MESSAGE(VERBO_DEF, ("Reading chunk %zu\n", chunk)); + + start[0] = 0; + start[1] = chunk; + count[0] = 1; + count[1] = 1; + block[0] = (hsize_t)CHUNK_DIM_0; + block[1] = 1; + if (H5Sselect_hyperslab(space_id, H5S_SELECT_SET, start, NULL, count, block) < 0) { + fprintf(stderr, "Failed to select hyperslab for chunk\n"); + goto error; + } + + if (H5Dread(dset_id, H5T_NATIVE_INT, mem_space_id, space_id, H5P_DEFAULT, read_buf) < 0) { + fprintf(stderr, "Failed to read from dataset\n"); + goto error; + } + } + + free(read_buf); + read_buf = NULL; + + if (H5Pclose(dcpl_id) < 0) + goto error; + if (H5Sclose(mem_space_id) < 0) + goto error; + if (H5Sclose(space_id) < 0) + goto error; + if (H5Dclose(dset_id) < 0) + goto error; + if (H5Fclose(file_id) < 0) + goto error; + + if (H5Fdelete(FILE_NAME, fapl_id) < 0) + goto error; + if (H5Pclose(fapl_id) < 0) + goto error; + + return; + +error: + IncTestNumErrs(); + + free(write_buf); + free(read_buf); + + H5E_BEGIN_TRY + { + H5Pclose(dcpl_id); + H5Sclose(mem_space_id); + H5Sclose(space_id); + H5Dclose(dset_id); + H5Fclose(file_id); + + H5Fdelete(FILE_NAME, fapl_id); + H5Pclose(fapl_id); + } + H5E_END_TRY +} +#undef FILE_NAME +#undef DATASET_NAME +#undef DATASET_TYPE +#undef CHUNK_DIM_0 + +int +main(int argc, char **argv) +{ + /* Initialize testing framework */ + if (TestInit(argv[0], NULL, NULL, NULL, NULL, 0) < 0) { + fprintf(stderr, "couldn't initialize testing framework\n"); + exit(EXIT_FAILURE); + } + + AddTest("chunk_non_contig_mem_io", chunk_non_contig_mem_io, NULL, NULL, NULL, 0, + "I/O on chunks that are non-contiguous with respect to memory layout"); + + /* Display testing information */ + TestInfo(stdout); + + /* Parse command line arguments */ + if (TestParseCmdLine(argc, argv) < 0) { + fprintf(stderr, "couldn't parse command-line arguments\n"); + TestShutdown(); + exit(EXIT_FAILURE); + } + + /* Perform requested testing */ + if (PerformTests() < 0) { + fprintf(stderr, "couldn't run tests\n"); + TestShutdown(); + exit(EXIT_FAILURE); + } + + /* Display test summary, if requested */ + if (GetTestSummary()) + TestSummary(stdout); + + /* Release test infrastructure */ + if (TestShutdown() < 0) { + fprintf(stderr, "couldn't shut down testing framework\n"); + exit(EXIT_FAILURE); + } + + /* Exit failure if errors encountered; else exit success. */ + /* No need to print anything since PerformTests() already does. */ + if (GetTestNumErrs() > 0) + exit(EXIT_FAILURE); + else + exit(EXIT_SUCCESS); +}