diff --git a/csharp/Platform.Ranges/Range.cs b/csharp/Platform.Ranges/Range.cs index 33422cd..2a52ebd 100644 --- a/csharp/Platform.Ranges/Range.cs +++ b/csharp/Platform.Ranges/Range.cs @@ -1,8 +1,10 @@ +using System.Runtime.CompilerServices; + namespace Platform.Ranges { /// - /// Contains static fields with constants. - /// Содержит статические поля с константами типа . + /// Contains static properties with constants. + /// Содержит статические свойства с константами типа . /// public static class Range { @@ -10,66 +12,110 @@ public static class Range /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range SByte = new Range(sbyte.MinValue, sbyte.MaxValue); + public static Range SByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(sbyte.MinValue, sbyte.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range Int16 = new Range(short.MinValue, short.MaxValue); + public static Range Int16 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(short.MinValue, short.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range Int32 = new Range(int.MinValue, int.MaxValue); + public static Range Int32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(int.MinValue, int.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range Int64 = new Range(long.MinValue, long.MaxValue); + public static Range Int64 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(long.MinValue, long.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range Byte = new Range(byte.MinValue, byte.MaxValue); + public static Range Byte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(byte.MinValue, byte.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range UInt16 = new Range(ushort.MinValue, ushort.MaxValue); + public static Range UInt16 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(ushort.MinValue, ushort.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range UInt32 = new Range(uint.MinValue, uint.MaxValue); + public static Range UInt32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(uint.MinValue, uint.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range UInt64 = new Range(ulong.MinValue, ulong.MaxValue); + public static Range UInt64 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(ulong.MinValue, ulong.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range Single = new Range(float.MinValue, float.MaxValue); + public static Range Single + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(float.MinValue, float.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range Double = new Range(double.MinValue, double.MaxValue); + public static Range Double + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(double.MinValue, double.MaxValue); + } /// /// Gets the whole values range. /// Возвращает весь диапазон значений . /// - public static readonly Range Decimal = new Range(decimal.MinValue, decimal.MaxValue); + public static Range Decimal + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(decimal.MinValue, decimal.MaxValue); + } } } diff --git a/experiments/Program.cs b/experiments/Program.cs new file mode 100644 index 0000000..144fd2b --- /dev/null +++ b/experiments/Program.cs @@ -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!"); + } + } +} \ No newline at end of file diff --git a/experiments/RangePerformanceBenchmark.cs b/experiments/RangePerformanceBenchmark.cs new file mode 100644 index 0000000..9f8f0b8 --- /dev/null +++ b/experiments/RangePerformanceBenchmark.cs @@ -0,0 +1,156 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Platform.Ranges; + +namespace Platform.Ranges.Experiments +{ + /// + /// Benchmark to compare performance of static readonly fields vs static properties with aggressive inlining + /// for Range constant access patterns. + /// + public static class RangePerformanceBenchmark + { + // Simulating the OLD approach (static readonly fields) + public static class OldRangeConstants + { + public static readonly Range Int32 = new Range(int.MinValue, int.MaxValue); + public static readonly Range Int64 = new Range(long.MinValue, long.MaxValue); + public static readonly Range Double = new Range(double.MinValue, double.MaxValue); + } + + // Simulating the NEW approach (static properties with aggressive inlining) + public static class NewRangeConstants + { + public static Range Int32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(int.MinValue, int.MaxValue); + } + + public static Range Int64 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(long.MinValue, long.MaxValue); + } + + public static Range Double + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Range(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; + } + } +} \ No newline at end of file diff --git a/experiments/RangePerformanceBenchmark.csproj b/experiments/RangePerformanceBenchmark.csproj new file mode 100644 index 0000000..babed36 --- /dev/null +++ b/experiments/RangePerformanceBenchmark.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + latest + true + true + + + + + + + \ No newline at end of file diff --git a/experiments/benchmark_results.txt b/experiments/benchmark_results.txt new file mode 100644 index 0000000..7242084 --- /dev/null +++ b/experiments/benchmark_results.txt @@ -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!