Add new *_atomic counter update function
PR gcov-profile/58306 * Makefile.in: New functions (modules) are added. * libgcov-profiler.c (__gcov_interval_profiler_atomic): New function. (__gcov_pow2_profiler_atomic): New function. (__gcov_one_value_profiler_body): New argument is instroduced. (__gcov_one_value_profiler): Call with the new argument. (__gcov_one_value_profiler_atomic): Likewise. (__gcov_indirect_call_profiler_v2): Likewise. (__gcov_time_profiler_atomic): New function. (__gcov_average_profiler_atomic): Likewise. (__gcov_ior_profiler_atomic): Likewise. * libgcov.h: Declare the aforementioned functions. PR gcov-profile/58306 * gcc.dg/tree-prof/val-profiler-threads-1.c: New test. PR gcov-profile/58306 * tree-profile.c (gimple_init_edge_profiler): Create conditionally atomic variants of profile update functions. From-SVN: r239324
This commit is contained in:
parent
22063dbc90
commit
a266236e85
8 changed files with 209 additions and 25 deletions
|
@ -1,3 +1,9 @@
|
|||
2016-08-10 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR gcov-profile/58306
|
||||
* tree-profile.c (gimple_init_edge_profiler): Create conditionally
|
||||
atomic variants of profile update functions.
|
||||
|
||||
2016-08-10 Martin Liska <mliska@suse.cz>
|
||||
|
||||
Cherry picked (and modified) from google-4_7 branch
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2016-08-10 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR gcov-profile/58306
|
||||
* gcc.dg/tree-prof/val-profiler-threads-1.c: New test.
|
||||
|
||||
2016-08-10 Martin Liska <mliska@suse.cz>
|
||||
|
||||
* g++.dg/gcov/gcov-threads-1.C: New test.
|
||||
|
|
41
gcc/testsuite/gcc.dg/tree-prof/val-profiler-threads-1.c
Normal file
41
gcc/testsuite/gcc.dg/tree-prof/val-profiler-threads-1.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* { dg-options "-O0 -pthread -fprofile-update=atomic" } */
|
||||
#include <pthread.h>
|
||||
|
||||
#define NUM_THREADS 8
|
||||
#define SIZE 1024
|
||||
#define ITERATIONS (1000 * 1000)
|
||||
|
||||
char buffer[SIZE];
|
||||
char buffer2[SIZE];
|
||||
|
||||
void *copy_memory(char *dst, char *src, unsigned size)
|
||||
{
|
||||
for (unsigned i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
dst[size % 10] = src[size % 20];
|
||||
}
|
||||
}
|
||||
|
||||
void *foo(void *d)
|
||||
{
|
||||
copy_memory (buffer, buffer2, SIZE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pthread_t threads[NUM_THREADS];
|
||||
int rc;
|
||||
long t;
|
||||
for(t=0;t<NUM_THREADS;t++){
|
||||
rc = pthread_create(&threads[t], NULL, foo, 0);
|
||||
if (rc){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int retval;
|
||||
for(t=0;t<NUM_THREADS;t++)
|
||||
pthread_join (threads[t], (void**)&retval);
|
||||
|
||||
return buffer[10];
|
||||
}
|
|
@ -128,9 +128,13 @@ gimple_init_edge_profiler (void)
|
|||
tree average_profiler_fn_type;
|
||||
tree time_profiler_fn_type;
|
||||
const char *profiler_fn_name;
|
||||
const char *fn_name;
|
||||
|
||||
if (!gcov_type_node)
|
||||
{
|
||||
const char *fn_suffix
|
||||
= flag_profile_update == PROFILE_UPDATE_ATOMIC ? "_atomic" : "";
|
||||
|
||||
gcov_type_node = get_gcov_type ();
|
||||
gcov_type_ptr = build_pointer_type (gcov_type_node);
|
||||
|
||||
|
@ -140,9 +144,10 @@ gimple_init_edge_profiler (void)
|
|||
gcov_type_ptr, gcov_type_node,
|
||||
integer_type_node,
|
||||
unsigned_type_node, NULL_TREE);
|
||||
tree_interval_profiler_fn
|
||||
= build_fn_decl ("__gcov_interval_profiler",
|
||||
interval_profiler_fn_type);
|
||||
fn_name = concat ("__gcov_interval_profiler", fn_suffix, NULL);
|
||||
tree_interval_profiler_fn = build_fn_decl (fn_name,
|
||||
interval_profiler_fn_type);
|
||||
free (CONST_CAST (char *, fn_name));
|
||||
TREE_NOTHROW (tree_interval_profiler_fn) = 1;
|
||||
DECL_ATTRIBUTES (tree_interval_profiler_fn)
|
||||
= tree_cons (get_identifier ("leaf"), NULL,
|
||||
|
@ -153,8 +158,9 @@ gimple_init_edge_profiler (void)
|
|||
= build_function_type_list (void_type_node,
|
||||
gcov_type_ptr, gcov_type_node,
|
||||
NULL_TREE);
|
||||
tree_pow2_profiler_fn = build_fn_decl ("__gcov_pow2_profiler",
|
||||
pow2_profiler_fn_type);
|
||||
fn_name = concat ("__gcov_pow2_profiler", fn_suffix, NULL);
|
||||
tree_pow2_profiler_fn = build_fn_decl (fn_name, pow2_profiler_fn_type);
|
||||
free (CONST_CAST (char *, fn_name));
|
||||
TREE_NOTHROW (tree_pow2_profiler_fn) = 1;
|
||||
DECL_ATTRIBUTES (tree_pow2_profiler_fn)
|
||||
= tree_cons (get_identifier ("leaf"), NULL,
|
||||
|
@ -165,9 +171,10 @@ gimple_init_edge_profiler (void)
|
|||
= build_function_type_list (void_type_node,
|
||||
gcov_type_ptr, gcov_type_node,
|
||||
NULL_TREE);
|
||||
tree_one_value_profiler_fn
|
||||
= build_fn_decl ("__gcov_one_value_profiler",
|
||||
one_value_profiler_fn_type);
|
||||
fn_name = concat ("__gcov_one_value_profiler", fn_suffix, NULL);
|
||||
tree_one_value_profiler_fn = build_fn_decl (fn_name,
|
||||
one_value_profiler_fn_type);
|
||||
free (CONST_CAST (char *, fn_name));
|
||||
TREE_NOTHROW (tree_one_value_profiler_fn) = 1;
|
||||
DECL_ATTRIBUTES (tree_one_value_profiler_fn)
|
||||
= tree_cons (get_identifier ("leaf"), NULL,
|
||||
|
@ -197,9 +204,9 @@ gimple_init_edge_profiler (void)
|
|||
time_profiler_fn_type
|
||||
= build_function_type_list (void_type_node,
|
||||
gcov_type_ptr, NULL_TREE);
|
||||
tree_time_profiler_fn
|
||||
= build_fn_decl ("__gcov_time_profiler",
|
||||
time_profiler_fn_type);
|
||||
fn_name = concat ("__gcov_time_profiler", fn_suffix, NULL);
|
||||
tree_time_profiler_fn = build_fn_decl (fn_name, time_profiler_fn_type);
|
||||
free (CONST_CAST (char *, fn_name));
|
||||
TREE_NOTHROW (tree_time_profiler_fn) = 1;
|
||||
DECL_ATTRIBUTES (tree_time_profiler_fn)
|
||||
= tree_cons (get_identifier ("leaf"), NULL,
|
||||
|
@ -209,16 +216,17 @@ gimple_init_edge_profiler (void)
|
|||
average_profiler_fn_type
|
||||
= build_function_type_list (void_type_node,
|
||||
gcov_type_ptr, gcov_type_node, NULL_TREE);
|
||||
tree_average_profiler_fn
|
||||
= build_fn_decl ("__gcov_average_profiler",
|
||||
average_profiler_fn_type);
|
||||
fn_name = concat ("__gcov_average_profiler", fn_suffix, NULL);
|
||||
tree_average_profiler_fn = build_fn_decl (fn_name,
|
||||
average_profiler_fn_type);
|
||||
free (CONST_CAST (char *, fn_name));
|
||||
TREE_NOTHROW (tree_average_profiler_fn) = 1;
|
||||
DECL_ATTRIBUTES (tree_average_profiler_fn)
|
||||
= tree_cons (get_identifier ("leaf"), NULL,
|
||||
DECL_ATTRIBUTES (tree_average_profiler_fn));
|
||||
tree_ior_profiler_fn
|
||||
= build_fn_decl ("__gcov_ior_profiler",
|
||||
average_profiler_fn_type);
|
||||
fn_name = concat ("__gcov_ior_profiler", fn_suffix, NULL);
|
||||
tree_ior_profiler_fn = build_fn_decl (fn_name, average_profiler_fn_type);
|
||||
free (CONST_CAST (char *, fn_name));
|
||||
TREE_NOTHROW (tree_ior_profiler_fn) = 1;
|
||||
DECL_ATTRIBUTES (tree_ior_profiler_fn)
|
||||
= tree_cons (get_identifier ("leaf"), NULL,
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
2016-08-10 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR gcov-profile/58306
|
||||
* Makefile.in: New functions (modules) are added.
|
||||
* libgcov-profiler.c (__gcov_interval_profiler_atomic): New
|
||||
function.
|
||||
(__gcov_pow2_profiler_atomic): New function.
|
||||
(__gcov_one_value_profiler_body): New argument is instroduced.
|
||||
(__gcov_one_value_profiler): Call with the new argument.
|
||||
(__gcov_one_value_profiler_atomic): Likewise.
|
||||
(__gcov_indirect_call_profiler_v2): Likewise.
|
||||
(__gcov_time_profiler_atomic): New function.
|
||||
(__gcov_average_profiler_atomic): Likewise.
|
||||
(__gcov_ior_profiler_atomic): Likewise.
|
||||
* libgcov.h: Declare the aforementioned functions.
|
||||
|
||||
2016-08-09 Martin Liska <mliska@suse.cz>
|
||||
|
||||
* libgcov-util.c: Fix typo and GNU coding style.
|
||||
|
|
|
@ -858,10 +858,18 @@ include $(iterator)
|
|||
|
||||
LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
|
||||
_gcov_merge_ior _gcov_merge_time_profile _gcov_merge_icall_topn
|
||||
LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler \
|
||||
LIBGCOV_PROFILER = _gcov_interval_profiler \
|
||||
_gcov_interval_profiler_atomic \
|
||||
_gcov_pow2_profiler \
|
||||
_gcov_pow2_profiler_atomic \
|
||||
_gcov_one_value_profiler \
|
||||
_gcov_average_profiler _gcov_ior_profiler \
|
||||
_gcov_indirect_call_profiler_v2 _gcov_time_profiler \
|
||||
_gcov_one_value_profiler_atomic \
|
||||
_gcov_average_profiler \
|
||||
_gcov_average_profiler_atomic \
|
||||
_gcov_ior_profiler \
|
||||
_gcov_ior_profiler_atomic \
|
||||
_gcov_indirect_call_profiler_v2 \
|
||||
_gcov_time_profiler \
|
||||
_gcov_indirect_call_topn_profiler
|
||||
LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork \
|
||||
_gcov_execl _gcov_execlp \
|
||||
|
|
|
@ -46,6 +46,26 @@ __gcov_interval_profiler (gcov_type *counters, gcov_type value,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_interval_profiler_atomic
|
||||
/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
|
||||
corresponding counter in COUNTERS. If the VALUE is above or below
|
||||
the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
|
||||
instead. Function is thread-safe. */
|
||||
|
||||
void
|
||||
__gcov_interval_profiler_atomic (gcov_type *counters, gcov_type value,
|
||||
int start, unsigned steps)
|
||||
{
|
||||
gcov_type delta = value - start;
|
||||
if (delta < 0)
|
||||
__atomic_fetch_add (&counters[steps + 1], 1, MEMMODEL_RELAXED);
|
||||
else if (delta >= steps)
|
||||
__atomic_fetch_add (&counters[steps], 1, MEMMODEL_RELAXED);
|
||||
else
|
||||
__atomic_fetch_add (&counters[delta], 1, MEMMODEL_RELAXED);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_pow2_profiler
|
||||
/* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
|
||||
COUNTERS[0] is incremented. */
|
||||
|
@ -60,6 +80,21 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_pow2_profiler_atomic
|
||||
/* If VALUE is a power of two, COUNTERS[1] is incremented. Otherwise
|
||||
COUNTERS[0] is incremented. Function is thread-safe. */
|
||||
|
||||
void
|
||||
__gcov_pow2_profiler_atomic (gcov_type *counters, gcov_type value)
|
||||
{
|
||||
if (value == 0 || (value & (value - 1)))
|
||||
__atomic_fetch_add (&counters[0], 1, MEMMODEL_RELAXED);
|
||||
else
|
||||
__atomic_fetch_add (&counters[1], 1, MEMMODEL_RELAXED);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Tries to determine the most common value among its inputs. Checks if the
|
||||
value stored in COUNTERS[0] matches VALUE. If this is the case, COUNTERS[1]
|
||||
is incremented. If this is not the case and COUNTERS[1] is not zero,
|
||||
|
@ -68,10 +103,12 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
|
|||
function is called more than 50% of the time with one value, this value
|
||||
will be in COUNTERS[0] in the end.
|
||||
|
||||
In any case, COUNTERS[2] is incremented. */
|
||||
In any case, COUNTERS[2] is incremented. If USE_ATOMIC is set to 1,
|
||||
COUNTERS[2] is updated with an atomic instruction. */
|
||||
|
||||
static inline void
|
||||
__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
|
||||
__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value,
|
||||
int use_atomic)
|
||||
{
|
||||
if (value == counters[0])
|
||||
counters[1]++;
|
||||
|
@ -82,14 +119,36 @@ __gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
|
|||
}
|
||||
else
|
||||
counters[1]--;
|
||||
counters[2]++;
|
||||
|
||||
if (use_atomic)
|
||||
__atomic_fetch_add (&counters[2], 1, MEMMODEL_RELAXED);
|
||||
else
|
||||
counters[2]++;
|
||||
}
|
||||
|
||||
#ifdef L_gcov_one_value_profiler
|
||||
void
|
||||
__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
|
||||
{
|
||||
__gcov_one_value_profiler_body (counters, value);
|
||||
__gcov_one_value_profiler_body (counters, value, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_one_value_profiler_atomic
|
||||
|
||||
/* Update one value profilers (COUNTERS) for a given VALUE.
|
||||
|
||||
CAVEAT: Following function is not thread-safe, only total number
|
||||
of executions (COUNTERS[2]) is update with an atomic instruction.
|
||||
Problem is that one cannot atomically update two counters
|
||||
(COUNTERS[0] and COUNTERS[1]), for more information please read
|
||||
following email thread:
|
||||
https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html. */
|
||||
|
||||
void
|
||||
__gcov_one_value_profiler_atomic (gcov_type *counters, gcov_type value)
|
||||
{
|
||||
__gcov_one_value_profiler_body (counters, value, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -265,7 +324,7 @@ __gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
|
|||
if (cur_func == __gcov_indirect_call_callee
|
||||
|| (__LIBGCC_VTABLE_USES_DESCRIPTORS__ && __gcov_indirect_call_callee
|
||||
&& *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
|
||||
__gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
|
||||
__gcov_one_value_profiler_body (__gcov_indirect_call_counters, value, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -282,8 +341,19 @@ __gcov_time_profiler (gcov_type* counters)
|
|||
if (!counters[0])
|
||||
counters[0] = ++function_counter;
|
||||
}
|
||||
|
||||
/* Sets corresponding COUNTERS if there is no value.
|
||||
Function is thread-safe. */
|
||||
|
||||
void
|
||||
__gcov_time_profiler_atomic (gcov_type* counters)
|
||||
{
|
||||
if (!counters[0])
|
||||
counters[0] = __atomic_add_fetch (&function_counter, 1, MEMMODEL_RELAXED);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef L_gcov_average_profiler
|
||||
/* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
|
||||
to saturate up. */
|
||||
|
@ -296,6 +366,18 @@ __gcov_average_profiler (gcov_type *counters, gcov_type value)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_average_profiler_atomic
|
||||
/* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want
|
||||
to saturate up. Function is thread-safe. */
|
||||
|
||||
void
|
||||
__gcov_average_profiler_atomic (gcov_type *counters, gcov_type value)
|
||||
{
|
||||
__atomic_fetch_add (&counters[0], value, MEMMODEL_RELAXED);
|
||||
__atomic_fetch_add (&counters[1], 1, MEMMODEL_RELAXED);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_ior_profiler
|
||||
/* Bitwise-OR VALUE into COUNTER. */
|
||||
|
||||
|
@ -306,4 +388,15 @@ __gcov_ior_profiler (gcov_type *counters, gcov_type value)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef L_gcov_ior_profiler_atomic
|
||||
/* Bitwise-OR VALUE into COUNTER. Function is thread-safe. */
|
||||
|
||||
void
|
||||
__gcov_ior_profiler_atomic (gcov_type *counters, gcov_type value)
|
||||
{
|
||||
__atomic_fetch_or (&counters[0], value, MEMMODEL_RELAXED);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* inhibit_libc */
|
||||
|
|
|
@ -268,12 +268,19 @@ extern void __gcov_merge_icall_topn (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
|
|||
|
||||
/* The profiler functions. */
|
||||
extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
|
||||
extern void __gcov_interval_profiler_atomic (gcov_type *, gcov_type, int,
|
||||
unsigned);
|
||||
extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
|
||||
extern void __gcov_pow2_profiler_atomic (gcov_type *, gcov_type);
|
||||
extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
|
||||
extern void __gcov_one_value_profiler_atomic (gcov_type *, gcov_type);
|
||||
extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
|
||||
extern void __gcov_time_profiler (gcov_type *);
|
||||
extern void __gcov_time_profiler_atomic (gcov_type *);
|
||||
extern void __gcov_average_profiler (gcov_type *, gcov_type);
|
||||
extern void __gcov_average_profiler_atomic (gcov_type *, gcov_type);
|
||||
extern void __gcov_ior_profiler (gcov_type *, gcov_type);
|
||||
extern void __gcov_ior_profiler_atomic (gcov_type *, gcov_type);
|
||||
extern void __gcov_indirect_call_topn_profiler (gcov_type, void *);
|
||||
extern void gcov_sort_n_vals (gcov_type *, int);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue