Improve formatting performance by having better primitives on PSObject (#8785)

By not doing excessive amounts of extra work, formatting can be sped up quite significantly (about 8x faster).
The main change comes from adding new, more efficient, primitive to query an object for the existence of an instance member.
The formatting system has been checking for if an object has properties other than some decorated properties added by PS remoting, and it doesn't this by retrieving all properties which results in heavy allocations and wasted cycles.
By adding `GetFirstOrDefault` to `PSObject` and similar primitives to the underlying Adapters, we are able to return early, without having to get all properties back.
This commit is contained in:
Staffan Gustafsson
2019-03-20 18:43:52 -07:00
committed by Dongbo Wang
parent 95a8091991
commit 42c289fc8e
28 changed files with 1227 additions and 596 deletions
+127
View File
@@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Management.Automation.Language;
using Xunit;
namespace PSTests.Parallel
{
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Xml;
using Microsoft.Management.Infrastructure;
public static class PSObjectTests
{
[Fact]
public static void TestEmptyObjectHasNoProperty()
{
var psObject = new PSObject();
var actual = psObject.GetFirstPropertyOrDefault(name => true);
Assert.Null(actual);
}
[Fact]
public static void TestWrappedDateTimeHasReflectedMember()
{
var psObject = new PSObject(DateTime.Now);
var member = psObject.GetFirstPropertyOrDefault(name => name == "DayOfWeek");
Assert.NotNull(member);
Assert.Equal("DayOfWeek", member.Name);
}
[Fact]
public static void TestAdaptedMember()
{
var psObject = new PSObject(DateTime.Now);
psObject.Members.Add(new PSNoteProperty("NewMember", "AValue"));
var member = psObject.GetFirstPropertyOrDefault(name => name == "NewMember");
Assert.NotNull(member);
Assert.Equal("NewMember", member.Name);
}
[Fact]
public static void TestShadowedMember()
{
var psObject = new PSObject(DateTime.Now);
psObject.Members.Add(new PSNoteProperty("DayOfWeek", "AValue"));
var member = psObject.GetFirstPropertyOrDefault(name => name == "DayOfWeek");
Assert.NotNull(member);
Assert.Equal("DayOfWeek", member.Name);
Assert.Equal("AValue", member.Value);
}
[Fact]
public static void TestMemberSetIsNotProperty()
{
var psObject = new PSObject(DateTime.Now);
var psNoteProperty = new PSNoteProperty("NewMember", "AValue");
psObject.Members.Add(psNoteProperty);
psObject.Members.Add(new PSMemberSet("NewMemberSet", new[] { psNoteProperty }));
var member = psObject.GetFirstPropertyOrDefault(name => name == "NewMemberSet");
Assert.Null(member);
}
[Fact]
public static void TestMemberSet()
{
var psObject = new PSObject(DateTime.Now);
var psNoteProperty = new PSNoteProperty("NewMember", "AValue");
psObject.Members.Add(psNoteProperty);
psObject.Members.Add(new PSMemberSet("NewMemberSet", new[] { psNoteProperty }));
var member = psObject.Members.FirstOrDefault(name => name == "NewMemberSet");
Assert.NotNull(member);
Assert.Equal("NewMemberSet", member.Name);
}
[Fact]
public static void TextXmlElementMember()
{
var doc = new XmlDocument();
var root = doc.CreateElement("root");
doc.AppendChild(root);
var firstChild = doc.CreateElement("elem1");
root.AppendChild(firstChild);
root.InsertAfter(doc.CreateElement("elem2"), firstChild);
var psObject = new PSObject(root);
var member = psObject.GetFirstPropertyOrDefault(name => name.StartsWith("elem"));
Assert.Equal("elem1", member.Name);
}
[Fact]
public static void TextXmlAttributeMember()
{
var doc = new XmlDocument();
var root = doc.CreateElement("root");
doc.AppendChild(root);
root.SetAttribute("attr", "value");
root.AppendChild(doc.CreateElement("elem"));
var psObject = new PSObject(root);
var member = psObject.GetFirstPropertyOrDefault(name => name.StartsWith("attr"));
Assert.Equal("attr", member.Name);
}
[SkippableFact]
public static void TestCimInstanceProperty()
{
Skip.IfNot(Platform.IsWindows);
var iss = InitialSessionState.CreateDefault2();
iss.Commands.Add(new SessionStateCmdletEntry("Get-CimInstance", typeof(Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand), null));
using (var ps = PowerShell.Create(iss))
{
ps.AddCommand("Get-CimInstance").AddParameter("ClassName", "Win32_BIOS");
var res = ps.Invoke().FirstOrDefault();
Assert.NotNull(res);
var member = res.GetFirstPropertyOrDefault(name => name == "Name");
Assert.NotNull(member);
}
}
}
}
+5 -4
View File
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<Import Project="../Test.Common.props"/>
<Import Project="../Test.Common.props" />
<PropertyGroup>
<Description>PowerShell xUnit Tests</Description>
@@ -16,9 +16,10 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj"/>
<ProjectReference Include="../../src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj"/>
<ProjectReference Include="../../src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj"/>
<ProjectReference Include="../../src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj" />
<ProjectReference Include="../../src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj" />
<ProjectReference Include="../../src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj" />
<ProjectReference Include="../../src/Microsoft.Management.Infrastructure.CimCmdlets/Microsoft.Management.Infrastructure.CimCmdlets.csproj" />
</ItemGroup>
<ItemGroup>