From 87d6ec0eb7fe8a67f0674a42237344ed323183a5 Mon Sep 17 00:00:00 2001 From: BFuerchau <92720745+BFuerchau@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:33:24 +0200 Subject: [PATCH 1/4] Update GdsStatement.cs Version 10 Change _rows-Queue from DbValue[] to object[] Store only object[]-Array Use single DbValue[]-Array Advantage: Less object creation, less Garbagecollector, performance increase up to 10% --- .../Client/Managed/Version10/GdsStatement.cs | 101 ++++++++++++++++-- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs index f920c270..d085b920 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs @@ -1,4 +1,4 @@ -/* +/* * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); * you may not use this file except in compliance with the @@ -19,6 +19,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using FirebirdSql.Data.Common; @@ -36,7 +37,7 @@ internal class GdsStatement : StatementBase protected Descriptor _parameters; protected Descriptor _fields; protected bool _allRowsFetched; - private Queue _rows; + private Queue _rows; private int _fetchSize; #endregion @@ -110,7 +111,7 @@ public GdsStatement(GdsDatabase database, GdsTransaction transaction) { _handle = IscCodes.INVALID_OBJECT; _fetchSize = 200; - _rows = new Queue(); + _rows = new Queue(); OutputParameters = new Queue(); _database = database; @@ -375,6 +376,16 @@ public override async ValueTask ExecuteAsync(int timeout, IDescriptorFiller desc } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected DbValue[] GetDbValues(object[] values) + { + for (int i = 0; i < _fields.Count; ++i) + { + _dbValues[i].SetValue(values[i]); + } + return _dbValues; + } + public override DbValue[] Fetch() { EnsureNotDeallocated(); @@ -407,6 +418,8 @@ public override DbValue[] Fetch() var operation = _database.ReadOperation(); if (operation == IscCodes.op_fetch_response) { + CreateDbValues(); + var hasOperation = true; while (!_allRowsFetched) { @@ -418,7 +431,7 @@ public override DbValue[] Fetch() { if (fetchResponse.Count > 0 && fetchResponse.Status == 0) { - _rows.Enqueue(ReadRow()); + _rows.Enqueue(ReadRowObjectValues()); } else if (fetchResponse.Status == 100) { @@ -448,7 +461,7 @@ public override DbValue[] Fetch() if (_rows != null && _rows.Count > 0) { - return _rows.Dequeue(); + return GetDbValues(_rows.Dequeue()); } else { @@ -456,6 +469,18 @@ public override DbValue[] Fetch() return null; } } + + protected DbValue[] _dbValues; + protected void CreateDbValues() + { + _dbValues = new DbValue[_fields.Count]; + for (int i = 0; i < _fields.Count; i++) + { + _dbValues[i] = new DbValue(this, _fields[i], null); + } + } + + public override async ValueTask FetchAsync(CancellationToken cancellationToken = default) { EnsureNotDeallocated(); @@ -488,6 +513,8 @@ public override async ValueTask FetchAsync(CancellationToken cancella var operation = await _database.ReadOperationAsync(cancellationToken).ConfigureAwait(false); if (operation == IscCodes.op_fetch_response) { + CreateDbValues(); + var hasOperation = true; while (!_allRowsFetched) { @@ -499,7 +526,7 @@ public override async ValueTask FetchAsync(CancellationToken cancella { if (fetchResponse.Count > 0 && fetchResponse.Status == 0) { - _rows.Enqueue(await ReadRowAsync(cancellationToken).ConfigureAwait(false)); + _rows.Enqueue(await ReadRowObjectValuesAsync(cancellationToken).ConfigureAwait(false)); } else if (fetchResponse.Status == 100) { @@ -529,7 +556,7 @@ public override async ValueTask FetchAsync(CancellationToken cancella if (_rows != null && _rows.Count > 0) { - return _rows.Dequeue(); + return GetDbValues(_rows.Dequeue()); } else { @@ -1735,6 +1762,36 @@ protected void ClearAll() _fields = null; } + protected virtual object[] ReadRowObjectValues() // Optimized + { + var row = new object[_fields.Count]; + try + { + for (var i = 0; i < _fields.Count; i++) + { + var value = ReadRawValue(_database.Xdr, _fields[i]); + var sqlInd = _database.Xdr.ReadInt32(); + if (sqlInd == -1) + { + row[i] = DBNull.Value; + } + else if (sqlInd == 0) + { + row[i] = value; + } + else + { + throw IscException.ForStrParam($"Invalid {nameof(sqlInd)} value: {sqlInd}."); + } + } + } + catch (IOException ex) + { + throw IscException.ForIOException(ex); + } + return row; + } + protected virtual DbValue[] ReadRow() { var row = new DbValue[_fields.Count]; @@ -1764,6 +1821,36 @@ protected virtual DbValue[] ReadRow() } return row; } + protected virtual async ValueTask ReadRowObjectValuesAsync(CancellationToken cancellationToken = default) + { + var row = new object[_fields.Count]; + try + { + for (var i = 0; i < _fields.Count; i++) + { + var value = await ReadRawValueAsync(_database.Xdr, _fields[i], cancellationToken).ConfigureAwait(false); + var sqlInd = await _database.Xdr.ReadInt32Async(cancellationToken).ConfigureAwait(false); + if (sqlInd == -1) + { + row[i] = DBNull.Value; + } + else if (sqlInd == 0) + { + row[i] = value; + } + else + { + throw IscException.ForStrParam($"Invalid {nameof(sqlInd)} value: {sqlInd}."); + } + } + } + catch (IOException ex) + { + throw IscException.ForIOException(ex); + } + return row; + } + protected virtual async ValueTask ReadRowAsync(CancellationToken cancellationToken = default) { var row = new DbValue[_fields.Count]; From 651f5bcf75bf023caea2d37a2153e80434871bf5 Mon Sep 17 00:00:00 2001 From: BFuerchau <92720745+BFuerchau@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:36:49 +0200 Subject: [PATCH 2/4] Update GdsStatement.cs Version 13 matching override to gdsstatement version 10 --- .../Client/Managed/Version13/GdsStatement.cs | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs index 0ea95378..2cfc55ed 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs @@ -1,4 +1,4 @@ -/* +/* * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); * you may not use this file except in compliance with the @@ -131,6 +131,34 @@ protected override async ValueTask WriteParametersAsync(CancellationToke } } + protected override object[] ReadRowObjectValues() + { + var row = new object[_fields.Count]; + try + { + if (_fields.Count > 0) + { + var nullBytes = _database.Xdr.ReadOpaque((int)Math.Ceiling(_fields.Count / 8d)); + var nullBits = new BitArray(nullBytes); + for (var i = 0; i < _fields.Count; i++) + { + if (nullBits.Get(i)) + { + row[i] = DBNull.Value; + } + else + { + row[i] = ReadRawValue(_database.Xdr, _fields[i]); + } + } + } + } + catch (IOException ex) + { + throw IscException.ForIOException(ex); + } + return row; + } protected override DbValue[] ReadRow() { var row = new DbValue[_fields.Count]; @@ -189,6 +217,34 @@ protected override async ValueTask ReadRowAsync(CancellationToken can } return row; } + protected override async ValueTask ReadRowObjectValuesAsync(CancellationToken cancellationToken = default) + { + var row = new object[_fields.Count]; + try + { + if (_fields.Count > 0) + { + var nullBytes = await _database.Xdr.ReadOpaqueAsync((int)Math.Ceiling(_fields.Count / 8d), cancellationToken).ConfigureAwait(false); + var nullBits = new BitArray(nullBytes); + for (var i = 0; i < _fields.Count; i++) + { + if (nullBits.Get(i)) + { + row[i] = DBNull.Value; + } + else + { + row[i] = await ReadRawValueAsync(_database.Xdr, _fields[i], cancellationToken).ConfigureAwait(false); + } + } + } + } + catch (IOException ex) + { + throw IscException.ForIOException(ex); + } + return row; + } #endregion } From 1525364610947cc313593f530572371f7dee602d Mon Sep 17 00:00:00 2001 From: BFuerchau <92720745+BFuerchau@users.noreply.github.com> Date: Mon, 2 Oct 2023 17:01:18 +0200 Subject: [PATCH 3/4] Update GdsStatement.cs Simplified to ReadRow(), ReadRowAsync() --- .../Client/Managed/Version10/GdsStatement.cs | 120 +++++------------- 1 file changed, 30 insertions(+), 90 deletions(-) diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs index d085b920..a4d5c18e 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs @@ -111,7 +111,8 @@ public GdsStatement(GdsDatabase database, GdsTransaction transaction) { _handle = IscCodes.INVALID_OBJECT; _fetchSize = 200; - _rows = new Queue(); + //_rows = new Queue(); + _rows = new Queue(); // optimized OutputParameters = new Queue(); _database = database; @@ -376,10 +377,20 @@ public override async ValueTask ExecuteAsync(int timeout, IDescriptorFiller desc } } + protected DbValue[] _dbValues; // optimized + protected void CreateDbValues() + { + _dbValues = new DbValue[_fields.Count]; + for (int i = 0; i < _fields.Count; i++) + { + _dbValues[i] = new DbValue(this, _fields[i], null); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected DbValue[] GetDbValues(object[] values) { - for (int i = 0; i < _fields.Count; ++i) + for (int i=0; i < _fields.Count; ++i) { _dbValues[i].SetValue(values[i]); } @@ -423,15 +434,13 @@ public override DbValue[] Fetch() var hasOperation = true; while (!_allRowsFetched) { - var response = hasOperation + if ((hasOperation ? _database.ReadResponse(operation) - : _database.ReadResponse(); - hasOperation = false; - if (response is FetchResponse fetchResponse) + : _database.ReadResponse()) is FetchResponse fetchResponse) { if (fetchResponse.Count > 0 && fetchResponse.Status == 0) { - _rows.Enqueue(ReadRowObjectValues()); + _rows.Enqueue(ReadRow()); } else if (fetchResponse.Status == 100) { @@ -446,6 +455,7 @@ public override DbValue[] Fetch() { break; } + hasOperation = false; } } else @@ -469,18 +479,6 @@ public override DbValue[] Fetch() return null; } } - - protected DbValue[] _dbValues; - protected void CreateDbValues() - { - _dbValues = new DbValue[_fields.Count]; - for (int i = 0; i < _fields.Count; i++) - { - _dbValues[i] = new DbValue(this, _fields[i], null); - } - } - - public override async ValueTask FetchAsync(CancellationToken cancellationToken = default) { EnsureNotDeallocated(); @@ -514,19 +512,16 @@ public override async ValueTask FetchAsync(CancellationToken cancella if (operation == IscCodes.op_fetch_response) { CreateDbValues(); - var hasOperation = true; while (!_allRowsFetched) { - var response = hasOperation + if ((hasOperation ? await _database.ReadResponseAsync(operation, cancellationToken).ConfigureAwait(false) - : await _database.ReadResponseAsync(cancellationToken).ConfigureAwait(false); - hasOperation = false; - if (response is FetchResponse fetchResponse) + : await _database.ReadResponseAsync(cancellationToken).ConfigureAwait(false)) is FetchResponse fetchResponse) { if (fetchResponse.Count > 0 && fetchResponse.Status == 0) { - _rows.Enqueue(await ReadRowObjectValuesAsync(cancellationToken).ConfigureAwait(false)); + _rows.Enqueue(await ReadRowAsync(cancellationToken).ConfigureAwait(false)); } else if (fetchResponse.Status == 100) { @@ -541,6 +536,7 @@ public override async ValueTask FetchAsync(CancellationToken cancella { break; } + hasOperation = false; } } else @@ -877,7 +873,8 @@ protected void ProcessStoredProcedureExecuteResponse(SqlResponse response) { if (response.Count > 0) { - OutputParameters.Enqueue(ReadRow()); + CreateDbValues(); + OutputParameters.Enqueue(GetDbValues(ReadRow())); } } catch (IOException ex) @@ -891,7 +888,8 @@ protected async ValueTask ProcessStoredProcedureExecuteResponseAsync(SqlResponse { if (response.Count > 0) { - OutputParameters.Enqueue(await ReadRowAsync(cancellationToken).ConfigureAwait(false)); + CreateDbValues(); + OutputParameters.Enqueue(GetDbValues(await ReadRowAsync(cancellationToken).ConfigureAwait(false))); } } catch (IOException ex) @@ -1762,7 +1760,7 @@ protected void ClearAll() _fields = null; } - protected virtual object[] ReadRowObjectValues() // Optimized + protected virtual object[] ReadRow() { var row = new object[_fields.Count]; try @@ -1773,7 +1771,7 @@ protected virtual object[] ReadRowObjectValues() // Optimized var sqlInd = _database.Xdr.ReadInt32(); if (sqlInd == -1) { - row[i] = DBNull.Value; + row[i] = null; } else if (sqlInd == 0) { @@ -1792,36 +1790,8 @@ protected virtual object[] ReadRowObjectValues() // Optimized return row; } - protected virtual DbValue[] ReadRow() - { - var row = new DbValue[_fields.Count]; - try - { - for (var i = 0; i < _fields.Count; i++) - { - var value = ReadRawValue(_database.Xdr, _fields[i]); - var sqlInd = _database.Xdr.ReadInt32(); - if (sqlInd == -1) - { - row[i] = new DbValue(this, _fields[i], null); - } - else if (sqlInd == 0) - { - row[i] = new DbValue(this, _fields[i], value); - } - else - { - throw IscException.ForStrParam($"Invalid {nameof(sqlInd)} value: {sqlInd}."); - } - } - } - catch (IOException ex) - { - throw IscException.ForIOException(ex); - } - return row; - } - protected virtual async ValueTask ReadRowObjectValuesAsync(CancellationToken cancellationToken = default) + + protected virtual async ValueTask ReadRowAsync(CancellationToken cancellationToken = default) { var row = new object[_fields.Count]; try @@ -1832,7 +1802,7 @@ protected virtual async ValueTask ReadRowObjectValuesAsync(Cancellatio var sqlInd = await _database.Xdr.ReadInt32Async(cancellationToken).ConfigureAwait(false); if (sqlInd == -1) { - row[i] = DBNull.Value; + row[i] = null; } else if (sqlInd == 0) { @@ -1851,36 +1821,6 @@ protected virtual async ValueTask ReadRowObjectValuesAsync(Cancellatio return row; } - protected virtual async ValueTask ReadRowAsync(CancellationToken cancellationToken = default) - { - var row = new DbValue[_fields.Count]; - try - { - for (var i = 0; i < _fields.Count; i++) - { - var value = await ReadRawValueAsync(_database.Xdr, _fields[i], cancellationToken).ConfigureAwait(false); - var sqlInd = await _database.Xdr.ReadInt32Async(cancellationToken).ConfigureAwait(false); - if (sqlInd == -1) - { - row[i] = new DbValue(this, _fields[i], null); - } - else if (sqlInd == 0) - { - row[i] = new DbValue(this, _fields[i], value); - } - else - { - throw IscException.ForStrParam($"Invalid {nameof(sqlInd)} value: {sqlInd}."); - } - } - } - catch (IOException ex) - { - throw IscException.ForIOException(ex); - } - return row; - } - #endregion #region Protected Internal Methods From 19350854cb75413b4efeeacea3657851087c4500 Mon Sep 17 00:00:00 2001 From: BFuerchau <92720745+BFuerchau@users.noreply.github.com> Date: Mon, 2 Oct 2023 17:03:38 +0200 Subject: [PATCH 4/4] Update GdsStatement.cs Simplified to ReadRow(), ReadRowAsync() --- .../Client/Managed/Version13/GdsStatement.cs | 67 ++----------------- 1 file changed, 5 insertions(+), 62 deletions(-) diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs index 2cfc55ed..c09d5c42 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version13/GdsStatement.cs @@ -131,7 +131,7 @@ protected override async ValueTask WriteParametersAsync(CancellationToke } } - protected override object[] ReadRowObjectValues() + protected override object[] ReadRow() { var row = new object[_fields.Count]; try @@ -144,7 +144,7 @@ protected override object[] ReadRowObjectValues() { if (nullBits.Get(i)) { - row[i] = DBNull.Value; + row[i] = null; } else { @@ -159,65 +159,8 @@ protected override object[] ReadRowObjectValues() } return row; } - protected override DbValue[] ReadRow() - { - var row = new DbValue[_fields.Count]; - try - { - if (_fields.Count > 0) - { - var nullBytes = _database.Xdr.ReadOpaque((int)Math.Ceiling(_fields.Count / 8d)); - var nullBits = new BitArray(nullBytes); - for (var i = 0; i < _fields.Count; i++) - { - if (nullBits.Get(i)) - { - row[i] = new DbValue(this, _fields[i], null); - } - else - { - var value = ReadRawValue(_database.Xdr, _fields[i]); - row[i] = new DbValue(this, _fields[i], value); - } - } - } - } - catch (IOException ex) - { - throw IscException.ForIOException(ex); - } - return row; - } - protected override async ValueTask ReadRowAsync(CancellationToken cancellationToken = default) - { - var row = new DbValue[_fields.Count]; - try - { - if (_fields.Count > 0) - { - var nullBytes = await _database.Xdr.ReadOpaqueAsync((int)Math.Ceiling(_fields.Count / 8d), cancellationToken).ConfigureAwait(false); - var nullBits = new BitArray(nullBytes); - for (var i = 0; i < _fields.Count; i++) - { - if (nullBits.Get(i)) - { - row[i] = new DbValue(this, _fields[i], null); - } - else - { - var value = await ReadRawValueAsync(_database.Xdr, _fields[i], cancellationToken).ConfigureAwait(false); - row[i] = new DbValue(this, _fields[i], value); - } - } - } - } - catch (IOException ex) - { - throw IscException.ForIOException(ex); - } - return row; - } - protected override async ValueTask ReadRowObjectValuesAsync(CancellationToken cancellationToken = default) + + protected override async ValueTask ReadRowAsync(CancellationToken cancellationToken = default) { var row = new object[_fields.Count]; try @@ -230,7 +173,7 @@ protected override async ValueTask ReadRowObjectValuesAsync(Cancellati { if (nullBits.Get(i)) { - row[i] = DBNull.Value; + row[i] = null; } else {