mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
dfc2ac7296
In all these cases the size of the buffer can be computed with sizeof.
498 lines
13 KiB
C
498 lines
13 KiB
C
/*********************************************************************
|
|
* Copyright 2018, University Corporation for Atmospheric Research
|
|
* See netcdf/README file for copying and redistribution conditions.
|
|
* $Header: /upc/share/CVS/netcdf-3/nctest/nctime.c,v 1.12 1996/04/30 17:56:58 davis Exp $
|
|
*********************************************************************/
|
|
|
|
/*
|
|
* This is a standalone benchmark program for timing netCDF hyperslab accesses.
|
|
* Once it is built, the benchmarks are run by invoking it with the shape of a
|
|
* four-dimensional netCDF variable, e.g.
|
|
*
|
|
* nctime 10 20 30 40
|
|
*
|
|
* which will run timing benchmarks accessing 1-, 2-, 3-, and 4-dimensional
|
|
* slabs from 10 by 20 by 30 by 40 variables of each type. The first dimension
|
|
* varies most slowly and is an unlimited (record) dimension.
|
|
*
|
|
* This program is especially useful for testing the effect of various compiler
|
|
* optimization levels or platform-specific optimizations on the performance of
|
|
* netCDF I/O.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h> /* for HZ */
|
|
#include <sys/times.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
|
|
#ifndef HZ
|
|
#ifdef CLK_TCK
|
|
#define HZ CLK_TCK
|
|
#else
|
|
#define HZ 60
|
|
#endif
|
|
#endif
|
|
|
|
#include "netcdf.h"
|
|
|
|
struct ncdim { /* dimension */
|
|
char *name;
|
|
long size;
|
|
};
|
|
|
|
struct ncvar { /* variable */
|
|
char *name;
|
|
nc_type type;
|
|
int ndims;
|
|
int *dims;
|
|
int natts;
|
|
};
|
|
|
|
|
|
#define LEN_OF(array) ((sizeof array) / (sizeof array[0]))
|
|
|
|
/* Number of dimensions. Changing this requires other changes as well. */
|
|
#define NDIMS 4
|
|
|
|
#define NVARS 6 /* number of variables, one for each type */
|
|
|
|
/* Any function that maps dimension values 1-1 to values is OK here */
|
|
#define VF(w) 1000*w[0]+100*w[1]+10*w[2]+w[3]
|
|
|
|
static int DEFAULTDIMS[NDIMS] = {10, 20, 30, 40};
|
|
|
|
/*
|
|
* Fill typed array element with specified value, that is
|
|
*
|
|
* v[ii] = val;
|
|
*/
|
|
static void
|
|
val_stuff(type, v, ii, val) /* v[ii] = val */
|
|
nc_type type; /* netcdf type of v, NC_BYTE, ..., NC_DOUBLE */
|
|
void *v; /* array of specified type */
|
|
int ii; /* it's v[ii] we want to store into */
|
|
long val; /* value to store */
|
|
{
|
|
union gp {
|
|
char cp[1];
|
|
short sp[1];
|
|
nclong lp[1];
|
|
float fp[1];
|
|
double dp[1];
|
|
} *gp;
|
|
|
|
gp = (union gp *) v;
|
|
switch (type) {
|
|
case NC_BYTE:
|
|
case NC_CHAR:
|
|
gp->cp[ii] = (char) val;
|
|
break;
|
|
case NC_SHORT:
|
|
gp->sp[ii] = (short) val;
|
|
break;
|
|
case NC_LONG:
|
|
gp->lp[ii] = (nclong) val;
|
|
break;
|
|
case NC_FLOAT:
|
|
gp->fp[ii] = (float) val;
|
|
break;
|
|
case NC_DOUBLE:
|
|
gp->dp[ii] = (double) val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Compare typed array element with specified value, that is return
|
|
*
|
|
* (v[ii] != val)
|
|
*
|
|
* returns 0 if equal, 1 if not equal
|
|
*/
|
|
|
|
static int
|
|
val_diff(type, v, ii, val) /* v[ii] != val */
|
|
nc_type type; /* netcdf type of v, NC_BYTE, ..., NC_DOUBLE */
|
|
void *v; /* array of specified type */
|
|
int ii; /* it's v[ii] we want to compare */
|
|
long val; /* value to compare with */
|
|
{
|
|
union gp {
|
|
char cp[1];
|
|
short sp[1];
|
|
nclong lp[1];
|
|
float fp[1];
|
|
double dp[1];
|
|
} *gp;
|
|
|
|
gp = (union gp *) v;
|
|
switch (type) {
|
|
case NC_BYTE:
|
|
case NC_CHAR:
|
|
return (gp->cp[ii] != (char) val);
|
|
case NC_SHORT:
|
|
return (gp->sp[ii] != (short) val);
|
|
case NC_LONG:
|
|
return (gp->lp[ii] != (nclong) val);
|
|
case NC_FLOAT:
|
|
return (gp->fp[ii] != (float) val);
|
|
case NC_DOUBLE:
|
|
return (gp->dp[ii] != (double) val);
|
|
}
|
|
/* NOTREACHED */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The following timing macros can be used by including the necessary
|
|
* declarations with
|
|
*
|
|
* TIMING_DECLS ;
|
|
*
|
|
* and surrounding sections of code to be timed with the "statements"
|
|
*
|
|
* TIMING_START ;
|
|
* [code to be timed goes here]
|
|
* TIMING_END ;
|
|
*
|
|
* (The terminating semicolon is required for TIMING_DECLS and TIMING_END.)
|
|
* The macros assume the user has stored a description of what is being timed
|
|
* in the user-declared string time_mess, and has included <sys/times.h>
|
|
*/
|
|
|
|
#define TIMING_DECLS \
|
|
long TMreps; /* counts repetitions of timed code */ \
|
|
long TMrepeats; /* repetitions needed to exceed a second */ \
|
|
clock_t TMus, TMsy; /* user and system time in clock ticks */ \
|
|
float TMelapsed; /* elapsed time in seconds */ \
|
|
struct tms TMru;
|
|
|
|
#define TIMING_START \
|
|
TMrepeats = 1; \
|
|
do { /* loop enough times for at least 0.1 second elapsed time */ \
|
|
TMrepeats *= 2; \
|
|
times(&TMru); \
|
|
TMus = TMru.tms_utime; \
|
|
TMsy = TMru.tms_stime; \
|
|
for(TMreps=0;TMreps < TMrepeats;TMreps++) {
|
|
|
|
#define TIMING_END \
|
|
} \
|
|
times(&TMru); \
|
|
TMus = TMru.tms_utime - TMus; \
|
|
TMsy = TMru.tms_stime - TMsy; \
|
|
TMelapsed= (float) (TMus+TMsy) / (float) HZ; \
|
|
if (TMreps < TMrepeats) break; \
|
|
} while (TMelapsed < 0.1 ); \
|
|
printf("time for %-20.20s %10.3f msec\n", \
|
|
time_mess, TMelapsed*1000./(TMreps+1))
|
|
|
|
|
|
|
|
/*
|
|
* For each type of variable, put a four-dimensional hypercube of values
|
|
* with a single call to ncvarput. Then use ncvarget to retrieve a single
|
|
* value, a vector of values along each of the four dimensions, a plane of
|
|
* values along each of the six pairs of dimensions, a cube of values along
|
|
* each of the four triples of dimensions, and all the values.
|
|
*/
|
|
void
|
|
test_slabs(ncid, sizes)
|
|
int ncid; /* handle of netcdf open and in data mode */
|
|
int *sizes; /* dimension sizes */
|
|
{
|
|
char time_mess[100];
|
|
|
|
struct ncdim dims[NDIMS];
|
|
int dimids[NDIMS]; /* dimension ids */
|
|
long corner[NDIMS], edge[NDIMS], point[NDIMS];
|
|
|
|
static struct ncvar va[NVARS] = { /* variables of all types */
|
|
{"byte_var", NC_BYTE, NDIMS, 0, 0},
|
|
{"char_var", NC_CHAR, NDIMS, 0, 0},
|
|
{"short_var", NC_SHORT, NDIMS, 0, 0},
|
|
{"long_var", NC_LONG, NDIMS, 0, 0},
|
|
{"float_var", NC_FLOAT, NDIMS, 0, 0},
|
|
{"double_var", NC_DOUBLE, NDIMS, 0, 0},
|
|
};
|
|
void *v;
|
|
|
|
int varid[NVARS], iv; /* variable id */
|
|
int idim, jdim, kdim, ldim;
|
|
int iw, ix, iy, iz, ii, jj, kk;
|
|
static char* dnames[] = {"w", "x", "y", "z", "u", "v", "a", "b", "c", "d"};
|
|
|
|
assert(NDIMS <= LEN_OF(dnames));
|
|
for (idim = 0; idim < NDIMS; idim++) {
|
|
dims[idim].size = sizes[idim];
|
|
dims[idim].name = dnames[idim];
|
|
}
|
|
|
|
/* back in define mode OK, now add dimensions */
|
|
|
|
dimids[0] = ncdimdef(ncid, dims[0].name, NC_UNLIMITED);
|
|
if (dimids[0] == -1) {
|
|
ncclose(ncid);
|
|
return;
|
|
}
|
|
for (idim = 1; idim < NDIMS; idim++) {
|
|
dimids[idim] = ncdimdef(ncid, dims[idim].name, dims[idim].size);
|
|
if (dimids[idim] == -1) {
|
|
ncclose(ncid);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* define a multi-dimensional variable of each type */
|
|
|
|
for (iv = 0; iv < NVARS; iv++) {
|
|
va[iv].dims = (int *) malloc(sizeof(int) * (unsigned)va[iv].ndims);
|
|
for (idim = 0; idim < va[iv].ndims; idim++)
|
|
va[iv].dims[idim] = dimids[idim];
|
|
varid[iv] = ncvardef(ncid, va[iv].name, va[iv].type, va[iv].ndims,
|
|
va[iv].dims);
|
|
if (varid[iv] == -1) {
|
|
ncclose(ncid); return;
|
|
}
|
|
}
|
|
|
|
if (ncendef (ncid) == -1) {
|
|
ncclose(ncid); return;
|
|
}
|
|
|
|
printf("Note: first ncvarput writes fill values for all variables.\n");
|
|
|
|
for (iv = 0; iv < NVARS; iv++) { /* test each type of variable */
|
|
TIMING_DECLS ;
|
|
printf("\n----- %s(%d,%d,%d,%d)\n",
|
|
va[iv].name, sizes[0], sizes[1], sizes[2], sizes[3]);
|
|
|
|
v = (void *) malloc((unsigned)sizes[0]*sizes[1]*sizes[2]*sizes[3]
|
|
* nctypelen(va[iv].type));
|
|
|
|
/* fill it with values using a function of dimension indices */
|
|
ii = 0;
|
|
for (iw=0; iw < sizes[0]; iw++) {
|
|
corner[0] = iw;
|
|
for (ix=0; ix < sizes[1]; ix++) {
|
|
corner[1] = ix;
|
|
for (iy=0; iy < sizes[2]; iy++) {
|
|
corner[2] = iy;
|
|
for (iz=0; iz < sizes[3]; iz++) {
|
|
corner[3] = iz;
|
|
/* v[ii++] = VF(corner); */
|
|
val_stuff(va[iv].type, v, ii, VF(corner));
|
|
ii++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (idim = 0; idim < NDIMS; idim++) {
|
|
corner[idim] = 0;
|
|
edge[idim] = dims[idim].size;
|
|
}
|
|
|
|
snprintf(time_mess, sizeof(time_mess),"ncvarput %ldx%ldx%ldx%ld",
|
|
edge[0], edge[1], edge[2], edge[3]);
|
|
|
|
TIMING_START ;
|
|
/* ncvarput the whole variable */
|
|
if (ncvarput(ncid, varid[iv], corner, edge, (void *) v) == -1) {
|
|
ncclose(ncid);
|
|
return;
|
|
}
|
|
TIMING_END ;
|
|
|
|
/*
|
|
* For several combinations of fixed dimensions, get a slab and compare
|
|
* values to function values.
|
|
*/
|
|
|
|
/* get an interior point */
|
|
for (idim=0; idim < NDIMS; idim++) {
|
|
corner[idim] = dims[idim].size/2;
|
|
edge[idim] = 1;
|
|
point[idim] = corner[idim];
|
|
}
|
|
|
|
snprintf(time_mess, sizeof(time_mess),"ncvarget %ldx%ldx%ldx%ld"
|
|
,edge[0],edge[1],edge[2],edge[3]);
|
|
|
|
TIMING_START ;
|
|
if (ncvarget(ncid, varid[iv], corner, edge, (void *) v) == -1)
|
|
return;
|
|
TIMING_END ;
|
|
|
|
/* if (v[0] != VF(point)) */
|
|
if (val_diff(va[iv].type, v, 0, VF(point)))
|
|
fprintf(stderr,"ncvarget got wrong value for point");
|
|
|
|
/* get a vector in each direction */
|
|
for (idim=0; idim < NDIMS; idim++) {
|
|
for (jdim=0; jdim < NDIMS; jdim++) {
|
|
corner[jdim] = 0;
|
|
edge[jdim] = 1;
|
|
point[jdim] = corner[jdim];
|
|
}
|
|
corner[idim] = 0; /* get vector along dimension idim */
|
|
edge[idim] = dims[idim].size;
|
|
|
|
snprintf(time_mess, sizeof(time_mess),"ncvarget %ldx%ldx%ldx%ld"
|
|
,edge[0],edge[1],edge[2],edge[3]);
|
|
|
|
TIMING_START ;
|
|
if (ncvarget(ncid, varid[iv], corner, edge, (void *) v) == -1)
|
|
return;
|
|
TIMING_END ;
|
|
|
|
for (ii=corner[idim]; ii < edge[idim]; ii++) {
|
|
point[idim] = ii;
|
|
/* if (v[ii] != VF(point)) */
|
|
if (val_diff(va[iv].type, v, ii, VF(point)))
|
|
fprintf(stderr,"ncvarget got wrong value for vector");
|
|
}
|
|
}
|
|
|
|
/* get a plane in each direction */
|
|
for (idim=0; idim < NDIMS; idim++) {
|
|
for (jdim=idim+1; jdim < NDIMS; jdim++) {
|
|
for (kdim=0; kdim < NDIMS; kdim++) { /* reset corners and edges */
|
|
corner[kdim] = 0;
|
|
edge[kdim] = 1;
|
|
point[kdim] = corner[kdim];
|
|
}
|
|
corner[idim] = 0; /* plane along dimensions idim jdim */
|
|
corner[jdim] = 0;
|
|
edge[idim] = dims[idim].size;
|
|
edge[jdim] = dims[jdim].size;
|
|
|
|
snprintf(time_mess, sizeof(time_mess),"ncvarget %ldx%ldx%ldx%ld"
|
|
,edge[0],edge[1],edge[2],edge[3]);
|
|
|
|
TIMING_START ;
|
|
if (ncvarget(ncid, varid[iv], corner, edge, (void *) v) == -1)
|
|
return;
|
|
TIMING_END ;
|
|
|
|
for (ii=corner[idim]; ii < edge[idim]; ii++) {
|
|
for (jj=corner[jdim]; jj < edge[jdim]; jj++) {
|
|
point[idim] = ii;
|
|
point[jdim] = jj;
|
|
/* if (v[(ii)*edge[jdim]+jj] != VF(point)) { */
|
|
if (val_diff(va[iv].type, v,
|
|
(ii)*(int)edge[jdim]+jj, VF(point))) {
|
|
fprintf(stderr,
|
|
"ncvarget got wrong value in plane");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get a cube in each direction */
|
|
for (idim=0; idim < NDIMS; idim++) {
|
|
for (jdim=idim+1; jdim < NDIMS; jdim++) {
|
|
for (kdim=jdim+1; kdim < NDIMS; kdim++) {
|
|
for (ldim=0; ldim < NDIMS; ldim++) { /* reset corners, edges */
|
|
corner[ldim] = 0;
|
|
edge[ldim] = 1;
|
|
point[ldim] = corner[ldim];
|
|
}
|
|
corner[idim] = 0; /* intr. cube along idim jdim kdim */
|
|
corner[jdim] = 0;
|
|
corner[kdim] = 0;
|
|
edge[idim] = dims[idim].size;
|
|
edge[jdim] = dims[jdim].size;
|
|
edge[kdim] = dims[kdim].size;
|
|
|
|
snprintf(time_mess, sizeof(time_mess),"ncvarget %ldx%ldx%ldx%ld"
|
|
,edge[0],edge[1],edge[2],edge[3]);
|
|
|
|
TIMING_START ;
|
|
if (ncvarget(ncid, varid[iv], corner, edge,
|
|
(void *) v) == -1)
|
|
return;
|
|
TIMING_END ;
|
|
|
|
for (ii=corner[idim]; ii < edge[idim]; ii++) {
|
|
for (jj=corner[jdim]; jj < edge[jdim]; jj++) {
|
|
for (kk=corner[kdim]; kk < edge[kdim]; kk++) {
|
|
point[idim] = ii;
|
|
point[jdim] = jj;
|
|
point[kdim] = kk;
|
|
/* if (v[((ii)*edge[jdim]+jj)*
|
|
edge[kdim]+kk] != VF(point)) { */
|
|
if (val_diff(va[iv].type,v,
|
|
((ii)*(int)edge[jdim]+jj)*
|
|
(int)edge[kdim]+kk,VF(point))) {
|
|
fprintf(stderr,
|
|
"ncvarget - bad value in cube");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get one 4-D slab of data */
|
|
for(idim = 0; idim < NDIMS; idim++) {
|
|
corner[idim] = 0;
|
|
edge[idim] = dims[idim].size;
|
|
}
|
|
|
|
snprintf(time_mess, sizeof(time_mess),"ncvarget %ldx%ldx%ldx%ld"
|
|
,edge[0],edge[1],edge[2],edge[3]);
|
|
|
|
TIMING_START ;
|
|
if (ncvarget(ncid, varid[iv], corner, edge, (void *) v) == -1)
|
|
return;
|
|
TIMING_END ;
|
|
|
|
free(v);
|
|
}
|
|
}
|
|
|
|
void
|
|
usage(argv)
|
|
char **argv;
|
|
{
|
|
int i;
|
|
fprintf(stderr, "usage: %s ", argv[0]);
|
|
for (i=0; i < NDIMS; i++)
|
|
fprintf(stderr, "dim%d ", i);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
int ncid;
|
|
int i;
|
|
int w[NDIMS];
|
|
|
|
if (argc != NDIMS+1) {
|
|
for (i = 0; i < NDIMS; i++)
|
|
w[i] = DEFAULTDIMS[i];
|
|
} else {
|
|
for (i = 0; i < NDIMS; i++)
|
|
w[i] = atoi(argv[i+1]);
|
|
}
|
|
|
|
ncid = nccreate("benchmark.nc",NC_CLOBBER);
|
|
|
|
test_slabs(ncid, w);
|
|
|
|
ncclose(ncid);
|
|
return 0;
|
|
}
|