Commit 9f8c65b2 authored by Rusty Russell's avatar Rusty Russell

likely: new module

parent 6c579abb
#include <string.h>
#include <stdio.h>
#include "config.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.
*
* Licence: LGPL (2 or any later version)
*
* 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) {
printf("ccan/str\n");
printf("ccan/hashtable\n");
printf("ccan/hash\n");
return 0;
}
return 1;
}
#ifdef DEBUG
#include <ccan/likely/likely.h>
#include <ccan/hash/hash.h>
#include <ccan/hashtable/hashtable.h>
#include <stdlib.h>
#include <stdio.h>
static struct hashtable *htable;
struct trace {
const char *condstr;
const char *file;
unsigned int line;
bool expect;
unsigned long count, right;
};
/* We hash the pointers, which will be identical for same call. */
static unsigned long hash_trace(const struct trace *trace)
{
return hash_pointer(trace->condstr,
hash_pointer(trace->file,
trace->line + trace->expect));
}
static bool hash_cmp(const void *htelem, void *cmpdata)
{
const struct trace *t1 = htelem, *t2 = cmpdata;
return t1->condstr == t2->condstr
&& t1->file == t2->file
&& t1->line == t2->line
&& t1->expect == t2->expect;
}
static unsigned long rehash(const void *elem, void *priv)
{
return hash_trace(elem);
}
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 char *condstr,
const char *file, unsigned int line, bool expect)
{
struct trace *trace = malloc(sizeof(*trace));
init_trace(trace, condstr, file, line, expect);
hashtable_add(htable, hash_trace(trace), trace);
return trace;
}
long _likely_trace(bool cond, bool expect,
const char *condstr,
const char *file, unsigned int line)
{
struct trace *p, trace;
if (!htable)
htable = hashtable_new(rehash, NULL);
init_trace(&trace, condstr, file, line, expect);
p = hashtable_find(htable, hash_trace(&trace), hash_cmp, &trace);
if (!p)
p = add_trace(condstr, file, line, expect);
p->count++;
if (cond == expect)
p->right++;
return cond;
}
struct get_stats_info {
struct trace *worst;
unsigned int min_hits;
double worst_ratio;
};
static double right_ratio(const struct trace *t)
{
return (double)t->right / t->count;
}
static bool get_stats(void *elem, void *vinfo)
{
struct trace *trace = elem;
struct get_stats_info *info = vinfo;
if (trace->count < info->min_hits)
return false;
if (right_ratio(trace) < info->worst_ratio) {
info->worst = trace;
info->worst_ratio = right_ratio(trace);
}
return false;
}
const char *likely_stats(unsigned int min_hits, unsigned int percent)
{
struct get_stats_info info;
char *ret;
if (!htable)
return NULL;
info.min_hits = min_hits;
info.worst = NULL;
info.worst_ratio = 2;
/* This is O(n), but it's not likely called that often. */
hashtable_traverse(htable, get_stats, &info);
if (info.worst_ratio * 100 > percent)
return NULL;
ret = malloc(strlen(info.worst->condstr) +
strlen(info.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)",
info.worst->file, info.worst->line,
info.worst->expect ? "" : "un", info.worst->condstr,
(unsigned)(info.worst_ratio * 100),
info.worst->right, info.worst->count);
hashtable_del(htable, hash_trace(info.worst), info.worst);
free(info.worst);
return ret;
}
#endif /*DEBUG*/
#ifndef CCAN_LIKELY_H
#define CCAN_LIKELY_H
#include "config.h"
#include <ccan/str/str.h>
#include <stdbool.h>
#ifndef 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(), unlikely_func, 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(), unlikely_func, likely_stats()
*
* 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 /* DEBUG versions */
#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);
#endif
#if HAVE_ATTRIBUTE_COLD
/**
* unlikely_func - indicate that a function is unlikely to be called.
*
* This uses a compiler extension where available to indicate an unlikely
* code path and optimize appropriately; see unlikely() above.
*
* It is usually used on logging or error routines.
*
* See Also:
* unlikely()
*
* Example:
* void unlikely_func die_moaning(const char *reason)
* {
* fprintf(stderr, "Dying: %s\n", reason);
* exit(1);
* }
*/
#define unlikely_func __attribute__((cold))
#else
#define unlikely_func
#endif
#ifdef DEBUG
/**
* likely_stats - return description of abused likely()/unlikely()
* @min_hits: minimum number of hits
* @percent: maximum percentage correct
*
* When DEBUG is defined, likely() and unlikely() trace their results: this
* causes a significant slowdown, but allows analysis of whether the stats
* are correct (unlikely_func can't traced).
*
* 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 DEBUG
* const char *bad;
*
* while ((bad = likely_stats(2, 95)) != NULL) {
* printf("Suspicious likely: %s", bad);
* free(bad);
* }
* #endif
* }
*/
const char *likely_stats(unsigned int min_hits, unsigned int percent);
#endif /* DEBUG */
#endif /* CCAN_LIKELY_H */
#define 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(int argc, char *argv[])
{
const char *bad;
plan_tests(13);
/* 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);
/* 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);
/* 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);
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);
bad = likely_stats(0, 100);
ok(strends(bad, "run-debug.c:24:likely(val1 == 1) correct 100% (4/4)"),
"likely_stats returned %s", bad);
ok1(!likely_stats(0, 100));
exit(exit_status());
}
#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 unlikely_func bool calling_is_unlikely(void)
{
return true;
}
int main(int argc, char *argv[])
{
plan_tests(5);
/* 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));
ok1(calling_is_unlikely());
exit(exit_status());
}
/* Simple config.h for gcc. */
#define HAVE_ALIGNOF 1
#define HAVE_ATTRIBUTE_COLD 1
#define HAVE_ATTRIBUTE_PRINTF 1
#define HAVE_BIG_ENDIAN 0
#define HAVE_BUILTIN_CHOOSE_EXPR 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment