Skip to content

Options generator perf improvements #91073

@stephentoub

Description

@stephentoub

The options generator is emitting code like this:

            var baseName = (string.IsNullOrEmpty(name) ? "FirstModel" : name) + ".";
            var builder = new global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder();
            var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
            var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
            var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(1);

            context.MemberName = "P1";
            context.DisplayName = baseName + "P1";
            validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A3);
            if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1!, context, validationResults, validationAttributes))
            {
                builder.AddResults(validationResults);
            }

            context.MemberName = "P2";
            context.DisplayName = baseName + "P2";
            validationResults.Clear();
            validationAttributes.Clear();
            validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A4);
            if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P2!, context, validationResults, validationAttributes))
            {
                builder.AddResults(validationResults);
            }

            return builder.Build();
  1. There's a concatenation string allocation that results as part of creating baseName, even in the common case where name is null and both things being concatenated are literals. There's then another concatenation allocation per member, as the implementation tacks on a literal suffix. Instead of var baseName = (string.IsNullOrEmpty(name) ? "FirstModel" : name) + ".";, that line should be deleted, the each display name creation should instead be like: context.DisplayName = string.IsNullOrEmpty(name) ? "FirstModel.P2" : name + ".P2";. That way, when the name is null or empty, there are zero string allocations, and when the name is non-null and non-empty, it saves the one intermediate allocation.
  2. ValidateOptionsResultBuilder is a class and is being allocated even when there aren't any validation errors. We should instead lazily allocate it only when one of the TryValidateValue calls returns false.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions