Skip to content

Commit cfd34a2

Browse files
committed
Add benchmarks for PixelConverter
Added PixelConverterBenchmarks under benchmarks/SIPSorceryMedia.Abstractions with .csproj and solution registration. Implemented multiple BenchmarkDotNet classes for pixel format conversions (I420, NV12, BGR, BGRA) with array and buffer writer variants. Introduced custom BenchmarkDotNet config (Config.cs) for job setup and output. Added Utils.cs for bitmap-to-buffer and odd-dimension buffer helpers. Included Program.cs to run benchmarks, global.json for .NET SDK pinning, and ensured test images copy to output. Updated solution structure accordingly.
1 parent cfbd872 commit cfd34a2

17 files changed

Lines changed: 811 additions & 0 deletions

SIPSorcery.slnx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
<Project Path="test/unit/SIPSorcery.UnitTests.csproj" />
2222
<Project Path="test/VideoCaptureTest/VideoCaptureTest.csproj" />
2323
</Folder>
24+
<Folder Name="/benchmarks/" />
25+
<Folder Name="/benchmarks/SIPSorceryMedia.Abstractions/">
26+
<Project Path="benchmarks/SIPSorceryMedia.Abstractions/PixelConverterBenchmarks/PixelConverterBenchmarks.csproj" />
27+
</Folder>
2428
<Folder Name="/examples/">
2529
<Project Path="examples/sipcmdline/sipcmdline.csproj" />
2630
<Project Path="examples/StunServer/StunServer.csproj" />
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System.Collections.Immutable;
2+
using BenchmarkDotNet.Columns;
3+
using BenchmarkDotNet.Configs;
4+
using BenchmarkDotNet.Environments;
5+
using BenchmarkDotNet.Filters;
6+
using BenchmarkDotNet.Jobs;
7+
using BenchmarkDotNet.Order;
8+
using BenchmarkDotNet.Reports;
9+
using BenchmarkDotNet.Running;
10+
11+
#nullable disable
12+
13+
namespace PixelConverterBenchmarks;
14+
15+
internal sealed class Config : ManualConfig
16+
{
17+
private const string _currentJobId = "Current";
18+
19+
public Config()
20+
{
21+
Runtime[] targetRuntimes = [CoreRuntime.Core10_0];
22+
string[] targetVersions = ["10.0.15", ""];
23+
24+
foreach (var version in targetVersions)
25+
{
26+
var isBaseline = string.Equals(version, targetVersions[0]);
27+
28+
foreach (var targetRuntime in targetRuntimes)
29+
{
30+
AddJob(Job.MediumRun
31+
.WithRuntime(targetRuntime)
32+
.WithMsBuildArguments($"/p:LibVersion={version}")
33+
.WithId(isBaseline ? version : _currentJobId)
34+
.WithBaseline(isBaseline)
35+
);
36+
}
37+
}
38+
39+
AddFilter(new SimpleFilter(static benchmark =>
40+
{
41+
var isBufferWriter = benchmark.Descriptor.WorkloadMethod.Name.EndsWith("BufferWriter", StringComparison.Ordinal);
42+
43+
var isBaselineJob = string.Equals(_currentJobId, benchmark.Job.Id, StringComparison.Ordinal);
44+
45+
return !isBufferWriter || isBaselineJob;
46+
}));
47+
48+
WithOrderer(new RuntimeGroupedOrderer());
49+
50+
AddExporter(BenchmarkDotNet.Exporters.MarkdownExporter.GitHub);
51+
52+
AddColumnProvider(BenchmarkDotNet.Columns.DefaultColumnProviders.Instance);
53+
HideColumns(Column.Arguments, Column.Error, Column.Median, Column.StdDev, Column.RatioSD);
54+
55+
WithSummaryStyle(SummaryStyle.Default.WithMaxParameterColumnWidth(int.MaxValue));
56+
57+
AddDiagnoser(BenchmarkDotNet.Diagnosers.MemoryDiagnoser.Default);
58+
59+
AddLogger(BenchmarkDotNet.Loggers.ConsoleLogger.Default);
60+
}
61+
62+
private sealed class RuntimeGroupedOrderer : IOrderer
63+
{
64+
public IEnumerable<BenchmarkCase> GetExecutionOrder(
65+
ImmutableArray<BenchmarkCase> benchmarksCase,
66+
IEnumerable<BenchmarkLogicalGroupRule> order = null)
67+
=> benchmarksCase;
68+
69+
public IEnumerable<BenchmarkCase> GetSummaryOrder(
70+
ImmutableArray<BenchmarkCase> benchmarksCases,
71+
Summary summary)
72+
=> benchmarksCases
73+
.OrderBy(b => b.Job.Environment.Runtime?.Name)
74+
.ThenBy(b => b.Descriptor.WorkloadMethod.Name)
75+
.ThenBy(b => b.Job.Id == _currentJobId ? 0 : 1)
76+
.ThenByDescending(b => b.Job.Id);
77+
78+
public string GetHighlightGroupKey(BenchmarkCase benchmarkCase)
79+
=> benchmarkCase.Job.Environment.Runtime?.Name;
80+
81+
public string GetLogicalGroupKey(
82+
ImmutableArray<BenchmarkCase> allBenchmarksCases,
83+
BenchmarkCase benchmarkCase)
84+
=> benchmarkCase.Job.Environment.Runtime?.Name;
85+
86+
public IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrder(
87+
IEnumerable<IGrouping<string, BenchmarkCase>> logicalGroups,
88+
IEnumerable<BenchmarkLogicalGroupRule> order = null)
89+
=> logicalGroups.OrderBy(g => g.Key);
90+
91+
public bool SeparateLogicalGroups => true;
92+
}
93+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Diagnostics;
2+
using BenchmarkDotNet.Attributes;
3+
using SIPSorceryMedia.Abstractions;
4+
5+
namespace PixelConverterBenchmarks;
6+
7+
public class ConvertI420ToNV12Benchmarks
8+
{
9+
#if !LibVersion
10+
CommunityToolkit.HighPerformance.Buffers.ArrayPoolBufferWriter<byte>? _nv12Writer = new();
11+
#endif
12+
private byte[]? _i420;
13+
private const int Width = 640;
14+
private const int Height = 480;
15+
16+
[GlobalSetup]
17+
public void GlobalSetup()
18+
{
19+
_i420 = File.ReadAllBytes("img/ref-i420.yuv");
20+
}
21+
22+
[IterationSetup]
23+
public void IterationSetup()
24+
{
25+
#if !LibVersion
26+
Debug.Assert(_nv12Writer is not null);
27+
_nv12Writer.Clear();
28+
#endif
29+
}
30+
31+
[Benchmark]
32+
public int ConvertI420ToNV12Array()
33+
{
34+
Debug.Assert(_i420 is not null);
35+
var nv12 = PixelConverter.I420toNV12(_i420, Width, Height);
36+
return nv12.Length;
37+
}
38+
39+
[Benchmark]
40+
public int ConvertI420ToNV12BufferWriter()
41+
{
42+
#if LibVersion
43+
return 0;
44+
#else
45+
Debug.Assert(_i420 is not null);
46+
Debug.Assert(_nv12Writer is not null);
47+
PixelConverter.I420toNV12(_nv12Writer, _i420.AsSpan(), Width, Height);
48+
return _nv12Writer.WrittenCount;
49+
#endif
50+
}
51+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Diagnostics;
2+
using BenchmarkDotNet.Attributes;
3+
using SIPSorceryMedia.Abstractions;
4+
5+
namespace PixelConverterBenchmarks;
6+
7+
public class ConvertKnownI420ToBGRBenchmarks
8+
{
9+
#if !LibVersion
10+
CommunityToolkit.HighPerformance.Buffers.ArrayPoolBufferWriter<byte>? _bgrWriter = new();
11+
#endif
12+
private byte[]? _i420;
13+
private int _width = 640;
14+
private int _height = 480;
15+
16+
[GlobalSetup]
17+
public void GlobalSetup()
18+
{
19+
_i420 = File.ReadAllBytes("img/ref-i420.yuv");
20+
}
21+
22+
[IterationSetup]
23+
public void IterationSetup()
24+
{
25+
#if !LibVersion
26+
Debug.Assert(_bgrWriter is not null);
27+
_bgrWriter.Clear();
28+
#endif
29+
}
30+
31+
[Benchmark]
32+
public int RoundtripBgr24ToI420Array()
33+
{
34+
Debug.Assert(_i420 is not null);
35+
var bgr = PixelConverter.I420toBGR(_i420, _width, _height, out _);
36+
return bgr.Length;
37+
}
38+
39+
[Benchmark]
40+
public int RoundtripBgr24ToI420BufferWriter()
41+
{
42+
#if LibVersion
43+
return 0;
44+
#else
45+
Debug.Assert(_i420 is not null);
46+
Debug.Assert(_bgrWriter is not null);
47+
PixelConverter.I420toBGR(_bgrWriter, _i420, _width, _height, out _);
48+
return _bgrWriter.WrittenCount;
49+
#endif
50+
}
51+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Diagnostics;
2+
using BenchmarkDotNet.Attributes;
3+
using SIPSorceryMedia.Abstractions;
4+
5+
namespace PixelConverterBenchmarks;
6+
7+
public class ConvertKnownNV12ToBGRBenchmarks
8+
{
9+
#if !LibVersion
10+
CommunityToolkit.HighPerformance.Buffers.ArrayPoolBufferWriter<byte>? _bgrWriter = new();
11+
#endif
12+
private byte[]? _nv12;
13+
private int _width = 640;
14+
private int _height = 480;
15+
private int _stride;
16+
17+
[GlobalSetup]
18+
public void GlobalSetup()
19+
{
20+
_nv12 = File.ReadAllBytes("img/ref-nv12.yuv");
21+
_stride = _width * 3;
22+
}
23+
24+
[IterationSetup]
25+
public void IterationSetup()
26+
{
27+
#if !LibVersion
28+
Debug.Assert(_bgrWriter is not null);
29+
_bgrWriter.Clear();
30+
#endif
31+
}
32+
33+
[Benchmark]
34+
public int RoundtripBgr24ToI420Array()
35+
{
36+
Debug.Assert(_nv12 is not null);
37+
var bgr = PixelConverter.NV12toBGR(_nv12, _width, _height, _stride);
38+
return bgr.Length;
39+
}
40+
41+
[Benchmark]
42+
public int RoundtripBgr24ToI420BufferWriter()
43+
{
44+
#if LibVersion
45+
return 0;
46+
#else
47+
Debug.Assert(_nv12 is not null);
48+
Debug.Assert(_bgrWriter is not null);
49+
PixelConverter.NV12toBGR(_bgrWriter, _nv12, _width, _height, _stride);
50+
return _bgrWriter.WrittenCount;
51+
#endif
52+
}
53+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.Diagnostics;
2+
using BenchmarkDotNet.Attributes;
3+
using SIPSorceryMedia.Abstractions;
4+
5+
namespace PixelConverterBenchmarks;
6+
7+
public class ConvertOddDimensionI420ToNV12Benchmarks
8+
{
9+
#if !LibVersion
10+
CommunityToolkit.HighPerformance.Buffers.ArrayPoolBufferWriter<byte>? _nv12Writer = new();
11+
#endif
12+
private byte[]? _oddI420;
13+
private const int OddWidth = 5;
14+
private const int OddHeight = 3;
15+
16+
[GlobalSetup]
17+
public void GlobalSetup()
18+
{
19+
var i420 = File.ReadAllBytes("img/ref-i420.yuv");
20+
_oddI420 = Utils.CreateOddDimensionBuffer(OddWidth, OddHeight);
21+
}
22+
23+
[IterationSetup]
24+
public void IterationSetup()
25+
{
26+
#if !LibVersion
27+
Debug.Assert(_nv12Writer is not null);
28+
_nv12Writer.Clear();
29+
#endif
30+
}
31+
32+
[Benchmark]
33+
public int ConvertOddDimensionI420ToNV12Array()
34+
{
35+
Debug.Assert(_oddI420 is not null);
36+
var nv12 = PixelConverter.I420toNV12(_oddI420, OddWidth, OddHeight);
37+
return nv12.Length;
38+
}
39+
40+
[Benchmark]
41+
public int ConvertOddDimensionI420ToNV12BufferWriter()
42+
{
43+
#if LibVersion
44+
return 0;
45+
#else
46+
Debug.Assert(_nv12Writer is not null);
47+
Debug.Assert(_oddI420 is not null);
48+
PixelConverter.I420toNV12(_nv12Writer, _oddI420.AsSpan(), OddWidth, OddHeight);
49+
return _nv12Writer.WrittenCount;
50+
#endif
51+
}
52+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Diagnostics;
2+
using BenchmarkDotNet.Attributes;
3+
using SIPSorceryMedia.Abstractions;
4+
5+
namespace PixelConverterBenchmarks;
6+
7+
public class ConvertOddDimensionNV12ToI420Banchmarks
8+
{
9+
#if !LibVersion
10+
CommunityToolkit.HighPerformance.Buffers.ArrayPoolBufferWriter<byte>? _i420Writer = new();
11+
#endif
12+
private byte[]? _oddNv12;
13+
private const int OddWidth = 5;
14+
private const int OddHeight = 3;
15+
16+
[GlobalSetup]
17+
public void GlobalSetup()
18+
{
19+
_oddNv12 = Utils.CreateOddDimensionBuffer(OddWidth, OddHeight);
20+
}
21+
22+
[IterationSetup]
23+
public void IterationSetup()
24+
{
25+
#if !LibVersion
26+
Debug.Assert(_i420Writer is not null);
27+
_i420Writer.Clear();
28+
#endif
29+
}
30+
31+
[Benchmark]
32+
public int ConvertOddDimensionNV12ToI420Array()
33+
{
34+
Debug.Assert(_oddNv12 is not null);
35+
var i420 = PixelConverter.NV12toI420(_oddNv12, OddWidth, OddHeight);
36+
return i420.Length;
37+
}
38+
39+
[Benchmark]
40+
public int ConvertOddDimensionNV12ToI420BufferWriter()
41+
{
42+
#if LibVersion
43+
return 0;
44+
#else
45+
Debug.Assert(_i420Writer is not null);
46+
Debug.Assert(_oddNv12 is not null);
47+
PixelConverter.NV12toI420(_i420Writer, _oddNv12.AsSpan(), OddWidth, OddHeight);
48+
return _i420Writer.WrittenCount;
49+
#endif
50+
}
51+
}

0 commit comments

Comments
 (0)