Skip to content

Commit 8b5d46f

Browse files
Kalesh Singhrostedt
authored andcommitted
tracing/histogram: Optimize division by constants
If the divisor is a constant use specific division functions to avoid extra branches when the trigger is hit. If the divisor constant but not a power of 2, the division can be replaced with a multiplication and shift in the following case: Let X = dividend and Y = divisor. Choose Z = some power of 2. If Y <= Z, then: X / Y = (X * (Z / Y)) / Z (Z / Y) is a constant (mult) which is calculated at parse time, so: X / Y = (X * mult) / Z The division by Z can be replaced by a shift since Z is a power of 2: X / Y = (X * mult) >> shift As long, as X < Z the results will not be off by more than 1. Link: https://lkml.kernel.org/r/[email protected] Suggested-by: Steven Rostedt <[email protected]> Signed-off-by: Kalesh Singh <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 01e181c commit 8b5d46f

File tree

1 file changed

+104
-1
lines changed

1 file changed

+104
-1
lines changed

kernel/trace/trace_events_hist.c

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
C(INVALID_SORT_FIELD, "Sort field must be a key or a val"), \
6969
C(INVALID_STR_OPERAND, "String type can not be an operand in expression"), \
7070
C(EXPECT_NUMBER, "Expecting numeric literal"), \
71-
C(UNARY_MINUS_SUBEXPR, "Unary minus not supported in sub-expressions"),
71+
C(UNARY_MINUS_SUBEXPR, "Unary minus not supported in sub-expressions"), \
72+
C(DIVISION_BY_ZERO, "Division by zero"),
7273

7374
#undef C
7475
#define C(a, b) HIST_ERR_##a
@@ -92,6 +93,7 @@ typedef u64 (*hist_field_fn_t) (struct hist_field *field,
9293
#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
9394
#define HIST_ACTIONS_MAX 8
9495
#define HIST_CONST_DIGITS_MAX 21
96+
#define HIST_DIV_SHIFT 20 /* For optimizing division by constants */
9597

9698
enum field_op_id {
9799
FIELD_OP_NONE,
@@ -160,6 +162,8 @@ struct hist_field {
160162

161163
/* Numeric literals are represented as u64 */
162164
u64 constant;
165+
/* Used to optimize division by constants */
166+
u64 div_multiplier;
163167
};
164168

165169
static u64 hist_field_none(struct hist_field *field,
@@ -311,6 +315,68 @@ static u64 hist_field_div(struct hist_field *hist_field,
311315
return div64_u64(val1, val2);
312316
}
313317

318+
static u64 div_by_power_of_two(struct hist_field *hist_field,
319+
struct tracing_map_elt *elt,
320+
struct trace_buffer *buffer,
321+
struct ring_buffer_event *rbe,
322+
void *event)
323+
{
324+
struct hist_field *operand1 = hist_field->operands[0];
325+
struct hist_field *operand2 = hist_field->operands[1];
326+
327+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
328+
329+
return val1 >> __ffs64(operand2->constant);
330+
}
331+
332+
static u64 div_by_not_power_of_two(struct hist_field *hist_field,
333+
struct tracing_map_elt *elt,
334+
struct trace_buffer *buffer,
335+
struct ring_buffer_event *rbe,
336+
void *event)
337+
{
338+
struct hist_field *operand1 = hist_field->operands[0];
339+
struct hist_field *operand2 = hist_field->operands[1];
340+
341+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
342+
343+
return div64_u64(val1, operand2->constant);
344+
}
345+
346+
static u64 div_by_mult_and_shift(struct hist_field *hist_field,
347+
struct tracing_map_elt *elt,
348+
struct trace_buffer *buffer,
349+
struct ring_buffer_event *rbe,
350+
void *event)
351+
{
352+
struct hist_field *operand1 = hist_field->operands[0];
353+
struct hist_field *operand2 = hist_field->operands[1];
354+
355+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
356+
357+
/*
358+
* If the divisor is a constant, do a multiplication and shift instead.
359+
*
360+
* Choose Z = some power of 2. If Y <= Z, then:
361+
* X / Y = (X * (Z / Y)) / Z
362+
*
363+
* (Z / Y) is a constant (mult) which is calculated at parse time, so:
364+
* X / Y = (X * mult) / Z
365+
*
366+
* The division by Z can be replaced by a shift since Z is a power of 2:
367+
* X / Y = (X * mult) >> HIST_DIV_SHIFT
368+
*
369+
* As long, as X < Z the results will not be off by more than 1.
370+
*/
371+
if (val1 < (1 << HIST_DIV_SHIFT)) {
372+
u64 mult = operand2->div_multiplier;
373+
374+
return (val1 * mult + ((1 << HIST_DIV_SHIFT) - 1)) >> HIST_DIV_SHIFT;
375+
}
376+
377+
return div64_u64(val1, operand2->constant);
378+
}
379+
314380
static u64 hist_field_mult(struct hist_field *hist_field,
315381
struct tracing_map_elt *elt,
316382
struct trace_buffer *buffer,
@@ -573,6 +639,25 @@ struct snapshot_context {
573639
void *key;
574640
};
575641

642+
/*
643+
* Returns the specific division function to use if the divisor
644+
* is constant. This avoids extra branches when the trigger is hit.
645+
*/
646+
static hist_field_fn_t hist_field_get_div_fn(struct hist_field *divisor)
647+
{
648+
u64 div = divisor->constant;
649+
650+
if (!(div & (div - 1)))
651+
return div_by_power_of_two;
652+
653+
/* If the divisor is too large, do a regular division */
654+
if (div > (1 << HIST_DIV_SHIFT))
655+
return div_by_not_power_of_two;
656+
657+
divisor->div_multiplier = div64_u64((u64)(1 << HIST_DIV_SHIFT), div);
658+
return div_by_mult_and_shift;
659+
}
660+
576661
static void track_data_free(struct track_data *track_data)
577662
{
578663
struct hist_elt_data *elt_data;
@@ -2575,6 +2660,24 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
25752660
expr->operands[0] = operand1;
25762661
expr->operands[1] = operand2;
25772662

2663+
if (field_op == FIELD_OP_DIV &&
2664+
operand2_flags & HIST_FIELD_FL_CONST) {
2665+
u64 divisor = var2 ? var2->constant : operand2->constant;
2666+
2667+
if (!divisor) {
2668+
hist_err(file->tr, HIST_ERR_DIVISION_BY_ZERO, errpos(str));
2669+
ret = -EDOM;
2670+
goto free;
2671+
}
2672+
2673+
/*
2674+
* Copy the divisor here so we don't have to look it up
2675+
* later if this is a var ref
2676+
*/
2677+
operand2->constant = divisor;
2678+
op_fn = hist_field_get_div_fn(operand2);
2679+
}
2680+
25782681
if (combine_consts) {
25792682
if (var1)
25802683
expr->operands[0] = var1;

0 commit comments

Comments
 (0)