aspnet / DataAccessPerformance

Benchmarks, prototypes and discussions for database access performance investigation
MIT License
115 stars 23 forks source link

Idea: better low level networking code #5

Open divega opened 7 years ago

divega commented 7 years ago

We have some ideas that we want to try, which involve giving low-level networking for ADO.NET providers a similar makeover treatment to what did with Kestrel some time ago.

@davidfowl (and @anpete?) volunteered to do some experimentation with PostgreSQL, but this could apply to any database.

divega commented 7 years ago

@davidfowl, @anpete could you please detail a bit more (and better) what you had in mind here?

davidfowl commented 7 years ago

@roji I'd like to get a good idea of the threading model and buffering strategy you chose for Npgsql. That'll help with my mental model (I'd like to do some experiments with pipelines).

divega commented 7 years ago

@davidfowl are you actively working on this?

roji commented 7 years ago

@davidfowl it's great to be having this conversation with you guys. Below are some notes on Npgsql works, please don't hesitate to ask more questions.

I/O and Buffering

Async and Sync I/O

Synchronization

I hope this is a first fair description of Npgsql's internals, whipped up just before I head off for work :) Please feel free to ask more questions, I'll be closely monitoring this thread and the other performance-related ones. I'd definitely welcome experimentation with a different I/O model (e.g. pipelines).

davidfowl commented 7 years ago

@davidfowl are you actively working on this?

No, it's on my (now shorter) list of things to look at. I'm looking at using minimal code to build a simple postgres driver based on a protocol spec that @mikeharder shared a while back. Basically a single query with results to benchmark some of the patterns.

@roji Is something like this interesting for npgsql?

roji commented 7 years ago

@davidfowl do you mean building a non-ADO.NET driver for PostgreSQL, possibly non-managed (i.e. based on libpq)? Or just an attempt to build things properly from the ground up to see where it leads?

Npgsql is obviously a fully managed ADO.NET driver, but of course anything you do in terms of PostgreSQL connectivity interests me - you're also welcome to ask me questions, I've had a lot of experience working with the wire protocol. Also, at some point in the email exchanges I mentioned the possibility of splitting Npgsql (or any other ADO.NET driver) into two parts: a low-level ultra-efficient API, on top of which we'd built an ADO.NET adapter. This would allow performance-hungry users to drop down to the low-level API as needed. The low-level API could simply be libpq, or it could be fully-managed implementation (possibly libq first and managed later). There's also the question of whether this low-level component would expose some sort of standardized new database API - a high-performance competitor to ADO.NET, which, among other things, would allow you to include database sockets in an epoll/kqueue programming model (currently impossible with ADO.NET).

All this is a bit theoretical, and this would obviously mean very big changes for Npgsql (it would definitely be Npgsql 4.0 :)). Let me know if this is the direction you were thinking of etc.

davidfowl commented 7 years ago

Or just an attempt to build things properly from the ground up to see where it leads?

This. I wasn't thinking about going as far as building an entire driver. The end goal would be to improve npgsql. The idea is to write a minimal low level API that does the working and protocol so we can focus on things like buffering, IO, async and threading models. Ideally we'd take a very simple query and write the simplest code possible then build up from there. I'd also like to see this example using the lower layer of npgsql (if that's possible).

roji commented 7 years ago

So yeah, I'm absolutely interested... Feel free to reach out if any questions arise during implementation. Note that at the moment Npgsql doesn't really have a low-level layer internally which can be easily separated from an higher-level ADO.NET layer - that was more an idea for the future.

davidfowl commented 7 years ago

@roji, I'm going to try to get something basic working within the next few days. I'll push the code to this repository (in a PR).

roji commented 7 years ago

Great, once you push something I'll definitely take a look - it'll be interesting to compare with Npgsql.

On my side I'm working on bringing the dev branch back to working order (there are some pending issues) and then the plan is to implement command-level caching (https://github.com/npgsql/npgsql/issues/1701), which will provide quite a substantial boost.

sebastienros commented 7 years ago

Here are the result based on @anpete 's implementation of the protocol for the fortunes query.

Same environment as TE on cloud (Azure D3V2 machines). Database server scaled up to 8 cores as it was a limiting factor for the max perf. The new driver is compared to npgsql in async mode with full cached strategy (see https://github.com/aspnet/DataAccessPerformance/issues/3 for explanation).

image

This configuration we actually max out the client CPU, the client Network and the Database CPU, changing any of these would not change the overall result significantly.

What we can learn from that is that we reached pgbench baseline numbers with a fully managed .NET Core implementation.

We also tried this driver in the TE Fortunes benchmark. We are seeing 45K rps, compared to 16K with in raw ADO.NET mode with npgsql. On this infrastructure the current best result (undertow framework) is at 30K rps.

Next step is to decide what to do with that data, and more specifically if we want to go further with the idea of a different abstraction layer than ADO.NET, or even no abstractions at all but custom providers for each micro/full ORM.

roji commented 7 years ago

@anpete and @sebastienros, it's really great to see Peregrine and its associated benchmarks. It indeed looks like a solid baseline of where we might end up with a managed implementation, and I think it shows that looking at a native solution (e.g. libpq wrapper) or a radically different I/O approach may not be worthwhile.

Here are some notes after reviewing Peregrine. Some of these will point out missing features, which are totally understandable in an early-stage attempt such as this, and the idea is mainly to get us thinking and to evaluate to what extent the high performance is a result of a lack of needed functionality.

sebastienros commented 6 years ago

@roji

As requested, here is and updated graph with your experimental version. This one is from Linux on the async mode. The improvements are impressive.

image

A note however is that it seems to have issues with the sync mode, with a lot of variation and nearing 0 sometimes.