Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(outputs): Only copy metric if its not filtered out #15883

Merged

Conversation

LarsStegman
Copy link
Contributor

@LarsStegman LarsStegman commented Sep 14, 2024

Summary

This PR makes sure that an output plugin will really select a metric for outputting, before copying it. This improvement had a big impact on runtime/gc time in real world performance. After this change the amount of gc time went down from 55% of the CPU time to 25%.

In our real world case every output plugin was only interested in a subset of the metric and there was no overlap between the subsets. This means that (x-1)/x% of all the copied metrics were immediately discarded, where x is the number of outputs.

Checklist

  • No AI generated code was used in this PR

Related issues

resolves #15882

@srebhan
Copy link
Member

srebhan commented Sep 16, 2024

Thanks a lot @LarsStegman for your investigation!!!

How about moving the copy into the running_output model and pass in a flag if a copy is required like

	for metric := range unit.src {
		for i, output := range unit.outputs {
			output.AddMetric(metric, i < len(a.Config.Outputs) - 1)
		}
	}

and in the model do

func (r *RunningOutput) AddMetric(m telegraf.Metric, requireCopy bool) {
	metric := m

	ok, err := r.Config.Filter.Select(metric)
	if err != nil {
		r.log.Errorf("filtering failed: %v", err)
	} else if !ok {
		r.metricFiltered(metric)
		return
	}

	if requireCopy {
		metric = m.Copy()
	}

	r.Config.Filter.Modify(metric)
	if len(metric.FieldList()) == 0 {
		r.metricFiltered(metric)
		return
	}
        ...
}

This way we do not need to expose the interna of the output model into the agent.

@srebhan srebhan self-assigned this Sep 16, 2024
@LarsStegman
Copy link
Contributor Author

Yeah, that's also a good solution for me. I will make that change!

@LarsStegman LarsStegman force-pushed the perf/only-copy-metric-when-needed branch 2 times, most recently from 784da6d to a61bddf Compare September 17, 2024 13:48
@LarsStegman
Copy link
Contributor Author

@srebhan I am not sure why the memory leak test is failing. I should be making fewer allocations, not more. Do you have any idea?

@srebhan
Copy link
Member

srebhan commented Sep 19, 2024

@LarsStegman this is unrelated and unfortunately a flaky test... We need to look at it some time but currently things are busy. Ignore the issue for now...

Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! I wonder if we should keep the original AddMetric function signature and always copy the metric and have a second function AddMetricNoCopy which does not copy the metric. This way we save a few ifs and can keep the tests as they were...

@LarsStegman
Copy link
Contributor Author

That does sound like a better solution to be honest. All the ifs were getting a bit iffy.

@LarsStegman LarsStegman force-pushed the perf/only-copy-metric-when-needed branch from a61bddf to b080e56 Compare September 19, 2024 11:57
@LarsStegman
Copy link
Contributor Author

Alright, I do like this better!

@LarsStegman LarsStegman force-pushed the perf/only-copy-metric-when-needed branch from 589c177 to 6da9241 Compare September 21, 2024 06:12
Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Just get rid of the underscore in the function name and we are good to go.

models/running_output.go Outdated Show resolved Hide resolved
Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thanks @LarsStegman!

@srebhan srebhan changed the title perf(agent): only copy metric to output when needed feat(outputs): Only copy metric to output when needed Sep 30, 2024
@telegraf-tiger telegraf-tiger bot added the feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin label Sep 30, 2024
@srebhan srebhan added area/agent plugin/output 1. Request for new output plugins 2. Issues/PRs that are related to out plugins labels Sep 30, 2024
@srebhan
Copy link
Member

srebhan commented Oct 1, 2024

@LarsStegman you need this

diff --git a/plugins/inputs/cloud_pubsub_push/cloud_pubsub_push_test.go b/plugins/inputs/cloud_pubsub_push/cloud_pubsub_push_test.go
index 252b843fc..9e8aa07d1 100644
--- a/plugins/inputs/cloud_pubsub_push/cloud_pubsub_push_test.go
+++ b/plugins/inputs/cloud_pubsub_push/cloud_pubsub_push_test.go
@@ -196,6 +196,7 @@ func TestServeHTTP(t *testing.T) {
                        for m := range d {
                                ro.AddMetric(m)
                                ro.Write() //nolint:errcheck // test will fail anyway if the write fails
+                               m.Accept()
                        }
                }(dst)
 

to pass the unit-tests. Those tests are horrible but that's the easiest fix...

@telegraf-tiger
Copy link
Contributor

telegraf-tiger bot commented Oct 1, 2024

Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks @LarsStegman!

@srebhan srebhan changed the title feat(outputs): Only copy metric to output when needed feat(outputs): Only copy metric if its not filtered out Oct 2, 2024
@srebhan srebhan added the ready for final review This pull request has been reviewed and/or tested by multiple users and is ready for a final review. label Oct 2, 2024
@srebhan srebhan assigned DStrand1 and unassigned srebhan Oct 2, 2024
if err != nil {
r.log.Errorf("filtering failed: %v", err)
} else if !ok {
r.MetricsFiltered.Incr(1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just one minor nitpick: is there any reason this isn't calling r.metricFiltered(metric) like the similar functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that function also calls Drop on the metric, which we should not do if we haven't copied it yet, since we haven't taken ownership of it until then.

@DStrand1 DStrand1 merged commit 8561ded into influxdata:master Oct 2, 2024
28 of 29 checks passed
@github-actions github-actions bot added this to the v1.33.0 milestone Oct 2, 2024
@LarsStegman LarsStegman deleted the perf/only-copy-metric-when-needed branch October 2, 2024 19:15
asaharn pushed a commit to asaharn/telegraf that referenced this pull request Oct 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/agent feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin plugin/output 1. Request for new output plugins 2. Issues/PRs that are related to out plugins ready for final review This pull request has been reviewed and/or tested by multiple users and is ready for a final review.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Many memory allocations with multiple output plugins
3 participants