Add reusable stress test class (#2419)

This commit is contained in:
Reiley Yang 2021-09-28 11:29:58 -07:00 committed by GitHub
parent 7b26a647a3
commit f47ea55a20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 203 additions and 71 deletions

View File

@ -0,0 +1,32 @@
// <copyright file="Meat.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System.Runtime.CompilerServices;
// namespace OpenTelemetry.Tests.Stress;
public partial class Program
{
public static void Main()
{
Stress(1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static void Run()
{
}
}

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1;net5.0;net462</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,131 @@
// <copyright file="Skeleton.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
// namespace OpenTelemetry.Tests.Stress;
public partial class Program
{
private static volatile bool bContinue = true;
private static volatile string output = "Test results not available yet.";
public static void Stress(int concurrency = 0)
{
if (concurrency < 0)
{
throw new ArgumentOutOfRangeException(nameof(concurrency), "concurrency level should be a non-negative number.");
}
if (concurrency == 0)
{
concurrency = Environment.ProcessorCount;
}
var statistics = new long[concurrency];
Parallel.Invoke(
() =>
{
Console.WriteLine($"Running (concurrency = {concurrency}), press <Esc> to stop...");
var bOutput = false;
var watch = new Stopwatch();
while (true)
{
if (Console.KeyAvailable)
{
var key = Console.ReadKey(true).Key;
switch (key)
{
case ConsoleKey.Enter:
Console.WriteLine(string.Format("{0} {1}", DateTime.UtcNow.ToString("O"), output));
break;
case ConsoleKey.Escape:
bContinue = false;
return;
case ConsoleKey.Spacebar:
bOutput = !bOutput;
break;
}
continue;
}
if (bOutput)
{
Console.WriteLine(string.Format("{0} {1}", DateTime.UtcNow.ToString("O"), output));
}
var cntLoopsOld = (ulong)statistics.Sum();
var cntCpuCyclesOld = GetCpuCycles();
watch.Restart();
Thread.Sleep(200);
watch.Stop();
var cntLoopsNew = (ulong)statistics.Sum();
var cntCpuCyclesNew = GetCpuCycles();
var nLoops = cntLoopsNew - cntLoopsOld;
var nCpuCycles = cntCpuCyclesNew - cntCpuCyclesOld;
var nLoopsPerSecond = (double)nLoops / ((double)watch.ElapsedMilliseconds / 1000.0);
var nCpuCyclesPerLoop = nLoops == 0 ? 0 : nCpuCycles / nLoops;
output = $"Loops: {cntLoopsNew:n0}, Loops/Second: {nLoopsPerSecond:n0}, CPU Cycles/Loop: {nCpuCyclesPerLoop:n0}";
Console.Title = output;
}
}, () =>
{
Parallel.For(0, concurrency, (i) =>
{
statistics[i] = 0;
while (bContinue)
{
statistics[i]++;
Run();
}
});
});
Console.WriteLine(output);
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool QueryProcessCycleTime(IntPtr hProcess, out ulong cycles);
private static ulong GetCpuCycles()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return 0;
}
if (!QueryProcessCycleTime((IntPtr)(-1), out var cycles))
{
return 0;
}
return cycles;
}
}

View File

@ -15,84 +15,44 @@
// </copyright>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using OpenTelemetry;
using OpenTelemetry.Metrics;
namespace OpenTelemetry.Metrics.Tests.Stress
// namespace OpenTelemetry.Tests.Stress;
public partial class Program
{
public class Program
private const int ArraySize = 10;
private static readonly Meter TestMeter = new Meter("TestMeter", "1.0.0");
private static readonly Counter<long> TestCounter = TestMeter.CreateCounter<long>("TestCounter");
private static readonly string[] DimensionValues = new string[ArraySize];
private static readonly ThreadLocal<Random> ThreadLocalRandom = new ThreadLocal<Random>(() => new Random());
public static void Main()
{
private static readonly Meter MyMeter = new Meter("TestMeter", "0.0.1");
private static readonly Counter<long> Counter = MyMeter.CreateCounter<long>("counter");
private static string[] dimensionValues = new string[] { "DimVal1", "DimVal2", "DimVal3", "DimVal4", "DimVal5", "DimVal6", "DimVal7", "DimVal8", "DimVal9", "DimVal10" };
private static Random random = new Random();
public static void Main(string[] args)
for (int i = 0; i < ArraySize; i++)
{
long numberOfMetricWriters = Environment.ProcessorCount;
long maxWritesPerWriter = 10000000;
var writes = new long[numberOfMetricWriters];
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestMeter")
.Build();
Stopwatch sw = Stopwatch.StartNew();
Parallel.Invoke(
() =>
{
var watch = new Stopwatch();
while (true)
{
long totalWrites = 0;
for (int i = 0; i < numberOfMetricWriters; i++)
{
totalWrites += writes[i];
}
watch.Restart();
Thread.Sleep(1000);
watch.Stop();
long newTotalWrites = 0;
for (int i = 0; i < numberOfMetricWriters; i++)
{
newTotalWrites += writes[i];
}
var writesPerSec = (newTotalWrites - totalWrites) / (watch.ElapsedMilliseconds / 1000.0);
Console.Title = $"Writes (Million/Sec): {writesPerSec / 1000000}";
if (totalWrites > numberOfMetricWriters * maxWritesPerWriter)
{
break;
}
}
}, () =>
{
Parallel.For(0, numberOfMetricWriters, i
=>
{
Console.WriteLine($"Metric writer {i} started.");
while (writes[i]++ < maxWritesPerWriter)
{
// 10 * 10 * 10 = 1000 unique combination is produced
// which is well within the current hard-coded cap of
// 2000.
var tag1 = new KeyValuePair<string, object>("DimName1", dimensionValues[random.Next(0, 10)]);
var tag2 = new KeyValuePair<string, object>("DimName2", dimensionValues[random.Next(0, 10)]);
var tag3 = new KeyValuePair<string, object>("DimName3", dimensionValues[random.Next(0, 10)]);
Counter.Add(100, tag1, tag2, tag3);
}
Console.WriteLine($"Metric writer {i} completed.");
});
});
var rate = (double)(numberOfMetricWriters * maxWritesPerWriter) / (sw.ElapsedMilliseconds / 1000);
Console.WriteLine($"{rate / 1000000} M/sec.");
DimensionValues[i] = $"DimValue{i}";
}
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestMeter")
.Build();
Stress();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static void Run()
{
var random = ThreadLocalRandom.Value;
TestCounter.Add(
100,
new("DimName1", DimensionValues[random.Next(0, ArraySize)]),
new("DimName2", DimensionValues[random.Next(0, ArraySize)]),
new("DimName3", DimensionValues[random.Next(0, ArraySize)]));
}
}

View File

@ -1,12 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<NoWarn>$(NoWarn),SA1000</NoWarn>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1;net5.0;net462</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests.Stress\Skeleton.cs" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
</ItemGroup>