Skip to content

Commit 1463538

Browse files
lostmsufilmor
authored andcommitted
Adds performance tests, that compare to published NuGet (pythonnet#975)
Add performance tests that compare to published NuGet
1 parent f2e6f6f commit 1463538

7 files changed

+454
-7
lines changed

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ indent_size = 2
1919
[*.{csproj,pyproj,config}]
2020
indent_size = 2
2121

22+
# .NET formatting settings
23+
[*.{cs,vb}]
24+
dotnet_sort_system_directives_first = true
25+
dotnet_separate_import_directive_groups = true
26+
27+
[*.cs]
28+
csharp_new_line_before_open_brace = true
29+
csharp_new_line_before_else = true
30+
csharp_new_line_before_catch = true
31+
csharp_new_line_before_finally = true
32+
2233
# Solution
2334
[*.sln]
2435
indent_style = tab

pythonnet.15.sln

Lines changed: 184 additions & 7 deletions
Large diffs are not rendered by default.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
6+
using Python.Runtime;
7+
8+
namespace Python.PerformanceTests
9+
{
10+
public class BaselineComparisonBenchmarkBase
11+
{
12+
public BaselineComparisonBenchmarkBase()
13+
{
14+
Console.WriteLine($"CWD: {Environment.CurrentDirectory}");
15+
Console.WriteLine($"Using Python.Runtime from {typeof(PythonEngine).Assembly.Location} {typeof(PythonEngine).Assembly.GetName()}");
16+
17+
try {
18+
PythonEngine.Initialize();
19+
Console.WriteLine("Python Initialized");
20+
if (PythonEngine.BeginAllowThreads() == IntPtr.Zero)
21+
throw new PythonException();
22+
Console.WriteLine("Threading enabled");
23+
}
24+
catch (Exception e) {
25+
Console.WriteLine(e);
26+
throw;
27+
}
28+
}
29+
30+
static BaselineComparisonBenchmarkBase()
31+
{
32+
string pythonRuntimeDll = Environment.GetEnvironmentVariable(BaselineComparisonConfig.EnvironmentVariableName);
33+
if (string.IsNullOrEmpty(pythonRuntimeDll))
34+
{
35+
throw new ArgumentException(
36+
"Required environment variable is missing",
37+
BaselineComparisonConfig.EnvironmentVariableName);
38+
}
39+
40+
Console.WriteLine("Preloading " + pythonRuntimeDll);
41+
Assembly.LoadFrom(pythonRuntimeDll);
42+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
43+
if (assembly.FullName.StartsWith("Python.Runtime"))
44+
Console.WriteLine(assembly.Location);
45+
foreach(var dependency in assembly.GetReferencedAssemblies())
46+
if (dependency.FullName.Contains("Python.Runtime")) {
47+
Console.WriteLine($"{assembly} -> {dependency}");
48+
}
49+
}
50+
51+
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
52+
}
53+
54+
static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) {
55+
if (!args.Name.StartsWith("Python.Runtime"))
56+
return null;
57+
58+
var preloaded = AppDomain.CurrentDomain.GetAssemblies()
59+
.FirstOrDefault(a => a.GetName().Name == "Python.Runtime");
60+
if (preloaded != null) return preloaded;
61+
62+
string pythonRuntimeDll = Environment.GetEnvironmentVariable(BaselineComparisonConfig.EnvironmentVariableName);
63+
if (string.IsNullOrEmpty(pythonRuntimeDll))
64+
return null;
65+
66+
return Assembly.LoadFrom(pythonRuntimeDll);
67+
}
68+
}
69+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection;
5+
6+
using BenchmarkDotNet.Configs;
7+
using BenchmarkDotNet.Jobs;
8+
9+
namespace Python.PerformanceTests
10+
{
11+
public class BaselineComparisonConfig : ManualConfig
12+
{
13+
public const string EnvironmentVariableName = "PythonRuntimeDLL";
14+
15+
public BaselineComparisonConfig()
16+
{
17+
this.Options |= ConfigOptions.DisableOptimizationsValidator;
18+
19+
string deploymentRoot = BenchmarkTests.DeploymentRoot;
20+
21+
var baseJob = Job.Default;
22+
this.Add(baseJob
23+
.WithId("baseline")
24+
.WithEnvironmentVariable(EnvironmentVariableName,
25+
Path.Combine(deploymentRoot, "baseline", "Python.Runtime.dll"))
26+
.WithBaseline(true));
27+
this.Add(baseJob
28+
.WithId("new")
29+
.WithEnvironmentVariable(EnvironmentVariableName,
30+
Path.Combine(deploymentRoot, "new", "Python.Runtime.dll")));
31+
}
32+
33+
static BaselineComparisonConfig() {
34+
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
35+
}
36+
37+
static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) {
38+
Console.WriteLine(args.Name);
39+
if (!args.Name.StartsWith("Python.Runtime"))
40+
return null;
41+
string pythonRuntimeDll = Environment.GetEnvironmentVariable(EnvironmentVariableName);
42+
if (string.IsNullOrEmpty(pythonRuntimeDll))
43+
pythonRuntimeDll = Path.Combine(BenchmarkTests.DeploymentRoot, "baseline", "Python.Runtime.dll");
44+
return Assembly.LoadFrom(pythonRuntimeDll);
45+
}
46+
}
47+
}

src/perf_tests/BenchmarkTests.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.CompilerServices;
6+
using System.Reflection;
7+
8+
using BenchmarkDotNet.Reports;
9+
using BenchmarkDotNet.Running;
10+
using NUnit.Framework;
11+
12+
namespace Python.PerformanceTests
13+
{
14+
public class BenchmarkTests
15+
{
16+
Summary summary;
17+
18+
[OneTimeSetUp]
19+
public void SetUp()
20+
{
21+
Environment.CurrentDirectory = Path.Combine(DeploymentRoot, "new");
22+
this.summary = BenchmarkRunner.Run<PythonCallingNetBenchmark>();
23+
Assert.IsNotEmpty(this.summary.Reports);
24+
Assert.IsTrue(this.summary.Reports.All(r => r.Success));
25+
}
26+
27+
[Test]
28+
public void ReadInt64Property()
29+
{
30+
double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports);
31+
Assert.LessOrEqual(optimisticPerfRatio, 0.68);
32+
}
33+
34+
[Test]
35+
public void WriteInt64Property()
36+
{
37+
double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports);
38+
Assert.LessOrEqual(optimisticPerfRatio, 0.66);
39+
}
40+
41+
static double GetOptimisticPerfRatio(
42+
IReadOnlyList<BenchmarkReport> reports,
43+
[CallerMemberName] string methodName = null)
44+
{
45+
reports = reports.Where(r => r.BenchmarkCase.Descriptor.WorkloadMethod.Name == methodName).ToArray();
46+
if (reports.Count == 0)
47+
throw new ArgumentException(
48+
message: $"No reports found for {methodName}. "
49+
+ "You have to match test method name to benchmark method name or "
50+
+ "pass benchmark method name explicitly",
51+
paramName: nameof(methodName));
52+
53+
var baseline = reports.Single(r => r.BenchmarkCase.Job.ResolvedId == "baseline").ResultStatistics;
54+
var @new = reports.Single(r => r.BenchmarkCase.Job.ResolvedId != "baseline").ResultStatistics;
55+
56+
double newTimeOptimistic = @new.Mean - (@new.StandardDeviation + baseline.StandardDeviation) * 0.5;
57+
58+
return newTimeOptimistic / baseline.Mean;
59+
}
60+
61+
public static string DeploymentRoot => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
62+
}
63+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net461</TargetFramework>
5+
<Configurations>DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3</Configurations>
6+
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
12+
<PackageReference Include="nunit" Version="3.12.0" />
13+
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
15+
<PackageReference Include="pythonnet" Version="2.3.0" GeneratePathProperty="true">
16+
<IncludeAssets>compile</IncludeAssets>
17+
</PackageReference>
18+
</ItemGroup>
19+
20+
<Target Name="GetRuntimeLibBuildOutput" BeforeTargets="Build">
21+
<MSBuild Projects="..\runtime\Python.Runtime.15.csproj" Properties="PYTHONNET_PY3_VERSION=PYTHON35;Configuration=$(Configuration);TargetFramework=net40;Python3Version=PYTHON35;OutputPath=bin\for_perf\">
22+
<Output TaskParameter="TargetOutputs" ItemName="NewPythonRuntime" />
23+
</MSBuild>
24+
</Target>
25+
26+
<Target Name="CopyBaseline" AfterTargets="Build">
27+
<Copy SourceFiles="$(Pkgpythonnet)\lib\net40\Python.Runtime.dll" DestinationFolder="$(OutDir)\baseline" />
28+
</Target>
29+
30+
<Target Name="CopyNewBuild" AfterTargets="Build">
31+
<Copy SourceFiles="@(NewPythonRuntime)" DestinationFolder="$(OutDir)\new" />
32+
</Target>
33+
34+
</Project>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
using BenchmarkDotNet.Attributes;
6+
using Python.Runtime;
7+
8+
namespace Python.PerformanceTests
9+
{
10+
[Config(typeof(BaselineComparisonConfig))]
11+
public class PythonCallingNetBenchmark: BaselineComparisonBenchmarkBase
12+
{
13+
[Benchmark]
14+
public void ReadInt64Property()
15+
{
16+
using (Py.GIL())
17+
{
18+
var locals = new PyDict();
19+
locals.SetItem("a", new NetObject().ToPython());
20+
PythonEngine.Exec($@"
21+
s = 0
22+
for i in range(300000):
23+
s += a.{nameof(NetObject.LongProperty)}
24+
", locals: locals.Handle);
25+
}
26+
}
27+
28+
[Benchmark]
29+
public void WriteInt64Property() {
30+
using (Py.GIL()) {
31+
var locals = new PyDict();
32+
locals.SetItem("a", new NetObject().ToPython());
33+
PythonEngine.Exec($@"
34+
s = 0
35+
for i in range(300000):
36+
a.{nameof(NetObject.LongProperty)} += i
37+
", locals: locals.Handle);
38+
}
39+
}
40+
}
41+
42+
class NetObject
43+
{
44+
public long LongProperty { get; set; } = 42;
45+
}
46+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy