/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  If you do not have          *
 * access to either file, you may request a copy from help@hdfgroup.org.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * Programmer:	Raymond Lu <slu@ncsa.uiuc.edu>
 *		Friday, Oct 3, 2004
 *
 * Purpose:	Tests performance of metadata
 */

#include "h5test.h"
#include <sys/time.h>

#ifdef H5_HAVE_PARALLEL
#define MAINPROCESS	(!mpi_rank)	/* define process 0 as main process */
#endif /*H5_HAVE_PARALLEL*/

/* File_Access_type bits */
#define FACC_DEFAULT	0x0	/* serial as default */
#define FACC_MPIO	0x1	/* MPIO */
#define FACC_MPIPOSIX   0x8	/* MPIPOSIX */

/* Which test to run */
int RUN_TEST = 0x0;     /* all tests as default */
int TEST_1   = 0x1;     /* Test 1 */
int TEST_2   = 0x2;     /* Test 2 */
int TEST_3   = 0x4;     /* Test 3 */


const char *FILENAME[] = {
    "meta_perf_1",
    "meta_perf_2",
    "meta_perf_3",
    NULL
};

/* Default values for performance. Can be changed through command line options */
int 	NUM_DSETS = 16;
int 	NUM_ATTRS = 8;
int 	BATCH_ATTRS = 2;
hbool_t flush_dset = FALSE;
hbool_t flush_attr = FALSE;
int 	nerrors = 0;			/* errors count */
hid_t	fapl;

/* Data space IDs */
hid_t	space;
hid_t	small_space;

/* Performance data */
typedef struct p_time {
    double total;
    double avg;
    double max;
    double min;
    double start;
    char   func[32];
} p_time;

/*Test file access type for parallel.  MPIO as default */
int facc_type = FACC_DEFAULT;

double  retrieve_time(void);
void    perf(p_time *perf_t, double start_t, double end_t);
void    print_perf(p_time, p_time, p_time);


/*-------------------------------------------------------------------------
 * Function:	parse_options
 *
  Purpose:	Parse command line options
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int
parse_options(int argc, char **argv)
{
    int t;

    /* Use default values */
    if(argc==1)
	return(0);

    while (--argc){
	if (**(++argv) != '-'){
	    break;
	}else{
	    switch(*(*argv+1)){
                case 'h':   /* Help page */
                            return(1);

		case 'd':   /* Number of datasets */
                            NUM_DSETS = atoi((*argv+1)+1);
			    if (NUM_DSETS < 0){
				nerrors++;
				return(1);
			    }
			    break;

		case 'a':   /* Number of attributes per dataset */
                            NUM_ATTRS = atoi((*argv+1)+1);
			    if (NUM_ATTRS < 0){
				nerrors++;
				return(1);
			    }
			    break;

		case 'n':   /* Number of attributes to be created in batch */
                            BATCH_ATTRS = atoi((*argv+1)+1);
			    if (BATCH_ATTRS < 0){
				nerrors++;
				return(1);
			    }
			    break;

		case 'p':   /* Use the MPI-POSIX driver access */
			    facc_type = FACC_MPIPOSIX;
			    break;

		case 'm':   /* Use the MPI-POSIX driver access */
			    facc_type = FACC_MPIO;
			    break;

                case 'f':   /* Call H5Fflush for each dataset or attribute */
                            if(!strcmp("a", (*argv+2)))
                                flush_attr = TRUE;
                            else if(!strcmp("d", (*argv+2)))
                                flush_dset = TRUE;
                            else {
                                nerrors++;
                                return(1);
                            }
                            break;

		case 't':   /* Which test to run */
                            t = atoi((*argv+1)+1);
			    if (t < 1 || t > 3){
				nerrors++;
				return(1);
			    }
                            if(t == 1)
                                RUN_TEST |= TEST_1;
                            else if(t == 2)
                                RUN_TEST |= TEST_2;
                            else
                                RUN_TEST |= TEST_3;

			    break;

 		default:    nerrors++;
			    return(1);
	    }
	}
    } /*while*/

    /* Check valid values */
#ifndef H5_HAVE_PARALLEL
    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX)
    {
        nerrors++;
        return(1);
    }
#endif /*H5_HAVE_PARALLEL*/

    if(NUM_ATTRS && !BATCH_ATTRS)
        NUM_ATTRS = 0;

    if(!NUM_ATTRS && BATCH_ATTRS)
        BATCH_ATTRS = 0;

    if(!NUM_DSETS) {
        nerrors++;
        return(1);
    }

    if(NUM_ATTRS && BATCH_ATTRS) {
        if(BATCH_ATTRS > NUM_ATTRS || NUM_ATTRS % BATCH_ATTRS) {
	    nerrors++;
            return(1);
        }
    }

    return(0);
}


/*-------------------------------------------------------------------------
 * Function:	usage
 *
  Purpose:	Prints help page
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void
usage(void)
{
    printf("Usage: perf_meta [-h] [-m] [-p] [-d<num_datasets>]"
           "[-a<num_attributes>]\n"
           "\t[-n<batch_attributes>] [-f<option>] [-t<test>]\n");
    printf("\t-h"
	"\t\t\thelp page.\n");
    printf("\t-m"
	"\t\t\tset MPIO as the file driver when parallel HDF5\n"
        "\t\t\t\tis enabled.  Either -m or -p has be to \n"
        "\t\t\t\tspecified when running parallel program.\n");
    printf("\t-p"
	"\t\t\tset MPI POSIX as the file driver when parallel \n"
	"\t\t\t\tHDF5 is enabled.  Either -m or -p has be to \n"
        "\t\t\t\tspecified when running parallel program.\n");
    printf("\t-d<num_datasets>"
	"\tset number of datasets for meta data \n"
        "\t\t\t\tperformance test\n");
    printf("\t-a<num_attributes>"
        "\tset number of attributes per dataset for meta \n"
        "\t\t\t\tdata performance test.\n");
    printf("\t-n<batch_attributes>"
	"\tset batch number of attributes for dataset \n"
        "\t\t\t\tfor meta data performance test.\n");
    printf("\t-f<option>"
	"\t\tflush data to disk after closing a dataset \n"
        "\t\t\t\tor attribute.  Valid options are \"d\" for \n"
        "\t\t\t\tdataset, \"a\" for attribute.  Disabled is \n"
        "\t\t\t\tthe default.\n");
    printf("\t-t<tests>"
	"\t\trun specific test.  Give only one number each \n"
        "\t\t\t\ttime. i.e. \"-t1 -t3\" will run test 1 and 3. \n"
        "\t\t\t\tDefault is all three tests.  The 3 tests are: \n\n"
        "\t\t\t\t1. Create <num_attributes> attributes for each \n"
        "\t\t\t\t   of <num_datasets> existing datasets.\n"
        "\t\t\t\t2. Create <num_attributes> attributes for each \n"
        "\t\t\t\t   of <num_datasets> new datasets.\n"
        "\t\t\t\t3. Create <batch_attributes> attributes for \n"
        "\t\t\t\t   each of <num_dataset> new datasets for \n"
        "\t\t\t\t   <num_attributes>/<batch_attributes> times.\n");
}


/*-------------------------------------------------------------------------
 * Function:	create_dspace
 *
 * Purpose:	Attempts to create data space.
 *
 * Return:	Success:	0
 *
 *		Failure:	-1
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
create_dspace(void)
{
    hsize_t	dims[2];
    hsize_t	small_dims[2];

    /* Create the data space */
    dims[0] = 256;
    dims[1] = 512;
    if((space = H5Screate_simple(2, dims, NULL)) < 0)
	    goto error;

    /* Create a small data space for attributes */
    small_dims[0] = 16;
    small_dims[1] = 8;
    if((small_space = H5Screate_simple(2, small_dims, NULL)) < 0)
	    goto error;

    return 0;

error:
    return -1;
}


/*-------------------------------------------------------------------------
 * Function:	create_dsets
 *
 * Purpose:	Attempts to create some datasets.
 *
 * Return:	Success:	0
 *
 *		Failure:	-1
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
create_dsets(hid_t file)
{
    hid_t	dataset;
    char	dset_name[32];
    int		i;

    /*
     * Create a dataset using the default dataset creation properties.
     */
    for(i = 0; i < NUM_DSETS; i++) {
	sprintf(dset_name, "dataset %d", i);
    	if((dataset = H5Dcreate2(file, dset_name, H5T_NATIVE_DOUBLE, space,
                H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
            goto error;

    	if(H5Dclose(dataset) < 0)
            goto error;
    } /* end for */

    return 0;

error:
    return -1;

}


/*-------------------------------------------------------------------------
 * Function:	create_attrs_1
 *
 * Purpose:	Attempts to create all attributes for each existing dataset.
 *
 * Return:	Success:	0
 *
 *		Failure:	-1
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
create_attrs_1(void)
{
    hid_t	file, dataset, attr;
    char	filename[128];
    char	dset_name[64];
    char	attr_name[128];
    int		i, j;
    p_time      attr_t  = {0, 0, 0, 1000000, 0, ""};
    p_time      open_t  = {0, 0, 0, 1000000, 0, "H5Dopen2"};
    p_time      close_t = {0, 0, 0, 1000000, 0, ""};

#ifdef H5_HAVE_PARALLEL
    /* need the rank for printing data */
    int         mpi_rank;
    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX)
        MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
#endif /*H5_HAVE_PARALLEL*/

    h5_fixname(FILENAME[0], fapl, filename, sizeof filename);

    if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT,
	fapl)) < 0)
	goto error;

    if(create_dsets(file) < 0)
	goto error;

    /*
     * Create all(user specifies the number) attributes for each dataset
     */
    for(i = 0; i < NUM_DSETS; i++) {
	sprintf(dset_name, "dataset %d", i);
        open_t.start = retrieve_time();
	if((dataset = H5Dopen2(file, dset_name, H5P_DEFAULT)) < 0)
		goto error;
	perf(&open_t, open_t.start, retrieve_time());

	for(j = 0; j < NUM_ATTRS; j++) {
            sprintf(attr_name, "all attrs for each dset %d", j);
            attr_t.start = retrieve_time();
            if((attr = H5Acreate2(dataset, attr_name, H5T_NATIVE_DOUBLE,
                    small_space, H5P_DEFAULT, H5P_DEFAULT)) < 0)
                goto error;
            if(H5Aclose(attr) < 0)
                goto error;
            perf(&attr_t, attr_t.start, retrieve_time());
            if(flush_attr && H5Fflush(file, H5F_SCOPE_LOCAL) < 0)
                goto error;
    	} /* end for */

	close_t.start = retrieve_time();
    	if(H5Dclose(dataset) < 0)
            goto error;
	perf(&close_t, close_t.start, retrieve_time());
        if(flush_dset && H5Fflush(file,  H5F_SCOPE_LOCAL) < 0)
            goto error;
    } /* end for */

    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX) {
#ifdef H5_HAVE_PARALLEL
        MPI_Barrier(MPI_COMM_WORLD);
#endif /*H5_HAVE_PARALLEL*/
    }

#ifdef H5_HAVE_PARALLEL
    if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS)) /* only process 0 reports */
#endif /*H5_HAVE_PARALLEL*/
    {
        /* Calculate the average time */
        open_t.avg  = open_t.total / NUM_DSETS;
        close_t.avg = close_t.total / NUM_DSETS;
        if(NUM_ATTRS)
            attr_t.avg  = attr_t.total / (NUM_ATTRS*NUM_DSETS);

        /* Print out the performance result */
        fprintf(stderr, "1.  Create %d attributes for each of %d existing datasets\n",
            NUM_ATTRS, NUM_DSETS);
        print_perf(open_t, close_t, attr_t);
    }

    if (H5Fclose(file) < 0) goto error;

    return 0;

error:
    return -1;
}


/*-------------------------------------------------------------------------
 * Function:	create_attrs_2
 *
 * Purpose:	Attempts to create all attributes for each new dataset.
 *
 * Return:	Success:	0
 *
 *		Failure:	-1
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
create_attrs_2(void)
{
    hid_t	file, dataset, attr;
    char	filename[128];
    char	dset_name[64];
    char	attr_name[128];
    int		i, j;
    p_time      attr_t  = {0, 0, 0, 1000000, 0, ""};
    p_time      create_t  = {0, 0, 0, 1000000, 0, "H5Dcreate2"};
    p_time      close_t = {0, 0, 0, 1000000, 0, ""};

#ifdef H5_HAVE_PARALLEL
    /* need the rank for printing data */
    int         mpi_rank;
    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX)
        MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
#endif /*H5_HAVE_PARALLEL*/

    h5_fixname(FILENAME[1], fapl, filename, sizeof filename);

    if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
	goto error;

    /*
     * Create all(user specifies the number) attributes for each new dataset
     */
    for(i = 0; i < NUM_DSETS; i++) {
	sprintf(dset_name, "dataset %d", i);
        create_t.start = retrieve_time();
   	if((dataset = H5Dcreate2(file, dset_name, H5T_NATIVE_DOUBLE,
                space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
            goto error;
	perf(&create_t, create_t.start, retrieve_time());

	for(j = 0; j < NUM_ATTRS; j++) {
            sprintf(attr_name, "all attrs for each dset %d", j);
            attr_t.start = retrieve_time();
            if((attr = H5Acreate2(dataset, attr_name, H5T_NATIVE_DOUBLE,
                    small_space, H5P_DEFAULT, H5P_DEFAULT)) < 0)
                goto error;
            if(H5Aclose(attr) < 0)
                goto error;
            perf(&attr_t, attr_t.start, retrieve_time());
            if(flush_attr && H5Fflush(file,  H5F_SCOPE_LOCAL) < 0)
                goto error;
	} /* end for */

	close_t.start = retrieve_time();
    	if(H5Dclose(dataset) < 0)
            goto error;
	perf(&close_t, close_t.start, retrieve_time());
        if(flush_dset && H5Fflush(file,  H5F_SCOPE_LOCAL) < 0)
            goto error;
    } /* end for */

    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX) {
#ifdef H5_HAVE_PARALLEL
    MPI_Barrier(MPI_COMM_WORLD);
#endif /*H5_HAVE_PARALLEL*/
    }

#ifdef H5_HAVE_PARALLEL
    /* only process 0 reports if parallel */
    if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS))
#endif /*H5_HAVE_PARALLEL*/
    {
        /* Calculate the average time */
        create_t.avg = create_t.total / NUM_DSETS;
        close_t.avg  = close_t.total / NUM_DSETS;
        if(NUM_ATTRS)
            attr_t.avg = attr_t.total / (NUM_ATTRS*NUM_DSETS);

        /* Print out the performance result */
        fprintf(stderr, "2.  Create %d attributes for each of %d new datasets\n",
            NUM_ATTRS, NUM_DSETS);
        print_perf(create_t, close_t, attr_t);
    }

    if (H5Fclose(file) < 0) goto error;

    return 0;

error:
    return -1;
}


/*-------------------------------------------------------------------------
 * Function:	create_attrs_3
 *
 * Purpose:	Attempts to create some attributes for each dataset in a
 * 		loop.
 *
 * Return:	Success:	0
 *
 *		Failure:	-1
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
create_attrs_3(void)
{
    hid_t	file, dataset, attr;
    char	filename[128];
    char	dset_name[64];
    char	attr_name[128];
    int		loop_num;
    int		i, j, k;
    p_time      attr_t  = {0, 0, 0, 1000000, 0, ""};
    p_time      open_t  = {0, 0, 0, 1000000, 0, "H5Dopen2"};
    p_time      close_t = {0, 0, 0, 1000000, 0, ""};

#ifdef H5_HAVE_PARALLEL
    /* need the rank for printing data */
    int         mpi_rank;
    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX)
        MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
#endif /*H5_HAVE_PARALLEL*/

    h5_fixname(FILENAME[2], fapl, filename, sizeof filename);

    if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT,
	fapl)) < 0)
	goto error;

    if(create_dsets(file) < 0)
	goto error;

    /*
     * Create some(user specifies the number) attributes for each dataset
     * in a loop
     */
    loop_num = NUM_ATTRS/BATCH_ATTRS;

    for(i = 0; i < loop_num; i++) {
    	for(j = 0; j < NUM_DSETS; j++) {
            sprintf(dset_name, "dataset %d", j);
            open_t.start = retrieve_time();
            if((dataset = H5Dopen2(file, dset_name, H5P_DEFAULT)) < 0)
                goto error;
            perf(&open_t, open_t.start, retrieve_time());

            for(k = 0; k < BATCH_ATTRS; k++) {
                sprintf(attr_name, "some attrs for each dset %d %d", i, k);
                attr_t.start = retrieve_time();
                if((attr = H5Acreate2(dataset, attr_name, H5T_NATIVE_DOUBLE,
                        small_space, H5P_DEFAULT, H5P_DEFAULT)) < 0)
                    goto error;
                if(H5Aclose(attr) < 0)
                    goto error;
                perf(&attr_t, attr_t.start, retrieve_time());
                if(flush_attr && H5Fflush(file,  H5F_SCOPE_LOCAL) < 0)
                    goto error;
            } /* end for */

            close_t.start = retrieve_time();
            if(H5Dclose(dataset) < 0)
                goto error;
            perf(&close_t, close_t.start, retrieve_time());
            if(flush_dset && H5Fflush(file,  H5F_SCOPE_LOCAL) < 0)
                goto error;
    	} /* end for */
    } /* end for */

    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX) {
#ifdef H5_HAVE_PARALLEL
        MPI_Barrier(MPI_COMM_WORLD);
#endif /*H5_HAVE_PARALLEL*/
    }

#ifdef H5_HAVE_PARALLEL
    /* only process 0 reports if parallel */
    if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS))
#endif /*H5_HAVE_PARALLEL*/
    {
        /* Calculate the average time */
        open_t.avg = open_t.total / (loop_num*NUM_DSETS);
        close_t.avg = close_t.total / (loop_num*NUM_DSETS);
        attr_t.avg = attr_t.total / (NUM_ATTRS*NUM_DSETS);

        /* Print out the performance result */
        fprintf(stderr, "3.  Create %d attributes for each of %d existing datasets for %d times\n",
            BATCH_ATTRS, NUM_DSETS, loop_num);
        print_perf(open_t, close_t, attr_t);
    }

    if (H5Fclose(file) < 0) goto error;

    return 0;

error:
    return -1;
}


/*-------------------------------------------------------------------------
 * Function:	retrieve_time
 *
 * Purpose:     Returns time in seconds, in a double number.
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
double retrieve_time(void)
{
#ifdef H5_HAVE_PARALLEL
    if(facc_type == FACC_DEFAULT) {
#endif /*H5_HAVE_PARALLEL*/
        struct timeval t;
        gettimeofday(&t, NULL);
        return ((double)t.tv_sec + (double)t.tv_usec / 1000000);
#ifdef H5_HAVE_PARALLEL
    } else {
        return MPI_Wtime();
    }
#endif /*H5_HAVE_PARALLEL*/
}


/*-------------------------------------------------------------------------
 * Function:	perf
 *
 * Purpose:	Calculate total time, maximal and minimal time of
 *              performance.
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void perf(p_time *perf_t, double start_t, double end_t)
{
	double t = end_t - start_t;

    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX) {
#ifdef H5_HAVE_PARALLEL
        double reduced_t;
        double t_max, t_min;
        int    mpi_size, mpi_rank;

        MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
        MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
        MPI_Barrier(MPI_COMM_WORLD);

        MPI_Reduce(&t, &reduced_t, 1, MPI_DOUBLE, MPI_SUM, 0,
                MPI_COMM_WORLD);
        reduced_t /= mpi_size;

        MPI_Reduce(&t, &t_max, 1, MPI_DOUBLE, MPI_MAX, 0,
                MPI_COMM_WORLD);
        MPI_Reduce(&t, &t_min, 1, MPI_DOUBLE, MPI_MIN, 0,
                MPI_COMM_WORLD);

        if (MAINPROCESS) {
            perf_t->total += reduced_t;

	    if(t_max > perf_t->max)
		perf_t->max = t_max;
	    if(t_min < perf_t->min)
		perf_t->min = t_min;
        }
#endif /*H5_HAVE_PARALLEL*/
    } else {
	perf_t->total += t;

	if(t > perf_t->max)
		perf_t->max = t;
	if(t < perf_t->min)
		perf_t->min = t;
    }
}


/*-------------------------------------------------------------------------
 * Function:	print_perf
 *
 * Purpose:	Print out performance data.
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void print_perf(p_time open_t, p_time close_t, p_time attr_t)
{
    fprintf(stderr, "\t%s:\t\tavg=%.6fs;\tmax=%.6fs;\tmin=%.6fs\n",
		open_t.func, open_t.avg, open_t.max, open_t.min);
    fprintf(stderr, "\tH5Dclose:\t\tavg=%.6fs;\tmax=%.6fs;\tmin=%.6fs\n",
		close_t.avg, close_t.max, close_t.min);
    if(NUM_ATTRS)
        fprintf(stderr, "\tH5A(create & close):\tavg=%.6fs;\tmax=%.6fs;\tmin=%.6fs\n",
		attr_t.avg, attr_t.max, attr_t.min);
}


/*-------------------------------------------------------------------------
 * Function:	main
 *
 * Purpose:	Tests
 *
 * Return:	Success:	exit(0)
 *
 *		Failure:	exit(1)
 *
 * Programmer:	Raymond Lu
 *		Friday, Oct 3, 2003
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int
main(int argc, char **argv)
{
#ifdef H5_HAVE_PARALLEL
    int mpi_size, mpi_rank;				/* mpi variables */
#endif /*H5_HAVE_PARALLEL*/

    if(parse_options(argc, argv) != 0) {
	   usage();
	   return 0;
    }

    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX) {
#ifdef H5_HAVE_PARALLEL
        MPI_Init(&argc, &argv);
        MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
        MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
#endif /*H5_HAVE_PARALLEL*/
    }

#ifdef H5_HAVE_PARALLEL
    if (facc_type == FACC_DEFAULT || (facc_type != FACC_DEFAULT && MAINPROCESS))
#endif /*H5_HAVE_PARALLEL*/
        fprintf(stderr, "\t\tPerformance result of metadata for datasets and attributes\n\n");

    fapl = H5Pcreate (H5P_FILE_ACCESS);
    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX) {
#ifdef H5_HAVE_PARALLEL
        if(facc_type == FACC_DEFAULT || facc_type == FACC_MPIO)
            H5Pset_fapl_mpio(fapl, MPI_COMM_WORLD, MPI_INFO_NULL);
        else if(facc_type == FACC_MPIPOSIX)
            H5Pset_fapl_mpiposix(fapl, MPI_COMM_WORLD, FALSE);
#endif /*H5_HAVE_PARALLEL*/
    }

    nerrors += create_dspace() < 0 	?1:0;

    if((RUN_TEST & TEST_1) || !RUN_TEST)
        nerrors += create_attrs_1() < 0 	?1:0;
    if((RUN_TEST & TEST_2) || !RUN_TEST)
        nerrors += create_attrs_2() < 0 	?1:0;
    if(((RUN_TEST & TEST_3) || !RUN_TEST) && BATCH_ATTRS && NUM_ATTRS)
        nerrors += create_attrs_3() < 0 	?1:0;

    if (H5Sclose(space) < 0) goto error;
    if (H5Sclose(small_space) < 0) goto error;

    h5_cleanup(FILENAME, fapl);

    if(facc_type == FACC_MPIO || facc_type == FACC_MPIPOSIX) {
#ifdef H5_HAVE_PARALLEL
        /* MPI_Finalize must be called AFTER H5close which may use MPI calls */
        MPI_Finalize();
#endif /*H5_HAVE_PARALLEL*/
    }

    if (nerrors) goto error;
#ifdef H5_HAVE_PARALLEL
    if (facc_type != FACC_DEFAULT && MAINPROCESS)
#endif /*H5_HAVE_PARALLEL*/
        printf("All metadata performance tests passed.\n");

    return 0;

 error:
    nerrors = MAX(1, nerrors);
#ifdef H5_HAVE_PARALLEL
    if (facc_type != FACC_DEFAULT && MAINPROCESS)
#endif /*H5_HAVE_PARALLEL*/
        printf("***** %d PERFORMANCE TEST%s FAILED! *****\n",
	   nerrors, 1 == nerrors ? "" : "S");

    return 1;
}