diff options
-rw-r--r-- | configure.ac | 13 | ||||
-rw-r--r-- | libdimension/Makefile.am | 6 | ||||
-rw-r--r-- | libdimension/compiler.h | 44 | ||||
-rw-r--r-- | libdimension/dimension-impl.h | 4 | ||||
-rw-r--r-- | libdimension/dimension.h | 15 | ||||
-rw-r--r-- | libdimension/dimension/error.h | 13 | ||||
-rw-r--r-- | libdimension/png.c | 1 | ||||
-rw-r--r-- | libdimension/profile.c | 106 | ||||
-rw-r--r-- | libdimension/profile.h | 44 |
9 files changed, 231 insertions, 15 deletions
diff --git a/configure.ac b/configure.ac index 75a2deb..8b6e363 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,19 @@ AC_ARG_ENABLE([gl], [enable_gl=yes]) AM_CONDITIONAL([GL], [test "$enable_gl" != "no"]) +dnl Built-in profiling support +AC_ARG_ENABLE([profile], + [AS_HELP_STRING([--enable-profile], + [Enable built-in branch profiling support [default=no]])], + [], + [enable_profile=no]) +AM_CONDITIONAL([PROFILE], [test "$enable_profile" = "yes"]) +if test "$enable_profile" = "yes"; then + AC_DEFINE([DMNSN_PROFILE], [1]) +else + AC_DEFINE([DMNSN_PROFILE], [0]) +fi + dnl Timing library for benchmarks PKG_CHECK_MODULES([libsandglass], [libsandglass >= 0.2], [], diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am index 1d5077c..6a8d4bb 100644 --- a/libdimension/Makefile.am +++ b/libdimension/Makefile.am @@ -65,6 +65,7 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ checker.c \ color.c \ color_map.c \ + compiler.h \ cone.c \ cube.c \ csg.c \ @@ -91,6 +92,7 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ plane.c \ platform.c \ platform.h \ + profile.h \ point_light.c \ polynomial.c \ progress.c \ @@ -126,6 +128,10 @@ else libdimension_la_SOURCES += gl-stubs.c endif +if PROFILE +libdimension_la_SOURCES += profile.c +endif + clean-local: clean-docs doc: diff --git a/libdimension/compiler.h b/libdimension/compiler.h new file mode 100644 index 0000000..b1cf60c --- /dev/null +++ b/libdimension/compiler.h @@ -0,0 +1,44 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/** + * @file + * Compiler abstractions. + */ + +#ifndef DIMENSION_IMPL_COMPILER_H +#define DIMENSION_IMPL_COMPILER_H + +#include <stdbool.h> + +#if DMNSN_PROFILE +#define dmnsn_likely(test) \ + dmnsn_expect((test), true, DMNSN_FUNC, __FILE__, __LINE__) +#define dmnsn_unlikely(test) \ + dmnsn_expect((test), false, DMNSN_FUNC, __FILE__, __LINE__) +#elif defined(__GNUC__) +#define dmnsn_likely(test) __builtin_expect(!!(test), true) +#define dmnsn_unlikely(test) __builtin_expect(!!(test), false) +#else +#define dmnsn_likely(test) (test) +#define dmnsn_unlikely(test) (test) +#endif + +#endif /* DIMENSION_IMPL_COMPILER_H */ diff --git a/libdimension/dimension-impl.h b/libdimension/dimension-impl.h index 2746b1c..4712a1f 100644 --- a/libdimension/dimension-impl.h +++ b/libdimension/dimension-impl.h @@ -29,8 +29,10 @@ #define _GNU_SOURCE #include "dimension.h" -#include "progress-impl.h" +#include "compiler.h" +#include "profile.h" #include "platform.h" +#include "progress-impl.h" #include "threads.h" #include "prtree.h" diff --git a/libdimension/dimension.h b/libdimension/dimension.h index 9f7fd67..73cab50 100644 --- a/libdimension/dimension.h +++ b/libdimension/dimension.h @@ -44,6 +44,21 @@ extern "C" { #endif +/* Common macros */ + +/** + * @internal + * @def DMNSN_FUNC + * @brief Expands to the name of the current function + */ +#ifdef __GNUC__ + #define DMNSN_FUNC __PRETTY_FUNCTION__ +#elif __STDC_VERSION__ >= 199901L + #define DMNSN_FUNC __func__ +#else + #define DMNSN_FUNC "<unknown function>" +#endif + /* Common types */ /** diff --git a/libdimension/dimension/error.h b/libdimension/dimension/error.h index fd78115..d785f96 100644 --- a/libdimension/dimension/error.h +++ b/libdimension/dimension/error.h @@ -37,19 +37,6 @@ typedef enum dmnsn_severity { } dmnsn_severity; /** - * @internal - * @def DMNSN_FUNC - * @brief Expands to the name of the current function - */ -#ifdef __GNUC__ - #define DMNSN_FUNC __PRETTY_FUNCTION__ -#elif __STDC_VERSION__ >= 199901L - #define DMNSN_FUNC __func__ -#else - #define DMNSN_FUNC "<unknown function>" -#endif - -/** * Report an error. * @param[in] severity A @ref dmnsn_severity representing the severity of the * error. DMNSN_SEVERITY_HIGH will always terminate the diff --git a/libdimension/png.c b/libdimension/png.c index 63cb71e..5fe51ed 100644 --- a/libdimension/png.c +++ b/libdimension/png.c @@ -24,7 +24,6 @@ */ #include "dimension-impl.h" -#include <pthread.h> #include <png.h> #include <errno.h> #include <setjmp.h> diff --git a/libdimension/profile.c b/libdimension/profile.c new file mode 100644 index 0000000..c88e3ef --- /dev/null +++ b/libdimension/profile.c @@ -0,0 +1,106 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/** + * @file + * Branch profile accounting. + */ + +#include "dimension-impl.h" +#include <pthread.h> +#include <stdio.h> +#include <inttypes.h> + +/** Info for one predicted branch. */ +typedef struct { + char *location; + uint64_t predicted, branches; +} dmnsn_profile_info; + +/** Count of mispredicted branches. */ +static dmnsn_dictionary *dmnsn_profile_data = NULL; +/** Mutex which protects \c dmnsn_profile_data. */ +static pthread_mutex_t dmnsn_profile_mutex = PTHREAD_MUTEX_INITIALIZER; + +bool +dmnsn_expect(bool result, bool expected, const char *func, const char *file, + unsigned int line) +{ + int size = snprintf(NULL, 0, "%s:%s:%u", file, func, line) + 1; + if (size < 1) { + dmnsn_error(DMNSN_SEVERITY_MEDIUM, "sprintf() failed."); + } else { + char key[size]; + if (snprintf(key, size, "%s:%s:%u", file, func, line) > 0) { + if (pthread_mutex_lock(&dmnsn_profile_mutex) != 0) { + dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't lock mutex."); + } + + if (!dmnsn_profile_data) { + dmnsn_profile_data = dmnsn_new_dictionary(sizeof(dmnsn_profile_info)); + } + dmnsn_profile_info *info = dmnsn_dictionary_at(dmnsn_profile_data, key); + if (info) { + ++info->branches; + if (result == expected) { + ++info->predicted; + } + } else { + dmnsn_profile_info info = { + .location = dmnsn_strdup(key), + .predicted = (result == expected) ? 1 : 0, + .branches = 1 + }; + dmnsn_dictionary_insert(dmnsn_profile_data, key, &info); + } + + if (pthread_mutex_unlock(&dmnsn_profile_mutex) != 0) { + dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't lock mutex."); + } + } else { + dmnsn_error(DMNSN_SEVERITY_MEDIUM, "sprintf() failed."); + } + } + + return result; +} + +static void +dmnsn_print_bad_prediction(void *ptr) +{ + dmnsn_profile_info *info = ptr; + double rate = ((double)info->predicted)/info->branches; + if (rate < 0.75 || info->branches < 100000) { + fprintf(stderr, + "Bad branch prediction: %s: %" PRIu64 "/%" PRIu64 "\n", + info->location, info->predicted, info->branches); + } + + dmnsn_free(info->location); +} + +static void __attribute__((destructor)) +dmnsn_print_bad_predictions(void) +{ + if (dmnsn_profile_data) { + dmnsn_dictionary_apply(dmnsn_profile_data, &dmnsn_print_bad_prediction); + dmnsn_delete_dictionary(dmnsn_profile_data); + } +} diff --git a/libdimension/profile.h b/libdimension/profile.h new file mode 100644 index 0000000..e891771 --- /dev/null +++ b/libdimension/profile.h @@ -0,0 +1,44 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/** + * @file + * Built-in branch profiler. + */ + +#ifndef DIMENSION_IMPL_PROFILE_H +#define DIMENSION_IMPL_PROFILE_H + +#include <stdbool.h> + +/** + * Record an test and its expected result. Called by dmnsn_[un]likely(); + * don't call directly. + * @param[in] value The result of the test. + * @param[in] expected The expected result of the test. + * @param[in] func The name of the function the test occurs in. + * @param[in] file The name of the file the test occurs in. + * @param[in] line The line number on which the test occurs. + * @return \p value. + */ +bool dmnsn_expect(bool result, bool expected, + const char *func, const char *file, unsigned int line); + +#endif /* DIMENSION_IMPL_PROFILE_H */ |