Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: ⚡envelope caching with MemoryCache and waveform utils improvements #24

Merged
merged 2 commits into from
Jul 1, 2023
Merged
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
9 changes: 8 additions & 1 deletion Qynit.Pulsewave.sln
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{3B8B8B3E
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{6741804E-962A-4762-B289-CD4899A62AD8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WaveGenDemo", "examples\WaveGenDemo\WaveGenDemo.csproj", "{B83CB224-4C00-4011-BF6A-C76C7D786908}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WaveGenDemo", "examples\WaveGenDemo\WaveGenDemo.csproj", "{B83CB224-4C00-4011-BF6A-C76C7D786908}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WaveGenBenchmarks", "examples\WaveGenBenchmarks\WaveGenBenchmarks.csproj", "{ECE2579D-700C-4BB3-9009-F35395177B7A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -38,6 +40,10 @@ Global
{B83CB224-4C00-4011-BF6A-C76C7D786908}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B83CB224-4C00-4011-BF6A-C76C7D786908}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B83CB224-4C00-4011-BF6A-C76C7D786908}.Release|Any CPU.Build.0 = Release|Any CPU
{ECE2579D-700C-4BB3-9009-F35395177B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECE2579D-700C-4BB3-9009-F35395177B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECE2579D-700C-4BB3-9009-F35395177B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ECE2579D-700C-4BB3-9009-F35395177B7A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -46,6 +52,7 @@ Global
{8A71F7BA-E700-4DA3-BBE7-5E7C866466B8} = {3EBAD22F-230A-4BCB-B16C-A9063E2A4E9A}
{33659EDC-29F9-45DB-A9FA-E01E648BE4B9} = {3B8B8B3E-9095-4A7E-9309-661439C30D9A}
{B83CB224-4C00-4011-BF6A-C76C7D786908} = {6741804E-962A-4762-B289-CD4899A62AD8}
{ECE2579D-700C-4BB3-9009-F35395177B7A} = {6741804E-962A-4762-B289-CD4899A62AD8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1DC52AEE-D9FC-4B2F-9492-0C92B0479A3C}
Expand Down
43 changes: 43 additions & 0 deletions WaveGenBenchmarks.WaveformUtilsBench-report-github.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
``` ini

BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.22621.1848/22H2/2022Update/SunValley2)
AMD Ryzen 5 5600, 1 CPU, 12 logical and 6 physical cores
.NET SDK=7.0.304
[Host] : .NET 7.0.7 (7.0.723.27404), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.7 (7.0.723.27404), X64 RyuJIT AVX2


```
| Method | Length | Mean | Error | StdDev | Median | Ratio | RatioSD |
|------------------------ |------- |------------:|----------:|----------:|------------:|------:|--------:|
| **MixAddPlateau** | **16** | **12.94 ns** | **0.027 ns** | **0.023 ns** | **12.95 ns** | **0.25** | **0.00** |
| MixAddPlateauFrequency | 16 | 28.13 ns | 0.090 ns | 0.080 ns | 28.13 ns | 0.54 | 0.00 |
| MixAdd | 16 | 20.65 ns | 0.047 ns | 0.042 ns | 20.66 ns | 0.40 | 0.00 |
| MixAddFrequency | 16 | 38.46 ns | 0.113 ns | 0.088 ns | 38.49 ns | 0.74 | 0.00 |
| MixAddWithDrag | 16 | 29.16 ns | 0.143 ns | 0.134 ns | 29.16 ns | 0.56 | 0.00 |
| MixAddFrequencyWithDrag | 16 | 51.62 ns | 0.134 ns | 0.119 ns | 51.61 ns | 1.00 | 0.00 |
| Simple | 16 | 63.72 ns | 0.322 ns | 0.301 ns | 63.59 ns | 1.23 | 0.01 |
| | | | | | | | |
| **MixAddPlateau** | **64** | **42.69 ns** | **0.113 ns** | **0.105 ns** | **42.71 ns** | **0.53** | **0.01** |
| MixAddPlateauFrequency | 64 | 46.73 ns | 0.218 ns | 0.193 ns | 46.74 ns | 0.58 | 0.01 |
| MixAdd | 64 | 37.26 ns | 0.101 ns | 0.095 ns | 37.27 ns | 0.46 | 0.00 |
| MixAddFrequency | 64 | 62.61 ns | 0.192 ns | 0.160 ns | 62.61 ns | 0.78 | 0.01 |
| MixAddWithDrag | 64 | 51.32 ns | 0.466 ns | 0.363 ns | 51.19 ns | 0.64 | 0.01 |
| MixAddFrequencyWithDrag | 64 | 80.17 ns | 0.779 ns | 0.691 ns | 80.01 ns | 1.00 | 0.00 |
| Simple | 64 | 217.39 ns | 4.377 ns | 7.892 ns | 213.14 ns | 2.78 | 0.13 |
| | | | | | | | |
| **MixAddPlateau** | **256** | **72.68 ns** | **0.321 ns** | **0.300 ns** | **72.73 ns** | **0.37** | **0.00** |
| MixAddPlateauFrequency | 256 | 122.84 ns | 0.210 ns | 0.197 ns | 122.80 ns | 0.62 | 0.00 |
| MixAdd | 256 | 74.62 ns | 0.266 ns | 0.249 ns | 74.60 ns | 0.38 | 0.00 |
| MixAddFrequency | 256 | 163.94 ns | 0.327 ns | 0.306 ns | 164.00 ns | 0.83 | 0.01 |
| MixAddWithDrag | 256 | 141.93 ns | 0.305 ns | 0.285 ns | 141.89 ns | 0.72 | 0.00 |
| MixAddFrequencyWithDrag | 256 | 196.62 ns | 1.133 ns | 1.060 ns | 196.16 ns | 1.00 | 0.00 |
| Simple | 256 | 782.64 ns | 4.104 ns | 3.839 ns | 781.45 ns | 3.98 | 0.03 |
| | | | | | | | |
| **MixAddPlateau** | **1024** | **277.91 ns** | **0.331 ns** | **0.294 ns** | **277.91 ns** | **0.40** | **0.00** |
| MixAddPlateauFrequency | 1024 | 424.67 ns | 0.439 ns | 0.389 ns | 424.60 ns | 0.61 | 0.00 |
| MixAdd | 1024 | 274.94 ns | 1.182 ns | 1.048 ns | 275.11 ns | 0.39 | 0.00 |
| MixAddFrequency | 1024 | 566.09 ns | 1.301 ns | 1.086 ns | 566.40 ns | 0.81 | 0.00 |
| MixAddWithDrag | 1024 | 534.09 ns | 2.571 ns | 2.405 ns | 533.32 ns | 0.76 | 0.00 |
| MixAddFrequencyWithDrag | 1024 | 701.37 ns | 2.718 ns | 2.270 ns | 701.43 ns | 1.00 | 0.00 |
| Simple | 1024 | 3,061.59 ns | 12.202 ns | 10.189 ns | 3,060.17 ns | 4.37 | 0.02 |
5 changes: 5 additions & 0 deletions examples/WaveGenBenchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using BenchmarkDotNet.Running;

using WaveGenBenchmarks;

BenchmarkRunner.Run<WaveformUtilsBench>();
18 changes: 18 additions & 0 deletions examples/WaveGenBenchmarks/WaveGenBenchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Qynit.Pulsewave\Qynit.Pulsewave.csproj" />
</ItemGroup>

</Project>
97 changes: 97 additions & 0 deletions examples/WaveGenBenchmarks/WaveformUtilsBench.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.Numerics;

using BenchmarkDotNet.Attributes;

using Qynit.Pulsewave;

namespace WaveGenBenchmarks;
public class WaveformUtilsBench
{
[Params(16, 64, 256, 1024)]
public int Length { get; set; }
private PooledComplexArray<double>? Source { get; set; }
private PooledComplexArray<double>? Target { get; set; }
private IqPair<double> Amplitude { get; } = new IqPair<double>(1, 1);
private IqPair<double> DragAmplitude { get; } = new IqPair<double>(1, 1);
private double DPhase { get; } = Math.PI / 11;

[GlobalSetup]
public void Init()
{
Source = new PooledComplexArray<double>(Length, true);
Source.DataI.Fill(0.125);
Source.DataQ.Fill(-0.125);
Target = new PooledComplexArray<double>(Length, true);
}

[Benchmark]
public void MixAddPlateau()
{
WaveformUtils.MixAddPlateau(Target!, Amplitude);
}

[Benchmark]
public void MixAddPlateauFrequency()
{
WaveformUtils.MixAddPlateauFrequency(Target!, Amplitude, DPhase);
}

[Benchmark]
public void MixAdd()
{
WaveformUtils.MixAdd(Target!, Source!, Amplitude);
}

[Benchmark]
public void MixAddFrequency()
{
WaveformUtils.MixAddFrequency(Target!, Source!, Amplitude, DPhase);
}

[Benchmark]
public void MixAddWithDrag()
{
WaveformUtils.MixAddWithDrag(Target!, Source!, Amplitude, DragAmplitude);
}

[Benchmark(Baseline = true)]
public void MixAddFrequencyWithDrag()
{
WaveformUtils.MixAddFrequencyWithDrag(Target!, Source!, Amplitude, DragAmplitude, DPhase);
}

[Benchmark]
public void Simple()
{
MixAddWithDragSimple(Target!, Source!, Amplitude, DragAmplitude, DPhase);
}

private static void MixAddWithDragSimple<T>(ComplexSpan<T> target, ComplexReadOnlySpan<T> source, IqPair<T> amplitude, IqPair<T> dragAmplitude, T dPhase)
where T : unmanaged, IFloatingPointIeee754<T>
{
var length = source.Length;
var sourceI = source.DataI;
var sourceQ = source.DataQ;
var targetI = target.DataI;
var targetQ = target.DataQ;

var carrier = amplitude;
var dragCarrier = dragAmplitude;
var phaser = IqPair<T>.FromPolarCoordinates(T.One, dPhase);
for (var i = 0; i < length; i++)
{
var diff = i switch
{
0 => new IqPair<T>(sourceI[i + 1], sourceQ[i + 1]) - new IqPair<T>(sourceI[i], sourceQ[i]),
_ when i == length - 1 => new IqPair<T>(sourceI[i], sourceQ[i]) - new IqPair<T>(sourceI[i - 1], sourceQ[i - 1]),
_ => (new IqPair<T>(sourceI[i + 1], sourceQ[i + 1]) - new IqPair<T>(sourceI[i - 1], sourceQ[i - 1])) * T.CreateChecked(0.5),
};
var sourceIq = new IqPair<T>(sourceI[i], sourceQ[i]);
var totalIq = sourceIq * carrier + diff * dragCarrier;
targetI[i] += totalIq.I;
targetQ[i] += totalIq.Q;
carrier *= phaser;
dragCarrier *= phaser;
}
}
}
44 changes: 0 additions & 44 deletions src/Qynit.Pulsewave/ComplexArrayReadOnlySpan.cs

This file was deleted.

39 changes: 0 additions & 39 deletions src/Qynit.Pulsewave/ComplexArraySpan.cs

This file was deleted.

44 changes: 44 additions & 0 deletions src/Qynit.Pulsewave/ComplexReadOnlySpan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Qynit.Pulsewave;
public readonly ref struct ComplexReadOnlySpan<T>
where T : unmanaged
{
public ReadOnlySpan<T> DataI { get; }
public ReadOnlySpan<T> DataQ { get; }
public int Length => DataI.Length;
public bool IsEmpty => Length == 0;
internal ComplexReadOnlySpan(ReadOnlySpan<T> dataI, ReadOnlySpan<T> dataQ)
{
Debug.Assert(dataI.Length == dataQ.Length);
DataI = dataI;
DataQ = dataQ;
}
public static implicit operator ComplexReadOnlySpan<T>(PooledComplexArray<T> source)
{
return new ComplexReadOnlySpan<T>(source.DataI, source.DataQ);
}
public static implicit operator ComplexReadOnlySpan<T>(ComplexSpan<T> source)
{
return new ComplexReadOnlySpan<T>(source.DataI, source.DataQ);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ComplexReadOnlySpan<T> Slice(int start)
{
return new ComplexReadOnlySpan<T>(DataI[start..], DataQ[start..]);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ComplexReadOnlySpan<T> Slice(int start, int length)
{
return new ComplexReadOnlySpan<T>(DataI.Slice(start, length), DataQ.Slice(start, length));
}

public void CopyTo(ComplexSpan<T> destination)
{
DataI.CopyTo(destination.DataI);
DataQ.CopyTo(destination.DataQ);
}
}
49 changes: 49 additions & 0 deletions src/Qynit.Pulsewave/ComplexSpan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Qynit.Pulsewave;
public readonly ref struct ComplexSpan<T>
where T : unmanaged
{
public Span<T> DataI { get; }
public Span<T> DataQ { get; }
public int Length => DataI.Length;
public bool IsEmpty => Length == 0;
internal ComplexSpan(Span<T> dataI, Span<T> dataQ)
{
Debug.Assert(dataI.Length == dataQ.Length);
DataI = dataI;
DataQ = dataQ;
}
public static implicit operator ComplexSpan<T>(PooledComplexArray<T> source)
{
return new ComplexSpan<T>(source.DataI, source.DataQ);
}

public void Fill(T i, T q)
{
DataI.Fill(i);
DataQ.Fill(q);
}
public void Clear()
{
DataI.Clear();
DataQ.Clear();
}
public void CopyTo(ComplexSpan<T> destination)
{
((ComplexReadOnlySpan<T>)this).CopyTo(destination);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ComplexSpan<T> Slice(int start)
{
return new ComplexSpan<T>(DataI[start..], DataQ[start..]);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ComplexSpan<T> Slice(int start, int length)
{
return new ComplexSpan<T>(DataI.Slice(start, length), DataQ.Slice(start, length));
}
}
2 changes: 2 additions & 0 deletions src/Qynit.Pulsewave/Envelope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public readonly record struct Envelope
public IPulseShape? Shape { get; }
public double Width { get; }
public double Plateau { get; }
public bool IsRectangle => Shape is null;
public bool IsZero => Width == 0 && Plateau == 0;
public Envelope(IPulseShape? shape, double width, double plateau)
{
Guard.IsGreaterThanOrEqualTo(width, 0);
Expand Down
2 changes: 2 additions & 0 deletions src/Qynit.Pulsewave/EnvelopeCacheKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
namespace Qynit.Pulsewave;
internal record EnvelopeCacheKey(EnvelopeInfo EnvelopeInfo, Envelope Envelope);
Loading