Compare commits

...

5 Commits

Author SHA1 Message Date
Neil Fortner
9b6a360acd Merge pull request #2418 from fortnern/select_io_tconv
Initial implementation of selection I/O with type conversion
2023-01-25 15:27:35 -06:00
Neil Fortner
87dbb64233 Merge branch 'select_io_tconv' of github.com:fortnern/hdf5 into select_io_tconv 2023-01-25 15:22:29 -06:00
Neil Fortner
896288e49c Fix spelling 2023-01-25 15:20:13 -06:00
github-actions
f7e9b183a1 Committing clang-format changes 2023-01-25 21:15:43 +00:00
Neil Fortner
3aa43dc6b4 Initial implementation of selection I/O with type conversion. Allows
Parallel collective I/O with type conversion, as long as selection I/O
is enabled.
2023-01-25 15:12:47 -06:00
6 changed files with 750 additions and 117 deletions

View File

@@ -2749,6 +2749,8 @@ H5D__chunk_read(H5D_io_info_t *io_info, H5D_dset_io_info_t *dset_info)
io_info->addrs[io_info->pieces_added] = udata.chunk_block.offset;
io_info->element_sizes[io_info->pieces_added] = element_sizes[0];
io_info->rbufs[io_info->pieces_added] = bufs[0];
if (io_info->sel_pieces)
io_info->sel_pieces[io_info->pieces_added] = chunk_info;
io_info->pieces_added++;
}
} /* end if */
@@ -3139,6 +3141,8 @@ H5D__chunk_write(H5D_io_info_t *io_info, H5D_dset_io_info_t *dset_info)
io_info->addrs[io_info->pieces_added] = udata.chunk_block.offset;
io_info->element_sizes[io_info->pieces_added] = element_sizes[0];
io_info->wbufs[io_info->pieces_added] = bufs[0];
if (io_info->sel_pieces)
io_info->sel_pieces[io_info->pieces_added] = chunk_info;
io_info->pieces_added++;
}
} /* end else */

View File

@@ -757,18 +757,15 @@ H5D__contig_may_use_select_io(const H5D_io_info_t *io_info, const H5D_dset_io_in
dataset = dset_info->dset;
/* Don't use selection I/O if it's globally disabled, if there is a type
* conversion, or if it's not a contiguous dataset, or if the sieve buffer
* exists (write) or is dirty (read) */
if (dset_info->io_ops.single_read != H5D__select_read ||
dset_info->layout_ops.readvv != H5D__contig_readvv ||
/* Don't use selection I/O if it's globally disabled, if it's not a contiguous dataset, or if the sieve
* buffer exists (write) or is dirty (read) */
if (dset_info->layout_ops.readvv != H5D__contig_readvv ||
(op_type == H5D_IO_OP_READ && dataset->shared->cache.contig.sieve_dirty) ||
(op_type == H5D_IO_OP_WRITE && dataset->shared->cache.contig.sieve_buf))
ret_value = FALSE;
else {
hbool_t page_buf_enabled;
HDassert(dset_info->io_ops.single_write == H5D__select_write);
HDassert(dset_info->layout_ops.writevv == H5D__contig_writevv);
/* Check if the page buffer is enabled */
@@ -841,6 +838,8 @@ H5D__contig_read(H5D_io_info_t *io_info, H5D_dset_io_info_t *dinfo)
io_info->addrs[io_info->pieces_added] = dinfo->store->contig.dset_addr;
io_info->element_sizes[io_info->pieces_added] = dinfo->type_info.src_type_size;
io_info->rbufs[io_info->pieces_added] = dinfo->buf.vp;
if (io_info->sel_pieces)
io_info->sel_pieces[io_info->pieces_added] = dinfo->layout_io_info.contig_piece_info;
io_info->pieces_added++;
}
}
@@ -911,6 +910,8 @@ H5D__contig_write(H5D_io_info_t *io_info, H5D_dset_io_info_t *dinfo)
io_info->addrs[io_info->pieces_added] = dinfo->store->contig.dset_addr;
io_info->element_sizes[io_info->pieces_added] = dinfo->type_info.dst_type_size;
io_info->wbufs[io_info->pieces_added] = dinfo->buf.cvp;
if (io_info->sel_pieces)
io_info->sel_pieces[io_info->pieces_added] = dinfo->layout_io_info.contig_piece_info;
io_info->pieces_added++;
}
}

View File

@@ -292,16 +292,16 @@ H5D__read(size_t count, H5D_dset_io_info_t *dset_info)
if (io_op_init == 0)
HGOTO_DONE(SUCCEED)
/* Perform second phase of type info initialization */
if (H5D__typeinfo_init_phase2(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to set up type info (second phase)")
#ifdef H5_HAVE_PARALLEL
/* Adjust I/O info for any parallel I/O */
if (H5D__ioinfo_adjust(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to adjust I/O info for parallel I/O")
#endif /*H5_HAVE_PARALLEL*/
/* Perform second phase of type info initialization */
if (H5D__typeinfo_init_phase2(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to set up type info (second phase)")
/* If multi dataset I/O callback is not provided, perform read IO via
* single-dset path with looping */
if (io_info.md_io_ops.multi_read_md) {
@@ -339,6 +339,7 @@ H5D__read(size_t count, H5D_dset_io_info_t *dset_info)
else {
haddr_t prev_tag = HADDR_UNDEF;
/* Allocate selection I/O parameter arrays if necessary */
if (!H5D_LAYOUT_CB_PERFORM_IO(&io_info) && io_info.piece_count > 0) {
if (NULL == (io_info.mem_spaces = H5MM_malloc(io_info.piece_count * sizeof(H5S_t *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
@@ -354,6 +355,11 @@ H5D__read(size_t count, H5D_dset_io_info_t *dset_info)
if (NULL == (io_info.rbufs = H5MM_malloc(io_info.piece_count * sizeof(void *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for read buffer list")
if (io_info.tconv_buf)
if (NULL ==
(io_info.sel_pieces = H5MM_malloc(io_info.piece_count * sizeof(io_info.sel_pieces[0]))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"unable to allocate array of selected pieces")
}
/* Loop with serial & single-dset read IO path */
@@ -373,16 +379,25 @@ H5D__read(size_t count, H5D_dset_io_info_t *dset_info)
H5AC_tag(prev_tag, NULL);
}
/* Make final multi dataset selection I/O call if we are using both
* features - in this case the multi_read callbacks did not perform the
* actual I/O */
/* Make final selection I/O call if the multi_read callbacks did not perform the actual I/O
* (if using selection I/O and either multi dataset or type conversion) */
if (!H5D_LAYOUT_CB_PERFORM_IO(&io_info)) {
/* Check for type conversion */
if (io_info.tconv_buf) {
/* Type conversion pathway */
if (H5D__scatgath_read_select(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "type conversion selection read failed")
}
else {
/* Call selection I/O directly */
H5_CHECK_OVERFLOW(io_info.pieces_added, size_t, uint32_t)
if (!H5D_LAYOUT_CB_PERFORM_IO(&io_info))
if (H5F_shared_select_read(io_info.f_sh, H5FD_MEM_DRAW, (uint32_t)io_info.pieces_added,
io_info.mem_spaces, io_info.file_spaces, io_info.addrs,
io_info.element_sizes, io_info.rbufs) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "selection read failed")
}
}
}
done:
/* Shut down the I/O op information */
@@ -663,16 +678,16 @@ H5D__write(size_t count, H5D_dset_io_info_t *dset_info)
HDassert(type_info_init == count);
HDassert(io_op_init == count);
/* Perform second phase of type info initialization */
if (H5D__typeinfo_init_phase2(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to set up type info (second phase)")
#ifdef H5_HAVE_PARALLEL
/* Adjust I/O info for any parallel I/O */
if (H5D__ioinfo_adjust(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to adjust I/O info for parallel I/O")
#endif /*H5_HAVE_PARALLEL*/
/* Perform second phase of type info initialization */
if (H5D__typeinfo_init_phase2(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to set up type info (second phase)")
/* If multi dataset I/O callback is not provided, perform write IO via
* single-dset path with looping */
if (io_info.md_io_ops.multi_write_md) {
@@ -710,6 +725,7 @@ H5D__write(size_t count, H5D_dset_io_info_t *dset_info)
else {
haddr_t prev_tag = HADDR_UNDEF;
/* Allocate selection I/O parameter arrays if necessary */
if (!H5D_LAYOUT_CB_PERFORM_IO(&io_info) && io_info.piece_count > 0) {
if (NULL == (io_info.mem_spaces = H5MM_malloc(io_info.piece_count * sizeof(H5S_t *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
@@ -724,7 +740,12 @@ H5D__write(size_t count, H5D_dset_io_info_t *dset_info)
"memory allocation failed for element size list")
if (NULL == (io_info.wbufs = H5MM_malloc(io_info.piece_count * sizeof(const void *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for read buffer list")
"memory allocation failed for write buffer list")
if (io_info.tconv_buf)
if (NULL ==
(io_info.sel_pieces = H5MM_malloc(io_info.piece_count * sizeof(io_info.sel_pieces[0]))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"unable to allocate array of selected pieces")
}
/* loop with serial & single-dset write IO path */
@@ -742,16 +763,25 @@ H5D__write(size_t count, H5D_dset_io_info_t *dset_info)
H5AC_tag(prev_tag, NULL);
}
/* Make final multi dataset selection I/O call if we are using both
* features - in this case the multi_write callbacks did not perform the
* actual I/O */
/* Make final selection I/O call if the multi_write callbacks did not perform the actual I/O
* (if using selection I/O and either multi dataset or type conversion) */
if (!H5D_LAYOUT_CB_PERFORM_IO(&io_info)) {
/* Check for type conversion */
if (io_info.tconv_buf) {
/* Type conversion pathway */
if (H5D__scatgath_write_select(&io_info) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "type conversion selection write failed")
}
else {
/* Call selection I/O directly */
H5_CHECK_OVERFLOW(io_info.pieces_added, size_t, uint32_t)
if (!H5D_LAYOUT_CB_PERFORM_IO(&io_info))
if (H5F_shared_select_write(io_info.f_sh, H5FD_MEM_DRAW, (uint32_t)io_info.pieces_added,
io_info.mem_spaces, io_info.file_spaces, io_info.addrs,
io_info.element_sizes, io_info.wbufs) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "selection write failed")
}
}
}
#ifdef OLD_WAY
/*
@@ -998,9 +1028,9 @@ H5D__typeinfo_init(H5D_io_info_t *io_info, H5D_dset_io_info_t *dset_info, hid_t
/* Check if the datatypes are compound subsets of one another */
type_info->cmpd_subset = H5T_path_compound_subset(type_info->tpath);
/* Update io_info->max_type_size */
io_info->max_type_size =
MAX3(io_info->max_type_size, type_info->src_type_size, type_info->dst_type_size);
/* Update io_info->max_tconv_type_size */
io_info->max_tconv_type_size =
MAX3(io_info->max_tconv_type_size, type_info->src_type_size, type_info->dst_type_size);
/* Check if we need a background buffer */
if ((io_info->op_type == H5D_IO_OP_WRITE) && H5T_detect_class(dset->shared->type, H5T_VLEN, FALSE))
@@ -1026,7 +1056,8 @@ done:
* Function: H5D__typeinfo_init_phase2
*
* Purpose: Finish initializing type info for all datasets after
* calculating the max type size across all datasets.
* calculating the max type size across all datasets. And
* after H5D__ioinfo_adjust().
*
* Return: Non-negative on success/Negative on failure
*
@@ -1043,12 +1074,74 @@ H5D__typeinfo_init_phase2(H5D_io_info_t *io_info)
HDassert(io_info);
/* Check if we need to allocate a shared type conversion buffer */
if (io_info->max_type_size) {
if (io_info->max_tconv_type_size) {
H5FD_mpio_xfer_t xfer_mode; /* Parallel transfer for this request */
size_t i; /* Local index variable */
/* Get the original state of parallel I/O transfer mode */
if (H5CX_get_io_xfer_mode(&xfer_mode) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get MPI-I/O transfer mode")
/* Check if we're doing collective I/O */
if (xfer_mode == H5FD_MPIO_COLLECTIVE) {
size_t tconv_buf_size = 0; /* Size of shared type conversion buffer */
/* Only implemented for selection I/O */
HDassert(io_info->use_select_io);
/* Collective I/O, conversion buffer must be large enough for entire I/O (for now).
* Stick with individual background buffers */
/* Calculate size of buffers */
for (i = 0; i < io_info->count; i++) {
H5D_type_info_t *type_info = &io_info->dsets_info[i].type_info;
/* Check if type conversion buffer is needed for this dataset */
if (!type_info->is_conv_noop || !type_info->is_xform_noop) {
/* Calculate location and size of this dataset's portion of the global type
* conversion buffer */
tconv_buf_size += io_info->dsets_info[i].nelmts *
MAX(type_info->src_type_size, type_info->dst_type_size);
/* Allocate background buffer, if necessary */
if (type_info->need_bkg) {
if (NULL ==
(type_info->bkg_buf = H5FL_BLK_CALLOC(type_conv, io_info->dsets_info[i].nelmts *
type_info->dst_type_size)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
"memory allocation failed for type conversion")
type_info->bkg_buf_allocated = TRUE;
/* Check if we need to fill the background buffer with the destination contents */
if (type_info->need_bkg == H5T_BKG_YES)
io_info->must_fill_bkg = TRUE;
}
}
}
/* Allocate global type conversion buffer (if any, could be none if datasets in this
* I/O have 0 elements selected) */
/* Allocating large buffers here will blow out all other type conversion buffers
* on the free list. Should we change this to a regular malloc? Would require
* keeping track of which version of free() to call. -NAF */
if (tconv_buf_size > 0) {
if (NULL == (io_info->tconv_buf = H5FL_BLK_MALLOC(type_conv, tconv_buf_size)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
"memory allocation failed for type conversion")
io_info->tconv_buf_allocated = TRUE;
}
}
else {
/* No collective I/O, only need to make sure it's big enough for one element */
void *tconv_buf; /* Temporary conversion buffer pointer */
void *bkgr_buf; /* Background conversion buffer pointer */
size_t max_temp_buf; /* Maximum temporary buffer size */
size_t target_size; /* Desired buffer size */
size_t i; /* Local index variable */
/* Disable selection I/O (for now) since the selection I/O type conversion code path
* assumes the tconv buf is large enough to hold all data in the I/O, and we don't want
* to impose this when there's no benefit */
io_info->use_select_io = FALSE;
/* Get info from API context */
if (H5CX_get_max_temp_buf(&max_temp_buf) < 0)
@@ -1056,24 +1149,25 @@ H5D__typeinfo_init_phase2(H5D_io_info_t *io_info)
if (H5CX_get_tconv_buf(&tconv_buf) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't retrieve temp. conversion buffer pointer")
if (H5CX_get_bkgr_buf(&bkgr_buf) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't retrieve background conversion buffer pointer")
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL,
"can't retrieve background conversion buffer pointer")
/* Set up datatype conversion/background buffers */
target_size = max_temp_buf;
/* If the buffer is too small to hold even one element (in the dataset with the largest , try to make
* it bigger */
if (target_size < io_info->max_type_size) {
/* If the buffer is too small to hold even one element (in the dataset with the largest , try to
* make it bigger */
if (target_size < io_info->max_tconv_type_size) {
hbool_t default_buffer_info; /* Whether the buffer information are the defaults */
/* Detect if we have all default settings for buffers */
default_buffer_info =
(hbool_t)((H5D_TEMP_BUF_SIZE == max_temp_buf) && (NULL == tconv_buf) && (NULL == bkgr_buf));
default_buffer_info = (hbool_t)((H5D_TEMP_BUF_SIZE == max_temp_buf) && (NULL == tconv_buf) &&
(NULL == bkgr_buf));
/* Check if we are using the default buffer info */
if (default_buffer_info)
/* OK to get bigger for library default settings */
target_size = io_info->max_type_size;
target_size = io_info->max_tconv_type_size;
else
/* Don't get bigger than the application has requested */
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "temporary buffer max size is too small")
@@ -1087,7 +1181,8 @@ H5D__typeinfo_init_phase2(H5D_io_info_t *io_info)
if (NULL == (io_info->tconv_buf = (uint8_t *)tconv_buf)) {
/* Allocate temporary buffer */
if (NULL == (io_info->tconv_buf = H5FL_BLK_MALLOC(type_conv, target_size)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for type conversion")
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
"memory allocation failed for type conversion")
io_info->tconv_buf_allocated = TRUE;
} /* end if */
@@ -1101,7 +1196,8 @@ H5D__typeinfo_init_phase2(H5D_io_info_t *io_info)
H5D_type_info_t *type_info = &io_info->dsets_info[i].type_info;
/* Compute the number of elements that will fit into buffer */
type_info->request_nelmts = target_size / MAX(type_info->src_type_size, type_info->dst_type_size);
type_info->request_nelmts =
target_size / MAX(type_info->src_type_size, type_info->dst_type_size);
;
/* Sanity check elements in temporary buffer */
@@ -1127,6 +1223,7 @@ H5D__typeinfo_init_phase2(H5D_io_info_t *io_info)
} /* end if */
}
}
}
done:
FUNC_LEAVE_NOAPI(ret_value)

View File

@@ -626,6 +626,8 @@ H5D__mpio_opt_possible(H5D_io_info_t *io_info)
if (!H5FD_mpi_opt_types_g)
local_cause[0] |= H5D_MPIO_MPI_OPT_TYPES_ENV_VAR_DISABLED;
/* Datatype conversions and transformations are allowed with selection I/O */
if (!io_info->use_select_io) {
/* Don't allow collective operations if datatype conversions need to happen */
if (!type_info->is_conv_noop)
local_cause[0] |= H5D_MPIO_DATATYPE_CONVERSION;
@@ -633,6 +635,7 @@ H5D__mpio_opt_possible(H5D_io_info_t *io_info)
/* Don't allow collective operations if data transform operations should occur */
if (!type_info->is_xform_noop)
local_cause[0] |= H5D_MPIO_DATA_TRANSFORMS;
}
/* Check whether these are both simple or scalar dataspaces */
if (!((H5S_SIMPLE == H5S_GET_EXTENT_TYPE(mem_space) ||

View File

@@ -70,7 +70,8 @@
#define H5D_BT2_MERGE_PERC 40
/* Macro to determine if the layout I/O callback should perform I/O */
#define H5D_LAYOUT_CB_PERFORM_IO(IO_INFO) (!(IO_INFO)->use_select_io || (IO_INFO)->count == 1)
#define H5D_LAYOUT_CB_PERFORM_IO(IO_INFO) \
(!(IO_INFO)->use_select_io || ((IO_INFO)->count == 1 && !(IO_INFO)->tconv_buf))
/****************************/
/* Package Private Typedefs */
@@ -269,7 +270,10 @@ typedef struct H5D_io_info_t {
hbool_t use_select_io; /* Whether to use selection I/O */
uint8_t *tconv_buf; /* Datatype conv buffer */
hbool_t tconv_buf_allocated; /* Whether the type conversion buffer was allocated */
size_t max_type_size; /* Largest of all source and destination type sizes */
size_t max_tconv_type_size; /* Largest of all source and destination type sizes involved in type
conversion */
hbool_t
must_fill_bkg; /* Whether any datasets need a background buffer filled with destination contents */
} H5D_io_info_t;
/* Created to pass both at once for callback func */
@@ -623,12 +627,14 @@ H5_DLL herr_t H5D__select_write(const H5D_io_info_t *io_info, const H5D_dset_io_
H5_DLL herr_t H5D_select_io_mem(void *dst_buf, H5S_t *dst_space, const void *src_buf, H5S_t *src_space,
size_t elmt_size, size_t nelmts);
/* Functions that perform scatter-gather serial I/O operations */
/* Functions that perform scatter-gather I/O operations */
H5_DLL herr_t H5D__scatter_mem(const void *_tscat_buf, H5S_sel_iter_t *iter, size_t nelmts, void *_buf);
H5_DLL size_t H5D__gather_mem(const void *_buf, H5S_sel_iter_t *iter, size_t nelmts,
void *_tgath_buf /*out*/);
H5_DLL herr_t H5D__scatgath_read(const H5D_io_info_t *io_info, const H5D_dset_io_info_t *dset_info);
H5_DLL herr_t H5D__scatgath_write(const H5D_io_info_t *io_info, const H5D_dset_io_info_t *dset_info);
H5_DLL herr_t H5D__scatgath_read_select(H5D_io_info_t *io_info);
H5_DLL herr_t H5D__scatgath_write_select(H5D_io_info_t *io_info);
/* Functions that operate on dataset's layout information */
H5_DLL herr_t H5D__layout_set_io_ops(const H5D_t *dataset);

View File

@@ -726,6 +726,528 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__scatgath_write() */
/*-------------------------------------------------------------------------
* Function: H5D__scatgath_read_select
*
* Purpose: Perform scatter/gather read from a list of dataset pieces
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
herr_t
H5D__scatgath_read_select(H5D_io_info_t *io_info)
{
H5S_t **tmp_mem_spaces = NULL; /* Memory spaces to use for read from disk */
H5S_sel_iter_t *mem_iter = NULL; /* Memory selection iteration info */
hbool_t mem_iter_init = FALSE; /* Memory selection iteration info has been initialized */
void **tmp_bufs = NULL; /* Buffers to use for read from disk */
size_t buf_bytes_used = 0; /* Number of bytes used so far in conversion/background buffer */
H5D_dset_io_info_t *last_bkg_buf_dset = NULL; /* Index of last dset that used a background buffer */
size_t i; /* Local index variable */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE
/* Sanity check */
HDassert(io_info);
HDassert(io_info->count > 0);
HDassert(io_info->mem_spaces || io_info->pieces_added == 0);
HDassert(io_info->file_spaces || io_info->pieces_added == 0);
HDassert(io_info->addrs || io_info->pieces_added == 0);
HDassert(io_info->element_sizes || io_info->pieces_added == 0);
HDassert(io_info->rbufs || io_info->pieces_added == 0);
/* Allocate list of buffers (within the tconv buf) */
if (NULL == (tmp_bufs = H5MM_malloc(io_info->pieces_added * sizeof(void *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "memory allocation failed for temporary buffer list")
/* Allocate the iterator */
if (NULL == (mem_iter = H5FL_MALLOC(H5S_sel_iter_t)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, "can't allocate memory iterator")
/* Allocate list of block memory spaces */
/*!FIXME delay doing this until we find the first mem space that is non-contiguous or doesn't start at 0
*/
if (NULL == (tmp_mem_spaces = H5MM_malloc(io_info->pieces_added * sizeof(H5S_t *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for temporary memory space list")
/* Build read operation to tconv buffer */
for (i = 0; i < io_info->pieces_added; i++) {
H5D_dset_io_info_t *dset_info = io_info->sel_pieces[i]->dset_info;
HDassert(io_info->sel_pieces[i]->piece_points > 0);
/* Check if this piece is involved in type conversion */
if (dset_info->type_info.is_xform_noop && dset_info->type_info.is_conv_noop) {
/* No type conversion, just copy the mem space and buffer */
tmp_mem_spaces[i] = io_info->mem_spaces[i];
tmp_bufs[i] = io_info->rbufs[i];
}
else {
/* Create block memory space */
if (NULL ==
(tmp_mem_spaces[i] = H5S_create_simple(1, &io_info->sel_pieces[i]->piece_points, NULL))) {
HDmemset(&tmp_mem_spaces[i], 0, (io_info->pieces_added - i) * sizeof(tmp_mem_spaces[0]));
HGOTO_ERROR(H5E_DATASET, H5E_CANTCREATE, FAIL, "unable to create simple memory dataspace")
}
/* Set buffer to point into type conversion buffer */
tmp_bufs[i] = io_info->tconv_buf + buf_bytes_used;
buf_bytes_used += io_info->sel_pieces[i]->piece_points *
MAX(dset_info->type_info.src_type_size, dset_info->type_info.dst_type_size);
}
}
/* Read data from all pieces */
H5_CHECK_OVERFLOW(io_info->pieces_added, size_t, uint32_t)
if (H5F_shared_select_read(io_info->f_sh, H5FD_MEM_DRAW, (uint32_t)io_info->pieces_added, tmp_mem_spaces,
io_info->file_spaces, io_info->addrs, io_info->element_sizes, tmp_bufs) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "selection read failed")
/* Perform type conversion and scatter data to memory buffers for datasets that need this */
for (i = 0; i < io_info->pieces_added; i++) {
H5D_dset_io_info_t *dset_info = io_info->sel_pieces[i]->dset_info;
HDassert(tmp_mem_spaces[i]);
/* Check if this piece is involved in type conversion */
if (tmp_mem_spaces[i] != io_info->mem_spaces[i]) {
H5_CHECK_OVERFLOW(io_info->sel_pieces[i]->piece_points, hsize_t, size_t);
/* Initialize memory iterator */
HDassert(!mem_iter_init);
if (H5S_select_iter_init(mem_iter, io_info->mem_spaces[i], dset_info->type_info.dst_type_size,
0) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL,
"unable to initialize memory selection information")
mem_iter_init = TRUE; /* Memory selection iteration info has been initialized */
/* If the source and destination are compound types and subset of each other
* and no conversion is needed, copy the data directly into user's buffer and
* bypass the rest of steps.
*/
if (dset_info->type_info.cmpd_subset &&
H5T_SUBSET_FALSE != dset_info->type_info.cmpd_subset->subset) {
if (H5D__compound_opt_read((size_t)io_info->sel_pieces[i]->piece_points, mem_iter,
&dset_info->type_info, tmp_bufs[i], io_info->rbufs[i] /*out*/) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "datatype conversion failed")
}
else {
void *tmp_bkg_buf = NULL;
/* Check for background buffer */
if (dset_info->type_info.need_bkg) {
HDassert(dset_info->type_info.bkg_buf);
/* Check if we moved to a new background buffer */
if (dset_info != last_bkg_buf_dset) {
/* Reset buf_bytes_used */
buf_bytes_used = 0;
last_bkg_buf_dset = dset_info;
}
/* Calculate background buffer position */
tmp_bkg_buf = dset_info->type_info.bkg_buf + buf_bytes_used;
buf_bytes_used +=
io_info->sel_pieces[i]->piece_points * dset_info->type_info.dst_type_size;
/* Gather data from read buffer to background buffer if necessary */
if (H5T_BKG_YES == dset_info->type_info.need_bkg) {
if ((size_t)io_info->sel_pieces[i]->piece_points !=
H5D__gather_mem(io_info->rbufs[i], mem_iter,
(size_t)io_info->sel_pieces[i]->piece_points,
tmp_bkg_buf /*out*/))
HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "mem gather failed")
/* Reset selection iterator */
HDassert(mem_iter_init);
if (H5S_SELECT_ITER_RELEASE(mem_iter) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "Can't release selection iterator")
mem_iter_init = FALSE;
if (H5S_select_iter_init(mem_iter, io_info->mem_spaces[i],
dset_info->type_info.dst_type_size, 0) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL,
"unable to initialize memory selection information")
mem_iter_init = TRUE; /* Memory selection iteration info has been initialized */
}
}
/*
* Perform datatype conversion.
*/
if (H5T_convert(dset_info->type_info.tpath, dset_info->type_info.src_type_id,
dset_info->type_info.dst_type_id,
(size_t)io_info->sel_pieces[i]->piece_points, (size_t)0, (size_t)0,
tmp_bufs[i], tmp_bkg_buf) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCONVERT, FAIL, "datatype conversion failed")
/* Do the data transform after the conversion (since we're using type mem_type) */
if (!dset_info->type_info.is_xform_noop) {
H5Z_data_xform_t *data_transform; /* Data transform info */
/* Retrieve info from API context */
if (H5CX_get_data_transform(&data_transform) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get data transform info")
if (H5Z_xform_eval(data_transform, tmp_bufs[i],
(size_t)io_info->sel_pieces[i]->piece_points,
dset_info->type_info.mem_type) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "Error performing data transform")
}
/* Scatter the data into memory */
if (H5D__scatter_mem(tmp_bufs[i], mem_iter, (size_t)io_info->sel_pieces[i]->piece_points,
io_info->rbufs[i] /*out*/) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "scatter failed")
}
/* Release selection iterator */
HDassert(mem_iter_init);
if (H5S_SELECT_ITER_RELEASE(mem_iter) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "Can't release selection iterator")
mem_iter_init = FALSE;
}
}
done:
/* Release and free selection iterator */
if (mem_iter_init && H5S_SELECT_ITER_RELEASE(mem_iter) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "Can't release selection iterator")
if (mem_iter)
mem_iter = H5FL_FREE(H5S_sel_iter_t, mem_iter);
/* Free tmp_bufs */
H5MM_free(tmp_bufs);
tmp_bufs = NULL;
/* Clear and free tmp_mem_spaces */
if (tmp_mem_spaces) {
for (i = 0; i < io_info->pieces_added; i++)
if (tmp_mem_spaces[i] != io_info->mem_spaces[i] && tmp_mem_spaces[i] &&
H5S_close(tmp_mem_spaces[i]) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "Can't close dataspace")
H5MM_free(tmp_mem_spaces);
tmp_mem_spaces = NULL;
}
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__scatgath_read_select() */
/*-------------------------------------------------------------------------
* Function: H5D__scatgath_write_select
*
* Purpose: Perform scatter/gather write to a list of dataset pieces.
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
herr_t
H5D__scatgath_write_select(H5D_io_info_t *io_info)
{
H5S_t **write_mem_spaces = NULL; /* Memory spaces to use for write to disk */
size_t spaces_added = 0; /* Number of spaces added to write_mem_spaces */
H5S_sel_iter_t *mem_iter = NULL; /* Memory selection iteration info */
hbool_t mem_iter_init = FALSE; /* Memory selection iteration info has been initialized */
const void **write_bufs = NULL; /* Buffers to use for write to disk */
size_t tconv_bytes_used = 0; /* Number of bytes used so far in conversion buffer */
size_t bkg_bytes_used = 0; /* Number of bytes used so far in background buffer */
H5D_dset_io_info_t *last_bkg_buf_dset = NULL; /* Index of last dset that used a background buffer */
H5S_t **bkg_mem_spaces = NULL; /* Array of memory spaces for read to background buffer */
H5S_t **bkg_file_spaces = NULL; /* Array of file spaces for read to background buffer */
haddr_t *bkg_addrs = NULL; /* Array of file addresses for read to background buffer */
size_t *bkg_element_sizes = NULL; /* Array of element sizes for read to background buffer */
void **bkg_bufs = NULL; /* Array background buffers for read of existing file contents */
size_t bkg_pieces = 0; /* Number of pieces that need to read the background data from disk */
size_t i; /* Local index variable */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE
/* Sanity check */
HDassert(io_info);
HDassert(io_info->count > 0);
HDassert(io_info->mem_spaces || io_info->pieces_added == 0);
HDassert(io_info->file_spaces || io_info->pieces_added == 0);
HDassert(io_info->addrs || io_info->pieces_added == 0);
HDassert(io_info->element_sizes || io_info->pieces_added == 0);
HDassert(io_info->wbufs || io_info->pieces_added == 0);
/* Allocate list of buffers (within the tconv buf) */
if (NULL == (write_bufs = (const void **)H5MM_malloc(io_info->pieces_added * sizeof(const void *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "memory allocation failed for temporary buffer list")
/* Allocate the iterator */
if (NULL == (mem_iter = H5FL_MALLOC(H5S_sel_iter_t)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, "can't allocate memory iterator")
/* Allocate list of block memory spaces */
/*!FIXME delay doing this until we find the first mem space that is non-contiguous or doesn't start at 0
*/
if (NULL == (write_mem_spaces = H5MM_malloc(io_info->pieces_added * sizeof(H5S_t *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for temporary memory space list")
/* Build operations to read data to background buffer and to write data */
for (i = 0; i < io_info->pieces_added; i++) {
H5D_dset_io_info_t *dset_info = io_info->sel_pieces[i]->dset_info;
HDassert(io_info->sel_pieces[i]->piece_points > 0);
/* Check if this piece is involved in type conversion */
if (dset_info->type_info.is_xform_noop && dset_info->type_info.is_conv_noop) {
/* No type conversion, just copy the mem space and buffer */
write_mem_spaces[i] = io_info->mem_spaces[i];
spaces_added++;
write_bufs[i] = io_info->wbufs[i];
}
else {
void *tmp_write_buf; /* To sidestep const warnings */
void *tmp_bkg_buf = NULL;
H5_CHECK_OVERFLOW(io_info->sel_pieces[i]->piece_points, hsize_t, size_t);
/* Initialize memory iterator */
HDassert(!mem_iter_init);
if (H5S_select_iter_init(mem_iter, io_info->mem_spaces[i], dset_info->type_info.src_type_size,
0) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL,
"unable to initialize memory selection information")
mem_iter_init = TRUE; /* Memory selection iteration info has been initialized */
/* Create block memory space */
if (NULL ==
(write_mem_spaces[i] = H5S_create_simple(1, &io_info->sel_pieces[i]->piece_points, NULL)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTCREATE, FAIL, "unable to create simple memory dataspace")
spaces_added++;
/* Set buffer to point into type conversion buffer */
tmp_write_buf = io_info->tconv_buf + tconv_bytes_used;
write_bufs[i] = (const void *)tmp_write_buf;
tconv_bytes_used += io_info->sel_pieces[i]->piece_points *
MAX(dset_info->type_info.src_type_size, dset_info->type_info.dst_type_size);
/* Gather data from application buffer into the datatype conversion buffer */
if ((size_t)io_info->sel_pieces[i]->piece_points !=
H5D__gather_mem(io_info->wbufs[i], mem_iter, (size_t)io_info->sel_pieces[i]->piece_points,
tmp_write_buf /*out*/))
HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "mem gather failed")
/* If the source and destination are compound types and the destination is
* is a subset of the source and no conversion is needed, copy the data
* directly into user's buffer and bypass the rest of steps. If the source
* is a subset of the destination, the optimization is done in conversion
* function H5T_conv_struct_opt to protect the background data.
*/
if (dset_info->type_info.cmpd_subset &&
H5T_SUBSET_DST == dset_info->type_info.cmpd_subset->subset &&
dset_info->type_info.dst_type_size == dset_info->type_info.cmpd_subset->copy_size) {
if (H5D__compound_opt_write((size_t)io_info->sel_pieces[i]->piece_points,
&dset_info->type_info, tmp_write_buf) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "datatype conversion failed")
/* No background buffer necessary, prevent this element from being considered in the second
* loop */
/* Add this to H5Tconv.c? -NAF */
dset_info->type_info.need_bkg = H5T_BKG_NO;
} /* end if */
else {
/* Check for background buffer */
if (dset_info->type_info.need_bkg) {
HDassert(dset_info->type_info.bkg_buf);
/* Check if we moved to a new background buffer */
if (dset_info != last_bkg_buf_dset) {
/* Reset bkg_bytes_used */
bkg_bytes_used = 0;
last_bkg_buf_dset = dset_info;
}
/* Calculate background buffer position */
tmp_bkg_buf = dset_info->type_info.bkg_buf + bkg_bytes_used;
bkg_bytes_used +=
io_info->sel_pieces[i]->piece_points * dset_info->type_info.dst_type_size;
}
/* Set up background buffer read operation if necessary */
if (H5T_BKG_YES == dset_info->type_info.need_bkg) {
HDassert(io_info->must_fill_bkg);
/* Allocate arrays of parameters for selection read to background buffer if necessary */
if (!bkg_mem_spaces) {
HDassert(!bkg_file_spaces && !bkg_addrs && !bkg_element_sizes && !bkg_bufs);
if (NULL == (bkg_mem_spaces = H5MM_malloc(io_info->pieces_added * sizeof(H5S_t *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for memory space list")
if (NULL == (bkg_file_spaces = H5MM_malloc(io_info->pieces_added * sizeof(H5S_t *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for file space list")
if (NULL == (bkg_addrs = H5MM_malloc(io_info->pieces_added * sizeof(haddr_t))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for piece address list")
if (NULL == (bkg_element_sizes = H5MM_malloc(io_info->pieces_added * sizeof(size_t))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for element size list")
if (NULL == (bkg_bufs = H5MM_malloc(io_info->pieces_added * sizeof(const void *))))
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
"memory allocation failed for write buffer list")
}
/* Use same (block) memory space, file space, address, and element size as write operation
*/
HDassert(bkg_mem_spaces && bkg_file_spaces && bkg_addrs && bkg_element_sizes && bkg_bufs);
bkg_mem_spaces[bkg_pieces] = write_mem_spaces[i];
bkg_file_spaces[bkg_pieces] = io_info->file_spaces[i];
bkg_addrs[bkg_pieces] = io_info->addrs[i];
bkg_element_sizes[bkg_pieces] = io_info->element_sizes[i];
/* Use previously calculated background buffer position */
bkg_bufs[bkg_pieces] = tmp_bkg_buf;
/* Add piece */
bkg_pieces++;
}
else {
/* Perform type conversion here to avoid second loop if no dsets use the background buffer
*/
/* Do the data transform before the type conversion (since
* transforms must be done in the memory type). */
if (!dset_info->type_info.is_xform_noop) {
H5Z_data_xform_t *data_transform; /* Data transform info */
/* Retrieve info from API context */
if (H5CX_get_data_transform(&data_transform) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get data transform info")
if (H5Z_xform_eval(data_transform, tmp_write_buf,
(size_t)io_info->sel_pieces[i]->piece_points,
dset_info->type_info.mem_type) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "Error performing data transform")
}
/*
* Perform datatype conversion.
*/
if (H5T_convert(dset_info->type_info.tpath, dset_info->type_info.src_type_id,
dset_info->type_info.dst_type_id,
(size_t)io_info->sel_pieces[i]->piece_points, (size_t)0, (size_t)0,
tmp_write_buf, tmp_bkg_buf) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCONVERT, FAIL, "datatype conversion failed")
}
}
/* Release selection iterator */
HDassert(mem_iter_init);
if (H5S_SELECT_ITER_RELEASE(mem_iter) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "Can't release selection iterator")
mem_iter_init = FALSE;
}
}
HDassert(spaces_added == io_info->pieces_added);
/* Gather data to background buffer if necessary */
if (io_info->must_fill_bkg) {
size_t j = 0; /* Index into array of background buffers */
/* Read data */
H5_CHECK_OVERFLOW(bkg_pieces, size_t, uint32_t)
if (H5F_shared_select_read(io_info->f_sh, H5FD_MEM_DRAW, (uint32_t)bkg_pieces, bkg_mem_spaces,
bkg_file_spaces, bkg_addrs, bkg_element_sizes, bkg_bufs) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "selection read to background buffer failed")
/* Perform type conversion on pieces with background buffers that were just read */
for (i = 0; i < io_info->pieces_added; i++) {
H5D_dset_io_info_t *dset_info = io_info->sel_pieces[i]->dset_info;
if (H5T_BKG_YES == dset_info->type_info.need_bkg) {
/* Non-const write_buf[i]. Use pointer math here to avoid const warnings. When
* there's a background buffer write_buf[i] always points inside the non-const tconv
* buf so this is OK. */
void *tmp_write_buf =
(void *)((uint8_t *)io_info->tconv_buf +
((const uint8_t *)write_bufs[i] - (const uint8_t *)io_info->tconv_buf));
/* Do the data transform before the type conversion (since
* transforms must be done in the memory type). */
if (!dset_info->type_info.is_xform_noop) {
H5Z_data_xform_t *data_transform; /* Data transform info */
/* Retrieve info from API context */
if (H5CX_get_data_transform(&data_transform) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get data transform info")
if (H5Z_xform_eval(data_transform, tmp_write_buf,
(size_t)io_info->sel_pieces[i]->piece_points,
dset_info->type_info.mem_type) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "Error performing data transform")
}
/*
* Perform datatype conversion.
*/
HDassert(j < bkg_pieces);
if (H5T_convert(dset_info->type_info.tpath, dset_info->type_info.src_type_id,
dset_info->type_info.dst_type_id,
(size_t)io_info->sel_pieces[i]->piece_points, (size_t)0, (size_t)0,
tmp_write_buf, bkg_bufs[j]) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCONVERT, FAIL, "datatype conversion failed")
/* Advance to next background buffer */
j++;
}
}
HDassert(j == bkg_pieces);
}
/* Write data to disk */
H5_CHECK_OVERFLOW(io_info->pieces_added, size_t, uint32_t)
if (H5F_shared_select_write(io_info->f_sh, H5FD_MEM_DRAW, (uint32_t)io_info->pieces_added,
write_mem_spaces, io_info->file_spaces, io_info->addrs,
io_info->element_sizes, write_bufs) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "selection write failed")
done:
/* Release and free selection iterator */
if (mem_iter_init && H5S_SELECT_ITER_RELEASE(mem_iter) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "Can't release selection iterator")
if (mem_iter)
mem_iter = H5FL_FREE(H5S_sel_iter_t, mem_iter);
/* Free write_bufs */
H5MM_free(write_bufs);
write_bufs = NULL;
/* Clear and free write_mem_spaces */
if (write_mem_spaces) {
for (i = 0; i < spaces_added; i++) {
HDassert(write_mem_spaces[i]);
if (write_mem_spaces[i] != io_info->mem_spaces[i] && H5S_close(write_mem_spaces[i]) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "Can't close dataspace")
}
H5MM_free(write_mem_spaces);
write_mem_spaces = NULL;
}
/* Free bakcground buffer parameter arrays */
H5MM_free(bkg_mem_spaces);
bkg_mem_spaces = NULL;
H5MM_free(bkg_file_spaces);
bkg_file_spaces = NULL;
H5MM_free(bkg_addrs);
bkg_addrs = NULL;
H5MM_free(bkg_element_sizes);
bkg_element_sizes = NULL;
H5MM_free(bkg_bufs);
bkg_bufs = NULL;
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__scatgath_write_select() */
/*-------------------------------------------------------------------------
* Function: H5D__compound_opt_read
*