Message ID | 20211020013153.4106001-3-kaleshsingh@google.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | tracing: Extend histogram triggers expression parsing | expand |
On Tue, 19 Oct 2021 18:31:39 -0700 Kalesh Singh <kaleshsingh@google.com> wrote: > +static u64 hist_field_div(struct hist_field *hist_field, > + struct tracing_map_elt *elt, > + struct trace_buffer *buffer, > + struct ring_buffer_event *rbe, > + void *event) > +{ > + struct hist_field *operand1 = hist_field->operands[0]; > + struct hist_field *operand2 = hist_field->operands[1]; > + > + u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event); > + u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event); > + > + /* Return -1 for the undefined case */ > + if (!val2) > + return -1; > + > + return div64_u64(val1, val2); > +} > + I wonder if you should add a shift operator as well? I mean, if for some reason you want to divide by a power of two, then why us the division. Especially if this is on a 32 bit machine. Of course, the parsing could detect that. If the divisor is a constant. Or we could even optimize the above with: if (!val2) return -1; if (!(val2 & (val2 - 1)) return val1 >> __ffs64(val2); Which should be faster than a divide, and even if it isn't a power of two, the subtract and & should be in the noise compared to the divide. Note, the above can be added to this. I'm not suggesting changing this patch. -- Steve
On Tue, Oct 19, 2021 at 7:28 PM Steven Rostedt <rostedt@goodmis.org> wrote: > > On Tue, 19 Oct 2021 18:31:39 -0700 > Kalesh Singh <kaleshsingh@google.com> wrote: > > > +static u64 hist_field_div(struct hist_field *hist_field, > > + struct tracing_map_elt *elt, > > + struct trace_buffer *buffer, > > + struct ring_buffer_event *rbe, > > + void *event) > > +{ > > + struct hist_field *operand1 = hist_field->operands[0]; > > + struct hist_field *operand2 = hist_field->operands[1]; > > + > > + u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event); > > + u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event); > > + > > + /* Return -1 for the undefined case */ > > + if (!val2) > > + return -1; > > + > > + return div64_u64(val1, val2); > > +} > > + > > I wonder if you should add a shift operator as well? > > I mean, if for some reason you want to divide by a power of two, then why > us the division. Especially if this is on a 32 bit machine. > > Of course, the parsing could detect that. If the divisor is a constant. Or > we could even optimize the above with: > > if (!val2) > return -1; > > if (!(val2 & (val2 - 1)) > return val1 >> __ffs64(val2); > > Which should be faster than a divide, and even if it isn't a power of two, > the subtract and & should be in the noise compared to the divide. > > Note, the above can be added to this. I'm not suggesting changing this > patch. Is it worth adding something like this for the multiplication case as well? - Kalesh > > -- Steve
On Wed, 20 Oct 2021 07:54:59 -0700
Kalesh Singh <kaleshsingh@google.com> wrote:
> Is it worth adding something like this for the multiplication case as well?
No, multiplication is a pretty fast operation, and the added branches to
test would cause more overhead than what you would save. But, division is a
very slow operation, and I believe that even with the extra branches it
would still help.
If we do this, it should be a separate patch anyway, where we can actual do
measurements to see if there was an improvement, and revert if not.
-- Steve
On Wed, Oct 20, 2021 at 8:13 AM Steven Rostedt <rostedt@goodmis.org> wrote: > > On Wed, 20 Oct 2021 07:54:59 -0700 > Kalesh Singh <kaleshsingh@google.com> wrote: > > > Is it worth adding something like this for the multiplication case as well? > > No, multiplication is a pretty fast operation, and the added branches to > test would cause more overhead than what you would save. But, division is a > very slow operation, and I believe that even with the extra branches it > would still help. > > If we do this, it should be a separate patch anyway, where we can actual do > measurements to see if there was an improvement, and revert if not. Sounds good. Thanks for the clarification Steve. - Kalesh > > -- Steve
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 8563a2d51f65..9415ee65acc0 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -99,6 +99,8 @@ enum field_op_id { FIELD_OP_PLUS, FIELD_OP_MINUS, FIELD_OP_UNARY_MINUS, + FIELD_OP_DIV, + FIELD_OP_MULT, }; /* @@ -287,6 +289,40 @@ static u64 hist_field_minus(struct hist_field *hist_field, return val1 - val2; } +static u64 hist_field_div(struct hist_field *hist_field, + struct tracing_map_elt *elt, + struct trace_buffer *buffer, + struct ring_buffer_event *rbe, + void *event) +{ + struct hist_field *operand1 = hist_field->operands[0]; + struct hist_field *operand2 = hist_field->operands[1]; + + u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event); + u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event); + + /* Return -1 for the undefined case */ + if (!val2) + return -1; + + return div64_u64(val1, val2); +} + +static u64 hist_field_mult(struct hist_field *hist_field, + struct tracing_map_elt *elt, + struct trace_buffer *buffer, + struct ring_buffer_event *rbe, + void *event) +{ + struct hist_field *operand1 = hist_field->operands[0]; + struct hist_field *operand2 = hist_field->operands[1]; + + u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event); + u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event); + + return val1 * val2; +} + static u64 hist_field_unary_minus(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, @@ -1595,6 +1631,12 @@ static char *expr_str(struct hist_field *field, unsigned int level) case FIELD_OP_PLUS: strcat(expr, "+"); break; + case FIELD_OP_DIV: + strcat(expr, "/"); + break; + case FIELD_OP_MULT: + strcat(expr, "*"); + break; default: kfree(expr); return NULL; @@ -1610,7 +1652,7 @@ static int contains_operator(char *str) enum field_op_id field_op = FIELD_OP_NONE; char *op; - op = strpbrk(str, "+-"); + op = strpbrk(str, "+-/*"); if (!op) return FIELD_OP_NONE; @@ -1631,6 +1673,12 @@ static int contains_operator(char *str) case '+': field_op = FIELD_OP_PLUS; break; + case '/': + field_op = FIELD_OP_DIV; + break; + case '*': + field_op = FIELD_OP_MULT; + break; default: break; } @@ -2370,10 +2418,26 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data, case FIELD_OP_PLUS: sep = "+"; break; + case FIELD_OP_DIV: + sep = "/"; + break; + case FIELD_OP_MULT: + sep = "*"; + break; default: goto free; } + /* + * Multiplication and division are only supported in single operator + * expressions, since the expression is always evaluated from right + * to left. + */ + if ((field_op == FIELD_OP_DIV || field_op == FIELD_OP_MULT) && level > 0) { + hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str)); + return ERR_PTR(-EINVAL); + } + operand1_str = strsep(&str, sep); if (!operand1_str || !str) goto free; @@ -2445,6 +2509,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data, case FIELD_OP_PLUS: expr->fn = hist_field_plus; break; + case FIELD_OP_DIV: + expr->fn = hist_field_div; + break; + case FIELD_OP_MULT: + expr->fn = hist_field_mult; + break; default: ret = -EINVAL; goto free;