Skip to content

Update PSConfiguration.ReadValueFromFile to make it faster and more memory efficient#10839

Merged
adityapatwardhan merged 8 commits intoPowerShell:masterfrom
daxian-dbw:perf
Oct 30, 2019
Merged

Update PSConfiguration.ReadValueFromFile to make it faster and more memory efficient#10839
adityapatwardhan merged 8 commits intoPowerShell:masterfrom
daxian-dbw:perf

Conversation

@daxian-dbw
Copy link
Copy Markdown
Member

@daxian-dbw daxian-dbw commented Oct 18, 2019

PR Summary

There are 2 changes included here:

  1. Change how configuration file is accessed to make it faster and memory efficient.
  2. Change TypeCatalogGen to generate slightly faster dictionary initialization code.

The TypeCatalogGen is more of a cleanup change, to use the collection initializer, which is slightly faster than the dict[xxx] = "..." operation.

Benchmark results for the PSConfiguration change

For the PSConfiguration change, I ran a benchmark. The benchmark code and results can be found below.
The new implementation is a lot faster than the current, but overall it won't affect much the startup of pwsh, because we spent very little time in PSConfiguraiton.
The new implementation has a lot less allocation (88.63KB vs. 1.69KB). This is the more interesting part.

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Benchmark_ReadConfigData>();
        }
    }

    [DisassemblyDiagnoser(printAsm: true, printSource: true, recursiveDepth: 2)]
    [MemoryDiagnoser]
    public class Benchmark_ReadConfigData
    {
        [Benchmark(Baseline = true)]
        public void ReadConfigUsingCurrentImpl()
        {
            // Those methods are called during the startup of 'pwsh', so using them as the operations for the benchmark.
            PowerShellConfig.Instance.GetExperimentalFeatures();
            PowerShellConfig.Instance.GetModulePath(ConfigScope.AllUsers);
            PowerShellConfig.Instance.GetModulePath(ConfigScope.CurrentUser);
            PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.CurrentUser);
            PowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.AllUsers);
            PowerShellConfig.Instance.GetExecutionPolicy(ConfigScope.CurrentUser, "Microsoft.PowerShell");
            PowerShellConfig.Instance.GetExecutionPolicy(ConfigScope.AllUsers, "Microsoft.PowerShell");
        }

        [Benchmark]
        public void ReadConfigUsingNewImpl()
        {
            // Those methods are called during the startup of 'pwsh', so using them as the operations for the benchmark.
            NewPowerShellConfig.Instance.GetExperimentalFeatures();
            NewPowerShellConfig.Instance.GetModulePath(ConfigScope.AllUsers);
            NewPowerShellConfig.Instance.GetModulePath(ConfigScope.CurrentUser);
            NewPowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.CurrentUser);
            NewPowerShellConfig.Instance.GetPowerShellPolicies(ConfigScope.AllUsers);
            NewPowerShellConfig.Instance.GetExecutionPolicy(ConfigScope.CurrentUser, "Microsoft.PowerShell");
            NewPowerShellConfig.Instance.GetExecutionPolicy(ConfigScope.AllUsers, "Microsoft.PowerShell");
        }
    }

image

PR Checklist

Comment on lines +66 to +68
private readonly JObject[] configRoots = new JObject[2];
private readonly JObject emptyConfig;
private readonly JsonSerializer serializer;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Should we use _ prefix for private field names?
  2. We initialize configRoots here and in constructor too.
  3. We could initialize configRoots with " empty config" that could simplify to introduce "DefaultConfig".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For (1) I don't plan to make that change in this PR given it's a style change that will make the PR harder to review.
For (2) good catch. Fixed.
For (3) emtpyConfig means we have attempted reading the file, and there is not configuration defined in the corresponding file (either an empty file, or file doesn't exist). So configRoots cannot be initialized to emptyConfig. They mean different things.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For (3) In the case emptyConfig is not reflect the purpose.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate why you think it doesn't reflect the purpose?

@daxian-dbw daxian-dbw changed the title WIP: Update PSConfiguration.ReadValueFromFile to make it faster and more memory efficient Update PSConfiguration.ReadValueFromFile to make it faster and more memory efficient Oct 21, 2019
foreach (KeyValuePair<string, TypeMetadata> pair in typeNameToAssemblyMap)
{
sourceCode.AppendLine(string.Format(CultureInfo.InvariantCulture, SourceFormat, pair.Key, pair.Value.AssemblyName));
sourceCode.Append(string.Format(CultureInfo.InvariantCulture, SourceFormat, pair.Key, pair.Value.AssemblyName, Environment.NewLine));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment.NewLine on first position it would be more clear.

@adityapatwardhan adityapatwardhan merged commit 7772418 into PowerShell:master Oct 30, 2019
@adityapatwardhan adityapatwardhan added the CL-Engine Indicates that a PR should be marked as an engine change in the Change Log label Oct 30, 2019
@adityapatwardhan adityapatwardhan added this to the 7.0.0-preview.6 milestone Oct 30, 2019
@daxian-dbw daxian-dbw deleted the perf branch November 1, 2019 06:25
@daxian-dbw daxian-dbw added CL-Performance Indicates that a PR should be marked as a performance improvement in the Change Log and removed CL-Engine Indicates that a PR should be marked as an engine change in the Change Log labels Nov 1, 2019
@ghost
Copy link
Copy Markdown

ghost commented Nov 21, 2019

🎉v7.0.0-preview.6 has been released which incorporates this pull request.:tada:

Handy links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-Performance Indicates that a PR should be marked as a performance improvement in the Change Log

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants