Skip to content

Commit 5ea66f2

Browse files
authored
Performance Improvements (mono#1277)
* Correct company name * Fix targets file * Hide native files from project tree * Add benchmark project * Add a feature to skip the object registration - ISKSkipObjectRegistration - objects implementing this interface will not be registered in the global dictionary - major perf boos (2.84x) due to not having to look up in a dictionary - use direct constructor instead of object factory * Throw an exception in "debug" builds
1 parent 887da1d commit 5ea66f2

38 files changed

+264
-69
lines changed

benchmarks/Directory.Build.props

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project>
2+
3+
<Import Project="$(MSBuildThisFileDirectory)..\source\SkiaSharp.Build.props" />
4+
5+
</Project>

benchmarks/Directory.Build.targets

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project>
2+
3+
<Import Project="$(MSBuildThisFileDirectory)..\source\SkiaSharp.Build.targets" />
4+
5+
</Project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Jobs;
3+
using BenchmarkDotNet.Running;
4+
5+
namespace SkiaSharp.Benchmarks
6+
{
7+
//[SimpleJob(RuntimeMoniker.CoreRt31)]
8+
[SimpleJob(RuntimeMoniker.Mono)]
9+
[SimpleJob(RuntimeMoniker.Net472)]
10+
[SimpleJob(RuntimeMoniker.NetCoreApp31)]
11+
public class Benchmark
12+
{
13+
public Benchmark()
14+
{
15+
// setup
16+
}
17+
18+
[Benchmark]
19+
public void TheBenchmark()
20+
{
21+
// benchmark
22+
}
23+
}
24+
25+
public class Program
26+
{
27+
public static void Main(string[] args)
28+
{
29+
var summary = BenchmarkRunner.Run<Benchmark>();
30+
}
31+
}
32+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks>
5+
<RootNamespace>SkiaSharp.Benchmarks</RootNamespace>
6+
<AssemblyName>SkiaSharp.Benchmarks</AssemblyName>
7+
<SkipGenerateAssemblyVersionInfo>true</SkipGenerateAssemblyVersionInfo>
8+
<SkipMDocGenerateDocs>true</SkipMDocGenerateDocs>
9+
<SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory>
10+
</PropertyGroup>
11+
<ItemGroup>
12+
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
13+
</ItemGroup>
14+
<ItemGroup>
15+
<ProjectReference Include="..\..\binding\HarfBuzzSharp\HarfBuzzSharp.csproj" />
16+
<ProjectReference Include="..\..\binding\SkiaSharp\SkiaSharp.csproj" />
17+
<ProjectReference Include="..\..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj" />
18+
</ItemGroup>
19+
<ItemGroup>
20+
<Content Include="..\..\output\native\windows\x64\libSkiaSharp.dll" CopyToOutputDirectory="Always" Visible="false"
21+
Condition=" Exists('..\..\output\native\windows\x64\libSkiaSharp.dll') or '$(IsWindows)' == 'true' " />
22+
<Content Include="..\..\output\native\windows\x64\libSkiaSharp.pdb" CopyToOutputDirectory="Always" Visible="false"
23+
Condition=" Exists('..\..\output\native\windows\x64\libSkiaSharp.pdb') or '$(IsWindows)' == 'true' " />
24+
<Content Include="..\..\output\native\windows\x64\libHarfBuzzSharp.dll" CopyToOutputDirectory="Always" Visible="false"
25+
Condition=" Exists('..\..\output\native\windows\x64\libHarfBuzzSharp.dll') or '$(IsWindows)' == 'true' " />
26+
<Content Include="..\..\output\native\windows\x64\libHarfBuzzSharp.pdb" CopyToOutputDirectory="Always" Visible="false"
27+
Condition=" Exists('..\..\output\native\windows\x64\libHarfBuzzSharp.pdb') or '$(IsWindows)' == 'true' " />
28+
<Content Include="..\..\output\native\osx\libSkiaSharp.dylib" CopyToOutputDirectory="Always" Visible="false"
29+
Condition=" Exists('..\..\output\native\osx\libSkiaSharp.dylib') or '$(IsMacOS)' == 'true' " />
30+
<Content Include="..\..\output\native\osx\libHarfBuzzSharp.dylib" CopyToOutputDirectory="Always" Visible="false"
31+
Condition=" Exists('..\..\output\native\osx\libHarfBuzzSharp.dylib') or '$(IsMacOS)' == 'true' " />
32+
<Content Include="..\..\output\native\linux\x64\libSkiaSharp.so" CopyToOutputDirectory="Always" Visible="false"
33+
Condition=" Exists('..\..\output\native\linux\x64\libSkiaSharp.so') or '$(IsLinux)' == 'true' " />
34+
<Content Include="..\..\output\native\linux\x64\libHarfBuzzSharp.so" CopyToOutputDirectory="Always" Visible="false"
35+
Condition=" Exists('..\..\output\native\linux\x64\libHarfBuzzSharp.so') or '$(IsLinux)' == 'true' " />
36+
</ItemGroup>
37+
</Project>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29102.215
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp", "..\..\binding\SkiaSharp\SkiaSharp.csproj", "{3E1B158B-6C3B-4340-9F01-28E77D24F31D}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HarfBuzzSharp", "..\..\binding\HarfBuzzSharp\HarfBuzzSharp.csproj", "{38FFD397-8A5E-421A-8649-24FE463E1DE9}"
9+
EndProject
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.HarfBuzz", "..\..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj", "{0DE402FA-A101-438E-8528-6EA82E0FF803}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Benchmarks", "SkiaSharp.Benchmarks.csproj", "{8E5284C3-5AAF-4902-B12F-84E9172F20E0}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Release|Any CPU = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{3E1B158B-6C3B-4340-9F01-28E77D24F31D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{3E1B158B-6C3B-4340-9F01-28E77D24F31D}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{3E1B158B-6C3B-4340-9F01-28E77D24F31D}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{3E1B158B-6C3B-4340-9F01-28E77D24F31D}.Release|Any CPU.Build.0 = Release|Any CPU
24+
{38FFD397-8A5E-421A-8649-24FE463E1DE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{38FFD397-8A5E-421A-8649-24FE463E1DE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{38FFD397-8A5E-421A-8649-24FE463E1DE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{38FFD397-8A5E-421A-8649-24FE463E1DE9}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{0DE402FA-A101-438E-8528-6EA82E0FF803}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29+
{0DE402FA-A101-438E-8528-6EA82E0FF803}.Debug|Any CPU.Build.0 = Debug|Any CPU
30+
{0DE402FA-A101-438E-8528-6EA82E0FF803}.Release|Any CPU.ActiveCfg = Release|Any CPU
31+
{0DE402FA-A101-438E-8528-6EA82E0FF803}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{8E5284C3-5AAF-4902-B12F-84E9172F20E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{8E5284C3-5AAF-4902-B12F-84E9172F20E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{8E5284C3-5AAF-4902-B12F-84E9172F20E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{8E5284C3-5AAF-4902-B12F-84E9172F20E0}.Release|Any CPU.Build.0 = Release|Any CPU
36+
EndGlobalSection
37+
GlobalSection(SolutionProperties) = preSolution
38+
HideSolutionNode = FALSE
39+
EndGlobalSection
40+
GlobalSection(ExtensibilityGlobals) = postSolution
41+
SolutionGuid = {8355B800-A642-459B-8675-B545839DE6C7}
42+
EndGlobalSection
43+
EndGlobal

binding/Binding/GRBackendRenderTarget.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace SkiaSharp
55
{
6-
public unsafe class GRBackendRenderTarget : SKObject
6+
public unsafe class GRBackendRenderTarget : SKObject, ISKSkipObjectRegistration
77
{
88
internal GRBackendRenderTarget (IntPtr handle, bool owns)
99
: base (handle, owns)

binding/Binding/GRBackendTexture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace SkiaSharp
66
{
7-
public unsafe class GRBackendTexture : SKObject
7+
public unsafe class GRBackendTexture : SKObject, ISKSkipObjectRegistration
88
{
99
internal GRBackendTexture (IntPtr handle, bool owns)
1010
: base (handle, owns)

binding/Binding/GRContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace SkiaSharp
55
{
6-
public unsafe class GRContext : SKObject, ISKReferenceCounted
6+
public unsafe class GRContext : SKObject, ISKReferenceCounted, ISKSkipObjectRegistration
77
{
88
internal GRContext (IntPtr h, bool owns)
99
: base (h, owns)
@@ -105,6 +105,6 @@ public int GetMaxSurfaceSampleCount (SKColorType colorType) =>
105105
public int GetRecommendedSampleCount (GRPixelConfig config, float dpi) => 0;
106106

107107
internal static GRContext GetObject (IntPtr handle) =>
108-
GetOrAddObject (handle, (h, o) => new GRContext (h, o));
108+
handle == IntPtr.Zero ? null : new GRContext (handle, true);
109109
}
110110
}

binding/Binding/GRGlInterface.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace SkiaSharp
77
{
8-
public unsafe class GRGlInterface : SKObject, ISKReferenceCounted
8+
public unsafe class GRGlInterface : SKObject, ISKReferenceCounted, ISKSkipObjectRegistration
99
{
1010
internal GRGlInterface (IntPtr h, bool owns)
1111
: base (h, owns)
@@ -132,7 +132,7 @@ public bool HasExtension (string extension)
132132
}
133133

134134
internal static GRGlInterface GetObject (IntPtr handle) =>
135-
GetOrAddObject (handle, (h, o) => new GRGlInterface (h, o));
135+
handle == IntPtr.Zero ? null : new GRGlInterface (handle, true);
136136

137137
private static class AngleLoader
138138
{

binding/Binding/HandleDictionary.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
using System;
2-
using System.Collections.Concurrent;
32
using System.Collections.Generic;
43
using System.Threading;
4+
#if THROW_OBJECT_EXCEPTIONS
5+
using System.Collections.Concurrent;
6+
#endif
57

68
namespace SkiaSharp
79
{
810
internal static class HandleDictionary
911
{
12+
private static readonly Type SkipObjectRegistrationType = typeof (ISKSkipObjectRegistration);
1013

1114
#if THROW_OBJECT_EXCEPTIONS
1215
internal static readonly ConcurrentBag<Exception> exceptions = new ConcurrentBag<Exception> ();
@@ -27,6 +30,11 @@ internal static bool GetInstance<TSkiaObject> (IntPtr handle, out TSkiaObject in
2730
return false;
2831
}
2932

33+
if (SkipObjectRegistrationType.IsAssignableFrom (typeof (TSkiaObject))) {
34+
instance = null;
35+
return false;
36+
}
37+
3038
instancesLock.EnterReadLock ();
3139
try {
3240
return GetInstanceNoLocks (handle, out instance);
@@ -45,6 +53,16 @@ internal static TSkiaObject GetOrAddObject<TSkiaObject> (IntPtr handle, bool own
4553
if (handle == IntPtr.Zero)
4654
return null;
4755

56+
if (SkipObjectRegistrationType.IsAssignableFrom (typeof (TSkiaObject))) {
57+
#if THROW_OBJECT_EXCEPTIONS
58+
throw new InvalidOperationException (
59+
$"For some reason, the object was constructed using a factory function instead of the constructor. " +
60+
$"H: {handle.ToString ("x")} Type: {typeof (TSkiaObject)}");
61+
#else
62+
return objectFactory.Invoke (handle, owns);
63+
#endif
64+
}
65+
4866
instancesLock.EnterUpgradeableReadLock ();
4967
try {
5068
if (GetInstanceNoLocks<TSkiaObject> (handle, out var instance)) {
@@ -111,6 +129,9 @@ internal static void RegisterHandle (IntPtr handle, SKObject instance)
111129
if (handle == IntPtr.Zero || instance == null)
112130
return;
113131

132+
if (instance is ISKSkipObjectRegistration)
133+
return;
134+
114135
SKObject objectToDispose = null;
115136

116137
instancesLock.EnterWriteLock ();
@@ -146,6 +167,9 @@ internal static void DeregisterHandle (IntPtr handle, SKObject instance)
146167
if (handle == IntPtr.Zero)
147168
return;
148169

170+
if (instance is ISKSkipObjectRegistration)
171+
return;
172+
149173
instancesLock.EnterWriteLock ();
150174
try {
151175
var existed = instances.TryGetValue (handle, out var weak);

0 commit comments

Comments
 (0)