diff options
Diffstat (limited to 'ccan/likely')
| l--------- | ccan/likely/LICENSE | 1 | ||||
| -rw-r--r-- | ccan/likely/_info | 57 | ||||
| -rw-r--r-- | ccan/likely/likely.c | 136 | ||||
| -rw-r--r-- | ccan/likely/likely.h | 111 | ||||
| -rw-r--r-- | ccan/likely/test/run-debug.c | 105 | ||||
| -rw-r--r-- | ccan/likely/test/run.c | 30 |
6 files changed, 440 insertions, 0 deletions
diff --git a/ccan/likely/LICENSE b/ccan/likely/LICENSE new file mode 120000 index 0000000..b7951da --- /dev/null +++ b/ccan/likely/LICENSE @@ -0,0 +1 @@ +../../licenses/CC0
\ No newline at end of file diff --git a/ccan/likely/_info b/ccan/likely/_info new file mode 100644 index 0000000..095ed2f --- /dev/null +++ b/ccan/likely/_info @@ -0,0 +1,57 @@ +#include "config.h" +#include <string.h> +#include <stdio.h> + +/** + * likely - macros for annotating likely/unlikely branches in the code + * + * Inspired by Andi Kleen's macros for the Linux Kernel, these macros + * help you annotate rare paths in your code for the convenience of the + * compiler and the reader. + * + * With CCAN_LIKELY_DEBUG defined, it provides statistics for each + * likely()/unlikely() call (but note that this requires LGPL dependencies). + * + * License: CC0 (Public domain) + * Author: Rusty Russell <rusty@rustcorp.com.au> + * + * Example: + * #include <ccan/likely/likely.h> + * #include <stdio.h> + * + * int main(int argc, char *argv[]) + * { + * // This example is silly: the compiler knows exit() is unlikely. + * if (unlikely(argc == 1)) { + * fprintf(stderr, "Usage: %s <args>...\n", argv[0]); + * return 1; + * } + * for (argc++; argv[argc]; argc++) + * printf("%s\n", argv[argc]); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { +#ifdef CCAN_LIKELY_DEBUG + printf("ccan/str\n"); + printf("ccan/htable\n"); + printf("ccan/hash\n"); +#endif + return 0; + } + if (strcmp(argv[1], "testdepends") == 0) { +#ifndef CCAN_LIKELY_DEBUG + printf("ccan/str\n"); + printf("ccan/htable\n"); + printf("ccan/hash\n"); +#endif + return 0; + } + return 1; +} diff --git a/ccan/likely/likely.c b/ccan/likely/likely.c new file mode 100644 index 0000000..83e8d6f --- /dev/null +++ b/ccan/likely/likely.c @@ -0,0 +1,136 @@ +/* CC0 (Public domain) - see LICENSE file for details. */ +#ifdef CCAN_LIKELY_DEBUG +#include <ccan/likely/likely.h> +#include <ccan/hash/hash.h> +#include <ccan/htable/htable_type.h> +#include <stdlib.h> +#include <stdio.h> +struct trace { + const char *condstr; + const char *file; + unsigned int line; + bool expect; + unsigned long count, right; +}; + +static size_t hash_trace(const struct trace *trace) +{ + return hash(trace->condstr, strlen(trace->condstr), + hash(trace->file, strlen(trace->file), + trace->line + trace->expect)); +} + +static bool trace_eq(const struct trace *t1, const struct trace *t2) +{ + return t1->condstr == t2->condstr + && t1->file == t2->file + && t1->line == t2->line + && t1->expect == t2->expect; +} + +/* struct thash */ +HTABLE_DEFINE_TYPE(struct trace, (const struct trace *), hash_trace, trace_eq, + thash); + +static struct thash htable += { HTABLE_INITIALIZER(htable.raw, thash_hash, NULL) }; + +static void init_trace(struct trace *trace, + const char *condstr, const char *file, unsigned int line, + bool expect) +{ + trace->condstr = condstr; + trace->file = file; + trace->line = line; + trace->expect = expect; + trace->count = trace->right = 0; +} + +static struct trace *add_trace(const struct trace *t) +{ + struct trace *trace = malloc(sizeof(*trace)); + *trace = *t; + thash_add(&htable, trace); + return trace; +} + +long _likely_trace(bool cond, bool expect, + const char *condstr, + const char *file, unsigned int line) +{ + struct trace *p, trace; + + init_trace(&trace, condstr, file, line, expect); + p = thash_get(&htable, &trace); + if (!p) + p = add_trace(&trace); + + p->count++; + if (cond == expect) + p->right++; + + return cond; +} + +static double right_ratio(const struct trace *t) +{ + return (double)t->right / t->count; +} + +char *likely_stats(unsigned int min_hits, unsigned int percent) +{ + struct trace *worst; + double worst_ratio; + struct thash_iter i; + char *ret; + struct trace *t; + + worst = NULL; + worst_ratio = 2; + + /* This is O(n), but it's not likely called that often. */ + for (t = thash_first(&htable, &i); t; t = thash_next(&htable, &i)) { + if (t->count >= min_hits) { + if (right_ratio(t) < worst_ratio) { + worst = t; + worst_ratio = right_ratio(t); + } + } + } + + if (worst_ratio * 100 > percent) + return NULL; + + ret = malloc(strlen(worst->condstr) + + strlen(worst->file) + + sizeof(long int) * 8 + + sizeof("%s:%u:%slikely(%s) correct %u%% (%lu/%lu)")); + sprintf(ret, "%s:%u:%slikely(%s) correct %u%% (%lu/%lu)", + worst->file, worst->line, + worst->expect ? "" : "un", worst->condstr, + (unsigned)(worst_ratio * 100), + worst->right, worst->count); + + thash_del(&htable, worst); + free(worst); + + return ret; +} + +void likely_stats_reset(void) +{ + struct thash_iter i; + struct trace *t; + + /* This is a bit better than O(n^2), but we have to loop since + * first/next during delete is unreliable. */ + while ((t = thash_first(&htable, &i)) != NULL) { + for (; t; t = thash_next(&htable, &i)) { + thash_del(&htable, t); + free(t); + } + } + + thash_clear(&htable); +} +#endif /*CCAN_LIKELY_DEBUG*/ diff --git a/ccan/likely/likely.h b/ccan/likely/likely.h new file mode 100644 index 0000000..a8f003d --- /dev/null +++ b/ccan/likely/likely.h @@ -0,0 +1,111 @@ +/* CC0 (Public domain) - see LICENSE file for details */ +#ifndef CCAN_LIKELY_H +#define CCAN_LIKELY_H +#include "config.h" +#include <stdbool.h> + +#ifndef CCAN_LIKELY_DEBUG +#if HAVE_BUILTIN_EXPECT +/** + * likely - indicate that a condition is likely to be true. + * @cond: the condition + * + * This uses a compiler extension where available to indicate a likely + * code path and optimize appropriately; it's also useful for readers + * to quickly identify exceptional paths through functions. The + * threshold for "likely" is usually considered to be between 90 and + * 99%; marginal cases should not be marked either way. + * + * See Also: + * unlikely(), likely_stats() + * + * Example: + * // Returns false if we overflow. + * static inline bool inc_int(unsigned int *val) + * { + * (*val)++; + * if (likely(*val)) + * return true; + * return false; + * } + */ +#define likely(cond) __builtin_expect(!!(cond), 1) + +/** + * unlikely - indicate that a condition is unlikely to be true. + * @cond: the condition + * + * This uses a compiler extension where available to indicate an unlikely + * code path and optimize appropriately; see likely() above. + * + * See Also: + * likely(), likely_stats(), COLD (compiler.h) + * + * Example: + * // Prints a warning if we overflow. + * static inline void inc_int(unsigned int *val) + * { + * (*val)++; + * if (unlikely(*val == 0)) + * fprintf(stderr, "Overflow!"); + * } + */ +#define unlikely(cond) __builtin_expect(!!(cond), 0) +#else +#define likely(cond) (!!(cond)) +#define unlikely(cond) (!!(cond)) +#endif +#else /* CCAN_LIKELY_DEBUG versions */ +#include <ccan/str/str.h> + +#define likely(cond) \ + (_likely_trace(!!(cond), 1, stringify(cond), __FILE__, __LINE__)) +#define unlikely(cond) \ + (_likely_trace(!!(cond), 0, stringify(cond), __FILE__, __LINE__)) + +long _likely_trace(bool cond, bool expect, + const char *condstr, + const char *file, unsigned int line); +/** + * likely_stats - return description of abused likely()/unlikely() + * @min_hits: minimum number of hits + * @percent: maximum percentage correct + * + * When CCAN_LIKELY_DEBUG is defined, likely() and unlikely() trace their + * results: this causes a significant slowdown, but allows analysis of + * whether the branches are labelled correctly. + * + * This function returns a malloc'ed description of the least-correct + * usage of likely() or unlikely(). It ignores places which have been + * called less than @min_hits times, and those which were predicted + * correctly more than @percent of the time. It returns NULL when + * nothing meets those criteria. + * + * Note that this call is destructive; the returned offender is + * removed from the trace so that the next call to likely_stats() will + * return the next-worst likely()/unlikely() usage. + * + * Example: + * // Print every place hit more than twice which was wrong > 5%. + * static void report_stats(void) + * { + * #ifdef CCAN_LIKELY_DEBUG + * const char *bad; + * + * while ((bad = likely_stats(2, 95)) != NULL) { + * printf("Suspicious likely: %s", bad); + * free(bad); + * } + * #endif + * } + */ +char *likely_stats(unsigned int min_hits, unsigned int percent); + +/** + * likely_stats_reset - free up memory of likely()/unlikely() branches. + * + * This can also plug memory leaks. + */ +void likely_stats_reset(void); +#endif /* CCAN_LIKELY_DEBUG */ +#endif /* CCAN_LIKELY_H */ diff --git a/ccan/likely/test/run-debug.c b/ccan/likely/test/run-debug.c new file mode 100644 index 0000000..72015ee --- /dev/null +++ b/ccan/likely/test/run-debug.c @@ -0,0 +1,105 @@ +#define CCAN_LIKELY_DEBUG 1 +#include <ccan/likely/likely.c> +#include <ccan/likely/likely.h> +#include <ccan/tap/tap.h> +#include <stdlib.h> + +static bool one_seems_likely(unsigned int val) +{ + if (likely(val == 1)) + return true; + return false; +} + +static bool one_seems_unlikely(unsigned int val) +{ + if (unlikely(val == 1)) + return true; + return false; +} + +static bool likely_one_unlikely_two(unsigned int val1, unsigned int val2) +{ + /* Same line, check we don't get confused! */ + if (likely(val1 == 1) && unlikely(val2 == 2)) + return true; + return false; +} + +int main(void) +{ + char *bad; + + plan_tests(14); + + /* Correct guesses. */ + one_seems_likely(1); + ok1(likely_stats(0, 90) == NULL); + one_seems_unlikely(2); + ok1(likely_stats(0, 90) == NULL); + + /* Incorrect guesses. */ + one_seems_likely(0); + one_seems_likely(2); + /* Hasn't been hit 4 times, so this fails */ + ok1(!likely_stats(4, 90)); + bad = likely_stats(3, 90); + ok(strends(bad, "run-debug.c:9:likely(val == 1) correct 33% (1/3)"), + "likely_stats returned %s", bad); + free(bad); + + /* Nothing else above 90% */ + ok1(!likely_stats(0, 90)); + + /* This should get everything. */ + bad = likely_stats(0, 100); + ok(strends(bad, "run-debug.c:16:unlikely(val == 1) correct 100% (1/1)"), + "likely_stats returned %s", bad); + free(bad); + + /* Nothing left (table is actually cleared) */ + ok1(!likely_stats(0, 100)); + + /* Make sure unlikely works */ + one_seems_unlikely(0); + one_seems_unlikely(2); + one_seems_unlikely(1); + + bad = likely_stats(0, 90); + ok(strends(bad, "run-debug.c:16:unlikely(val == 1) correct 66% (2/3)"), + "likely_stats returned %s", bad); + free(bad); + ok1(!likely_stats(0, 100)); + + likely_one_unlikely_two(1, 1); + likely_one_unlikely_two(1, 1); + likely_one_unlikely_two(1, 1); + ok1(!likely_stats(0, 90)); + likely_one_unlikely_two(1, 2); + + bad = likely_stats(0, 90); + ok(strends(bad, "run-debug.c:24:unlikely(val2 == 2) correct 75% (3/4)"), + "likely_stats returned %s", bad); + free(bad); + bad = likely_stats(0, 100); + ok(strends(bad, "run-debug.c:24:likely(val1 == 1) correct 100% (4/4)"), + "likely_stats returned %s", bad); + free(bad); + + ok1(!likely_stats(0, 100)); + + /* Check that reset works! */ + one_seems_unlikely(0); + one_seems_unlikely(2); + one_seems_unlikely(1); + likely_stats_reset(); + + ok1(!likely_stats(0, 100)); + + exit(exit_status()); +} + +/* Fools ccanlint: it doesn't think we use str, htable or hash. */ +#include <ccan/hash/hash.h> +#include <ccan/htable/htable.h> +#include <ccan/str/str.h> diff --git a/ccan/likely/test/run.c b/ccan/likely/test/run.c new file mode 100644 index 0000000..28ea798 --- /dev/null +++ b/ccan/likely/test/run.c @@ -0,0 +1,30 @@ +#include <ccan/likely/likely.c> +#include <ccan/likely/likely.h> +#include <ccan/tap/tap.h> +#include <stdlib.h> + +static bool one_seems_likely(unsigned int val) +{ + if (likely(val == 1)) + return true; + return false; +} + +static bool one_seems_unlikely(unsigned int val) +{ + if (unlikely(val == 1)) + return true; + return false; +} + +int main(void) +{ + plan_tests(4); + + /* Without debug, we can only check that it doesn't effect functions. */ + ok1(one_seems_likely(1)); + ok1(!one_seems_likely(2)); + ok1(one_seems_unlikely(1)); + ok1(!one_seems_unlikely(2)); + exit(exit_status()); +} |
