Skip to content
This repository has been archived by the owner on Aug 30, 2019. It is now read-only.

{writer,info}: fix races #501

Merged
merged 5 commits into from Oct 23, 2018
Merged

{writer,info}: fix races #501

merged 5 commits into from Oct 23, 2018

Conversation

gbbr
Copy link
Contributor

@gbbr gbbr commented Oct 23, 2018

No description provided.

@gbbr gbbr added this to the 6.7.0 milestone Oct 23, 2018
@gbbr gbbr requested a review from LotharSee October 23, 2018 10:23
@@ -51,6 +51,8 @@ func (s *Sampler) GetDefaultSampleRate() float64 {
}

func (s *Sampler) backendScoreToSamplerScore(score float64) float64 {

Choose a reason for hiding this comment

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

This is getting called every time we sample a trace. Is there any way to avoid having this lock in the hotpath? Which variable is subject to a race condition?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd guess both of them are, right? The issue is calling SetSignatureCoefficients at the same time. AFAIK the performance loss is not really significant when there is not much contention. The read lock is simply an atomic.LoadInt* under the hood.

Copy link

@LotharSee LotharSee Oct 23, 2018

Choose a reason for hiding this comment

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

Yeah I was curious about how it compares vs using atomic.LoadInt directly on signatureScoreFactor and signatureScoreSlope. I guess it should be very close.

One or the other, what about systematically introduce a benchmark whenever we add a lock somewhere, to illustrate the resulting trace-off? (the same way we introduce a test when we fix a bug).

Copy link
Contributor Author

@gbbr gbbr Oct 23, 2018

Choose a reason for hiding this comment

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

atomic.LoadInt wouldn't work here, because it's a float. But I'm happy to introduce a benchmark. Although, what would the benchmark do? Run AdjustScoring every 10*time.Second and then in parallel GetCountScore (this is what happens in the agent)? Or were you thinking simply benchmarking calling GetCountScore with and without locks?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess the latter probably makes most sense. I will add that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, so the results are in:

name                          old time/op  new time/op  delta
BackendScoreToSamplerScore-8  16.3ns ± 0%  48.6ns ± 0%  +198.16%

Using this benchmark:

func BenchmarkBackendScoreToSamplerScore(b *testing.B) {
	s := newSampler(1.0, 10)
	for i := 0; i < b.N; i++ {
		s.backendScoreToSamplerScore(10)
	}
}

It's not looking too good. Do you have any ideas? I'm not sure if it's ok to leave this race here. Or?

Choose a reason for hiding this comment

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

Could the cost of a lock be actually much more than an atomic? Maybe that's the defer?

We can try to implement an atomic float (just copy what's in https://github.com/uber-go/atomic/blob/master/atomic.go#L267 ), use it on signatureScoreFactor and signatureScoreSlope and compare?

Also, all of that is to make it "the right way". In absolute that duration might not be that much compared to all the stuff on-going when doing sampling. So I wouldn't worry too too much.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice ideas!

With no defer:

name                          old time/op  new time/op  delta
BackendScoreToSamplerScore-8  16.3ns ± 0%  25.1ns ± 0%  +53.99%

With Uber's atomic Float64:

name                          old time/op  new time/op  delta
BackendScoreToSamplerScore-8  16.3ns ± 0%  17.5ns ± 0%  +7.36%

I'll go ahead an add the Uber version.

Choose a reason for hiding this comment

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

Nice, that's great learnings! defer is big.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed!

@gbbr gbbr force-pushed the gbbr/sampler-races branch 4 times, most recently from daad8d6 to 220f2d7 Compare October 23, 2018 14:35
Difference between no lock and lock:

```
name                          old time/op  new time/op  delta
BackendScoreToSamplerScore-8  16.3ns �± 0%  48.6ns �± 0%  +198.16%
```
@gbbr
Copy link
Contributor Author

gbbr commented Oct 23, 2018

@LotharSee should be super close to what it was now, without the race:

name                          old time/op  new time/op  delta
BackendScoreToSamplerScore-8  16.3ns ± 0%  17.5ns ± 0%  +7.36%

PTAL

```
name                          old time/op  new time/op  delta
BackendScoreToSamplerScore-8  16.3ns �± 0%  17.4ns �± 0%  +6.75%
```
@gbbr gbbr merged commit a05b4ea into master Oct 23, 2018
@gbbr gbbr deleted the gbbr/sampler-races branch October 23, 2018 14:53
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants