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

Perf: Cache delegates for non query and xml reader calls #1194

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
private static int _objectTypeCount; // EventSource Counter
internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText;

// these static delegates are used to avoid per-call instance delegate allocations.
// They work by storing a delegate to a static function which takes the instance as a parameter, unpacks it and makes the call.
private static readonly Func<AsyncCallback, object, IAsyncResult> s_beginExecuteReaderAsync = BeginExecuteReaderAsyncCallback;
private static readonly Func<IAsyncResult, SqlDataReader> s_endExecuteReaderAsync = EndExecuteReaderAsyncCallback;
private static readonly Action<Task<SqlDataReader>> s_cleanupExecuteReaderAsync = CleanupExecuteReaderAsyncCallback;
Expand All @@ -37,6 +39,10 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
private static readonly Func<SqlCommand, CommandBehavior, AsyncCallback, object, int, bool, bool, IAsyncResult> s_beginExecuteReaderInternal = BeginExecuteReaderInternalCallback;
private static readonly Func<SqlCommand, CommandBehavior, AsyncCallback, object, int, bool, bool, IAsyncResult> s_beginExecuteXmlReaderInternal = BeginExecuteXmlReaderInternalCallback;
private static readonly Func<SqlCommand, CommandBehavior, AsyncCallback, object, int, bool, bool, IAsyncResult> s_beginExecuteNonQueryInternal = BeginExecuteNonQueryInternalCallback;
private static readonly Func<AsyncCallback, object, IAsyncResult> s_beginExecuteNonQueryAsync = BeginExecuteNonQueryAsyncCallback;
private static readonly Func<IAsyncResult, int> s_endExecuteNonQueryAsync = EndExecuteNonQueryAsyncCallback;
private static readonly Func<AsyncCallback, object, IAsyncResult> s_beginExecuteXmlReaderAsync = BeginExecuteXmlReaderAsyncCallback;
private static readonly Func<IAsyncResult, XmlReader> s_endExecuteXmlReaderAsync = EndExecuteXmlReaderAsyncCallback;

internal sealed class ExecuteReaderAsyncCallContext : AAsyncCallContext<SqlCommand, SqlDataReader>
{
Expand Down Expand Up @@ -1177,6 +1183,11 @@ public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObj
return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false);
}

private static IAsyncResult BeginExecuteNonQueryAsyncCallback(AsyncCallback callback, object stateObject)
{
return ((SqlCommand)stateObject).BeginExecuteNonQueryAsync(callback, stateObject);
}

private IAsyncResult BeginExecuteNonQueryAsync(AsyncCallback callback, object stateObject)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteNonQueryAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
Expand Down Expand Up @@ -1387,6 +1398,11 @@ private void ThrowIfReconnectionHasBeenCanceled()
}
}

private static int EndExecuteNonQueryAsyncCallback(IAsyncResult asyncResult)
{
return ((SqlCommand)asyncResult.AsyncState).EndExecuteNonQueryAsync(asyncResult);
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/EndExecuteNonQueryAsync[@name="IAsyncResult"]/*'/>
public int EndExecuteNonQueryAsync(IAsyncResult asyncResult)
{
Expand Down Expand Up @@ -1684,6 +1700,10 @@ public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateOb
return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false);
}

private static IAsyncResult BeginExecuteXmlReaderAsyncCallback(AsyncCallback callback, object stateObject)
{
return ((SqlCommand)stateObject).BeginExecuteXmlReaderAsync(callback, stateObject);
}
private IAsyncResult BeginExecuteXmlReaderAsync(AsyncCallback callback, object stateObject)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
Expand Down Expand Up @@ -1809,6 +1829,11 @@ public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult)
}
}

private static XmlReader EndExecuteXmlReaderAsyncCallback(IAsyncResult asyncResult)
{
return ((SqlCommand)asyncResult.AsyncState).EndExecuteXmlReaderAsync(asyncResult);
}

private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.EndExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
Expand Down Expand Up @@ -2478,28 +2503,31 @@ private Task<int> InternalExecuteNonQueryAsync(CancellationToken cancellationTok
{
returnedTask = RegisterForConnectionCloseNotification(returnedTask);

Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) =>
{
registration.Dispose();
if (t.IsFaulted)
{
Exception e = t.Exception.InnerException;
_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
source.SetException(e);
}
else
Task<int>.Factory.FromAsync(s_beginExecuteNonQueryAsync, s_endExecuteNonQueryAsync, this).ContinueWith(
(Task<int> t) =>
{
if (t.IsCanceled)
registration.Dispose();
if (t.IsFaulted)
{
source.SetCanceled();
Exception e = t.Exception.InnerException;
_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
source.SetException(e);
}
else
{
source.SetResult(t.Result);
if (t.IsCanceled)
{
source.SetCanceled();
}
else
{
source.SetResult(t.Result);
}
_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
}
_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
}
}, TaskScheduler.Default);
},
TaskScheduler.Default
);
}
catch (Exception e)
{
Expand Down Expand Up @@ -2755,28 +2783,31 @@ private Task<XmlReader> InternalExecuteXmlReaderAsync(CancellationToken cancella
{
returnedTask = RegisterForConnectionCloseNotification(returnedTask);

Task<XmlReader>.Factory.FromAsync(BeginExecuteXmlReaderAsync, EndExecuteXmlReaderAsync, null).ContinueWith((t) =>
{
registration.Dispose();
if (t.IsFaulted)
Task<XmlReader>.Factory.FromAsync(s_beginExecuteXmlReaderAsync, s_endExecuteXmlReaderAsync, this).ContinueWith(
(Task<XmlReader> t) =>
{
Exception e = t.Exception.InnerException;
_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
source.SetException(e);
}
else
{
if (t.IsCanceled)
registration.Dispose();
if (t.IsFaulted)
{
source.SetCanceled();
Exception e = t.Exception.InnerException;
_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
source.SetException(e);
}
else
{
source.SetResult(t.Result);
if (t.IsCanceled)
{
source.SetCanceled();
}
else
{
source.SetResult(t.Result);
}
_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
}
_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
}
}, TaskScheduler.Default);
},
TaskScheduler.Default
);
}
catch (Exception e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ public sealed class SqlCommand : DbCommand, ICloneable
private static int _objectTypeCount; // EventSource Counter
internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);

// these static delegates are used to avoid per-call instance delegate allocations.
// They work by storing a delegate to a static function which takes the instance as a parameter, unpacks it and makes the call.
private static readonly Func<AsyncCallback, object, IAsyncResult> s_beginExecuteNonQueryAsync = BeginExecuteNonQueryAsyncCallback;
private static readonly Func<IAsyncResult, int> s_endExecuteNonQueryAsync = EndExecuteNonQueryAsyncCallback;
private static readonly Func<AsyncCallback, object, IAsyncResult> s_beginExecuteXmlReaderAsync = BeginExecuteXmlReaderAsyncCallback;
private static readonly Func<IAsyncResult, XmlReader> s_endExecuteXmlReaderAsync = EndExecuteXmlReaderAsyncCallback;

private string _commandText;
private CommandType _commandType;
private int? _commandTimeout;
Expand Down Expand Up @@ -1511,6 +1518,11 @@ public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObj
return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false);
}

private static IAsyncResult BeginExecuteNonQueryAsyncCallback(AsyncCallback callback, object stateObject)
{
return ((SqlCommand)stateObject).BeginExecuteNonQueryAsync(callback, stateObject);
}

private IAsyncResult BeginExecuteNonQueryAsync(AsyncCallback callback, object stateObject)
{
return BeginExecuteNonQueryInternal(0, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true);
Expand Down Expand Up @@ -1745,6 +1757,11 @@ private void ThrowIfReconnectionHasBeenCanceled()
}
}

private static int EndExecuteNonQueryAsyncCallback(IAsyncResult asyncResult)
{
return ((SqlCommand)asyncResult.AsyncState).EndExecuteNonQueryAsync(asyncResult);
}

private int EndExecuteNonQueryAsync(IAsyncResult asyncResult)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent("<sc.SqlCommand.EndExecuteNonQueryAsync|Info|Correlation> ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current);
Expand Down Expand Up @@ -2106,6 +2123,11 @@ public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateOb
return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false);
}

private static IAsyncResult BeginExecuteXmlReaderAsyncCallback(AsyncCallback callback, object stateObject)
{
return ((SqlCommand)stateObject).BeginExecuteXmlReaderAsync(callback, stateObject);
}

private IAsyncResult BeginExecuteXmlReaderAsync(AsyncCallback callback, object stateObject)
{
return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true);
Expand Down Expand Up @@ -2258,6 +2280,11 @@ public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult)
}
}

private static XmlReader EndExecuteXmlReaderAsyncCallback(IAsyncResult asyncResult)
{
return ((SqlCommand)asyncResult.AsyncState).EndExecuteXmlReaderAsync(asyncResult);
}

private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult)
{
SqlClientEventSource.Log.TryCorrelationTraceEvent("<sc.SqlCommand.EndExecuteXmlReaderAsync|Info|Correlation> ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current);
Expand Down Expand Up @@ -2953,26 +2980,29 @@ private Task<int> InternalExecuteNonQueryAsync(CancellationToken cancellationTok
{
RegisterForConnectionCloseNotification(ref returnedTask);

Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) =>
{
registration.Dispose();
if (t.IsFaulted)
{
Exception e = t.Exception.InnerException;
source.SetException(e);
}
else
Task<int>.Factory.FromAsync(s_beginExecuteNonQueryAsync, s_endExecuteNonQueryAsync, this).ContinueWith(
(Task<int> t) =>
{
if (t.IsCanceled)
registration.Dispose();
if (t.IsFaulted)
{
source.SetCanceled();
Exception e = t.Exception.InnerException;
source.SetException(e);
}
else
{
source.SetResult(t.Result);
if (t.IsCanceled)
{
source.SetCanceled();
}
else
{
source.SetResult(t.Result);
}
}
}
}, TaskScheduler.Default);
},
TaskScheduler.Default
);
}
catch (Exception e)
{
Expand Down Expand Up @@ -3200,26 +3230,29 @@ private Task<XmlReader> InternalExecuteXmlReaderAsync(CancellationToken cancella
{
RegisterForConnectionCloseNotification(ref returnedTask);

Task<XmlReader>.Factory.FromAsync(BeginExecuteXmlReaderAsync, EndExecuteXmlReaderAsync, null).ContinueWith((t) =>
{
registration.Dispose();
if (t.IsFaulted)
{
Exception e = t.Exception.InnerException;
source.SetException(e);
}
else
Task<XmlReader>.Factory.FromAsync(s_beginExecuteXmlReaderAsync, s_endExecuteXmlReaderAsync, this).ContinueWith(
(Task<XmlReader> t) =>
{
if (t.IsCanceled)
registration.Dispose();
if (t.IsFaulted)
{
source.SetCanceled();
Exception e = t.Exception.InnerException;
source.SetException(e);
}
else
{
source.SetResult(t.Result);
if (t.IsCanceled)
{
source.SetCanceled();
}
else
{
source.SetResult(t.Result);
}
}
}
}, TaskScheduler.Default);
},
TaskScheduler.Default
);
}
catch (Exception e)
{
Expand Down