Skip to content
Open
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
72 changes: 59 additions & 13 deletions csharp/Platform.Ranges/Range.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,121 @@
using System.Runtime.CompilerServices;

namespace Platform.Ranges
{
/// <summary>
/// <para>Contains static fields with <see cref="Range{T}"/> constants.</para>
/// <para>Содержит статические поля с константами типа <see cref="Range{T}"/>.</para>
/// <para>Contains static properties with <see cref="Range{T}"/> constants.</para>
/// <para>Содержит статические свойства с константами типа <see cref="Range{T}"/>.</para>
/// </summary>
public static class Range
{
/// <summary>
/// <para>Gets the whole <see cref="sbyte"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="sbyte"/>.</para>
/// </summary>
public static readonly Range<sbyte> SByte = new Range<sbyte>(sbyte.MinValue, sbyte.MaxValue);
public static Range<sbyte> SByte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<sbyte>(sbyte.MinValue, sbyte.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="short"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="short"/>.</para>
/// </summary>
public static readonly Range<short> Int16 = new Range<short>(short.MinValue, short.MaxValue);
public static Range<short> Int16
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<short>(short.MinValue, short.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="int"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="int"/>.</para>
/// </summary>
public static readonly Range<int> Int32 = new Range<int>(int.MinValue, int.MaxValue);
public static Range<int> Int32
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<int>(int.MinValue, int.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="long"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="long"/>.</para>
/// </summary>
public static readonly Range<long> Int64 = new Range<long>(long.MinValue, long.MaxValue);
public static Range<long> Int64
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<long>(long.MinValue, long.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="byte"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="byte"/>.</para>
/// </summary>
public static readonly Range<byte> Byte = new Range<byte>(byte.MinValue, byte.MaxValue);
public static Range<byte> Byte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<byte>(byte.MinValue, byte.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="ushort"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="ushort"/>.</para>
/// </summary>
public static readonly Range<ushort> UInt16 = new Range<ushort>(ushort.MinValue, ushort.MaxValue);
public static Range<ushort> UInt16
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<ushort>(ushort.MinValue, ushort.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="uint"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="uint"/>.</para>
/// </summary>
public static readonly Range<uint> UInt32 = new Range<uint>(uint.MinValue, uint.MaxValue);
public static Range<uint> UInt32
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<uint>(uint.MinValue, uint.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="ulong"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="ulong"/>.</para>
/// </summary>
public static readonly Range<ulong> UInt64 = new Range<ulong>(ulong.MinValue, ulong.MaxValue);
public static Range<ulong> UInt64
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<ulong>(ulong.MinValue, ulong.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="float"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="float"/>.</para>
/// </summary>
public static readonly Range<float> Single = new Range<float>(float.MinValue, float.MaxValue);
public static Range<float> Single
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<float>(float.MinValue, float.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="double"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="double"/>.</para>
/// </summary>
public static readonly Range<double> Double = new Range<double>(double.MinValue, double.MaxValue);
public static Range<double> Double
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<double>(double.MinValue, double.MaxValue);
}

/// <summary>
/// <para>Gets the whole <see cref="decimal"/> values range.</para>
/// <para>Возвращает весь диапазон значений <see cref="decimal"/>.</para>
/// </summary>
public static readonly Range<decimal> Decimal = new Range<decimal>(decimal.MinValue, decimal.MaxValue);
public static Range<decimal> Decimal
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<decimal>(decimal.MinValue, decimal.MaxValue);
}
}
}
20 changes: 20 additions & 0 deletions experiments/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using Platform.Ranges.Experiments;

namespace Platform.Ranges.Experiments
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Platform.Ranges Performance Analysis");
Console.WriteLine("====================================");
Console.WriteLine();

RangePerformanceBenchmark.RunBenchmark();

Console.WriteLine();
Console.WriteLine("Benchmark completed!");
}
}
}
156 changes: 156 additions & 0 deletions experiments/RangePerformanceBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Platform.Ranges;

namespace Platform.Ranges.Experiments
{
/// <summary>
/// Benchmark to compare performance of static readonly fields vs static properties with aggressive inlining
/// for Range constant access patterns.
/// </summary>
public static class RangePerformanceBenchmark
{
// Simulating the OLD approach (static readonly fields)
public static class OldRangeConstants
{
public static readonly Range<int> Int32 = new Range<int>(int.MinValue, int.MaxValue);
public static readonly Range<long> Int64 = new Range<long>(long.MinValue, long.MaxValue);
public static readonly Range<double> Double = new Range<double>(double.MinValue, double.MaxValue);
}

// Simulating the NEW approach (static properties with aggressive inlining)
public static class NewRangeConstants
{
public static Range<int> Int32
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<int>(int.MinValue, int.MaxValue);
}

public static Range<long> Int64
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<long>(long.MinValue, long.MaxValue);
}

public static Range<double> Double
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new Range<double>(double.MinValue, double.MaxValue);
}
}

public static void RunBenchmark()
{
const int iterations = 10_000_000;

Console.WriteLine("Range Performance Benchmark");
Console.WriteLine("===========================");
Console.WriteLine($"Iterations: {iterations:N0}");
Console.WriteLine();

// Warmup
Console.WriteLine("Warming up...");
BenchmarkOldApproach(iterations / 100);
BenchmarkNewApproach(iterations / 100);
BenchmarkCurrentImplementation(iterations / 100);

Console.WriteLine("\nStarting benchmarks...");

// Benchmark OLD approach (static readonly fields)
var oldTime = BenchmarkOldApproach(iterations);
Console.WriteLine($"Old approach (static readonly fields): {oldTime:F2} ms");

// Benchmark NEW approach (static properties with aggressive inlining)
var newTime = BenchmarkNewApproach(iterations);
Console.WriteLine($"New approach (static properties): {newTime:F2} ms");

// Benchmark current implementation in Range class
var currentTime = BenchmarkCurrentImplementation(iterations);
Console.WriteLine($"Current implementation: {currentTime:F2} ms");

Console.WriteLine();
Console.WriteLine("Performance Analysis:");
Console.WriteLine($"New vs Old: {(newTime / oldTime * 100):F1}% ({(newTime > oldTime ? "slower" : "faster")})");
Console.WriteLine($"Current vs Old: {(currentTime / oldTime * 100):F1}% ({(currentTime > oldTime ? "slower" : "faster")})");

if (Math.Abs(newTime - currentTime) < 0.01)
{
Console.WriteLine("✓ Current implementation performs similarly to new approach");
}
}

private static double BenchmarkOldApproach(int iterations)
{
var stopwatch = Stopwatch.StartNew();

var sum = 0L;
for (int i = 0; i < iterations; i++)
{
// Access the constants multiple times to simulate real usage
var intRange = OldRangeConstants.Int32;
var longRange = OldRangeConstants.Int64;
var doubleRange = OldRangeConstants.Double;

// Use the ranges to prevent optimizations
sum += intRange.Minimum + longRange.Minimum + (long)doubleRange.Minimum;
}

stopwatch.Stop();

// Prevent dead code elimination
if (sum == 0) Console.WriteLine("Unexpected result");

return stopwatch.Elapsed.TotalMilliseconds;
}

private static double BenchmarkNewApproach(int iterations)
{
var stopwatch = Stopwatch.StartNew();

var sum = 0L;
for (int i = 0; i < iterations; i++)
{
// Access the properties multiple times to simulate real usage
var intRange = NewRangeConstants.Int32;
var longRange = NewRangeConstants.Int64;
var doubleRange = NewRangeConstants.Double;

// Use the ranges to prevent optimizations
sum += intRange.Minimum + longRange.Minimum + (long)doubleRange.Minimum;
}

stopwatch.Stop();

// Prevent dead code elimination
if (sum == 0) Console.WriteLine("Unexpected result");

return stopwatch.Elapsed.TotalMilliseconds;
}

private static double BenchmarkCurrentImplementation(int iterations)
{
var stopwatch = Stopwatch.StartNew();

var sum = 0L;
for (int i = 0; i < iterations; i++)
{
// Access the current implementation multiple times
var intRange = Range.Int32;
var longRange = Range.Int64;
var doubleRange = Range.Double;

// Use the ranges to prevent optimizations
sum += intRange.Minimum + longRange.Minimum + (long)doubleRange.Minimum;
}

stopwatch.Stop();

// Prevent dead code elimination
if (sum == 0) Console.WriteLine("Unexpected result");

return stopwatch.Elapsed.TotalMilliseconds;
}
}
}
15 changes: 15 additions & 0 deletions experiments/RangePerformanceBenchmark.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../csharp/Platform.Ranges/Platform.Ranges.csproj" />
</ItemGroup>

</Project>
19 changes: 19 additions & 0 deletions experiments/benchmark_results.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Platform.Ranges Performance Analysis
====================================

Range Performance Benchmark
===========================
Iterations: 10,000,000

Warming up...

Starting benchmarks...
Old approach (static readonly fields): 31.25 ms
New approach (static properties): 4099.61 ms
Current implementation: 4022.19 ms

Performance Analysis:
New vs Old: 13119.7% (slower)
Current vs Old: 12872.0% (slower)

Benchmark completed!
Loading