netcdf-c/libnczarr/zchunking.c
Dennis Heimbigner d2316f866c Additional Fixes to NCZarr
Primary Fixes:
* Add a whole variable optimization -- used in the rare case that nc_get/put_vara covers the whole of a variable and the variable has a single chunk.
* Fix chunking error when stride causes whole chunks to be skipped.
* Fix some memory leaks
* Add test cases
* Add one performance test to nczarr_test/. This uses the timer utils from unit_test: timer_utils.[ch].
* Move ncdumpchunks utility from ncdump to nczarr_test

Misc. Other Changes:
* Make check for aws libraries conditional on --enable-nczarr-s3
* Remove all but one bm tests from nczarr_test until they are working.
* Remove another dependency on HDF5 from supposedly non-HDF5 specific code; specifically hdf5_log_hdf5.
* Make the BAIL2 macro be hdf5 specific and replace elsewhere with an HDF5 independent equivalent.
* Move hdf5cache.c to libsrc4/nc4cache.c because it is used by nczarr.
* Modify unit_tests so that some of them are run even if using Windows.
* Misc. small bug fixes and refactors and memory leaks.
* Rename some conflicting tests for cmake.
* Attempted to make nc_perf work with cmake and failed.
2020-12-16 20:48:02 -07:00

260 lines
8.2 KiB
C

/*********************************************************************
* Copyright 2018, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
#include "zincludes.h"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
static int pcounter = 0;
/* Forward */
static int compute_intersection(const NCZSlice* slice, const size64_t chunklen, NCZChunkRange* range);
static void skipchunk(const NCZSlice* slice, size64_t offset, NCZProjection* projection);
/**************************************************/
/* Goal:create a vector of chunk ranges: one for each slice in
the top-level input. For each slice, compute the index (not
absolute position) of the first chunk that intersects the slice
and the index of the last chunk that intersects the slice.
In practice, the count = last - first + 1 is stored instead of the last index.
*/
int
NCZ_compute_chunk_ranges(
int rank, /* variable rank */
const NCZSlice* slices, /* the complete set of slices |slices| == R*/
const size64_t* chunklen, /* the chunk length corresponding to the dimensions */
NCZChunkRange* ncr)
{
int stat = NC_NOERR;
int i;
for(i=0;i<rank;i++) {
if((stat = compute_intersection(&slices[i],chunklen[i],&ncr[i])))
goto done;
}
done:
return stat;
}
static int
compute_intersection(
const NCZSlice* slice,
const size64_t chunklen,
NCZChunkRange* range)
{
range->start = floordiv(slice->start, chunklen);
range->stop = ceildiv(slice->stop, chunklen);
return NC_NOERR;
}
/**
Compute the projection of a slice as applied to n'th chunk.
This is somewhat complex because:
1. for the first projection, the start is the slice start,
but after that, we have to take into account that for
a non-one stride, the start point in a projection may
be offset by some value in the range of 0..(slice.stride-1).
2. The stride might be so large as to completely skip some chunks.
*/
int
NCZ_compute_projections(int r, size64_t dimlen, size64_t chunklen, size64_t chunkindex, const NCZSlice* slice, size_t n, NCZProjection* projections)
{
int stat = NC_NOERR;
size64_t offset;
NCZProjection* projection = NULL;
NC_UNUSED(r);
projection = &projections[n];
projection->id = ++pcounter;
projection->chunkindex = chunkindex;
offset = chunklen * chunkindex; /* with respect to dimension (WRD) */
/* Actual limit of the n'th touched chunk, taking
dimlen and stride->stop into account. */
#if 0
projection->limit = (chunkindex + 1) * chunklen; /* WRD */
if(projection->limit > slice->stop) projection->limit = slice->stop;
if(projection->limit > dimlen) projection->limit = dimlen;
#else
projection->limit = MIN(MIN((chunkindex + 1) * chunklen,slice->stop),dimlen);
#endif
/* See if the next point after the last one in prev lands in the current projection.
If not, then we have skipped the current chunk. Also take limit into account.
Note by definition, n must be greater than zero because we always start in a relevant chunk.
*/
if(n > 0) {
NCZProjection* prev = &projections[n-1];
/* Watch out for negative value: want (prev->last + slice->stride) - prev->limit >= projection->limit */
if((prev->last + slice->stride) >= prev->limit + projection->limit) { /* this chunk is being skipped */
skipchunk(slice,offset,projection);
goto done;
}
}
if(n == 0) {
/*initial case: original slice start is in 1st projection */
projection->first = slice->start - offset;
projection->iopos = 0;
} else { /* n > 0 */
/* Compute start point in this chunk */
/* Compute limit of previous chunk */
size64_t rem = (offset - slice->start) % slice->stride;
projection->first = 0;
if(rem)
projection->first += (slice->stride - rem);
projection->iopos = ceildiv((offset - slice->start),slice->stride);
}
if(slice->stop > projection->limit)
projection->stop = chunklen;
else
projection->stop = slice->stop - offset;
/* Compute the slice relative to this chunk.
Recall the possibility that start+stride >= projection->limit */
projection->chunkslice.start = projection->first;
projection->chunkslice.stop = projection->stop;
projection->chunkslice.stride = slice->stride;
projection->chunkslice.len = chunklen;
projection->iocount = ceildiv((projection->stop - projection->first),slice->stride);
/* Last place to be touched */
projection->last = projection->first + (slice->stride * (projection->iocount - 1));
projection->memslice.start = projection->iopos;
projection->memslice.stop = projection->iopos + projection->iocount;
// projection->memslice.stride = 1;
projection->memslice.stride = slice->stride;
projection->memslice.len = projection->memslice.stop;
// projection->memslice.len = dimlen;
// projection->memslice.len = chunklen;
done:
return stat;
}
static void
skipchunk(const NCZSlice* slice, size64_t offset, NCZProjection* projection)
{
projection->first = 0;
projection->last = 0;
projection->iopos = ceildiv(offset - slice->start, slice->stride);
projection->iocount = 0;
projection->chunkslice.start = 0;
projection->chunkslice.stop = 0;
projection->chunkslice.stride = 1;
projection->chunkslice.len = 0;
projection->memslice.start = 0;
projection->memslice.stop = 0;
projection->memslice.stride = 1;
projection->memslice.len = 0;
}
/* Goal:
Create a vector of projections wrt a slice and a sequence of chunks.
*/
int
NCZ_compute_per_slice_projections(
int r, /* which dimension are we projecting? */
const NCZSlice* slice, /* the slice for which projections are computed */
const NCZChunkRange* range, /* range */
size64_t dimlen, /* the dimension length for r'th dimension */
size64_t chunklen, /* the chunk length corresponding to the dimension */
NCZSliceProjections* slp)
{
int stat = NC_NOERR;
size64_t index,slicecount;
size_t n;
/* Part fill the Slice Projections */
slp->r = r;
slp->range = *range;
slp->count = range->stop - range->start;
if((slp->projections = calloc(slp->count,sizeof(NCZProjection))) == NULL)
{stat = NC_ENOMEM; goto done;}
/* Compute the total number of output items defined by this slice
(equivalent to count as used by nc_get_vars) */
slicecount = ceildiv((slice->stop - slice->start), slice->stride);
if(slicecount < 0) slicecount = 0;
/* Iterate over each chunk that intersects slice to produce projection */
for(n=0,index=range->start;index<range->stop;index++,n++) {
if((stat = NCZ_compute_projections(r,dimlen, chunklen, index, slice, n, slp->projections)))
goto done;
}
done:
return stat;
}
/* Goal:create a vector of SliceProjection instances: one for each
slice in the top-level input. For each slice, compute a set
of projections from it wrt a dimension and a chunk size
associated with that dimension.
*/
int
NCZ_compute_all_slice_projections(
int rank, /* variable rank */
const NCZSlice* slices, /* the complete set of slices |slices| == R*/
const size64_t* dimlen, /* the dimension lengths associated with a variable */
const size64_t* chunklen, /* the chunk length corresponding to the dimensions */
const NCZChunkRange* ranges,
NCZSliceProjections* results)
{
int stat = NC_NOERR;
size64_t r;
for(r=0;r<rank;r++) {
/* Compute each of the rank SliceProjections instances */
NCZSliceProjections* slp = &results[r];
if((stat=NCZ_compute_per_slice_projections(
r,
&slices[r],
&ranges[r],
dimlen[r],
chunklen[r],
slp))) goto done;
}
done:
return stat;
}
/**************************************************/
/* Utilities */
void
NCZ_clearsliceprojections(int count, NCZSliceProjections* slpv)
{
if(slpv != NULL) {
int i;
for(i=0;i<count;i++) {
NCZSliceProjections* slp = &slpv[i];
nullfree(slp->projections);
}
}
}
#if 0
static void
clearallprojections(NCZAllProjections* nap)
{
if(nap != NULL) {
int i;
for(i=0;i<nap->rank;i++)
nclistfreeall(&nap->allprojections[i].projections);
}
}
#endif