8000 Optimized reading rows in a sequential mode, if they are already in the buffer by vonzshik · Pull Request #3428 · npgsql/npgsql · GitHub
[go: up one dir, main page]

Skip to content

Optimized reading rows in a sequential mode, if they are already in the buffer#3428

Merged
vonzshik merged 3 commits intonpgsql:mainfrom
vonzshik:2053-sequential-row-read-optimization
Jan 11, 2021
Merged

Optimized reading rows in a sequential mode, if they are already in the buffer#3428
vonzshik merged 3 commits intonpgsql:mainfrom
vonzshik:2053-sequential-row-read-optimization

Conversation

@vonzshik
Copy link
Contributor
@vonzshik vonzshik commented Dec 27, 2020

Fixes #2053

For comparison:

Before

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1256 (1909/November2018Update/19H2)
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.100
  [Host]     : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT
  DefaultJob : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT

Method NumRows Sequential Mean Error StdDev Median
Read 1 False 143.4 μs 4.16 μs 12.27 μs 140.3 μs
Read 1 True 143.3 μs 4.59 μs 13.23 μs 138.7 μs
Read 10 False 139.2 μs 3.18 μs 9.13 μs 138.4 μs
Read 10 True 147.0 μs 3.86 μs 11.26 μs 145.4 μs
Read 100 False 175.3 μs 4.68 μs 13.66 μs 172.0 μs
Read 100 True 205.3 μs 5.55 μs 16.19 μs 204.9 μs
Read 1000 False 401.3 μs 7.98 μs 11.94 μs 399.3 μs
Read 1000 True 590.9 μs 11.76 μs 12.58 μs 591.5 μs

After

Method NumRows Sequential Mean Error StdDev Median
Read 1 False 128.3 μs 2.56 μs 7.19 μs 128.0 μs
Read 1 True 125.9 μs 2.50 μs 4.31 μs 125.6 μs
Read 10 False 127.8 μs 2.51 μs 3.08 μs 127.1 μs
Read 10 True 136.1 μs 3.33 μs 9.71 μs 133.7 μs
Read 100 False 160.7 μs 4.41 μs 13.02 μs 158.2 μs
Read 100 True 164.5 μs 5.26 μs 15.17 μs 158.7 μs
Read 1000 False 395.2 μs 7.50 μs 7.71 μs 396.5 μs
Read 1000 True 394.7 μs 6.72 μs 7.74 μs 394.6 μs

Copy link
Member
@roji roji left a comment

Choose a reason for hiding this comment

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

Thanks for investigating and optimizing this @vonzshik... great to see sequential getting some love :) Do you have the updated stats on sequential vs. non-sequential? I'm curious where the gap is (if at all) after this.

One thought about this... If I'm reading the code right, your optimization only works if the entire message fits in the buffer. If it doesn't, and the user reads additional data into the buffer, and then does Read to go to the next row, we still invoke ConsumeRowSequential although we don't have to. In other words, unless I'm mistaken, we could update _dataMsgEnd as we read row data, so that the optimization would kick in even for rows that are larger than the buffer. I'm not sure exactly how valuable this would be, what do you think?

8000
@vonzshik
Copy link
Contributor Author
vonzshik commented Jan 7, 2021

Thanks for investigating and optimizing this @vonzshik... great to see sequential getting some love :) Do you have the updated stats on sequential vs. non-sequential? I'm curious where the gap is (if at all) after this.

One thought about this... If I'm reading the code right, your optimization only works if the entire message fits in the buffer. If it doesn't, and the user reads additional data into the buffer, and then does Read to go to the next row, we still invoke ConsumeRowSequential although we don't have to. In other words, unless I'm mistaken, we could update _dataMsgEnd as we read row data, so that the optimization would kick in even for rows that are larger than the buffer. I'm not sure exactly how valuable this would be, what do you think?

Yeah, that's exactly how it works. I did take a look, and that's a very tricky to fix. The problem is, _dataMsgEnd is essentially an end position of the row in the buffer. And the only time when we can (and should) update it, is when the buffer is reset (that is, some data was requested from the buffer when it's full).

@vonzshik
Copy link
Contributor Author
vonzshik commented Jan 7, 2021

Do you have the updated stats on sequential vs. non-sequential? I'm curious where the gap is (if at all) after this.

Not sure what you've meant here - do you want me to test some other cases?

@roji
Copy link
Member
roji commented Jan 7, 2021

@vonzshik am just curious if after your changes sequential is still slower than non-sequential, and by how much :) But not urgent or blocking for this PR of course.

@roji
Copy link
Member
roji commented Jan 7, 2021

I did take a look, and that's a very tricky to fix. The problem is, _dataMsgEnd is essentially an end position of the row in the buffer. And the only time when we can (and should) update it, is when the buffer is reset (that is, some data was requested from the buffer when it's full).

Yeah, I figured as much. FWIW the usage of _dataMsgEnd is slightly weird to me; previously it really was just an end position of the row in the buffer (because the entire row was always contained in the buffer). Now it can also be a point way beyond the end of the buffer, which actually has no real meaning in that case.

I think it should be possible to update it as we read more data into the buffer (or some similar mechanism), but I'm also fine with merging as-is if you don't feel like the extra effort is worth it (this is already a really nice improvement).

@vonzshik vonzshik merged commit 6d08dfa into npgsql:main Jan 11, 2021
vonzshik added a commit that referenced this pull request Jan 11, 2021
@vonzshik vonzshik deleted the 2053-sequential-row-read-optimization branch January 11, 2021 10:45
@vonzshik
Copy link
Contributor Author

Backported to 5.0.2 via 945adc7

@vonzshik
Copy link
Contributor Author

@roji I did some more benchmarking - in some cases sequential read might be slower by 2-3%. I'll create another issue and post my findings there.

@roji
Copy link
Member
roji commented Jan 11, 2021

Note for the future: I think we shouldn't be releasing perf optimizations such as this in patch versions... We should strive to only fix bugs, and where it really makes sense, merge very low-risk features which have a lot of value.

@vonzshik
Copy link
Contributor Author

Note for the future: I think we shouldn't be releasing perf optimizations such as this in patch versions... We should strive to only fix bugs, and where it really makes sense, merge very low-risk features which have a lot of value.

Fully agree with this. Although, I do think th 8000 is pr is relatively low-risk and does bring a lot of value. But if you're still against it, I'm willing to revert it.

@roji
Copy link
Member
roji commented Jan 11, 2021

I do think we're better off reverting this - after all it really is a perf optimization, rather than a feature that could unblocked someone out there. The code around sequential/non-sequential reading has also proven to be trickier than it looks, so from a risk perspective this isn't great.

But I'll leave it up to your judgement... I just prefer to not have to worry about new bugs in patch releases...

vonzshik added a commit that referenced this pull request Jan 11, 2021
…ady in the buffer (#3428)"

This reverts commit 945adc7.

Reverted per discussion at #3428.
@YohDeadfall
Copy link
Contributor

Personally, I would like to have it in the upcoming release since it tunes perf a bit, but without drastic changes, and it doesn't look dangerous. It's clean and simple.

@vonzshik
Copy link
Contributor Author

😩

@YohDeadfall
Copy link
Contributor

You still can revert the revert by force pushing to the hotfix branch 😈

@vonzshik
Copy link
Contributor Author

Well, if we're going to do it right, we will do it right. We can always release 5.1 in the next few months.

@YohDeadfall
Copy link
Contributor

Not sure about it since there's not so much changes for 5.1, so I bet the next release is 6.0.

@roji
Copy link
Member
roji commented Jan 11, 2021

We can indeed decide to release 5.1 at some point - this may be a good reason to hold off on breaking changes (since we don't want those in 6.0). Th 3D16 e question is if we'll have enough interesting stuff to warrant a 5.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate/improve perf for CommandBehavior.SequentialAccess (and ExecuteScalar())

3 participants

0