From 2ac453aeba987ea0e84fb0986f45d180757a1ce4 Mon Sep 17 00:00:00 2001 From: Zhang Xianyi Date: Fri, 17 Apr 2015 00:25:33 -0500 Subject: [PATCH] Add basic performance and correct test. --- CMakeLists.txt | 12 + include/openvml.h | 16 +- include/openvml_reference.h | 54 +++++ reference/CMakeLists.txt | 7 + reference/vadd.c | 72 ++++++ test/CMakeLists.txt | 15 ++ test/ctest.h | 463 ++++++++++++++++++++++++++++++++++++ test/test_add.c | 38 +++ test/timer.c | 104 ++++++++ test/vml_test.c | 48 ++++ test/vml_test.h | 76 ++++++ test/vml_util.c | 295 +++++++++++++++++++++++ 12 files changed, 1199 insertions(+), 1 deletion(-) create mode 100644 include/openvml_reference.h create mode 100644 reference/CMakeLists.txt create mode 100644 reference/vadd.c create mode 100644 test/CMakeLists.txt create mode 100644 test/ctest.h create mode 100644 test/test_add.c create mode 100644 test/timer.c create mode 100644 test/vml_test.c create mode 100644 test/vml_test.h create mode 100644 test/vml_util.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c401617..82beca3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ set (OpenVML_FUNC_PREFIX "") set (OpenVML_FUNC_SUFFIX "") option(BUILD_SINGLE_THREAD "Only build the single thread" ON) +option(BUILD_OpenVML_TEST "Build Test" ON) ##################################################### @@ -86,6 +87,9 @@ set_target_properties(${OpenVML_LIBNAME}_static PROPERTIES OUTPUT_NAME ${OpenVML set_target_properties(${OpenVML_LIBNAME}_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) set_target_properties(${OpenVML_LIBNAME} PROPERTIES CLEAN_DIRECT_OUTPUT 1) +target_link_libraries(${OpenVML_LIBNAME} m) +target_link_libraries(${OpenVML_LIBNAME}_static m) + set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) set_target_properties(${OpenVML_LIBNAME} PROPERTIES @@ -111,3 +115,11 @@ install(FILES ${PROJECT_BINARY_DIR}/include/openvml_config.h DESTINATION include install(FILES ${PROJECT_SOURCE_DIR}/include/openvml.h DESTINATION include) install(FILES ${PROJECT_SOURCE_DIR}/include/openvml_common.h DESTINATION include) + +#test +if(BUILD_OpenVML_TEST) +add_subdirectory(reference) + +enable_testing() +add_subdirectory(test) +endif(BUILD_OpenVML_TEST) diff --git a/include/openvml.h b/include/openvml.h index 03a821f..ca748ed 100644 --- a/include/openvml.h +++ b/include/openvml.h @@ -35,11 +35,25 @@ extern "C" { OPENVML_EXPORT void OpenVML_FUNCNAME(vsAdd)(VML_INT n, const float * a, const float * b, float * y); OPENVML_EXPORT void OpenVML_FUNCNAME(vdAdd)(VML_INT n, const double * a, const double * b, double * y); +OPENVML_EXPORT void OpenVML_FUNCNAME(vcAdd)(VML_INT n, const float * a, const float * b, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME(vzAdd)(VML_INT n, const double * a, const double * b, double * y); OPENVML_EXPORT void OpenVML_FUNCNAME(vsSub)(VML_INT n, const float * a, const float * b, float * y); OPENVML_EXPORT void OpenVML_FUNCNAME(vdSub)(VML_INT n, const double * a, const double * b, double * y); +OPENVML_EXPORT void OpenVML_FUNCNAME(vcSub)(VML_INT n, const float * a, const float * b, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME(vzSub)(VML_INT n, const double * a, const double * b, double * y); + +OPENVML_EXPORT void OpenVML_FUNCNAME(vsPow)(VML_INT n, const float * a, const float * b, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME(vdPow)(VML_INT n, const double * a, const double * b, double * y); + +OPENVML_EXPORT void OpenVML_FUNCNAME(vsExp)(VML_INT n, const float * a, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME(vdExp)(VML_INT n, const double * a, double * y); + +OPENVML_EXPORT void OpenVML_FUNCNAME(vsTanh)(VML_INT n, const float * a, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME(vdTanh)(VML_INT n, const double * a, double * y); + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/include/openvml_reference.h b/include/openvml_reference.h new file mode 100644 index 0000000..bde7517 --- /dev/null +++ b/include/openvml_reference.h @@ -0,0 +1,54 @@ +/* * Copyright (c) 2014, 2015 Zhang Xianyi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OPENVML_REFERENCE_H_ +#define _OPENVML_REFERENCE_H_ + +#include "openvml_export.h" +#include "openvml_common.h" + +#define OpenVML_FUNCNAME_REF(x) x##_ref + + +#ifdef __cplusplus +extern "C" { +#endif + +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vsAdd)(VML_INT n, const float * a, const float * b, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vdAdd)(VML_INT n, const double * a, const double * b, double * y); +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vcAdd)(VML_INT n, const float * a, const float * b, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vzAdd)(VML_INT n, const double * a, const double * b, double * y); + +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vsSub)(VML_INT n, const float * a, const float * b, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vdSub)(VML_INT n, const double * a, const double * b, double * y); +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vcSub)(VML_INT n, const float * a, const float * b, float * y); +OPENVML_EXPORT void OpenVML_FUNCNAME_REF(vzSub)(VML_INT n, const double * a, const double * b, double * y); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/reference/CMakeLists.txt b/reference/CMakeLists.txt new file mode 100644 index 0000000..faddd6e --- /dev/null +++ b/reference/CMakeLists.txt @@ -0,0 +1,7 @@ +set(OpenVML_REF_SRC + vadd.c + ) + +add_library(${OpenVML_LIBNAME}_ref SHARED ${OpenVML_REF_SRC}) +target_link_libraries(${OpenVML_LIBNAME}_ref m) + diff --git a/reference/vadd.c b/reference/vadd.c new file mode 100644 index 0000000..2eda5e9 --- /dev/null +++ b/reference/vadd.c @@ -0,0 +1,72 @@ +/* * Copyright (c) 2014, 2015 Zhang Xianyi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +void OpenVML_FUNCNAME_REF(vsAdd)(VML_INT n, const float * a, const float * b, float *y) +{ + if (n<=0) return; + if (a==NULL || b==NULL || y==NULL) return; + + VML_INT i; + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +//#define COLOR_OK + +static size_t ctest_errorsize; +static char* ctest_errormsg; +#define MSG_SIZE 8192 +static char ctest_errorbuffer[MSG_SIZE]; +static jmp_buf ctest_err; +static int color_output = 1; +static const char* suite_name; +static const char* test_name; + +typedef int (*filter_func)(struct ctest*); + +#define ANSI_BLACK "\033[0;30m" +#define ANSI_RED "\033[0;31m" +#define ANSI_GREEN "\033[0;32m" +#define ANSI_YELLOW "\033[0;33m" +#define ANSI_BLUE "\033[0;34m" +#define ANSI_MAGENTA "\033[0;35m" +#define ANSI_CYAN "\033[0;36m" +#define ANSI_GREY "\033[0;37m" +#define ANSI_DARKGREY "\033[01;30m" +#define ANSI_BRED "\033[01;31m" +#define ANSI_BGREEN "\033[01;32m" +#define ANSI_BYELLOW "\033[01;33m" +#define ANSI_BBLUE "\033[01;34m" +#define ANSI_BMAGENTA "\033[01;35m" +#define ANSI_BCYAN "\033[01;36m" +#define ANSI_WHITE "\033[01;37m" +#define ANSI_NORMAL "\033[0m" + +static CTEST(suite, test) { } + +static void msg_start(const char* color, const char* title) { +int size; +if (color_output) { + size = snprintf(ctest_errormsg, ctest_errorsize, "%s", color); + ctest_errorsize -= size; + ctest_errormsg += size; + } + size = snprintf(ctest_errormsg, ctest_errorsize, " %s: ", title); + ctest_errorsize -= size; + ctest_errormsg += size; +} + +static void msg_end() { + int size; + if (color_output) { + size = snprintf(ctest_errormsg, ctest_errorsize, ANSI_NORMAL); + ctest_errorsize -= size; + ctest_errormsg += size; + } + size = snprintf(ctest_errormsg, ctest_errorsize, "\n"); + ctest_errorsize -= size; + ctest_errormsg += size; +} + +void CTEST_LOG(char *fmt, ...) +{ + va_list argp; + msg_start(ANSI_BLUE, "LOG"); + + va_start(argp, fmt); + int size = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, argp); + ctest_errorsize -= size; + ctest_errormsg += size; + va_end(argp); + + msg_end(); +} + +void CTEST_ERR(char *fmt, ...) +{ + va_list argp; + msg_start(ANSI_YELLOW, "ERR"); + + va_start(argp, fmt); + int size = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, argp); + ctest_errorsize -= size; + ctest_errormsg += size; + va_end(argp); + + msg_end(); + longjmp(ctest_err, 1); +} + +void assert_str(const char* exp, const char* real, const char* caller, int line) { + if ((exp == NULL && real != NULL) || + (exp != NULL && real == NULL) || + (exp && real && strcmp(exp, real) != 0)) { + CTEST_ERR("%s:%d expected '%s', got '%s'", caller, line, exp, real); + } +} + +void assert_data(const unsigned char* exp, int expsize, + const unsigned char* real, int realsize, + const char* caller, int line) { + int i; + if (expsize != realsize) { + CTEST_ERR("%s:%d expected %d bytes, got %d", caller, line, expsize, realsize); + } + for (i=0; issname, strlen(suite_name)) == 0; +} + +static int suite_test_filter(struct ctest* t) { + int suit_match, test_match; + suit_match=(strncmp(suite_name, t->ssname, strlen(suite_name)) == 0); + test_match=(strncmp(test_name, t->ttname, strlen(test_name)) == 0); + return (suit_match & test_match); +} + +static uint64_t getCurrentTime() { + struct timeval now; + gettimeofday(&now, NULL); + uint64_t now64 = now.tv_sec; + now64 *= 1000000; + now64 += (now.tv_usec); + return now64; +} + +static void color_print(const char* color, const char* text) { + if (color_output) + printf("%s%s"ANSI_NORMAL"\n", color, text); + else + printf("%s\n", text); +} + +#ifdef __APPLE__ +static void *find_symbol(struct ctest *test, const char *fname) +{ + size_t len = strlen(test->ssname) + 1 + strlen(fname); + char *symbol_name = (char *) malloc(len + 1); + memset(symbol_name, 0, len + 1); + snprintf(symbol_name, len + 1, "%s_%s", test->ssname, fname); + + //fprintf(stderr, ">>>> dlsym: loading %s\n", symbol_name); + void *symbol = dlsym(RTLD_DEFAULT, symbol_name); + if (!symbol) { + //fprintf(stderr, ">>>> ERROR: %s\n", dlerror()); + } + // returns NULL on error + + free(symbol_name); + return symbol; +} +#endif + +#ifdef CTEST_SEGFAULT +#include +static void sighandler(int signum) +{ + char msg[128]; + sprintf(msg, "[SIGNAL %d: %s]", signum, sys_siglist[signum]); + color_print(ANSI_BRED, msg); + fflush(stdout); + + /* "Unregister" the signal handler and send the signal back to the process + * so it can terminate as expected */ + signal(signum, SIG_DFL); + kill(getpid(), signum); +} +#endif + +int ctest_main(int argc, char *argv[]) +{ + static int total = 0; + static int num_ok = 0; + static int num_fail = 0; + static int num_skip = 0; + static int index = 1; + static filter_func filter = suite_all; + +#ifdef CTEST_SEGFAULT + signal(SIGSEGV, sighandler); +#endif + + if (argc == 2) { + suite_name = argv[1]; + filter = suite_filter; + } + + if (argc == 3) { + suite_name = argv[1]; + test_name = argv[2]; + filter = suite_test_filter; + } + + color_output = isatty(1); + uint64_t t1 = getCurrentTime(); + + struct ctest* ctest_begin = &__TNAME(suite, test); + struct ctest* ctest_end = &__TNAME(suite, test); + // find begin and end of section by comparing magics + while (1) { + struct ctest* t = ctest_begin-1; + if (t->magic != __CTEST_MAGIC) break; + ctest_begin--; + } + while (1) { + struct ctest* t = ctest_end+1; + if (t->magic != __CTEST_MAGIC) break; + ctest_end++; + } + ctest_end++; // end after last one + + static struct ctest* test; + for (test = ctest_begin; test != ctest_end; test++) { + if (test == &__ctest_suite_test) continue; + if (filter(test)) total++; + } + + for (test = ctest_begin; test != ctest_end; test++) { + if (test == &__ctest_suite_test) continue; + if (filter(test)) { + ctest_errorbuffer[0] = 0; + ctest_errorsize = MSG_SIZE-1; + ctest_errormsg = ctest_errorbuffer; + printf("TEST %d/%d %s:%s ", index, total, test->ssname, test->ttname); + fflush(stdout); + if (test->skip) { + color_print(ANSI_BYELLOW, "[SKIPPED]"); + num_skip++; + } else { + int result = setjmp(ctest_err); + if (result == 0) { +#ifdef __APPLE__ + if (!test->setup) { + test->setup = find_symbol(test, "setup"); + } + if (!test->teardown) { + test->teardown = find_symbol(test, "teardown"); + } +#endif + + if (test->setup) test->setup(test->data); + if (test->data) + test->run(test->data); + else + test->run(); + if (test->teardown) test->teardown(test->data); + // if we got here it's ok +#ifdef COLOR_OK + color_print(ANSI_BGREEN, "[OK]"); +#else + printf("[OK]\n"); +#endif + num_ok++; + } else { + color_print(ANSI_BRED, "[FAIL]"); + num_fail++; + } + if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer); + } + index++; + } + } + uint64_t t2 = getCurrentTime(); + + const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN; + char results[80]; + sprintf(results, "RESULTS: %d tests (%d ok, %d failed, %d skipped) ran in %"PRIu64" ms", total, num_ok, num_fail, num_skip, (t2 - t1)/1000); + color_print(color, results); + return num_fail; +} + +#endif + +#endif diff --git a/test/test_add.c b/test/test_add.c new file mode 100644 index 0000000..50b55ae --- /dev/null +++ b/test/test_add.c @@ -0,0 +1,38 @@ +#include "vml_test.h" +#include +#include +#include + +static char* funcname[4]={"vsAdd", "vdAdd", "vcAdd","vzAdd"}; +static double flop_per_elem[4]={1.0, 1.0, 2.0, 2.0}; + +static ab_y_func_t ref_vadd[] = { + (ab_y_func_t)OpenVML_FUNCNAME_REF(vsAdd), + (ab_y_func_t)OpenVML_FUNCNAME_REF(vdAdd), + (ab_y_func_t)OpenVML_FUNCNAME_REF(vcAdd), + (ab_y_func_t)OpenVML_FUNCNAME_REF(vzAdd), +}; + +static ab_y_func_t test_vadd[] = { + (ab_y_func_t)OpenVML_FUNCNAME(vsAdd), + (ab_y_func_t)OpenVML_FUNCNAME(vdAdd), + (ab_y_func_t)OpenVML_FUNCNAME(vcAdd), + (ab_y_func_t)OpenVML_FUNCNAME(vzAdd), +}; + + +CTEST2(check_result_s, add){ + run_test_ab_y(data->parameter, funcname, test_vadd, ref_vadd, flop_per_elem); +} + +CTEST2(check_result_d, add){ + run_test_ab_y(data->parameter, funcname, test_vadd, ref_vadd, flop_per_elem); +} + +CTEST2(check_result_c, add){ + run_test_ab_y(data->parameter, funcname, test_vadd, ref_vadd, flop_per_elem); +} + +CTEST2(check_result_z, add){ + run_test_ab_y(data->parameter, funcname, test_vadd, ref_vadd, flop_per_elem); +} diff --git a/test/timer.c b/test/timer.c new file mode 100644 index 0000000..97b0bce --- /dev/null +++ b/test/timer.c @@ -0,0 +1,104 @@ +/* + * Author: David Robert Nadeau + * Site: http://NadeauSoftware.com/ + * License: Creative Commons Attribution 3.0 Unported License + * http://creativecommons.org/licenses/by/3.0/deed.en_US + */ +#if defined(_WIN32) +#include + +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) +#include /* POSIX flags */ +#include /* clock_gettime(), time() */ +#include /* gethrtime(), gettimeofday() */ + +#if defined(__MACH__) && defined(__APPLE__) +#include +#include +#endif + +#else +#error "Unable to define getRealTime( ) for an unknown OS." +#endif + + + + + +/** + * Returns the real time, in seconds, or -1.0 if an error occurred. + * + * Time is measured since an arbitrary and OS-dependent start time. + * The returned real time is only useful for computing an elapsed time + * between two calls to this function. + */ +double getRealTime( ) +{ +#if defined(_WIN32) + FILETIME tm; + ULONGLONG t; +#if defined(NTDDI_WIN8) && NTDDI_VERSION >= NTDDI_WIN8 + /* Windows 8, Windows Server 2012 and later. ---------------- */ + GetSystemTimePreciseAsFileTime( &tm ); +#else + /* Windows 2000 and later. ---------------------------------- */ + GetSystemTimeAsFileTime( &tm ); +#endif + t = ((ULONGLONG)tm.dwHighDateTime << 32) | (ULONGLONG)tm.dwLowDateTime; + return (double)t / 10000000.0; + +#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) + /* HP-UX, Solaris. ------------------------------------------ */ + return (double)gethrtime( ) / 1000000000.0; + +#elif defined(__MACH__) && defined(__APPLE__) + /* OSX. ----------------------------------------------------- */ + static double timeConvert = 0.0; + if ( timeConvert == 0.0 ) + { + mach_timebase_info_data_t timeBase; + (void)mach_timebase_info( &timeBase ); + timeConvert = (double)timeBase.numer / + (double)timeBase.denom / + 1000000000.0; + } + return (double)mach_absolute_time( ) * timeConvert; + +#elif defined(_POSIX_VERSION) + /* POSIX. --------------------------------------------------- */ +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) + { + struct timespec ts; +#if defined(CLOCK_MONOTONIC_PRECISE) + /* BSD. --------------------------------------------- */ + const clockid_t id = CLOCK_MONOTONIC_PRECISE; +#elif defined(CLOCK_MONOTONIC_RAW) + /* Linux. ------------------------------------------- */ + const clockid_t id = CLOCK_MONOTONIC_RAW; +#elif defined(CLOCK_HIGHRES) + /* Solaris. ----------------------------------------- */ + const clockid_t id = CLOCK_HIGHRES; +#elif defined(CLOCK_MONOTONIC) + /* AIX, BSD, Linux, POSIX, Solaris. ----------------- */ + const clockid_t id = CLOCK_MONOTONIC; +#elif defined(CLOCK_REALTIME) + /* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */ + const clockid_t id = CLOCK_REALTIME; +#else + const clockid_t id = (clockid_t)-1;/* Unknown. */ +#endif /* CLOCK_* */ + if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) + return (double)ts.tv_sec + + (double)ts.tv_nsec / 1000000000.0; + /* Fall thru. */ + } +#endif /* _POSIX_TIMERS */ + + /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */ + struct timeval tm; + gettimeofday( &tm, NULL ); + return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0; +#else + return -1.0;/* Failed. */ +#endif +} diff --git a/test/vml_test.c b/test/vml_test.c new file mode 100644 index 0000000..688b812 --- /dev/null +++ b/test/vml_test.c @@ -0,0 +1,48 @@ +#include +#define CTEST_MAIN +#include "vml_test.h" + +CTEST_SETUP(check_result_s) { + init_test_parameter(&(data->parameter), 0, 0); +} + +CTEST_SETUP(check_result_d) { + init_test_parameter(&(data->parameter), 0, 1); +} + +CTEST_SETUP(check_result_c) { + init_test_parameter(&(data->parameter), 1, 0); +} + +CTEST_SETUP(check_result_z) { + init_test_parameter(&(data->parameter), 1, 1); +} + +CTEST_TEARDOWN(check_result_s) { + free_test_parameter(&(data->parameter)); +} + +CTEST_TEARDOWN(check_result_d) { + free_test_parameter(&(data->parameter)); +} + +CTEST_TEARDOWN(check_result_c) { + free_test_parameter(&(data->parameter)); +} + +CTEST_TEARDOWN(check_result_z) { + free_test_parameter(&(data->parameter)); +} + + +int main(int argc, char ** argv){ + + int tmp_argc=1; + char ** tmp_argv=NULL; + + init_input_arg(argc, argv); + + ctest_main(argc, argv); + + return 0; +} diff --git a/test/vml_test.h b/test/vml_test.h new file mode 100644 index 0000000..591878f --- /dev/null +++ b/test/vml_test.h @@ -0,0 +1,76 @@ +#ifndef _VML_TEST_H_ +#define _VML_TEST_H_ + +#include +#include + +#include "ctest.h" + +#define STR_PASS "PASS" +#define STR_WARN "WARN" +#define STR_ERR "ERROR" + +typedef struct { + double fail; + double warn; +} eps_t; + +typedef struct { + void * a; + void * b; + void * y; + void * ref_a; + void * ref_b; + void * ref_y; + + eps_t * eps; + VML_INT start; + VML_INT end; + VML_INT step; + + int fp_type; //0 for real float, 1 for real double, + //2 for complex float, 3 for complex double + int element_size; //4 for float, 8 for double + int compose_size; //1 for real, 2 for complex + + void * flushcache; +} perf_arg_t; + + +typedef struct{ + VML_INT start; + VML_INT end; + VML_INT step; +} input_arg_t; + +CTEST_DATA(check_result_s) { + perf_arg_t * parameter; +}; + +CTEST_DATA(check_result_d) { + perf_arg_t * parameter; +}; + +CTEST_DATA(check_result_c) { + perf_arg_t * parameter; +}; + +CTEST_DATA(check_result_z) { + perf_arg_t * parameter; +}; + +void * vml_test_memory_alloc(size_t size); +void vml_test_memory_free(void * ptr); +input_arg_t * get_input_arg(); + +void init_test_parameter(perf_arg_t ** p, int iscomplex, int isdouble); +void free_test_parameter(perf_arg_t ** p); + +int check_result(double ref, double test, eps_t* thres); + +void init_rand(VML_INT n, void * a, int iscomplex, int isdouble); + +typedef void (*ab_y_func_t)(VML_INT, const void *, const void *, void *); +void run_test_ab_y(perf_arg_t * para, char* funcname[], ab_y_func_t*test_func, ab_y_func_t* ref_func, + double * flop_per_elem); +#endif diff --git a/test/vml_util.c b/test/vml_util.c new file mode 100644 index 0000000..6bc9951 --- /dev/null +++ b/test/vml_util.c @@ -0,0 +1,295 @@ +#include "vml_test.h" +#include +#include +#include + +#define FP_TYPE_NUM 4 +//32MB +#define FLUSHCACHE_SIZE 32*1024*1024 + +static eps_t threshold[FP_TYPE_NUM]={{ 1e-04, 1e-05 }, + { 1e-13, 1e-14 }, // for d + { 1e-04, 1e-05 }, // for c + { 1e-13, 1e-14 }}; + +double getRealTime(); + +#define VML_TEST_LOG printf + +void * vml_test_memory_alloc(size_t size) +{ + void * ptr=NULL; + ptr=malloc(size); + if(ptr==NULL){ + CTEST_ERR("memory alloc failed!\n"); + exit(0); + } + return ptr; +} + +void vml_test_memory_free(void * ptr) +{ + free(ptr); +} + +void print_help() +{ + printf("-d \n"); + printf(" input data size.\n"); + printf("-s test_suit\n"); + printf(" testsuit name\n"); +} + +static input_arg_t input_args; + +input_arg_t * get_input_arg() +{ + return &input_args; +} + +void init_input_arg(int argc, char *argv[]) +{ + input_args.start=100; + input_args.end=200; + input_args.step=10; +} + +void init_test_parameter(perf_arg_t ** p, int iscomplex, int isdouble) +{ + perf_arg_t * parameter=NULL; + + int float_type_id=0; + + parameter=(perf_arg_t*)vml_test_memory_alloc(sizeof(perf_arg_t)); + + memset(parameter,0,sizeof(perf_arg_t)); + + *p = parameter; + + float_type_id=(iscomplex<<1)|(isdouble); + + if(float_type_id<0 || float_type_id>=FP_TYPE_NUM) { + CTEST_ERR("Init data error!\n"); + } + + parameter->eps=&threshold[float_type_id]; + + parameter->fp_type=float_type_id; + parameter->element_size=(isdouble==0)?sizeof(float):sizeof(double); + parameter->compose_size=(iscomplex==0)?1:2; + + parameter->start=input_args.start; + parameter->end=input_args.end; + parameter->step=input_args.step; + + parameter->a=vml_test_memory_alloc(parameter->compose_size * parameter->element_size * + parameter->end); + + parameter->y=vml_test_memory_alloc(parameter->compose_size * parameter->element_size * + parameter->end); + + parameter->b=vml_test_memory_alloc(parameter->compose_size * parameter->element_size * + parameter->end); + + parameter->ref_a=vml_test_memory_alloc(parameter->compose_size * parameter->element_size * + parameter->end); + + parameter->ref_y=vml_test_memory_alloc(parameter->compose_size * parameter->element_size * + parameter->end); + + parameter->ref_b=vml_test_memory_alloc(parameter->compose_size * parameter->element_size * + parameter->end); + + + parameter->flushcache=vml_test_memory_alloc(FLUSHCACHE_SIZE); +} + +void free_test_parameter(perf_arg_t ** p) +{ + perf_arg_t * parameter=*p; + if(*p == NULL) return; + + if(parameter->a != NULL){ + vml_test_memory_free(parameter->a); + parameter->a=NULL; + } + + if(parameter->y != NULL){ + vml_test_memory_free(parameter->y); + parameter->y=NULL; + } + + if(parameter->y != NULL){ + vml_test_memory_free(parameter->y); + parameter->y=NULL; + } + + if(parameter->ref_a != NULL){ + vml_test_memory_free(parameter->ref_a); + parameter->ref_a=NULL; + } + + if(parameter->ref_y != NULL){ + vml_test_memory_free(parameter->ref_y); + parameter->ref_y=NULL; + } + + if(parameter->ref_b != NULL){ + vml_test_memory_free(parameter->ref_b); + parameter->ref_b=NULL; + } + + if(parameter->flushcache != NULL){ + vml_test_memory_free(parameter->flushcache); + parameter->flushcache=NULL; + } + vml_test_memory_free(parameter); +} + + +int check_result(double ref, double test, eps_t* thres) +{ + double tmp; + + tmp=abs(ref-test); + + if(tmp>=thres->warn && tmpfail) { + return 1; //warning + }else if (tmp >= thres->fail) { + return 2; //error + }else{ + return 0; //pass + } +} + +int check_vector(VML_INT n, void * ref, void * test, eps_t * thres, int iscomplex, int isdouble) +{ + VML_INT i; + VML_INT length; + int result=0, max_result=0; + length=(iscomplex==0)? n : 2*n; + + if(isdouble==0){ + //float + float * ref_f=(float*)ref; + float * test_f=(float*)test; + + for(i=0; imax_result) max_result=result; + } + }else{ + //double + double* ref_d=(double*)ref; + double * test_d=(double*)test; + + for(i=0; imax_result) max_result=result; + } + } + return max_result; +} + +void init_rand_float(VML_INT n, float * a) +{ + VML_INT i=0; + float e=5.0; + for(i=0; istart; + VML_INT end=para->end; + VML_INT step=para->step; + + double mflops=0.0; + double time=0.0, start_time, end_time; + + int iscomplex = (para->fp_type & 0x2) >> 1; + int isdouble = (para->fp_type & 0x1); + int result=0; + char * result_str; + int failed_count=0; + + VML_INT i; + + VML_TEST_LOG("\n"); + VML_TEST_LOG("Func\tN\tMFlops\t\tTime(s)\t\tResult\n"); + + init_rand(end, para->a, iscomplex, isdouble); + init_rand(end, para->b, iscomplex, isdouble); + + memcpy(para->ref_a, para->a, end * para->element_size * para->compose_size); + memcpy(para->ref_b, para->b, end * para->element_size * para->compose_size); + + for(i=start; i<=end; i+=step) { + + mflops=flops_per_elem[para->fp_type] * i; + + //need to clean cache + //flush_cache(para->flushcache); + + start_time=getRealTime(); + test_func[para->fp_type](i, para->a, para->b, para->y); + end_time=getRealTime(); + time=end_time-start_time; + + mflops=mflops/(double)(1000000)/time; + + ref_func[para->fp_type](i, para->ref_a, para->ref_b, para->ref_y); + + //check + result=check_vector(i, para->ref_y, para->y, para->eps, iscomplex, isdouble); + + if(result==0){ + result_str=STR_PASS; + }else if(result==1){ + result_str=STR_WARN; + }else{ + result_str=STR_ERR; + failed_count++; + } + + VML_TEST_LOG("%s\t%d\t%lf\t%lf\t%s\n", funcname[para->fp_type], i, mflops, time, result_str); + + } + + if(failed_count>0) CTEST_ERR("Result failed!\n"); +}