1/*
2  Copyright 2007, UCAR/Unidata
3  See COPYRIGHT file for copying and redistribution conditions.
4
5  This program benchmarks the write and read of some radar files with
6  different chunking and compression parameters set.
7
8  This program only works on classic model netCDF files. That is,
9  groups, user-defined types, and other new netCDF-4 features are not
10  handled by this program. (Input files may be in netCDF-4 format, but
11  they must conform to the classic model for this program to work.)
12
13  For the 3.7 and 4.0 netCDF releases, this program is not expected
14  for general use. It may be made safer and more general in future
15  releases, but for now, users should use this code with caution.
16
17  $Id: bm_file.c,v 1.64 2010/01/11 19:27:11 ed Exp $
18*/
19#include <nc_tests.h> /* The ERR macro is here... */
20#include <err_macros.h>
21
22#include <stdio.h>
23#include <string.h>
24#include <time.h>
25#include <sys/time.h> /* Extra high precision time info. */
26#include <math.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#ifdef USE_PARALLEL
31#include <mpi.h>
32#endif
33#include <netcdf.h>
34
35#define MILLION 1000000
36#define BAD -99
37#define NOMEM -98
38#define MAX_VO 50  /* Max number of var options on command line. */
39#define MAX_DIMS 7 /* Max dim for variables in input file. */
40
41/* This struct holds data about what options we want to apply to
42 * variable in the created file. (Chunking, compression, etc.) */
43typedef struct {
44      int varid;
45      int ndims;
46      int deflate_num;
47      int shuffle;
48      size_t chunksize[MAX_DIMS];
49      int endian;
50      size_t start[MAX_DIMS], count[MAX_DIMS], inc[MAX_DIMS];
51VAR_OPTS_T;
52
53/* This macro prints an error message with line number and name of
54 * test program. */
55#define ERR1(n) do {   \
56fflush(stdout); /* Make sure our stdout is synced with stderr. */ \
57fprintf(stderr, "Sorry! Unexpected result, %s, line: %d - %s\n", \
58 __FILE____LINE__nc_strerror(n));  \
59return n; \
60} while (0)
61
62#ifdef USE_PARALLEL
63/* Error handling code for MPI calls. */
64#define MPIERR(e) do { \
65MPI_Error_string(eerr_buffer, &resultlen); \
66printf("MPI error, line %d, file %s: %s\n", __LINE____FILE__err_buffer); \
67MPI_Finalize(); \
68return 2; \
69} while (0)
70#endif
71
72/* This function will fill the start and count arrays for the reads
73 * and writes. */
74static int
75get_starts_counts(int ndims, size_t *dimlen, int p, int my_rank,
76   int slow_count, int use_scsVAR_OPTS_T *vo,
77   int *num_steps, int *start_inc, int *slice_len,
78   size_t *last_count, size_t *start, size_t *count)
79{
80   int extra_step = 0;
81   int total[NC_MAX_VAR_DIMS];
82   int total_len;
83   int sd;
84
85   /* User has specified start/count/inc for this var. Parallel runs
86    * not allowed yet. */
87   if (use_scs)
88   {
89      /* Set the starts and counts for each dim, the len of the slice,
90       * the total len of the data, and the total extent of the
91       * dataset in each dimension. */
92      for (d = 0, *slice_len = 1, total_len = 1; d < vo->ndimsd++)
93      {
94  start[d] = vo->start[d];
95  count[d] = vo->count[d];
96  (*slice_len) *= count[d];
97  total_len *= dimlen[d];
98      }
99
100      /* The start increment is provided by the user. */
101      *start_inc = vo->inc[0];
102
103      /* How many steps to write/read these data? */
104      *num_steps = total_len / (*slice_len);
105
106      /* Init this for the total extent in each dim. */
107      for (d = 0; d < vo->ndimsd++)
108  total[d] = 0;
109
110      /* Check our numbers if we apply increments to start, and read
111       * count, for this many steps. */
112      for (s = 0; s < *num_stepss++)
113      {
114  for (d = 0; d < vo->ndimsd++)
115  {
116     total[d] += count[d];
117     if (total[d] >= dimlen[d])
118        break;
119  }
120  if (d != vo->ndims)
121     break;
122      }
123
124      /* If the numbers didn't come out clean, then figure out the
125       * last set of counts needed to completely read the data. */
126      if (s == (*num_steps) - 1)
127  *last_count =  count[0];
128      else
129      {
130  (*num_steps)++;
131  *last_count =  dimlen[0] - total[0];
132      }
133   }
134   else
135   {
136      *start_inc = dimlen[0]/slow_count;
137      while (*start_inc * slow_count < dimlen[0])
138  (*start_inc)++;
139      *slice_len = *start_inc;
140      start[0] = *start_inc * my_rank;
141      if (start[0] > dimlen[0])
142      {
143  fprintf(stderr, "slow_count too large for this many processors, "
144  "start_inc=%d, slow_count=%d, p=%d, my_rank=%d start[0]=%ld\n",
145  *start_incslow_countpmy_rankstart[0]);
146  return 2;
147      }
148      count[0] = *start_inc;
149      for (d = 1; d < ndimsd++)
150      {
151  start[d] = 0;
152  count[d] = dimlen[d];
153  *slice_len *= dimlen[d];
154      }
155      *num_steps = (float)dimlen[0] / (*start_inc * p);
156      if ((float)dimlen[0] / (*start_inc * p) != dimlen[0] / (*start_inc * p))
157      {
158  extra_step++;
159  (*num_steps)++;
160      }
161
162      if (p > 1)
163      {
164  if (!extra_step)
165     *last_count = 0;
166  else
167  {
168     int left;
169     left = dimlen[0] - (*num_steps - 1) * *start_inc * p;
170     if (left > (my_rank + 1) * *start_inc)
171        *last_count = *start_inc;
172     else
173     {
174        if (left - my_rank * *start_inc < 0)
175   *last_count = 0;
176        else
177   *last_count = left - my_rank * *start_inc;
178     }
179  }
180      }
181      else
182  *last_count = dimlen[0] - (*num_steps - 1) * *start_inc;
183   }
184   return 0;
185}
186
187/* This function finds the size of a file. */
188static size_t
189file_size(char* name)
190{
191   struct stat stbuf;
192   stat(name, &stbuf);
193   return stbuf.st_size;
194}
195
196
197/* Check attribute number a of variable varid in copied file ncid2 to ensure
198 * it is the same as the corresponding attribute in original file ncid1. */
199static int
200check_att(int ncid1, int ncid2, int varid, int a)
201{
202   int typeidtypeid2;
203   size_t lenlen2typelen;
204   char name[NC_MAX_NAME + 1];
205   void *d = NULL, *d2 = NULL;
206   int ret = 0;
207
208   /* Check the metadata about the metadata - name, type, length. */
209   if ((ret = nc_inq_attname(ncid1varidaname)))
210      return ret;
211   if ((ret = nc_inq_att(ncid1varidname, &typeid, &len)))
212      return ret;
213   if ((ret = nc_inq_att(ncid2varidname, &typeid2, &len2)))
214      return ret;
215   if (len != len2 || typeid != typeid2)
216      return BAD;
217   if ((ret = nc_inq_type(ncid1typeidNULL, &typelen)))
218      return ret;
219
220   /* Get the two attributes, if they are non-zero. */
221   if (len)
222   {
223      if(!(d = malloc(typelen * len)))
224  return NOMEM;
225      if(!(d2 = malloc(typelen * len)))
226      {
227  ret = NOMEM;
228  goto exit;
229      }
230      if ((ret = nc_get_att(ncid1varidnamed)))
231  goto exit;
232      if ((ret = nc_get_att(ncid2varidnamed2)))
233  goto exit;
234
235      /* Are they the same? */
236      if (memcmp(dd2typelen * len))
237  ret = BAD;
238   }
239
240  exit:
241   /* Free up our resources. */
242   if (d)
243      free(d);
244   if (d2)
245      free(d2);
246
247   return ret;
248}
249
250/* Do two files contain the same data and metadata? */
251static int
252cmp_file(char *file1, char *file2, int *meta_read_us, int *data_read_us,
253  int use_par, int par_access, int do_cmp, int p, int my_rank,
254  int slow_count, int verbose, int num_voVAR_OPTS_T *vo, int use_scs)
255{
256   int ncid1ncid2;
257   int unlimdimidunlimdimid2;
258   char name[NC_MAX_NAME + 1], name2[NC_MAX_NAME + 1];
259   size_t lenlen2;
260#ifdef USE_PARALLEL
261   double ftime;
262#endif
263   struct timeval start_timeend_timediff_time;
264   void *data = NULL, *data2 = NULL;
265   int avd;
266   nc_type xtypextype2;
267   int nvarsndimsdimids[NC_MAX_VAR_DIMS], nattsreal_ndims;
268   int nvars2ndims2dimids2[NC_MAX_VAR_DIMS], natts2;
269   size_t *count = NULL, *start = NULL;
270   int slice_len = 1;
271   size_t *dimlen = NULLtype_size = 0;
272   size_t last_count;
273   int start_inc;
274   int num_stepsstep;
275   int ret = NC_NOERR;
276
277   /* Note in the code below I only want to time stuff for file2. */
278
279   /* Read the metadata for both files. */
280   if (use_par)
281   {
282#ifdef USE_PARALLEL
283      if ((ret = nc_open_par(file1, 0, MPI_COMM_WORLDMPI_INFO_NULL, &ncid1)))
284  ERR1(ret);
285      MPI_Barrier(MPI_COMM_WORLD);
286      ftime = MPI_Wtime();
287      if ((ret = nc_open_par(file2, 0, MPI_COMM_WORLDMPI_INFO_NULL, &ncid2)))
288  ERR1(ret);
289      *meta_read_us += (MPI_Wtime() - ftime) * MILLION;
290#else
291      return NC_EPARINIT;
292#endif
293   }
294   else
295   {
296      if ((ret = nc_open(file1, 0, &ncid1)))
297  ERR1(ret);
298      if (gettimeofday(&start_timeNULL)) ERR;
299      if ((ret = nc_open(file2, 0, &ncid2)))
300  ERR1(ret);
301      if (gettimeofday(&end_timeNULL)) ERR;
302      if (nc4_timeval_subtract(&diff_time, &end_time, &start_time)) ERR;
303      *meta_read_us += (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
304   }
305   if (verbose)
306      printf("%d: reading metadata took %d micro-seconds\n",
307      my_rank, *meta_read_us);
308
309   /* Check the counts of dims, vars, and atts. */
310   if ((ret = nc_inq(ncid1, &ndims, &nvars, &natts, &unlimdimid)))
311      ERR1(ret);
312   if ((ret = nc_inq(ncid1, &ndims2, &nvars2, &natts2, &unlimdimid2)))
313      ERR1(ret);
314   if (ndims != ndims2 || nvars != nvars2 || natts != natts2 ||
315       unlimdimid != unlimdimid2)
316      ERR1(BAD);
317
318   /* Check dims. */
319   for (d = 0; d < ndimsd++)
320   {
321      if ((ret = nc_inq_dim(ncid1dname, &len)))
322  ERR1(ret);
323      if ((ret = nc_inq_dim(ncid2dname2, &len2)))
324  ERR1(ret);
325      if (len != len2 || strcmp(namename2))
326  ERR1(BAD);
327   }
328
329   /* Check global atts. */
330   for (a = 0; a < nattsa++)
331      if ((ret = check_att(ncid1ncid2NC_GLOBALa)))
332  ERR1(ret);
333
334   /* Check the variables. */
335   for (v = 0; v < nvarsv++)
336   {
337      /* Learn about this var in both files. */
338      if ((ret = nc_inq_var(ncid1vname, &xtype, &ndimsdimids, &natts)))
339  return ret;
340      if ((ret = nc_inq_var(ncid2vname2, &xtype2, &ndims2dimids2, &natts2)))
341  return ret;
342
343      /* Check var metadata. */
344      if (strcmp(namename2) || xtype != xtype2 || ndims != ndims2 || natts != natts2)
345  return BAD;
346      for (d = 0; d < ndimsd++)
347  if (dimids[d] != dimids2[d])
348     return BAD;
349
350      /* Check the attributes. */
351      for (a = 0; a < nattsa++)
352  if ((ret = check_att(ncid1ncid2va)))
353     ERR1(ret);
354
355      /* Check the data, one slice at a time. (slicing along slowest
356       * varying dimension.) */
357
358      /* Allocate memory for our start and count arrays. If ndims = 0
359  this is a scalar, which I will treat as a 1-D array with one
360  element. */
361      real_ndims = ndims ? ndims : 1;
362      if (!(start = malloc(real_ndims * sizeof(size_t))))
363  ERR1(NC_ENOMEM);
364      if (!(count = malloc(real_ndims * sizeof(size_t))))
365  ERR1(NC_ENOMEM);
366
367      /* The start array will be all zeros, except the first element,
368  which will be the slice number. Count will be the dimension
369  size, except for the first element, which will be one, because
370  we will copy one slice at a time. For this we need the var
371  shape. */
372      if (!(dimlen = malloc(real_ndims * sizeof(size_t))))
373  ERR1(NC_ENOMEM);
374      for (d=0; d<ndimsd++)
375  if ((ret = nc_inq_dimlen(ncid1dimids[d], &dimlen[d])))
376     ERR1(ret);
377
378      /* If this is a scalar, then set the dimlen to 1. */
379      if (ndims == 0)
380  dimlen[0] = 1;
381
382      if ((ret = get_starts_counts(ndimsdimlenpmy_rankslow_countuse_scs,
383    &vo[v], &num_steps, &start_inc, &slice_len,
384    &last_countstartcount)))
385  return ret;
386      if (verbose)
387  printf("%d: num_steps=%d, start_inc=%d, slice_len=%d, last_count=%ld\n",
388 my_ranknum_stepsstart_incslice_lenlast_count);
389
390      /* If there are no records, we're done. */
391      if (!dimlen[0])
392  goto exit;
393
394      /* Find the size of this type. */
395      if ((ret = nc_inq_type(ncid1xtypeNULL, &type_size)))
396  return ret;
397
398      /* I will read all this data the same way I eat a large pizze -
399       * one slice at a time. */
400      if (!(data = malloc(slice_len * type_size)))
401  ERR1(NC_ENOMEM);
402      if (!(data2 = malloc(slice_len * type_size)))
403  ERR1(NC_ENOMEM);
404
405      /* Check the var data for each slice. */
406/*      for (step = 0; !ret && step < num_steps; step++)*/
407      for (step = 0; !ret && step < num_stepsstep++)
408      {
409  if (step == num_steps - 1 && last_count)
410     count[0] = last_count;
411
412  /* Read data from file1. */
413  if (nc_get_vara(ncid1vstartcountdata)) ERR;
414
415  /* Read data from file2. */
416#ifdef USE_PARALLEL
417  ftime = MPI_Wtime();
418#else
419  if (gettimeofday(&start_timeNULL)) ERR;
420#endif
421  if (nc_get_vara(ncid2vstartcountdata2)) ERR;
422#ifdef USE_PARALLEL
423  *data_read_us += (MPI_Wtime() - ftime) * MILLION;
424#else
425  if (gettimeofday(&end_timeNULL)) ERR;
426  if (nc4_timeval_subtract(&diff_time, &end_time, &start_time)) ERR;
427  *data_read_us += (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
428#endif
429  if (verbose)
430     printf("%d: reading copy step %d, var %d took %d micro-seconds\n",
431    my_rankstepv, *data_read_us);
432
433  /* Check data. */
434  if (do_cmp)
435     if (memcmp(datadata2slice_len * type_size))
436        ERR1(BAD);
437
438  /* Increment the start index for the slowest-varying
439   * dimension. */
440  start[0] += start_inc;
441      }
442
443     exit:
444      if (data) free(data);
445      if (data2) free(data2);
446      if (dimlen) free(dimlen);
447      if (start) free(start);
448      if (count) free(count);
449   }
450
451   if ((ret = nc_close(ncid1)))
452      ERR1(ret);
453   if ((ret = nc_close(ncid2)))
454      ERR1(ret);
455
456   return 0;
457}
458
459/* Copy a netCDF file, changing cmode if desired, applying chuncking,
460 * deflate, shuffle, and endianness parameters if desired. */
461static
462int copy_file(char *file_name_in, char *file_name_out, int cmode_out,
463       int num_voVAR_OPTS_T *vo, int *meta_read_us, int *meta_write_us,
464       int *data_read_us, int *data_write_us, int *in_format, int use_par,
465       int par_access, long long *num_bytes, int p, int my_rank,
466       int slow_count, int verbose, int use_scs, int endianness,
467       int convert_unlim)
468{
469   int ncid_inncid_out;
470   int nattsnvarsndimsunlimdimid;
471   char name[NC_MAX_NAME + 1];
472   size_t len;
473   size_t last_count;
474   int avd;
475   int ret;
476   struct timeval start_timeend_timediff_time;
477#ifdef USE_PARALLEL
478   double ftime;
479#endif
480
481   if (use_par)
482   {
483#ifdef USE_PARALLEL
484      ftime = MPI_Wtime();
485      if ((ret = nc_open_par(file_name_in, 0, MPI_COMM_WORLDMPI_INFO_NULL, &ncid_in)))
486  ERR1(ret);
487      *meta_read_us += (MPI_Wtime() - ftime) * MILLION;
488#else
489      return NC_EPARINIT;
490#endif
491   }
492   else
493   {
494      if (gettimeofday(&start_timeNULL)) ERR;
495      if ((ret = nc_open(file_name_in, 0, &ncid_in)))
496  ERR1(ret);
497      if (gettimeofday(&end_timeNULL)) ERR;
498      if (nc4_timeval_subtract(&diff_time, &end_time, &start_time)) ERR;
499      *meta_read_us += (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
500   }
501   if (verbose)
502      printf("%d: reading metadata took %d micro-seconds.\n", my_rank, *meta_read_us);
503
504   /* Only classic model files may be used as input. */
505   if ((ret = nc_inq_format(ncid_inin_format)))
506      ERR1(ret);
507   if (*in_format == NC_FORMAT_NETCDF4)
508      ERR1(NC_ENOTNC3);
509
510   if (strlen(file_name_out))
511   {
512      if (use_par)
513      {
514#ifdef USE_PARALLEL
515  if ((ret = nc_create_par(file_name_outcmode_outMPI_COMM_WORLD,
516   MPI_INFO_NULL, &ncid_out)))
517     ERR1(ret);
518#else
519  return NC_EPARINIT;
520#endif
521      }
522      else
523      {
524#define SIXTEEN_MEG 16777216
525#define PREEMPTION .75
526#define NELEMS 7919
527  if ((ret = nc_set_chunk_cache(SIXTEEN_MEGNELEMSPREEMPTION)))
528     ERR1(ret);
529  if ((ret = nc_create(file_name_outcmode_out, &ncid_out)))
530     ERR1(ret);
531      }
532   }
533
534   if ((ret = nc_inq(ncid_in, &ndims, &nvars, &natts, &unlimdimid)))
535      ERR1(ret);
536
537   if (strlen(file_name_out))
538   {
539      /* Copy dims. */
540      for (d = 0; d < ndimsd++)
541      {
542  if ((ret = nc_inq_dim(ncid_indname, &len)))
543     ERR1(ret);
544  if (convert_unlim)
545  {
546     if ((ret = nc_def_dim(ncid_outnamelenNULL)))
547        ERR1(ret);
548  }
549  else
550  {
551     if ((ret = nc_def_dim(ncid_outname,
552   (d == unlimdimid) ? NC_UNLIMITED : len,
553   NULL)))
554        ERR1(ret);
555  }
556      }
557
558      /* Copy global atts. */
559      for (a = 0; a < nattsa++)
560      {
561  if (nc_inq_attname(ncid_inNC_GLOBALaname)) ERR;
562  if (nc_copy_att(ncid_inNC_GLOBALnamencid_outNC_GLOBAL)) ERR;
563      }
564
565      /* Copy the variable metadata. */
566      for (v = 0; v < nvarsv++)
567      {
568  char name[NC_MAX_NAME + 1];
569  char att_name[NC_MAX_NAME + 1];
570  nc_type xtype;
571  int ndimsdimids[NC_MAX_VAR_DIMS], natts;
572  int varid_out;
573  int ao1;
574  int ret = NC_NOERR;
575
576  /* Learn about this var. */
577  if ((ret = nc_inq_var(ncid_invname, &xtype, &ndimsdimids, &natts)))
578     return ret;
579
580  /* Create the output var. */
581  if (nc_def_var(ncid_outnamextypendimsdimids, &varid_out)) ERR;
582
583  /* Set the output endianness. For simplicity in this program,
584   * all vars get the same endianness. But there's no reason why
585   * this couldn't be varied from var to var, though it is hard to
586   * see why one would do so. */
587  if (endianness)
588     if (nc_def_var_endian(ncid_outvarid_outendianness)) ERR;
589
590  /* Sent chunking and compression if specified in the var options. */
591  for (o1 = 0; o1 < num_voo1++)
592     if (vo[o1].varid == v)
593     {
594        if (vo[o1].chunksize[0])
595        {
596   if (nc_def_var_chunking(ncid_outv, 0, vo[o1].chunksize)) ERR;
597        }
598        else
599        {
600   if (nc_def_var_chunking(ncid_outv, 1, NULL)) ERR;
601        }
602        if (vo[o1].deflate_num != -1)
603   if (nc_def_var_deflate(ncid_outvvo[o1].shuffle, 1, vo[o1].deflate_num)) ERR;
604        break;
605     }
606
607  /* Copy the attributes. */
608  for (a=0; a<nattsa++)
609  {
610     if (nc_inq_attname(ncid_invaatt_name)) ERR;
611     if (nc_copy_att(ncid_invatt_namencid_outvarid_out)) ERR;
612  }
613      }
614
615#ifdef USE_PARALLEL
616      ftime = MPI_Wtime();
617#else
618      if (gettimeofday(&start_timeNULL)) ERR;
619#endif
620      if ((ret = nc_enddef(ncid_out)))
621  ERR1(ret);
622#ifdef USE_PARALLEL
623      *meta_write_us += (MPI_Wtime() - ftime) * MILLION;
624#else
625      if (gettimeofday(&end_timeNULL)) ERR;
626      if (nc4_timeval_subtract(&diff_time, &end_time, &start_time)) ERR;
627      *meta_write_us += (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
628#endif
629
630      if (verbose)
631  printf("%d: copying %d vars, %d global atts, and %d dims took %d micro-seconds\n",
632 my_ranknvarsnattsndims, *meta_write_us);
633   }
634
635   /* Copy the variable data. */
636   for (v = 0; v < nvarsv++)
637   {
638      char name[NC_MAX_NAME + 1];
639      nc_type xtype;
640      int ndimsdimids[NC_MAX_VAR_DIMS], nattsreal_ndims;
641      int d;
642      void *data = NULL;
643      size_t *count = NULL, *start = NULL;
644      int slice_len = 1;
645      size_t *dimlen = NULL;
646      int ret = NC_NOERR;
647      size_t type_size;
648      char type_name[NC_MAX_NAME+1];
649      int start_inc;
650      int stepnum_steps;
651      int var_num_bytes;
652
653      /* Learn about this var. */
654      if ((ret = nc_inq_var(ncid_invname, &xtype, &ndimsdimids, &natts)))
655  return ret;
656
657      /* Later on, we will need to know the size of this type. */
658      if ((ret = nc_inq_type(ncid_inxtypetype_name, &type_size)))
659  return ret;
660
661      /* Allocate memory for our start and count arrays. If ndims = 0
662  this is a scalar, which I will treat as a 1-D array with one
663  element. */
664      real_ndims = ndims ? ndims : 1;
665
666      /* Get the variable shape information. */
667      if (!(dimlen = malloc(real_ndims * sizeof(size_t))))
668  ERR1(NC_ENOMEM);
669      for (d = 0; d < ndimsd++)
670  if ((ret = nc_inq_dimlen(ncid_indimids[d], &dimlen[d])))
671     ERR1(ret);
672
673      if (!(start = malloc(real_ndims * sizeof(size_t))))
674  ERR1(NC_ENOMEM);
675      if (!(count = malloc(real_ndims * sizeof(size_t))))
676  ERR1(NC_ENOMEM);
677
678      /* If this is really a scalar, then set the dimlen to 1. */
679      if (ndims == 0)
680  dimlen[0] = 1;
681
682      /* Get the start and count arrays, and also the increment of the
683       * start array zeroth element, the number of read steps, the
684       * length of a slice in number of elements, and the count needed
685       * for the final read, in the cases where the length of the
686       * zeroth dimension is not evenly divisible by slow_count. The
687       * variable slow_count is the number of elements in the slowest
688       * varying (i.e. the zeroth) dimension to read at one time. For
689       * vars with an unlimited dimension, this is the number of
690       * records to read at once. */
691      if ((ret = get_starts_counts(ndimsdimlenpmy_rankslow_countuse_scs,
692    &vo[v], &num_steps, &start_inc, &slice_len,
693    &last_countstartcount)))
694  return ret;
695      if (verbose)
696  printf("%d: num_steps=%d, start_inc=%d, slice_len=%d, last_count=%ld\n",
697 my_ranknum_stepsstart_incslice_lenlast_count);
698
699      /* If there are no records, we're done. */
700      if (!dimlen[0])
701  goto exit;
702
703      /* Allocate memory for one slice. */
704      if (!(data = malloc(slice_len * type_size)))
705  return NC_ENOMEM;
706
707      /* Copy the var data one slice at a time. */
708      for (step = 0; !ret && step < num_stepsstep++)
709      {
710  /* Make sure count is not too big. */
711  if (step == num_steps - 1 && last_count)
712     count[0] = last_count;
713
714/*   for (d=0; d<ndims; d++) */
715/*      printf("start[%d]=%d count[%d]=%d dimlen[%d]=%d, step=%d\n", */
716/*     d, start[d], d, count[d], d, dimlen[d], step); */
717
718  /* Read input data. */
719#ifdef USE_PARALLEL
720  ftime = MPI_Wtime();
721#else
722  if (gettimeofday(&start_timeNULL)) ERR;
723#endif
724  if ((ret = nc_get_vara(ncid_invstartcountdata)))
725     ERR1(ret);
726
727#ifdef USE_PARALLEL
728  *data_read_us += (MPI_Wtime() - ftime) * MILLION;
729#else
730  if (gettimeofday(&end_timeNULL)) ERR;
731  if (nc4_timeval_subtract(&diff_time, &end_time, &start_time)) ERR;
732  *data_read_us += (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
733#endif
734  if (verbose)
735     printf("%d: reading step %d, var %d took %d micro-seconds\n",
736    my_rankstepv, *data_read_us);
737
738  /* Write the data to the output file. */
739  if (strlen(file_name_out))
740  {
741#ifdef USE_PARALLEL
742     ftime = MPI_Wtime();
743#else
744     if (gettimeofday(&start_timeNULL)) ERR;
745#endif
746     if ((ret = nc_put_vara(ncid_outvstartcountdata)))
747        ERR1(ret);
748#ifdef USE_PARALLEL
749     *data_write_us += (MPI_Wtime() - ftime) * MILLION;
750#else
751     if (gettimeofday(&end_timeNULL)) ERR;
752     if (nc4_timeval_subtract(&diff_time, &end_time, &start_time)) ERR;
753     *data_write_us += (int)diff_time.tv_sec * MILLION + (int)diff_time.tv_usec;
754#endif
755     if (verbose)
756        printf("%d: writing step %d, var %d took %d micro-seconds\n",
757       my_rankstepv, *data_write_us);
758  }
759
760  /* Increment start index. */
761  start[0] += start_inc;
762      } /* next step */
763
764      /* Calculate the data read and write rates in MB/sec. */
765      for (d = 0, var_num_bytes = type_sized < ndimsd++)
766  var_num_bytes *= dimlen[d];
767      (*num_bytes) += var_num_bytes;
768
769     exit:
770      if (data) free(data);
771      if (dimlen) free(dimlen);
772      if (start) free(start);
773      if (count) free(count);
774   } /* next var */
775
776   if (nc_close(ncid_in)) ERR;
777   if (strlen(file_name_out))
778      if (nc_close(ncid_out)) ERR;
779
780   return NC_NOERR;
781}
782
783#define NDIMS 3
784#define MAX_DEFLATE 9
785#define INPUT_FILE "/upc/share/testdata/nssl/mosaic3d_nc/tile1/20070803-2300.netcdf"
786#define COLON ":"
787#define COMMA ","
788
789#define USAGE   "\
790  [-v]        Verbose\n\
791  [-o file]   Output file name\n\
792  [-f N]      Output format (1 - classic, 2 - 64-bit offset, 3 - netCDF-4, 4 - netCDF4/CLASSIC)\n\
793  [-h]        Print output header\n\
794  [-c V:Z:S:C:C:C[,V:Z:S:C:C:C, etc.]] Deflate, shuffle, and chunking parameters for vars\n\
795  [-t V:S:S:S[,V:S:S:S, etc.]] Starts for reads/writes\n\
796  [-u V:C:C:C[,V:C:C:C, etc.]] Counts for reads/writes\n\
797  [-r V:I:I:I[,V:I:I:I, etc.]] Incs for reads/writes\n\
798  [-d]        Doublecheck output by rereading each value\n\
799  [-m]        Do compare of each data value during doublecheck (slow for large files!)\n\
800  [-p]        Use parallel I/O\n\
801  [-s N]      Denom of fraction of slowest varying dimension read.\n\
802  [-i]        Use MPIIO (only relevant for parallel builds).\n\
803  [-l]        Convert unlimited dimensions to fixed dimensions.\n\
804  [-e 1|2]    Set the endianness of output (1=little 2=big).\n\
805  file        Name of netCDF file\n"
806
807static void
808usage(void)
809{
810   fprintf(stderr, "bm_file -v [-s N]|[-t V:S:S:S -u V:C:C:C -r V:I:I:I] -o file_out -f N -h"
811    " -c V:C:C,V:C:C:C -d -m -p -i -e 1|2 -l file\n%s", USAGE);
812}
813
814int
815main(int argc, char **argv)
816{
817   int num_vo = 0;
818   extern int optind;
819   extern int opterr;
820   extern char *optarg;
821   char file_in[NC_MAX_NAME + 1], file_out[NC_MAX_NAME + 1] = {""};
822   int c;
823   int out_formatin_formatheader = 0, doublecheck = 0;
824   int convert_unlim = 0;
825   char *str1, *str2, *token, *subtoken;
826   char *saveptr1, *saveptr2;
827   int indimso1;
828   int cmode = 0;
829   int mpiio = 0;
830   int meta_read_us = 0, meta_write_us = 0, data_read_us = 0, data_write_us = 0;
831   int meta_read2_us = 0, data_read2_us = 0;
832   int tmeta_read_us = 0, tmeta_write_us = 0, tdata_read_us = 0, tdata_write_us = 0;
833   int tmeta_read2_us = 0, tdata_read2_us = 0;
834   VAR_OPTS_T vo[MAX_VO];
835   int use_par = 0, par_access = 0;
836   int do_cmp = 0, verbose = 0;
837   int ret;
838   float read_ratewrite_ratereread_rate;
839   int slow_count = 10, use_scs = 0;
840   int endianness = 0;
841   long long num_bytes = 0;
842   int p = 1, my_rank = 0;
843   int vd;
844
845#ifdef USE_PARALLEL
846   MPI_Init(&argc, &argv);
847   MPI_Errhandler_set(MPI_COMM_WORLDMPI_ERRORS_RETURN);
848
849   MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
850   MPI_Comm_size(MPI_COMM_WORLD, &p);
851#endif
852
853   for (o1 = 0; o1 < MAX_VOo1++)
854      for (i = 0; i < MAX_DIMSi++)
855  vo[o1].chunksize[i] = 0;
856
857   while ((c = getopt(argcargv, "vo:f:hc:dpms:it:u:r:e:l")) != EOF)
858      switch(c)
859      {
860  case 'v':
861     verbose++;
862     break;
863  case 'o':
864     strcpy(file_outoptarg);
865     break;
866  case 'f':
867     sscanf(optarg, "%d", &out_format);
868     switch (out_format)
869     {
870        case NC_FORMAT_CLASSIC:
871   break;
872        case NC_FORMAT_64BIT_OFFSET:
873   cmode = NC_64BIT_OFFSET;
874   break;
875        case NC_FORMAT_CDF5:
876   cmode = NC_CDF5;
877   break;
878        case NC_FORMAT_NETCDF4:
879   cmode = NC_NETCDF4;
880   break;
881        case NC_FORMAT_NETCDF4_CLASSIC:
882   cmode = NC_NETCDF4|NC_CLASSIC_MODEL;
883   break;
884       default:
885  usage();
886  return 1;
887     }
888     break;
889  case 'h':
890     header++;
891     break;
892  case 'c':
893     for (num_vo = 0, str1 = optarg; ; num_vo++, str1 = NULL)
894     {
895        int got_z = 0, got_s = 0;
896        if (num_vo > MAX_VO)
897   return 1;
898        if (!(token = strtok_r(str1COMMA, &saveptr1)))
899   break;
900               for (ndims = 0, str2 = token; ; str2 = NULL)
901        {
902   int tmp_int;
903   if (!(subtoken = strtok_r(str2COLON, &saveptr2)))
904      break;
905   if (str2)
906      sscanf(subtoken, "%d", &(vo[num_vo].varid));
907   else if (!got_z++)
908      sscanf(subtoken, "%d", &(vo[num_vo].deflate_num));
909   else if (!got_s++)
910      sscanf(subtoken, "%d", &(vo[num_vo].shuffle));
911   else
912   {
913      sscanf(subtoken, "%d", &tmp_int);
914      vo[num_vo].chunksize[ndims++] = tmp_int;
915   }
916               }
917        vo[num_vo].ndims = ndims;
918     }
919     break;
920  case 't':
921     for (num_vo = 0, str1 = optarg; ; num_vo++, str1 = NULL)
922     {
923        if (num_vo > MAX_VO)
924   return 1;
925        if (!(token = strtok_r(str1COMMA, &saveptr1)))
926   break;
927               for (ndims = 0, str2 = token; ; str2 = NULL)
928        {
929   if (!(subtoken = strtok_r(str2COLON, &saveptr2)))
930      break;
931   if (str2)
932      sscanf(subtoken, "%d", &(vo[num_vo].varid));
933   else
934      sscanf(subtoken, "%ld", &(vo[num_vo].start[ndims++]));
935               }
936        vo[num_vo].ndims = ndims;
937     }
938     use_scs++;
939     break;
940  case 'u':
941     for (num_vo = 0, str1 = optarg; ; num_vo++, str1 = NULL)
942     {
943        if (num_vo > MAX_VO)
944   return 1;
945        if (!(token = strtok_r(str1COMMA, &saveptr1)))
946   break;
947               for (ndims = 0, str2 = token; ; str2 = NULL)
948        {
949   if (!(subtoken = strtok_r(str2COLON, &saveptr2)))
950      break;
951   if (str2)
952      sscanf(subtoken, "%d", &(vo[num_vo].varid));
953   else
954      sscanf(subtoken, "%ld", &(vo[num_vo].count[ndims++]));
955               }
956        vo[num_vo].ndims = ndims;
957     }
958     break;
959  case 'r':
960     for (num_vo = 0, str1 = optarg; ; num_vo++, str1 = NULL)
961     {
962        if (num_vo > MAX_VO)
963   return 1;
964        if (!(token = strtok_r(str1COMMA, &saveptr1)))
965   break;
966               for (ndims = 0, str2 = token; ; str2 = NULL)
967        {
968   if (!(subtoken = strtok_r(str2COLON, &saveptr2)))
969      break;
970   if (str2)
971      sscanf(subtoken, "%d", &(vo[num_vo].varid));
972   else
973      sscanf(subtoken, "%ld", &(vo[num_vo].inc[ndims++]));
974               }
975        vo[num_vo].ndims = ndims;
976     }
977     break;
978  case 'd':
979     doublecheck++;
980     break;
981  case 'm':
982     do_cmp++;
983     doublecheck++;
984     break;
985  case 'p':
986     use_par++;
987     break;
988  case 'i':
989     mpiio++;
990     break;
991  case 's':
992     sscanf(optarg, "%d", &slow_count);
993     break;
994  case 'e':
995     sscanf(optarg, "%d", &endianness);
996     break;
997  case 'l':
998     convert_unlim++;
999     break;
1000  case '?':
1001     usage();
1002     return 1;
1003      }
1004
1005   if (mpiio)
1006      cmode |= NC_MPIIO;
1007
1008   if (use_scs)
1009   {
1010      if (use_par)
1011      {
1012  printf("Can't use start/count/slice for parallel runs yet!\n");
1013  return 2;
1014      }
1015   }
1016   else
1017   {
1018      if (slow_count < p)
1019  slow_count = p;
1020      if (slow_count % p)
1021      {
1022  printf("slow_count must be even multiple of p\n");
1023  return 2;
1024      }
1025   }
1026
1027   argc -= optind;
1028   argv += optind;
1029
1030   /* If no file arguments left, report and exit */
1031   if (argc < 1)
1032   {
1033      printf("no file specified\n");
1034      return 0;
1035   }
1036
1037   /* Get the name of the file to copy. */
1038   strcpy(file_inargv[0]);
1039
1040   /* Verbose mode seems a bit stupid, but it's really useful when you
1041    * are running in batch mode on a supercomputer, and can't use
1042    * anything else to figure out what the heck is going on. */
1043   if (verbose && !my_rank)
1044   {
1045      printf("copying %s to %s on %d processors with endianness %d and...\n",
1046      file_infile_outpendianness);
1047      if (use_scs)
1048  for (v = 0; v < num_vov++)
1049  {
1050     printf("options for var %d:\n", vo[v].varid);
1051     for (d = 0; d < vo[v].ndimsd++)
1052        printf("start[%d]=%ld, count[%d]=%ld, inc[%d]=%ld\n",
1053       dvo[v].start[d], dvo[v].count[d], dvo[v].inc[d]);
1054  }
1055      else
1056  printf("slow_count=%d, doublecheck=%d\n", slow_countdoublecheck);
1057   }
1058
1059   /* Copy the file, keeping track of the read and write times for metadata and data. */
1060   if ((ret = copy_file(file_infile_outcmodenum_vovo, &meta_read_us, &meta_write_us,
1061 &data_read_us, &data_write_us, &in_formatuse_parpar_access,
1062 &num_bytespmy_rankslow_countverboseuse_scsendianness,
1063 convert_unlim)))
1064      return ret;
1065
1066   /* If the user wants a double check, make sure the data in the new
1067    * file is exactly the same. */
1068   if (doublecheck)
1069   {
1070#ifdef USE_PARALLEL
1071      MPI_Barrier(MPI_COMM_WORLD);
1072#endif
1073      if ((ret = cmp_file(file_infile_out, &meta_read2_us, &data_read2_us,
1074   use_parpar_accessdo_cmppmy_rankslow_count,
1075   verbosenum_vovouse_scs)))
1076  return ret;
1077   }
1078
1079   if (use_par)
1080   {
1081#ifdef USE_PARALLEL
1082      MPI_Reduce(&meta_read_us, &tmeta_read_us, 1, MPI_INTMPI_MAX, 0, MPI_COMM_WORLD);
1083      MPI_Reduce(&meta_write_us, &tmeta_write_us, 1, MPI_INTMPI_MAX, 0, MPI_COMM_WORLD);
1084      MPI_Reduce(&data_read_us, &tdata_read_us, 1, MPI_INTMPI_MAX, 0, MPI_COMM_WORLD);
1085      MPI_Reduce(&data_write_us, &tdata_write_us, 1, MPI_INTMPI_MAX, 0, MPI_COMM_WORLD);
1086      MPI_Reduce(&data_read2_us, &tdata_read2_us, 1, MPI_INTMPI_MAX, 0, MPI_COMM_WORLD);
1087#else
1088      return NC_EPARINIT;
1089#endif
1090   }
1091   else
1092   {
1093      tmeta_read_us = meta_read_us;
1094      tmeta_write_us = meta_write_us;
1095      tdata_read_us = data_read_us;
1096      tdata_write_us = data_write_us;
1097      tmeta_read2_us = meta_read2_us;
1098      tdata_read2_us = data_read2_us;
1099   }
1100
1101   if (verbose)
1102      printf("num_bytes=%lld tdata_read_us=%d\n", num_bytestdata_read_us);
1103
1104   read_rate = (float)num_bytes/((float)tdata_read_us/p);
1105   write_rate = (float)num_bytes/((float)tdata_write_us/p);
1106   reread_rate = (float)num_bytes/((float)tdata_read2_us/p);
1107   if (verbose)
1108      printf("%d: read rate %g, write rate %g, reread_rate %g\n", my_rankread_rate,
1109      write_ratereread_rate);
1110
1111   /* Print some output. */
1112   if (!my_rank)
1113   {
1114      /* Does the user want a text header for the data? */
1115      if (header)
1116      {
1117  printf("input format, output_format, input size, output size, meta read time, "
1118 "meta write time, data read time, data write time, enddianness, ");
1119  if (doublecheck)
1120     printf("metadata reread time, data reread time, read rate, "
1121    "write rate, reread rate, ");
1122  else
1123     printf("read rate, write rate, ");
1124  if (use_par)
1125     printf("num_proc, ");
1126  printf("deflate, shuffle, chunksize[0], chunksize[1], chunksize[2], "
1127 "chunksize[3]\n");
1128      }
1129
1130      printf("%d, %d, %ld, %ld, %d, %d, %d, %d, %d, ", in_formatout_formatfile_size(file_in),
1131      file_size(file_out), tmeta_read_ustmeta_write_ustdata_read_ustdata_write_us,
1132      endianness);
1133      if (doublecheck)
1134  printf("%d, %d, %g, %g, %g, ", tmeta_read2_ustdata_read2_usread_ratewrite_rate,
1135 reread_rate);
1136      else
1137  printf("%g, %g, ", read_ratewrite_rate);
1138      if (use_par)
1139  printf("%d, ", p);
1140      for (o1 = 0; o1 < num_voo1++)
1141      {
1142  printf("%d, %d, %d, %d, %d, %d ", vo[o1].deflate_numvo[o1].shuffle,
1143  (int)vo[o1].chunksize[0], (int)vo[o1].chunksize[1], (int)vo[o1].chunksize[2], (int)vo[o1].chunksize[3]);
1144  if (o1 != num_vo - 1)
1145     printf(", ");
1146      }
1147      printf("\n");
1148   }
1149
1150#ifdef USE_PARALLEL
1151   MPI_Finalize();
1152#endif
1153
1154   return 0;
1155}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed