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

Reuse buffers when parsing #52

Closed
wants to merge 9 commits into from

Conversation

balasanjay
Copy link

We try to reduce the number of allocations that we do in the common case by keeping our buffers around, and reusing them, if possible. Note that this requires us to be slightly careful about handing out byte slices, because the backing buffer might be reused. In most cases, we don't have to care, because the database/sql package will copy for us.

This already has #51 merged into it, so it is recommended to review that first.

This commit does not reuse bytes.Buffer, but merely lays the
groundwork for reusing buffers in the future.
Adding the bytes.Buffer version bloated the code slightly. I've added
a helper method that replicates the old functionality, so that changes
are more miniminal.
@julienschmidt
Copy link
Member

Unfortunately, I notice performance degradation of ~ 8-9% (avg.) in Go 1.1beta2 and ~10% in Go 1.0.3.
You can play around with SQL-Benchmark if you want to try to optimize this.
I like the idea of this change, but from my point of view the current performance degradation is too large.

@arnehormann
Copy link
Member

What about keeping the buffer and still killing the unneccessary allocations?
And as it's not referenced from anywhere yet: the pull request originated in a golang-nuts discussion on google groups.

julienschmidt added a commit that referenced this pull request Apr 21, 2013
julienschmidt added a commit that referenced this pull request Apr 21, 2013
No extra allocation when refilling the buffer
Closes #52
@julienschmidt
Copy link
Member

I tried another approach: completely remove the 2nd buffer (date / packet buffer). Instead we work with slices directly from the buffer. This results in more or less zero copy.

Performance:
Before


*************************************************************
   BENCHMARKING Go-MySQL-Driver [run 1]
*************************************************************

-------------------------------------------------------------
   [10000 * Query 100 Rows]
-------------------------------------------------------------
SimpleQuery: 2.1241215s [ 4708 queries/second ]
PreparedQuery: 1.854106s [ 5393 queries/second ]

-------------------------------------------------------------
   [100 * QueryRow] * 1000
-------------------------------------------------------------
AutoQueryRow: 13.3857656s [ 7471 queries/second ]
SimpleQueryRow: 7.2264133s [ 13838 queries/second ]
PreparedQueryRow: 6.8483917s [ 14602 queries/second ]

-------------------------------------------------------------
   [100000 * Exec]
-------------------------------------------------------------
SimpleExec: 3.6992116s [ 27033 queries/second ]
PreparedExec: 3.4701985s [ 28817 queries/second ]


*************************************************************
   BENCHMARKING Go-MySQL-Driver [run 2]
*************************************************************

-------------------------------------------------------------
   [10000 * Query 100 Rows]
-------------------------------------------------------------
SimpleQuery: 2.3471342s [ 4261 queries/second ]
PreparedQuery: 1.871107s [ 5344 queries/second ]

-------------------------------------------------------------
   [100 * QueryRow] * 1000
-------------------------------------------------------------
AutoQueryRow: 13.5567754s [ 7376 queries/second ]
SimpleQueryRow: 7.3384198s [ 13627 queries/second ]
PreparedQueryRow: 6.7943886s [ 14718 queries/second ]

-------------------------------------------------------------
   [100000 * Exec]
-------------------------------------------------------------
SimpleExec: 3.6942113s [ 27069 queries/second ]
PreparedExec: 3.5642039s [ 28057 queries/second ]

After

*************************************************************
   BENCHMARKING Go-MySQL-Driver [run 1]
*************************************************************

-------------------------------------------------------------
   [10000 * Query 100 Rows]
-------------------------------------------------------------
SimpleQuery: 1.9131094s [ 5227 queries/second ]
PreparedQuery: 1.555089s [ 6431 queries/second ]

-------------------------------------------------------------
   [100 * QueryRow] * 1000
-------------------------------------------------------------
AutoQueryRow: 13.1377515s [ 7612 queries/second ]
SimpleQueryRow: 6.8833938s [ 14528 queries/second ]
PreparedQueryRow: 6.4733703s [ 15448 queries/second ]

-------------------------------------------------------------
   [100000 * Exec]
-------------------------------------------------------------
SimpleExec: 3.6292076s [ 27554 queries/second ]
PreparedExec: 3.5092007s [ 28497 queries/second ]


*************************************************************
   BENCHMARKING Go-MySQL-Driver [run 2]
*************************************************************

-------------------------------------------------------------
   [10000 * Query 100 Rows]
-------------------------------------------------------------
SimpleQuery: 1.8621065s [ 5370 queries/second ]
PreparedQuery: 1.5790903s [ 6333 queries/second ]

-------------------------------------------------------------
   [100 * QueryRow] * 1000
-------------------------------------------------------------
AutoQueryRow: 13.2367571s [ 7555 queries/second ]
SimpleQueryRow: 6.9683986s [ 14350 queries/second ]
PreparedQueryRow: 6.4303678s [ 15551 queries/second ]

-------------------------------------------------------------
   [100000 * Exec]
-------------------------------------------------------------
SimpleExec: 3.6432084s [ 27448 queries/second ]
PreparedExec: 3.4141953s [ 29289 queries/second ]

@julienschmidt
Copy link
Member

I worked on such a "zero copy buffer" before but stopped when I found a reason why this concept couldn't work - at least I assumed it the be a reason.
Until now I found no case where the new buffer causes problems, but please test it as much as you can.
Especially cases where sql.RawBytes is used excessively could be interesting.

@balasanjay
Copy link
Author

@julienschmidt Nice!

Feel free to close this in favor of that one. I was just reading over the code again, and realized why this was slow (and wrong, to boot!)

I was resetting the buffer per-column, rather than per result row. This would have had some bad aliasing effects 👎, and negated the perf benefits. I think you'd have to store the buffer inside the conn, and reuse it every time columns were being parsed.

I'm going to prototype a stream-based parser and serializer that uses that idea; I think there are more gains to be had in this direction. Will keep you informed.

Cheers.

@julienschmidt julienschmidt mentioned this pull request Apr 21, 2013
@julienschmidt
Copy link
Member

Thanks for all you efforts!

Continued at #55

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

Successfully merging this pull request may close these issues.

3 participants