Skip to content

Commit 8c13152

Browse files
authored
Merge pull request commandlineparser#350 from bergbria/bergbria/perf
Improve performance by more than 400X
2 parents d6be4f9 + 65c6310 commit 8c13152

File tree

4 files changed

+31
-28
lines changed

4 files changed

+31
-28
lines changed

src/CommandLine/Core/InstanceBuilder.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ public static ParserResult<T> Build<T>(
2828
var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T));
2929

3030
var specProps = typeInfo.GetSpecifications(pi => SpecificationProperty.Create(
31-
Specification.FromProperty(pi), pi, Maybe.Nothing<object>()));
31+
Specification.FromProperty(pi), pi, Maybe.Nothing<object>()))
32+
.Memorize();
3233

3334
var specs = from pt in specProps select pt.Specification;
3435

3536
var optionSpecs = specs
3637
.ThrowingValidate(SpecificationGuards.Lookup)
37-
.OfType<OptionSpecification>();
38+
.OfType<OptionSpecification>()
39+
.Memorize();
3840

3941
Func<T> makeDefault = () =>
4042
typeof(T).IsMutable()
@@ -45,18 +47,19 @@ public static ParserResult<T> Build<T>(
4547
Func<IEnumerable<Error>, ParserResult<T>> notParsed =
4648
errs => new NotParsed<T>(makeDefault().GetType().ToTypeInfo(), errs);
4749

50+
var argumentsList = arguments.Memorize();
4851
Func<ParserResult<T>> buildUp = () =>
4952
{
50-
var tokenizerResult = tokenizer(arguments, optionSpecs);
53+
var tokenizerResult = tokenizer(argumentsList, optionSpecs);
5154

52-
var tokens = tokenizerResult.SucceededWith();
55+
var tokens = tokenizerResult.SucceededWith().Memorize();
5356

5457
var partitions = TokenPartitioner.Partition(
5558
tokens,
5659
name => TypeLookup.FindTypeDescriptorAndSibling(name, optionSpecs, nameComparer));
57-
var optionsPartition = partitions.Item1;
58-
var valuesPartition = partitions.Item2;
59-
var errorsPartition = partitions.Item3;
60+
var optionsPartition = partitions.Item1.Memorize();
61+
var valuesPartition = partitions.Item2.Memorize();
62+
var errorsPartition = partitions.Item3.Memorize();
6063

6164
var optionSpecPropsResult =
6265
OptionMapper.MapValues(
@@ -68,7 +71,7 @@ public static ParserResult<T> Build<T>(
6871
var valueSpecPropsResult =
6972
ValueMapper.MapValues(
7073
(from pt in specProps where pt.Specification.IsValue() orderby ((ValueSpecification)pt.Specification).Index select pt),
71-
valuesPartition,
74+
valuesPartition,
7275
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase));
7376

7477
var missingValueErrors = from token in errorsPartition
@@ -78,7 +81,7 @@ public static ParserResult<T> Build<T>(
7881
.FromOptionSpecification());
7982

8083
var specPropsWithValue =
81-
optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith());
84+
optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()).Memorize();
8285

8386
var setPropertyErrors = new List<Error>();
8487

@@ -130,11 +133,13 @@ join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToL
130133
return allErrors.Except(warnings).ToParserResult(instance);
131134
};
132135

133-
var preprocessorErrors = arguments.Any()
134-
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer))
135-
: Enumerable.Empty<Error>();
136+
var preprocessorErrors = (
137+
argumentsList.Any()
138+
? argumentsList.Preprocess(PreprocessorGuards.Lookup(nameComparer))
139+
: Enumerable.Empty<Error>()
140+
).Memorize();
136141

137-
var result = arguments.Any()
142+
var result = argumentsList.Any()
138143
? preprocessorErrors.Any()
139144
? notParsed(preprocessorErrors)
140145
: buildUp()

src/CommandLine/Core/OptionMapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ select Tuple.Create(
4343
((OptionSpecification)pt.Specification).FromOptionSpecification()))))
4444
: Tuple.Create(pt, Maybe.Nothing<Error>());
4545
}
46-
);
46+
).Memorize();
4747
return Result.Succeed(
4848
sequencesAndErrors.Select(se => se.Item1),
4949
sequencesAndErrors.Select(se => se.Item2).OfType<Just<Error>>().Select(se => se.Value));

src/CommandLine/Core/TokenPartitioner.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,20 @@ namespace CommandLine.Core
1111
static class TokenPartitioner
1212
{
1313
public static
14-
Tuple<
15-
IEnumerable<KeyValuePair<string, IEnumerable<string>>>, // options
16-
IEnumerable<string>, // values
17-
IEnumerable<Token> // errors
18-
> Partition(
14+
Tuple<IEnumerable<KeyValuePair<string, IEnumerable<string>>>, IEnumerable<string>, IEnumerable<Token>> Partition(
1915
IEnumerable<Token> tokens,
2016
Func<string, Maybe<TypeDescriptor>> typeLookup)
2117
{
18+
IEqualityComparer<Token> tokenComparer = ReferenceEqualityComparer.Default;
19+
2220
var tokenList = tokens.Memorize();
23-
var switches = Switch.Partition(tokenList, typeLookup).Memorize();
24-
var scalars = Scalar.Partition(tokenList, typeLookup).Memorize();
25-
var sequences = Sequence.Partition(tokenList, typeLookup).Memorize();
21+
var switches = new HashSet<Token>(Switch.Partition(tokenList, typeLookup), tokenComparer);
22+
var scalars = new HashSet<Token>(Scalar.Partition(tokenList, typeLookup), tokenComparer);
23+
var sequences = new HashSet<Token>(Sequence.Partition(tokenList, typeLookup), tokenComparer);
2624
var nonOptions = tokenList
27-
.Where(t => !switches.Contains(t, ReferenceEqualityComparer.Default))
28-
.Where(t => !scalars.Contains(t, ReferenceEqualityComparer.Default))
29-
.Where(t => !sequences.Contains(t, ReferenceEqualityComparer.Default)).Memorize();
25+
.Where(t => !switches.Contains(t))
26+
.Where(t => !scalars.Contains(t))
27+
.Where(t => !sequences.Contains(t)).Memorize();
3028
var values = nonOptions.Where(v => v.IsValue()).Memorize();
3129
var errors = nonOptions.Except(values, (IEqualityComparer<Token>)ReferenceEqualityComparer.Default).Memorize();
3230

src/CommandLine/Core/Tokenizer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static Result<IEnumerable<Token>, Error> Tokenize(
3636
select token)
3737
.Memorize();
3838

39-
var normalized = normalize(tokens);
39+
var normalized = normalize(tokens).Memorize();
4040

4141
var unkTokens = (from t in normalized where t.IsName() && nameLookup(t.Text) == NameLookupResult.NoOptionFound select t).Memorize();
4242

@@ -60,12 +60,12 @@ public static Result<IEnumerable<Token>, Error> ExplodeOptionList(
6060
Result<IEnumerable<Token>, Error> tokenizerResult,
6161
Func<string, Maybe<char>> optionSequenceWithSeparatorLookup)
6262
{
63-
var tokens = tokenizerResult.SucceededWith();
63+
var tokens = tokenizerResult.SucceededWith().Memorize();
6464

6565
var replaces = tokens.Select((t, i) =>
6666
optionSequenceWithSeparatorLookup(t.Text)
6767
.MapValueOrDefault(sep => Tuple.Create(i + 1, sep),
68-
Tuple.Create(-1, '\0'))).SkipWhile(x => x.Item1 < 0);
68+
Tuple.Create(-1, '\0'))).SkipWhile(x => x.Item1 < 0).Memorize();
6969

7070
var exploded = tokens.Select((t, i) =>
7171
replaces.FirstOrDefault(x => x.Item1 == i).ToMaybe()

0 commit comments

Comments
 (0)