question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Array will allocate less and little faster when creating RequestDelegateFactoryOptions in CreateRDFOptions

See original GitHub issue

It uses List<string>, which gets assigned to RouteParameterNames as IEnumerable. https://github.com/dotnet/aspnetcore/blob/432e6a061f28dda696ba16bc5442328d23b25d93/src/Http/Routing/src/RouteEndpointDataSource.cs#L260

Ran a benchmark with 10 strings of length 6. The results are below. image

Another run: image

The benchmark code used:

public class Benchmarks
    {
        private const int Length = 10;
        private readonly IReadOnlyCollection<string> _readOnlyCollection = new List<string>() { "param1","param2", "param1","param2", "param1","param2", "param1","param2", "param1","param2"}.AsReadOnly();
        [Benchmark]
        public void Add10StringsToList()
        {
            List<string> parameters = new(Length);
            foreach (var s in _readOnlyCollection)
            {
                parameters.Add(s);
            }

            
        }

        [Benchmark]
        public void Add10StringsToArray()
        {
            var parameters = new string[Length];
            var i = 0;
            foreach (var s in _readOnlyCollection)
            {
                parameters[i] = s;
                i++;
            }
            
        }
    }

Issue Analytics

  • State:closed
  • Created 8 months ago
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
marafiqcommented, Jan 10, 2023

After spending a little bit more time with it, it seems like even array allocation is not required since parameter names are sourced from IReadOnlyCollection, and RequestDelegateFactoryOptions only requires IEnumerable<string> so all the allocation can go away. Either using the Linq Select or with the local function that yields.

Benchmark results image

Benchmark code

[MemoryDiagnoser]
    public class Benchmarks
    {
        class RoutePatternPart
        {
            public RoutePatternPart(bool isOptional, string name)
            {
                IsOptional = isOptional;
                Name = name;
            }

            public bool IsOptional { get; }
            public string Name { get; }
        }

        private const int Length = 10;

        private readonly IReadOnlyCollection<RoutePatternPart> _readOnlyCollection = new List<RoutePatternPart>()
        {
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1"),
            new(true, "param1")
        };
        private readonly Consumer _consumer = new Consumer();
        [Benchmark]
        public void Add10StringsToList()
        {
            List<string> parameters = new(Length);
            foreach (var s in _readOnlyCollection)
            {
                parameters.Add(s.Name);
            }
            _consumer.Consume(parameters);
        }

        [Benchmark]
        public void Add10StringsToArray()
        {
            var parameters = new string[Length];
            var i = 0;
            foreach (var s in _readOnlyCollection)
            {
                parameters[i] = s.Name;
                i++;
            }
            _consumer.Consume(parameters);
        }
        
        [Benchmark]
        public void UseEnumerableWithLinqSelect()
        {
            var parameters = _readOnlyCollection.Select(x => x.Name);
            _consumer.Consume(parameters);
        }
        [Benchmark]
        public void UseEnumerableWithLocalFuncYield()
        {
            var parameters = LocalFuncEnumerable();

            _consumer.Consume(parameters);

            IEnumerable<string> LocalFuncEnumerable()
            {
                foreach (var routePatternPart in _readOnlyCollection)
                {
                    yield return routePatternPart.Name;
                }
            }
        }
    }

0reactions
marafiqcommented, Jan 13, 2023

Thanks, I understand. I opened the draft PR nervously for you to examine if it will make sense.

Read more comments on GitHub >

github_iconTop Results From Across the Web

No results found

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found