Skip to content

Commit

Permalink
feat(jsii-dotnet-runtime): Improve .NET Performance
Browse files Browse the repository at this point in the history
The .NET runtime performace has been enhanced by adding an Assembly Load cache to JsiiTypeAttributeBase to avoid expensive repeated
use of reflection and file system access.

Compliance tests now share a client to speed up execution and avoid problems with cache usage.

.NET integration tests now hard-reference the .NET runtime project, and has been added to the runtime solution. There is no disadvantage to doing this,
and it makes development easier.

Resolves #304
  • Loading branch information
costleya committed Nov 9, 2018
1 parent b7b91db commit 20321af
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 70 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules/
.BUILD_COMPLETED
lerna-debug.log
lerna-debug.log
.DS_Store
.idea
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Amazon.JSII.Tests.CalculatorPackageId" Version="$(JsiiVersion)" />
<PackageReference Include="Amazon.JSII.Tests.CalculatorPackageId" Version="0.7.8" />
</ItemGroup>

<ItemGroup>
Expand All @@ -19,4 +19,8 @@
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\jsii-dotnet-runtime\src\Amazon.JSII.Runtime\Amazon.JSII.Runtime.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.JSII.Runtime.Deputy;
using Amazon.JSII.Tests.CalculatorNamespace;
using Amazon.JSII.Tests.CalculatorNamespace.composition.CompositeOperation;
Expand All @@ -9,12 +8,14 @@
using Xunit;
using Xunit.Abstractions;

[assembly: CollectionBehavior(DisableTestParallelization = true)]

namespace Amazon.JSII.Runtime.IntegrationTests
{
/// <summary>
/// Ported from packages/jsii-java-runtime/src/test/java/org/jsii/testing/ComplianceTest.java.
/// </summary>
public class ComplianceTests : IntegrationTestBase
public class ComplianceTests : IClassFixture<ServiceContainerFixture>
{
class RuntimeException : Exception
{
Expand All @@ -29,8 +30,9 @@ public RuntimeException(string message)

const string Prefix = nameof(IntegrationTests) + ".Compliance.";

public ComplianceTests(ITestOutputHelper output) : base(output)
public ComplianceTests(ITestOutputHelper outputHelper, ServiceContainerFixture serviceContainerFixture)
{
serviceContainerFixture.SetOverride(outputHelper);
}

[Fact(DisplayName = Prefix + nameof(PrimitiveTypes))]
Expand Down Expand Up @@ -837,7 +839,7 @@ public NumberReturner(double number)
[JsiiProperty("numberProp", "{\"fqn\":\"@scope/jsii-calc-lib.Number\"}", true)]
public Number NumberProp { get; }

[JsiiMethod("obtainNumber", "{\"fqn\":\"@scope/jsii-calc-lib.IDoublable\"}", "[]",true)]
[JsiiMethod("obtainNumber", "{\"fqn\":\"@scope/jsii-calc-lib.IDoublable\"}", "[]", true)]
public IIDoublable ObtainNumber()
{
return new Doublable(this.NumberProp);
Expand All @@ -850,7 +852,7 @@ public Doublable(Number number)
this.DoubleValue = number.DoubleValue;
}

[JsiiProperty("doubleValue","{\"primitive\":\"number\"}",true)]
[JsiiProperty("doubleValue", "{\"primitive\":\"number\"}", true)]
public Double DoubleValue { get; }
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using Amazon.JSII.Runtime.Services;
using Xunit.Abstractions;

namespace Amazon.JSII.Runtime.IntegrationTests
{
public class ServiceContainerFixture : IDisposable
{
public ServiceContainerFixture()
{
Environment.SetEnvironmentVariable("JSII_DEBUG", "true");
}

public void SetOverride(ITestOutputHelper outputHelper)
{
if (ServiceContainer.ServiceProviderOverride == null)
{
ServiceContainer.ServiceProviderOverride = ServiceContainer.BuildServiceProvider(
new XUnitLoggerFactory(outputHelper)
);
}
}

public void Dispose()
{
ServiceContainer.ServiceProviderOverride = null;
}
}
}
6 changes: 6 additions & 0 deletions packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Amazon.JSII.Runtime.UnitTests", "Amazon.JSII.Runtime.UnitTests\Amazon.JSII.Runtime.UnitTests.csproj", "{96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.JSII.Runtime.IntegrationTests", "..\..\jsii-dotnet-runtime-test\test\Amazon.JSII.Runtime.IntegrationTests\Amazon.JSII.Runtime.IntegrationTests.csproj", "{7BD15A18-BE3A-4729-9B8C-570BF214C4CE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -28,6 +30,10 @@ Global
{96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}.Release|Any CPU.Build.0 = Release|Any CPU
{7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using Amazon.JSII.Runtime.Services;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using Amazon.JSII.Runtime.Services;
using Microsoft.Extensions.DependencyInjection;

namespace Amazon.JSII.Runtime.Deputy
{
public abstract class JsiiTypeAttributeBase : Attribute
{
private static readonly ConcurrentBag<string> ProcessedAssemblies =
new ConcurrentBag<string>();

protected JsiiTypeAttributeBase(Type nativeType, string fullyQualifiedName)
{
nativeType = nativeType ?? throw new ArgumentNullException(nameof(nativeType));
Expand All @@ -17,27 +20,32 @@ protected JsiiTypeAttributeBase(Type nativeType, string fullyQualifiedName)
Load(nativeType.Assembly);
}

void Load(Assembly assembly)
private static void Load(Assembly assembly)
{
IEnumerable<Assembly> dependencies = assembly.GetReferencedAssemblies()
.Select(assemblyName => Assembly.Load(assemblyName));
if (ProcessedAssemblies.Contains(GetAssemblyKey(assembly)))
{
return;
}

JsiiAssemblyAttribute attribute = assembly.GetCustomAttribute<JsiiAssemblyAttribute>();
var attribute = assembly.GetCustomAttribute<JsiiAssemblyAttribute>();
if (attribute == null)
{
ProcessedAssemblies.Add(GetAssemblyKey(assembly));
return;
}

foreach (Assembly dependency in dependencies)
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
{
Load(dependency);
var loadedReference = Assembly.Load(referencedAssembly);
Load(loadedReference);
}

// find the .tgz resource
var tarballResourceName = assembly.GetManifestResourceNames().FirstOrDefault(name => name.EndsWith(".tgz"));
if (tarballResourceName == null)
{
throw new JsiiException("Cannot find embedded tarball resource in assembly " + assembly.GetName(), null);
throw new JsiiException("Cannot find embedded tarball resource in assembly " + assembly.GetName(),
null);
}

IServiceProvider serviceProvider = ServiceContainer.ServiceProvider;
Expand All @@ -46,8 +54,13 @@ void Load(Assembly assembly)

IClient client = serviceProvider.GetRequiredService<IClient>();
client.LoadPackage(attribute.Name, attribute.Version, tarballPath);

ProcessedAssemblies.Add(GetAssemblyKey(assembly));


string GetAssemblyKey(Assembly assemblyReference) => assemblyReference.GetName().FullName;
}

public string FullyQualifiedName { get; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Amazon.JSII.Runtime.Deputy;
using System;
using System;
using System.Linq;
using System.Reflection;
using Amazon.JSII.Runtime.Deputy;

namespace Amazon.JSII.Runtime
{
Expand Down Expand Up @@ -35,7 +35,8 @@ public static MethodInfo GetNativeMethod(Type classType, string name)

if (methodInfo == null)
{
throw new ArgumentNullException($"Class {classType.Name} does not have a method called {name}", nameof(name));
throw new ArgumentNullException($"Class {classType.Name} does not have a method called {name}",
nameof(name));
}

return methodInfo;
Expand All @@ -52,7 +53,8 @@ public static PropertyInfo GetNativeProperty(Type classType, string name)

if (propertyInfo == null)
{
throw new ArgumentNullException($"Class {classType.Name} does not have a property called {name}", nameof(name));
throw new ArgumentNullException($"Class {classType.Name} does not have a property called {name}",
nameof(name));
}

return propertyInfo;
Expand All @@ -65,23 +67,13 @@ public static PropertyInfo GetIndexer(Type type)

public static JsiiClassAttribute GetClassAttribute(Type type)
{
Type current = type;

while (current != null)
if (type == null)
{
// JsiiClassAttribute can't be inheritable, because we need to distinguish between JSII
// types and native extensions of JSII types. So we have to search the inheritance tree
// manually.
JsiiClassAttribute classAttribute = current.GetCustomAttribute<JsiiClassAttribute>();
if (classAttribute != null)
{
return classAttribute;
}

current = current.BaseType;
return null;
}

return null;
return type.GetCustomAttribute<JsiiClassAttribute>()
?? GetClassAttribute(type.BaseType);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
using Amazon.JSII.JsonModel.Api;
using System;
using System.IO;
using Amazon.JSII.JsonModel.Api;
using Amazon.JSII.JsonModel.Api.Request;
using Amazon.JSII.JsonModel.Api.Response;
using Amazon.JSII.JsonModel.FileSystem;
using Amazon.JSII.JsonModel.Spec;
using Amazon.JSII.Runtime.Services.Converters;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Assembly = Amazon.JSII.JsonModel.Spec.Assembly;

namespace Amazon.JSII.Runtime.Services
{
Expand Down Expand Up @@ -171,7 +167,7 @@ public void LoadPackage(string package, string version, string tarballPath)
_logger.LogDebug($"Loading package {package}@{version}...");
_loadedPackages.Add(package);

LoadResponse response = Load(package, version, tarballPath);
Load(package, version, tarballPath);
}

public HelloResponse Hello()
Expand Down

0 comments on commit 20321af

Please sign in to comment.