Skip to content

Commit

Permalink
Clean up Stopwatch a bit (#111834)
Browse files Browse the repository at this point in the history
* Clean up Stopwatch a bit

- Remove unnecessary Reset call from ctor
- Remove unnecessary branch for _elapsed < 0
- Remove some defunct comments
- Clean up style of Stop to match that of Start

* More stylistic cleanup

- Use expression-bodied properties
- Remove unnecessary Debug.Asserts
- Remove private method whose impl could just have been that of an existing public property
- Change a private GetXx method to an Xx property.
- Remove some type names that could be inferred
  • Loading branch information
stephentoub authored Jan 26, 2025
1 parent 843f798 commit f41b65f
Showing 1 changed file with 22 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,22 @@

namespace System.Diagnostics
{
// This class uses high-resolution performance counter if the installed
// hardware supports it. Otherwise, the class will fall back to DateTime
// and uses ticks as a measurement.

[DebuggerDisplay("{DebuggerDisplay,nq}")]
public partial class Stopwatch
{
private long _elapsed;
private long _startTimeStamp;
private bool _isRunning;

// "Frequency" stores the frequency of the high-resolution performance counter,
// if one exists. Otherwise it will store TicksPerSecond.
// The frequency cannot change while the system is running,
// so we only need to initialize it once.
public static readonly long Frequency = QueryPerformanceFrequency();
public static readonly bool IsHighResolution = true;

// performance-counter frequency, in counts per ticks.
// This can speed up conversion from high frequency performance-counter
// to ticks.
// This can speed up conversion to ticks.
private static readonly double s_tickFrequency = (double)TimeSpan.TicksPerSecond / Frequency;

public Stopwatch()
{
Reset();
}

public void Start()
Expand All @@ -43,7 +33,7 @@ public void Start()

public static Stopwatch StartNew()
{
Stopwatch s = new Stopwatch();
Stopwatch s = new();
s.Start();
return s;
}
Expand All @@ -53,29 +43,16 @@ public void Stop()
// Calling stop on a stopped Stopwatch is a no-op.
if (_isRunning)
{
long endTimeStamp = GetTimestamp();
long elapsedThisPeriod = endTimeStamp - _startTimeStamp;
_elapsed += elapsedThisPeriod;
_elapsed += GetTimestamp() - _startTimeStamp;
_isRunning = false;

if (_elapsed < 0)
{
// When measuring small time periods the Stopwatch.Elapsed*
// properties can return negative values. This is due to
// bugs in the basic input/output system (BIOS) or the hardware
// abstraction layer (HAL) on machines with variable-speed CPUs
// (e.g. Intel SpeedStep).

_elapsed = 0;
}
}
}

public void Reset()
{
_elapsed = 0;
_isRunning = false;
_startTimeStamp = 0;
_isRunning = false;
}

// Convenience method for replacing {sw.Reset(); sw.Start();} with a single sw.Restart()
Expand All @@ -94,32 +71,30 @@ public void Restart()
/// </returns>
public override string ToString() => Elapsed.ToString();

public bool IsRunning
{
get { return _isRunning; }
}
public bool IsRunning => _isRunning;

public TimeSpan Elapsed
{
get { return new TimeSpan(GetElapsedDateTimeTicks()); }
}
public TimeSpan Elapsed => new(ElapsedTimeSpanTicks);

public long ElapsedMilliseconds
{
get { return GetElapsedDateTimeTicks() / TimeSpan.TicksPerMillisecond; }
}
public long ElapsedMilliseconds => ElapsedTimeSpanTicks / TimeSpan.TicksPerMillisecond;

public long ElapsedTicks
{
get { return GetRawElapsedTicks(); }
}
get
{
long timeElapsed = _elapsed;

public static long GetTimestamp()
{
Debug.Assert(IsHighResolution);
return QueryPerformanceCounter();
// If the Stopwatch is running, add elapsed time since the Stopwatch is started last time.
if (_isRunning)
{
timeElapsed += GetTimestamp() - _startTimeStamp;
}

return timeElapsed;
}
}

public static long GetTimestamp() => QueryPerformanceCounter();

/// <summary>Gets the elapsed time since the <paramref name="startingTimestamp"/> value retrieved using <see cref="GetTimestamp"/>.</summary>
/// <param name="startingTimestamp">The timestamp marking the beginning of the time period.</param>
/// <returns>A <see cref="TimeSpan"/> for the elapsed time between the starting timestamp and the time of this call.</returns>
Expand All @@ -131,31 +106,9 @@ public static TimeSpan GetElapsedTime(long startingTimestamp) =>
/// <param name="endingTimestamp">The timestamp marking the end of the time period.</param>
/// <returns>A <see cref="TimeSpan"/> for the elapsed time between the starting and ending timestamps.</returns>
public static TimeSpan GetElapsedTime(long startingTimestamp, long endingTimestamp) =>
new TimeSpan((long)((endingTimestamp - startingTimestamp) * s_tickFrequency));

// Get the elapsed ticks.
private long GetRawElapsedTicks()
{
long timeElapsed = _elapsed;
new((long)((endingTimestamp - startingTimestamp) * s_tickFrequency));

if (_isRunning)
{
// If the Stopwatch is running, add elapsed time since
// the Stopwatch is started last time.
long currentTimeStamp = GetTimestamp();
long elapsedUntilNow = currentTimeStamp - _startTimeStamp;
timeElapsed += elapsedUntilNow;
}
return timeElapsed;
}

// Get the elapsed ticks.
private long GetElapsedDateTimeTicks()
{
Debug.Assert(IsHighResolution);
// convert high resolution perf counter to DateTime ticks
return unchecked((long)(GetRawElapsedTicks() * s_tickFrequency));
}
private long ElapsedTimeSpanTicks => (long)(ElapsedTicks * s_tickFrequency);

private string DebuggerDisplay => $"{Elapsed} (IsRunning = {_isRunning})";
}
Expand Down

0 comments on commit f41b65f

Please sign in to comment.