Add reusable stress test class (#2419)
This commit is contained in:
parent
7b26a647a3
commit
f47ea55a20
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue