Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: List<T>.Create<TState>(int, TState, SpanAction<T, TState>) #80756

Closed
neon-sunset opened this issue Jan 17, 2023 · 5 comments
Closed
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Collections

Comments

@neon-sunset
Copy link
Contributor

neon-sunset commented Jan 17, 2023

Background and motivation

As of today, we do not have an API that would allow to safely initialize a List<T>'s underlying buffer directly.

While working on #80633 and investigating available ways to avoid using for (...) { list.Add(element); } in IListProvider<T>.ToList() implementations, there seemed to be a couple of viable options:

However, it appears we do already have an example of similar use case that solves the same issue: string.Create.
Re-using this pattern for List<T> would achieve safety (span cannot escape) and performance (no extra .Add() calls with state mutation, elided bounds check) at the same time without having to be hidden in CollectionsMarshal class.

API Proposal

namespace System.Collections.Generic;

public partial class List<T>
{
    public static List<T> Create<TState>(int count, TState state, SpanAction<T, TState> action);
}

API Usage

partial class SelectArrayIterator<TSource, TResult>
{
    public List<TResult> ToList()
    {
        return List<TResult>.Create(_source.Length, this, (span, instance) =>
        {
            TSource[] source = instance._source;
            Func<TSource, TResult> selector = instance._selector;
            
            // Assert .Length equality between span and source to elide bounds check
            for (int i = 0; i < span.Length; i++)
            {
                span[i] = _selector(source[i]);
            }
        }
    }
}

Alternative Designs

Risks

  • Possible inconvenience when using it same as with string.Create
  • Expanding a widely used type which increases code size and compilation time?
@neon-sunset neon-sunset added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jan 17, 2023
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jan 17, 2023
@neon-sunset neon-sunset changed the title [API Proposal]: Performance-oriented List<T>.Create<TState>(int, TState, SpanAction<T, TState>) [API Proposal]: List<T>.Create<TState>(int, TState, SpanAction<T, TState>) Jan 17, 2023
@ghost
Copy link

ghost commented Jan 17, 2023

Tagging subscribers to this area: @dotnet/area-system-collections
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

As of today, we do not have an API that would allow to safely initialize a List<T>'s underlying buffer directly.

While working on #80633 and investigating available ways to avoid using for (...) { list.Add(element); } in IListProvider<T>.ToList() implementations, there seemed to be a couple of viable options:

However, it appears we do already have an example of similar use case that solves the same issue: string.Create.
Re-using this pattern for List<T> would achieve safety (span cannot escape) and performance (no extra .Add() calls with state mutation, elided bounds check) at the same time without having to be hidden in CollectionsMarshal class.

API Proposal

namespace System.Collections.Generic;

public partial class List<T>
{
    public static List<T> Create<TState>(int count, TState state, SpanAction<T, TState> action);
}

API Usage

partial class SelectArrayIterator<TSource, TResult>
{
    public List<TResult> ToList()
    {
        return List<TResult>.Create(_source.Length, this, (span, instance) =>
        {
            TSource[] source = instance._source;
            Func<TSource, TResult> selector = instance._selector;
            
            // Assert .Length equality between span and source to elide bounds check
            for (int i = 0; i < span.Length; i++)
            {
                span[i] = _selector(source[i]);
            }
        }
    }
}

Alternative Designs

Risks

  • Possible inconvenience when using it same as with string.Create
  • Expanding a widely used type which increases code size and compilation time?
Author: neon-sunset
Assignees: -
Labels:

api-suggestion, area-System.Collections, untriaged

Milestone: -

@MichalPetryka
Copy link
Contributor

I don't see a point in this API, #55217 will make this fully possible with SetCount + AsSpan and reduced allocation scenarios in general target more advanced users.

@neon-sunset
Copy link
Contributor Author

neon-sunset commented Jan 18, 2023

@MichalPetryka Generally speaking I agree, was simply concerned regarding the fate of your proposal due to some discussion in PR so submitted an alternative design just in case. I'm happy as long as your or any of the alternatives get into BCL 🙂

@eiriktsarpalis
Copy link
Member

I'd be inclined to close this in favor #55217. We generally avoid adding new members to the List<T> type directly out of static footprint concerns unless they are absolutely necessary and/or widely used. Using SpanAction to populate a List<T> seems a bit baroque to me, unlike string this type is mutable.

@eiriktsarpalis eiriktsarpalis closed this as not planned Won't fix, can't repro, duplicate, stale Jan 19, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Jan 19, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 18, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Collections
Projects
None yet
Development

No branches or pull requests

4 participants