summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac13
-rw-r--r--libdimension/Makefile.am6
-rw-r--r--libdimension/compiler.h44
-rw-r--r--libdimension/dimension-impl.h4
-rw-r--r--libdimension/dimension.h15
-rw-r--r--libdimension/dimension/error.h13
-rw-r--r--libdimension/png.c1
-rw-r--r--libdimension/profile.c106
-rw-r--r--libdimension/profile.h44
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 */