Skip to content

Commit 69a924e

Browse files
cheenamalhotraCopilotSAY14489
authored
[5.1] Optimization: Use Environment.TickCount for SqlStatistics execution timing (#3831)
Co-authored-by: Copilot <[email protected]> Co-authored-by: Yusuf Mohammed <[email protected]>
1 parent cc5c81a commit 69a924e

File tree

5 files changed

+89
-12
lines changed

5 files changed

+89
-12
lines changed

src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,13 @@ internal static Delegate FindBuilder(MulticastDelegate mcd)
582582

583583
internal static long TimerCurrent() => DateTime.UtcNow.ToFileTimeUtc();
584584

585+
internal static long FastTimerCurrent() => Environment.TickCount;
586+
587+
internal static uint CalculateTickCountElapsed(long startTick, long endTick)
588+
{
589+
return (uint)(endTick - startTick);
590+
}
591+
585592
internal static long TimerFromSeconds(int seconds)
586593
{
587594
long result = checked((long)seconds * TimeSpan.TicksPerSecond);

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ internal static ValueSqlStatisticsScope TimedScope(SqlStatistics statistics)
3838
// internal values that are not exposed through properties
3939
internal long _closeTimestamp;
4040
internal long _openTimestamp;
41-
internal long _startExecutionTimestamp;
41+
internal long? _startExecutionTimestamp;
4242
internal long _startFetchTimestamp;
4343
internal long _startNetworkServerTimestamp;
4444

@@ -80,7 +80,7 @@ internal bool WaitForDoneAfterRow
8080

8181
internal void ContinueOnNewConnection()
8282
{
83-
_startExecutionTimestamp = 0;
83+
_startExecutionTimestamp = null;
8484
_startFetchTimestamp = 0;
8585
_waitForDoneAfterRow = false;
8686
_waitForReply = false;
@@ -108,7 +108,7 @@ internal IDictionary GetDictionary()
108108
{ "UnpreparedExecs", _unpreparedExecs },
109109

110110
{ "ConnectionTime", ADP.TimerToMilliseconds(_connectionTime) },
111-
{ "ExecutionTime", ADP.TimerToMilliseconds(_executionTime) },
111+
{ "ExecutionTime", _executionTime },
112112
{ "NetworkServerTime", ADP.TimerToMilliseconds(_networkServerTime) }
113113
};
114114
Debug.Assert(dictionary.Count == Count);
@@ -117,17 +117,17 @@ internal IDictionary GetDictionary()
117117

118118
internal bool RequestExecutionTimer()
119119
{
120-
if (_startExecutionTimestamp == 0)
120+
if (!_startExecutionTimestamp.HasValue)
121121
{
122-
_startExecutionTimestamp = ADP.TimerCurrent();
122+
_startExecutionTimestamp = ADP.FastTimerCurrent();
123123
return true;
124124
}
125125
return false;
126126
}
127127

128128
internal void RequestNetworkServerTimer()
129129
{
130-
Debug.Assert(_startExecutionTimestamp != 0, "No network time expected outside execution period");
130+
Debug.Assert(_startExecutionTimestamp.HasValue, "No network time expected outside execution period");
131131
if (_startNetworkServerTimestamp == 0)
132132
{
133133
_startNetworkServerTimestamp = ADP.TimerCurrent();
@@ -137,10 +137,11 @@ internal void RequestNetworkServerTimer()
137137

138138
internal void ReleaseAndUpdateExecutionTimer()
139139
{
140-
if (_startExecutionTimestamp > 0)
140+
if (_startExecutionTimestamp.HasValue)
141141
{
142-
_executionTime += (ADP.TimerCurrent() - _startExecutionTimestamp);
143-
_startExecutionTimestamp = 0;
142+
uint elapsed = ADP.CalculateTickCountElapsed(_startExecutionTimestamp.Value, ADP.FastTimerCurrent());
143+
_executionTime += elapsed;
144+
_startExecutionTimestamp = null;
144145
}
145146
}
146147

@@ -176,7 +177,7 @@ internal void Reset()
176177
_unpreparedExecs = 0;
177178
_waitForDoneAfterRow = false;
178179
_waitForReply = false;
179-
_startExecutionTimestamp = 0;
180+
_startExecutionTimestamp = null;
180181
_startNetworkServerTimestamp = 0;
181182
}
182183

src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<Compile Include="SqlConnectionStringBuilderTest.cs" />
6363
<Compile Include="SerializeSqlTypesTest.cs" />
6464
<Compile Include="TestTdsServer.cs" />
65+
<Compile Include="TickCountElapsedTest.cs" />
6566
<Compile Include="SqlHelperTest.cs" />
6667
<Compile Include="..\..\src\Microsoft\Data\Common\MultipartIdentifier.cs" />
6768
<Compile Include="AssertExtensions.cs" />
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Reflection;
6+
using Microsoft.Data.Common;
7+
using Xunit;
8+
9+
namespace Microsoft.Data.SqlClient.Tests
10+
{
11+
12+
/// <summary>
13+
/// Tests for Environment.TickCount elapsed time calculation with wraparound handling.
14+
/// </summary>
15+
public sealed class TickCountElapsedTest
16+
{
17+
/// <summary>
18+
/// Invokes the internal CalculateTickCountElapsed method to compute elapsed time between two tick counts.
19+
/// </summary>
20+
/// <param name="startTick"></param>
21+
/// <param name="endTick"></param>
22+
/// <returns></returns>
23+
internal static uint CalculateTickCountElapsed(long startTick, long endTick) {
24+
var adpType = Assembly.GetAssembly(typeof(SqlConnection)).GetType("Microsoft.Data.Common.ADP");
25+
return (uint) adpType.GetMethod("CalculateTickCountElapsed", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] {startTick, endTick});
26+
}
27+
28+
/// <summary>
29+
/// Verifies that normal elapsed time calculation works correctly.
30+
/// </summary>
31+
[Fact]
32+
public void CalculateTickCountElapsed_NormalCase_ReturnsCorrectElapsed()
33+
{
34+
uint elapsed = CalculateTickCountElapsed(1000, 1500);
35+
Assert.Equal(500u, elapsed);
36+
}
37+
38+
/// <summary>
39+
/// Verifies that wraparound from int.MaxValue to int.MinValue is handled correctly.
40+
/// </summary>
41+
[Fact]
42+
public void CalculateTickCountElapsed_MaxWraparound_ReturnsOne()
43+
{
44+
uint elapsed = CalculateTickCountElapsed(int.MaxValue, int.MinValue);
45+
Assert.Equal(1u, elapsed);
46+
}
47+
48+
/// <summary>
49+
/// Verifies that partial wraparound scenarios work correctly.
50+
/// </summary>
51+
[Theory]
52+
[InlineData(2147483600, -2147483600, 96u)]
53+
[InlineData(2147483647, -2147483647, 2u)]
54+
public void CalculateTickCountElapsed_PartialWraparound_ReturnsCorrectElapsed(long start, long end, uint expected)
55+
{
56+
uint elapsed = CalculateTickCountElapsed(start, end);
57+
Assert.Equal(expected, elapsed);
58+
}
59+
60+
/// <summary>
61+
/// Verifies that zero elapsed time returns zero.
62+
/// </summary>
63+
[Fact]
64+
public void CalculateTickCountElapsed_ZeroElapsed_ReturnsZero()
65+
{
66+
uint elapsed = CalculateTickCountElapsed(1000, 1000);
67+
Assert.Equal(0u, elapsed);
68+
}
69+
}
70+
}

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReader.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ public static void Test(string srcConstr, string dstConstr, string dstTable)
5252

5353
Assert.True(0 < (long)stats["BytesReceived"], "BytesReceived is non-positive.");
5454
Assert.True(0 < (long)stats["BytesSent"], "BytesSent is non-positive.");
55-
Assert.True((long)stats["ConnectionTime"] >= (long)stats["ExecutionTime"], "Connection Time is less than Execution Time.");
56-
Assert.True((long)stats["ExecutionTime"] >= (long)stats["NetworkServerTime"], "Execution Time is less than Network Server Time.");
5755
DataTestUtility.AssertEqualsWithDescription((long)0, (long)stats["UnpreparedExecs"], "Non-zero UnpreparedExecs value: " + (long)stats["UnpreparedExecs"]);
5856
DataTestUtility.AssertEqualsWithDescription((long)0, (long)stats["PreparedExecs"], "Non-zero PreparedExecs value: " + (long)stats["PreparedExecs"]);
5957
DataTestUtility.AssertEqualsWithDescription((long)0, (long)stats["Prepares"], "Non-zero Prepares value: " + (long)stats["Prepares"]);

0 commit comments

Comments
 (0)