FirebirdSQL / NETProvider

Firebird ADO.NET Data Provider
https://www.firebirdsql.org/en/net-provider/
Other
152 stars 63 forks source link

Real performance enhancements #1112

Closed BFuerchau closed 1 year ago

BFuerchau commented 1 year ago

In both GdsStatements (Version 10, Version 13) i have changed the Fetch and FetchAsync to store in the _rows-queue only object[]-arrays. If data can read, a fixed DbValue[] is created. When the queue is dequeued, the fixed DbValue[] is filled.

Performance on resultsets grows with more rows, i have reached until 10%. Advantage: less GC, because only objec[]-arrays must be garbaged per row and only one DbValue[] per result. Shure, this has most affect on local databases, but less GC is always a benefit.

GdsStatement.zip GdsStatement.zip

FbDataReader FbDataReader.zip

You may believe or not, but i have changed again, as in the past, the GetValues(object[] values). Instead of single call for each column the GetFieldValue<object>(int i), the call direct for _row[i].GetValue() is faster. 1 call and typecheck less for each column which was expensive. This brings further until 20%!

So both changes, GdsStatement and FbDataReader can load until 30% faster, if you have more than 1 row per request.

Additional change in FbDataReader: GetSchemaTable/GetSchemaTableAsync

You loop through all result fields and make a single request to the FB-Server. This are everytime minimum of 3 calls: BeginTransaction, ExecuteReader, CommitTransaction. If you have 10 columns, you need 30 request, 100 fields 300 requests and so on. If you have calculated columns, derived tables, grouping and aggregates, often the column has no relation so the request isn't neccessary. I have changed both to a single request for all involved relations which are not empty and load a datatable. I set the FetchSize to 32767 to take less server roundtrips. I have often queries with 100 and more columns, where no field has any relation to a table. In this case, no server request is done.

Yes, with this modification you are loading perhaps many columns, which isn't needed, but i think, that a single request is cheaper with only 6 columns.

Also the GetFieldValue<T> and GetFieldValueAsync<T> have shorter code. May be the optimizer can do it a little bit faster.

cincuranet commented 1 year ago

If you have any changes, create PR (for each change).

BFuerchau commented 1 year ago

Sorry, i didn't know how.