Skip to content
Draft
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
113 changes: 113 additions & 0 deletions core/diagnostics/PerformanceScenarios/BlockingScenarios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

internal static class BlockingScenarios
{
public static Task SyncOverAsyncAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
int taskCount = Math.Min(64, Math.Max(16, Environment.ProcessorCount * 2));
Task[] tasks = Enumerable.Range(0, taskCount)
.Select(_ => Task.Run(() => DelayedOperationAsync().Result))
.ToArray();
Task.WaitAll(tasks);
}
Comment on lines +11 to +15

return Task.CompletedTask;
}

public static async Task AsyncDelayAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
await Task.Delay(250, token);
}
}

public static Task ReaderWriterContentionAsync(CancellationToken token)
{
using ReaderWriterLockSlim gate = new();
int readerCount = Math.Min(32, Math.Max(8, Environment.ProcessorCount * 2));
Thread[] readers = Enumerable.Range(0, readerCount)
.Select(_ => new Thread(() =>
{
while (!token.IsCancellationRequested)
{
gate.EnterReadLock();
Thread.Sleep(20);
gate.ExitReadLock();
}
}))
.ToArray();
Thread writer = new(() =>
{
while (!token.IsCancellationRequested)
{
gate.EnterWriteLock();
Thread.Sleep(5);
gate.ExitWriteLock();
}
});

foreach (Thread reader in readers)
{
reader.Start();
}

writer.Start();
foreach (Thread reader in readers)
{
reader.Join();
}

writer.Join();
return Task.CompletedTask;
}

public static async Task DeadlockAsync(CancellationToken token)
{
object first = new();
object second = new();
using ManualResetEventSlim firstHeld = new();
using ManualResetEventSlim secondHeld = new();

new Thread(() =>
{
lock (first)
{
firstHeld.Set();
secondHeld.Wait();
lock (second)
{
}
}
})
{
IsBackground = true,
}.Start();

new Thread(() =>
{
lock (second)
{
secondHeld.Set();
firstHeld.Wait();
lock (first)
{
}
}
})
{
IsBackground = true,
}.Start();

await Task.Delay(Timeout.Infinite, token);
}

private static async Task<int> DelayedOperationAsync()
{
await Task.Delay(500);
return 42;
}
}
83 changes: 83 additions & 0 deletions core/diagnostics/PerformanceScenarios/CpuScenarios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

internal static class CpuScenarios
{
public static Task HotspotAsync(CancellationToken token)
{
long result = 0;
while (!token.IsCancellationRequested)
{
result += Fibonacci(36);
}

GC.KeepAlive(result);
return Task.CompletedTask;
}

public static Task InliningAsync(CancellationToken token)
{
long result = 0;
while (!token.IsCancellationRequested)
{
result += CallerOne();
result += CallerTwo();
result += CallerThree();
}

GC.KeepAlive(result);
return Task.CompletedTask;
}

public static Task NativeAsync(CancellationToken token)
{
const int BufferSize = 1024 * 1024;
IntPtr source = Marshal.AllocHGlobal(BufferSize);
IntPtr destination = Marshal.AllocHGlobal(BufferSize);
try
{
byte[] initial = new byte[BufferSize];
Random.Shared.NextBytes(initial);
Marshal.Copy(initial, 0, source, initial.Length);

while (!token.IsCancellationRequested)
{
Memcpy(destination, source, BufferSize);
}
}
finally
{
Marshal.FreeHGlobal(destination);
Marshal.FreeHGlobal(source);
}

return Task.CompletedTask;
}

private static long Fibonacci(int value)
{
return value <= 1 ? value : Fibonacci(value - 1) + Fibonacci(value - 2);
}

private static long CallerOne() => SharedCompute(4_000);

private static long CallerTwo() => SharedCompute(5_000);

private static long CallerThree() => SharedCompute(6_000);

private static long SharedCompute(int iterations)
{
long value = 17;
for (int index = 0; index < iterations; index++)
{
value = unchecked((value * 31) ^ index);
}

return value;
}

[DllImport("libc", EntryPoint = "memcpy")]
private static extern IntPtr Memcpy(IntPtr destination, IntPtr source, nuint count);
}
51 changes: 51 additions & 0 deletions core/diagnostics/PerformanceScenarios/IoScenarios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

internal static class IoScenarios
{
public static Task TinyWritesAsync(CancellationToken token)
{
string path = Path.Combine(Path.GetTempPath(), $"tiny-writes-{Environment.ProcessId}.dat");
try
{
using FileStream stream = new(path, FileMode.Create, FileAccess.Write, FileShare.Read, 1);
byte[] value = [42];
while (!token.IsCancellationRequested)
{
stream.Write(value);
stream.Flush();
}
}
finally
{
File.Delete(path);
}

return Task.CompletedTask;
}

public static Task SyncIoThreadPoolAsync(CancellationToken token)
{
string path = Path.Combine(Path.GetTempPath(), $"sync-io-{Environment.ProcessId}.dat");
byte[] data = new byte[4096];
try
{
while (!token.IsCancellationRequested)
{
Task[] tasks = Enumerable.Range(0, 64).Select(_ => Task.Run(() =>
{
using FileStream stream = new(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
stream.Write(data);
stream.Flush(flushToDisk: true);
})).ToArray();
Task.WaitAll(tasks);
}
Comment on lines +35 to +42
}
finally
{
File.Delete(path);
}

return Task.CompletedTask;
}
}
89 changes: 89 additions & 0 deletions core/diagnostics/PerformanceScenarios/MemoryScenarios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

internal static class MemoryScenarios
{
public static Task AllocationGcAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
string value = string.Empty;
for (int index = 0; index < 2_000; index++)
{
value += index.ToString();
}

GC.KeepAlive(value.Split('7'));
}

return Task.CompletedTask;
}

public static Task LohGcAsync(CancellationToken token)
{
List<byte[]> retained = [];
while (!token.IsCancellationRequested)
{
retained.Add(new byte[200_000]);
if (retained.Count > 200)
{
retained.RemoveRange(0, 100);
}
}

GC.KeepAlive(retained);
return Task.CompletedTask;
}

public static async Task ManagedGrowthAsync(CancellationToken token)
{
List<byte[]> retained = [];
while (!token.IsCancellationRequested)
{
retained.Add(new byte[512 * 1024]);
await Task.Delay(100, token);
}

GC.KeepAlive(retained);
}

public static Task InducedGcAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
byte[] data = new byte[1_000_000];
GC.KeepAlive(data);
GC.Collect(2, GCCollectionMode.Forced, blocking: true);
}

return Task.CompletedTask;
}

public static async Task NativeGrowthAsync(CancellationToken token)
{
List<IntPtr> allocations = [];
try
{
while (!token.IsCancellationRequested)
{
IntPtr memory = Marshal.AllocHGlobal(256 * 1024);
for (int offset = 0; offset < 256 * 1024; offset += 4096)
{
Marshal.WriteByte(memory, offset, 1);
}

allocations.Add(memory);
await Task.Delay(100, token);
}
}
finally
{
foreach (IntPtr allocation in allocations)
{
Marshal.FreeHGlobal(allocation);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
Loading
Loading