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

Make capping happen after scale of the CPU usage in ResourceUtilization #5388

Merged
merged 5 commits into from
Sep 4, 2024
Merged
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 @@ -38,8 +38,7 @@ public static ResourceUtilization CalculateUtilization(in Snapshot first, in Sna
long newUsageTicks = second.KernelTimeSinceStart.Ticks + second.UserTimeSinceStart.Ticks;
long totalUsageTickDelta = newUsageTicks - oldUsageTicks;

var utilization = Math.Max(0.0, totalUsageTickDelta / totalSystemTicks * Hundred);
var cpuUtilization = Math.Min(Hundred, utilization);
double cpuUtilization = Math.Max(0.0, totalUsageTickDelta / totalSystemTicks * Hundred);

return new ResourceUtilization(cpuUtilization, second.MemoryUsageInBytes, systemResources, second);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public ResourceUtilization(double cpuUsedPercentage, ulong memoryUsedInBytes, Sy
guaranteedCpuUnits = 1;
}

CpuUsedPercentage = Throw.IfLessThan(cpuUsedPercentage / guaranteedCpuUnits, 0.0);
CpuUsedPercentage = Math.Min(Hundred, Throw.IfLessThan(cpuUsedPercentage / guaranteedCpuUnits, 0.0));
MemoryUsedInBytes = Throw.IfLessThan(memoryUsedInBytes, 0);
SystemResources = systemResources;
MemoryUsedPercentage = Math.Min(Hundred, (double)MemoryUsedInBytes / systemResources.GuaranteedMemoryInBytes * Hundred);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ public sealed class CalculatorTests
[Fact]
public void BasicCalculation()
{
var secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));
TimeSpan secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));

// Now, what's the total number of available ticks between the two samples (for a single core)
var totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;
long totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;

var second = new Snapshot(
Snapshot second = new(
totalTimeSinceStart: secondSnapshotTimeSpan,

// assign 25% to kernel time
Expand All @@ -44,7 +44,7 @@ public void BasicCalculation()
memoryUsageInBytes: 500);

// Now, when we run the calculator, CPU should be at 50%.
var record = Calculator.CalculateUtilization(_firstSnapshot, second, _resources);
ResourceUtilization record = Calculator.CalculateUtilization(_firstSnapshot, second, _resources);
Assert.Equal(50.0, record.CpuUsedPercentage);

// Because we set it basically, memory should also clearly be at 50%.
Expand All @@ -61,14 +61,14 @@ public void BasicCalculation()
[Fact]
public void BasicCalculation_WithHalfCpuUnits()
{
var limitedResources = new SystemResources(0.5, 0.5, TotalMemoryInBytes, TotalMemoryInBytes);
SystemResources limitedResources = new(guaranteedCpuUnits: 0.5, maximumCpuUnits: 0.5, TotalMemoryInBytes, TotalMemoryInBytes);

var secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));
TimeSpan secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));

// Now, what's the total number of available ticks between the two samples (for a single core)
var totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;
long totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;

var second = new Snapshot(
Snapshot second = new(
totalTimeSinceStart: secondSnapshotTimeSpan,

// assign 25% to kernel time
Expand All @@ -80,7 +80,7 @@ public void BasicCalculation_WithHalfCpuUnits()

// Using the limited resources, CPU time is now cut in half. So, when we run
// the calculator, the CPU utilization should be at 100%.
var record = Calculator.CalculateUtilization(_firstSnapshot, second, limitedResources);
ResourceUtilization record = Calculator.CalculateUtilization(_firstSnapshot, second, limitedResources);
Assert.Equal(100.0, record.CpuUsedPercentage);
}

Expand All @@ -91,7 +91,7 @@ public void BasicCalculation_WithHalfCpuUnits()
public void Zeroes()
{
// No changes in the second snapshot
var secondSnapshot = new Snapshot(
Snapshot secondSnapshot = new(
totalTimeSinceStart: _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5)),
memoryUsageInBytes: 0,
kernelTimeSinceStart: _firstSnapshot.KernelTimeSinceStart,
Expand All @@ -100,7 +100,7 @@ public void Zeroes()
// Now, let's set each of kernel and user time to no time elapsed.

// Now, when we run the calculator, CPU should be at 0%.
var record = Calculator.CalculateUtilization(_firstSnapshot, secondSnapshot, _resources);
ResourceUtilization record = Calculator.CalculateUtilization(_firstSnapshot, secondSnapshot, _resources);
Assert.Equal(0.0, record.CpuUsedPercentage);

// Because we set it basically, memory should also clearly be at 0%.
Expand All @@ -118,19 +118,19 @@ public void Zeroes()
[Fact]
public void TimeGoesBackwards()
{
var firstSnapshot = new Snapshot(
Snapshot firstSnapshot = new(
totalTimeSinceStart: TimeSpan.FromTicks(new FakeTimeProvider().GetUtcNow().Ticks),
kernelTimeSinceStart: TimeSpan.FromTicks(1000),
userTimeSinceStart: TimeSpan.FromTicks(1000),
memoryUsageInBytes: 0);
var secondSnapshot = new Snapshot(
Snapshot secondSnapshot = new(
totalTimeSinceStart: firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5)),
memoryUsageInBytes: 0,
kernelTimeSinceStart: TimeSpan.FromTicks(firstSnapshot.KernelTimeSinceStart.Ticks - 1),
userTimeSinceStart: TimeSpan.FromTicks(firstSnapshot.UserTimeSinceStart.Ticks - 1));

// Now, when we run the calculator, CPU should be at 0%.
var record = Calculator.CalculateUtilization(firstSnapshot, secondSnapshot, _resources);
ResourceUtilization record = Calculator.CalculateUtilization(firstSnapshot, secondSnapshot, _resources);
Assert.Equal(0.0, record.CpuUsedPercentage);
}

Expand All @@ -141,19 +141,19 @@ public void TimeGoesBackwards()
[Fact]
public void FullyUtilized()
{
var secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));
TimeSpan secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));

// Now, what's the total number of available ticks between the two samples.
var totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;
long totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;

var secondSnapshot = new Snapshot(
Snapshot secondSnapshot = new(
totalTimeSinceStart: secondSnapshotTimeSpan,
kernelTimeSinceStart: TimeSpan.FromTicks(totalAvailableTicks / 2),
userTimeSinceStart: TimeSpan.FromTicks(totalAvailableTicks / 2),
memoryUsageInBytes: 1000);

// Now, when we run the calculator, CPU should 100%.
var record = Calculator.CalculateUtilization(_firstSnapshot, secondSnapshot, _resources);
ResourceUtilization record = Calculator.CalculateUtilization(_firstSnapshot, secondSnapshot, _resources);
Assert.Equal(100.0, record.CpuUsedPercentage);

// Assert that memory is at 100%.
Expand All @@ -166,15 +166,18 @@ public void FullyUtilized()
/// Ensure that stats work appropriately if the resources are overutilized.
/// </summary>
/// <remarks>The highest possible CPU and memory percentages should be 100%.</remarks>
[Fact]
public void OverUtilized()
[Theory]
[InlineData(1, 100.0)]
[InlineData(2, 100.0)]
public void OverUtilized(double cpuUnits, double expectedCpuUsage)
{
var secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));
SystemResources limitedResources = new(cpuUnits, cpuUnits, TotalMemoryInBytes, TotalMemoryInBytes);
TimeSpan secondSnapshotTimeSpan = _firstSnapshot.TotalTimeSinceStart.Add(TimeSpan.FromSeconds(5));

// Now, what's the total number of available ticks between the two samples.
var totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;
long totalAvailableTicks = secondSnapshotTimeSpan.Ticks - _firstSnapshot.TotalTimeSinceStart.Ticks;

var secondSnapshot = new Snapshot(
Snapshot secondSnapshot = new(
totalTimeSinceStart: secondSnapshotTimeSpan,

// Set each of kernel and uesr time to all the available ticks between
Expand All @@ -184,8 +187,8 @@ public void OverUtilized()
memoryUsageInBytes: 1500);

// Now, when we run the calculator, CPU should be at 100%.
var record = Calculator.CalculateUtilization(_firstSnapshot, secondSnapshot, _resources);
Assert.Equal(100.0, record.CpuUsedPercentage);
ResourceUtilization record = Calculator.CalculateUtilization(_firstSnapshot, secondSnapshot, _resources);
Assert.Equal(expectedCpuUsage, record.CpuUsedPercentage);

// Assert that memory is at 100%
Assert.Equal(100.0, record.MemoryUsedPercentage);
Expand Down