Skip to content

Commit

Permalink
GUID lookup optimizations and benchmarks (#1099)
Browse files Browse the repository at this point in the history
* Add GUID lookup benchmarks and optimization

* More optimizations.

* Add benchmarks

* Reduce calls to .GUID attribute where possible.

* Improve BindAs which showed up in managed QI calls.

* Optimize weak reference scenario to reduce allocations and add benchmarks

* Fix benchmark and replace GetIID call.

* Update testwinrt
  • Loading branch information
manodasanW authored Feb 7, 2022
1 parent 133204d commit e64e716
Show file tree
Hide file tree
Showing 30 changed files with 274 additions and 112 deletions.
1 change: 1 addition & 0 deletions src/Benchmarks/Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ItemGroup>
<Reference Include="$(MSBuildThisFileDirectory)..\Projections\Windows\bin\x64\Release\$(BenchmarkTargetFramework)\Microsoft.Windows.SDK.NET.dll" Condition="$(UseWinmd) == false"></Reference>
<Reference Include="$(MSBuildThisFileDirectory)..\Projections\Benchmark\bin\x64\Release\$(BenchmarkTargetFramework)\Benchmark.dll" Condition="$(UseWinmd) == false"></Reference>
<Reference Include="$(MSBuildThisFileDirectory)..\Projections\Benchmark\bin\x64\Release\$(BenchmarkTargetFramework)\WinRT.Runtime.dll" Condition="$(UseWinmd) == false"></Reference>

<ProjectReference Include="..\Projections\Windows\Windows.csproj" Condition="$(UseWinmd) == false And $(IsDotnetBuild) == false" />
<ProjectReference Include="..\Projections\Benchmark\Benchmark.csproj" Condition="$(UseWinmd) == false And $(IsDotnetBuild) == false" />
Expand Down
4 changes: 4 additions & 0 deletions src/Benchmarks/Benchmarks.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
name="BenchmarkComponent.EventOperations"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="BenchmarkComponent.Composable"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
</file>

</assembly>
81 changes: 81 additions & 0 deletions src/Benchmarks/GuidPerf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using BenchmarkComponent;
using BenchmarkDotNet.Attributes;
using System;
using System.Reflection;

#if !NETCOREAPP3_1
using WinRT;

namespace Benchmarks
{
[MemoryDiagnoser]
public class GuidPerf
{
[Benchmark]
public object GetClassGuid()
{
return WinRT.GuidGenerator.GetIID(typeof(ClassWithMarshalingRoutines));
}

[Benchmark]
public object GetDelegateGuid()
{
return WinRT.GuidGenerator.GetIID(typeof(Windows.Foundation.AsyncActionCompletedHandler));
}

[Benchmark]
public object CreateListGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IList<ClassWithMarshalingRoutines>));
}

[Benchmark]
public object CreateDictionaryWithStringKeyGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IDictionary<String, ClassWithMarshalingRoutines>));
}

[Benchmark]
public object CreateDictionaryWithBoolKeyGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IDictionary<bool, ClassWithMarshalingRoutines>));
}

[Benchmark]
public object CreateReadOnlyEnumListGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IReadOnlyList<Windows.Foundation.AsyncStatus>));
}

[Benchmark]
public object CreateReadOnlyFlagEnumListGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IReadOnlyList<Windows.Storage.FileAttributes>));
}

[Benchmark]
public object CreateReadOnlyStructListGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IReadOnlyList<NonBlittable>));
}

[Benchmark]
public object CreateReadOnlyInterfaceListGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IReadOnlyList<IIntProperties>));
}

[Benchmark]
public object CreateReadOnlyClassListGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IReadOnlyList<EventOperations>));
}

[Benchmark]
public object CreateReadOnlyDelegateListGuid()
{
return WinRT.GuidGenerator.CreateIID(typeof(System.Collections.Generic.IReadOnlyList<ProvideInt>));
}
}
}
#endif
32 changes: 32 additions & 0 deletions src/Benchmarks/QueryInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,37 @@

namespace Benchmarks
{
class ManagedObjectWithInterfaces : IIntProperties, IBoolProperties
{
private int intProperty;
private bool boolProperty;

public int IntProperty { get => intProperty; set => intProperty = value; }
public bool BoolProperty { get => boolProperty; set => boolProperty = value; }
}

class ManagedComposableObjectWithInterfaces : Composable, IIntProperties
{
private int intProperty;

public int IntProperty { get => intProperty; set => intProperty = value; }
}

[MemoryDiagnoser]
public class QueryInterfacePerf
{
ClassWithMultipleInterfaces instance;
ChatMessage message;
ManagedObjectWithInterfaces managedObject;
ManagedComposableObjectWithInterfaces composableObject;

[GlobalSetup]
public void Setup()
{
instance = new ClassWithMultipleInterfaces();
message = new ChatMessage();
managedObject = new ManagedObjectWithInterfaces();
composableObject = new ManagedComposableObjectWithInterfaces();
}

[Benchmark]
Expand Down Expand Up @@ -100,6 +120,18 @@ public int ConstructAndQueryNonDefaultInterfaceFirstCall()
public int StaticPropertyCall()
{
return Windows.System.Power.PowerManager.RemainingChargePercent;
}

[Benchmark]
public void QueryInterfaceOnManagedObject()
{
instance.QueryBoolInterface(managedObject);
}

[Benchmark]
public void QueryNativeInterfaceOnComposedObject()
{
instance.QueryBoolInterface(composableObject);
}
}
}
26 changes: 23 additions & 3 deletions src/Benchmarks/ReflectionPerf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ namespace Benchmarks
public class ReflectionPerf
{
ClassWithMarshalingRoutines instance;
IDictionary<String, WrappedClass> instanceDictionary;
IDictionary<String, WrappedClass> instanceDictionary;
ManagedObjectWithInterfaces managedObject;

[GlobalSetup]
public void Setup()
{
instance = new ClassWithMarshalingRoutines();
instanceDictionary = instance.ExistingDictionary;
instanceDictionary = instance.ExistingDictionary;
managedObject = new ManagedObjectWithInterfaces();
}

[Benchmark]
Expand Down Expand Up @@ -275,6 +277,24 @@ public void SetNonWinRTType()
public object GetExistingWinRTType()
{
return instance.ExistingType;
}
}

[Benchmark]
public void GetWeakReferenceOfManagedObject()
{
instance.GetWeakReference(managedObject);
}

[Benchmark]
public object GetAndResolveWeakReferenceOfManagedObject()
{
return instance.GetAndResolveWeakReference(managedObject);
}

[Benchmark]
public object GetWeakReferenceOfNativeObject()
{
return new WeakReference<ClassWithMarshalingRoutines>(instance);
}
}
}
6 changes: 3 additions & 3 deletions src/WinRT.Runtime/AgileReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public unsafe AgileReference(IObjectReference instance)
}

IntPtr agileReference = default;
Guid iid = typeof(IUnknownVftbl).GUID;
Guid iid = IUnknownVftbl.IID;
try
{
Marshal.ThrowExceptionForHR(Platform.RoGetAgileReference(
Expand All @@ -51,7 +51,7 @@ public unsafe AgileReference(IObjectReference instance)
MarshalInterface<IAgileReference>.DisposeAbi(agileReference);
}
}
public IObjectReference Get() => _cookie == IntPtr.Zero ? _agileReference?.Resolve(typeof(IUnknownVftbl).GUID) : Git.Value?.GetInterfaceFromGlobal(_cookie, typeof(IUnknownVftbl).GUID);
public IObjectReference Get() => _cookie == IntPtr.Zero ? _agileReference?.Resolve(IUnknownVftbl.IID) : Git.Value?.GetInterfaceFromGlobal(_cookie, IUnknownVftbl.IID);

protected virtual void Dispose(bool disposing)
{
Expand All @@ -75,7 +75,7 @@ protected virtual void Dispose(bool disposing)
private static unsafe IGlobalInterfaceTable GetGitTable()
{
Guid gitClsid = CLSID_StdGlobalInterfaceTable;
Guid gitIid = typeof(IGlobalInterfaceTable).GUID;
Guid gitIid = ABI.WinRT.Interop.IGlobalInterfaceTable.IID;
IntPtr gitPtr = default;

try
Expand Down
16 changes: 8 additions & 8 deletions src/WinRT.Runtime/ComWrappersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ internal static List<ComInterfaceEntry> GetInterfaceTableEntries(Type type)
Vtable = (IntPtr)ifaceAbiType.GetAbiToProjectionVftblPtr()
});

if (!hasCustomIMarshalInterface && iid == typeof(ABI.WinRT.Interop.IMarshal.Vftbl).GUID)
if (!hasCustomIMarshalInterface && iid == ABI.WinRT.Interop.IMarshal.IID)
{
hasCustomIMarshalInterface = true;
}
Expand Down Expand Up @@ -215,13 +215,13 @@ internal static List<ComInterfaceEntry> GetInterfaceTableEntries(Type type)

entries.Add(new ComInterfaceEntry
{
IID = typeof(ManagedIStringableVftbl).GUID,
IID = ManagedIStringableVftbl.IID,
Vtable = ManagedIStringableVftbl.AbiToProjectionVftablePtr
});

entries.Add(new ComInterfaceEntry
{
IID = typeof(ManagedCustomPropertyProviderVftbl).GUID,
IID = ManagedCustomPropertyProviderVftbl.IID,
Vtable = ManagedCustomPropertyProviderVftbl.AbiToProjectionVftablePtr
});

Expand All @@ -237,15 +237,15 @@ internal static List<ComInterfaceEntry> GetInterfaceTableEntries(Type type)
{
entries.Add(new ComInterfaceEntry
{
IID = typeof(ABI.WinRT.Interop.IMarshal.Vftbl).GUID,
IID = ABI.WinRT.Interop.IMarshal.IID,
Vtable = ABI.WinRT.Interop.IMarshal.Vftbl.AbiToProjectionVftablePtr
});
}

// Add IAgileObject to all CCWs
entries.Add(new ComInterfaceEntry
{
IID = typeof(ABI.WinRT.Interop.IAgileObject.Vftbl).GUID,
IID = ABI.WinRT.Interop.IAgileObject.IID,
Vtable = IUnknownVftbl.AbiToProjectionVftblPtr
});
return entries;
Expand Down Expand Up @@ -465,13 +465,13 @@ private static bool IsIReferenceType(Type type)
{
static bool IsIReferenceTypeHelper(Type type)
{
if ((type.GetCustomAttribute<WindowsRuntimeTypeAttribute>() is object) ||
if (type.IsDefined(typeof(WindowsRuntimeTypeAttribute)) ||
WinRT.Projections.IsTypeWindowsRuntimeType(type))
return true;
type = type.GetAuthoringMetadataType();
if (type is object)
{
if ((type.GetCustomAttribute<WindowsRuntimeTypeAttribute>() is object) ||
if (type.IsDefined(typeof(WindowsRuntimeTypeAttribute)) ||
WinRT.Projections.IsTypeWindowsRuntimeType(type))
return true;
}
Expand All @@ -495,7 +495,7 @@ static bool IsIReferenceTypeHelper(Type type)
private static ComInterfaceEntry IPropertyValueEntry =>
new ComInterfaceEntry
{
IID = global::WinRT.GuidGenerator.GetIID(typeof(global::Windows.Foundation.IPropertyValue)),
IID = ManagedIPropertyValueImpl.IID,
Vtable = ManagedIPropertyValueImpl.AbiToProjectionVftablePtr
};

Expand Down
22 changes: 14 additions & 8 deletions src/WinRT.Runtime/ComWrappersSupport.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,26 @@ public static IObjectReference CreateCCWForObject(object obj)
{
IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport);
return ObjectReference<IUnknownVftbl>.Attach(ref ccw);
}
}

internal static ObjectReference<T> CreateCCWForObject<T>(object obj, Guid iid)
{
internal static IntPtr CreateCCWForObjectForABI(object obj, Guid iid)
{
IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport);
try
{
Marshal.ThrowExceptionForHR(Marshal.QueryInterface(ccw, ref iid, out var iidCcw));
return ObjectReference<T>.Attach(ref iidCcw);
return iidCcw;
}
finally
{
MarshalInspectable<object>.DisposeAbi(ccw);
}
}
}

internal static ObjectReference<T> CreateCCWForObject<T>(object obj, Guid iid)
{
IntPtr ccw = CreateCCWForObjectForABI(obj, iid);
return ObjectReference<T>.Attach(ref ccw);
}

public static unsafe T FindObject<T>(IntPtr ptr)
Expand Down Expand Up @@ -462,14 +468,14 @@ static unsafe DefaultComWrappers()

entries.Add(new ComInterfaceEntry
{
IID = typeof(IInspectable).GUID,
IID = InterfaceIIDs.IInspectable_IID,
Vtable = IInspectable.Vftbl.AbiToProjectionVftablePtr
});

// This should be the last entry as it is included / excluded based on the flags.
entries.Add(new ComInterfaceEntry
{
IID = typeof(IUnknownVftbl).GUID,
IID = IUnknownVftbl.IID,
Vtable = IUnknownVftbl.AbiToProjectionVftblPtr
});

Expand Down Expand Up @@ -507,7 +513,7 @@ private static unsafe bool IsRuntimeImplementedRCW(Type objType)

private static object CreateObject(IntPtr externalComObject)
{
Guid inspectableIID = IInspectable.IID;
Guid inspectableIID = InterfaceIIDs.IInspectable_IID;
Guid weakReferenceIID = ABI.WinRT.Interop.IWeakReference.IID;
IntPtr ptr = IntPtr.Zero;

Expand Down
6 changes: 3 additions & 3 deletions src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,19 +330,19 @@ public ComCallableWrapper(object obj)

interfaceTableEntries.Add(new ComInterfaceEntry
{
IID = typeof(IUnknownVftbl).GUID,
IID = IUnknownVftbl.IID,
Vtable = IUnknownVftbl.AbiToProjectionVftblPtr
});

interfaceTableEntries.Add(new ComInterfaceEntry
{
IID = typeof(IInspectable).GUID,
IID = InterfaceIIDs.IInspectable_IID,
Vtable = IInspectable.Vftbl.AbiToProjectionVftablePtr
});

InitializeManagedQITable(interfaceTableEntries);

IdentityPtr = _managedQITable[typeof(IUnknownVftbl).GUID];
IdentityPtr = _managedQITable[IUnknownVftbl.IID];
}

~ComCallableWrapper()
Expand Down
2 changes: 1 addition & 1 deletion src/WinRT.Runtime/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ static class Context

public static IntPtr GetContextCallback()
{
Guid riid = typeof(IContextCallback).GUID;
Guid riid = ABI.WinRT.Interop.IContextCallback.IID;
Marshal.ThrowExceptionForHR(CoGetObjectContext(ref riid, out IntPtr contextCallbackPtr));
return contextCallbackPtr;
}
Expand Down
2 changes: 1 addition & 1 deletion src/WinRT.Runtime/GuidGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static string GetSignature(Type type)
{
if (type.IsEnum)
{
var isFlags = type.CustomAttributes.Any(cad => cad.AttributeType == typeof(FlagsAttribute));
var isFlags = type.IsDefined(typeof(FlagsAttribute));
return "enum(" + type.FullName + ";" + (isFlags ? "u4" : "i4") + ")";
}
if (!type.IsPrimitive)
Expand Down
Loading

0 comments on commit e64e716

Please sign in to comment.