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

Allow stackalloc span of reference types #38677

Closed
YairHalberstadt opened this issue Jul 1, 2020 · 5 comments
Closed

Allow stackalloc span of reference types #38677

YairHalberstadt opened this issue Jul 1, 2020 · 5 comments

Comments

@YairHalberstadt
Copy link
Contributor

I was looking at these old notes from the LDM: https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-09-19.md#params-spant

params Span<T>

Proposal: dotnet/csharplang#1757

This would be especially interesting if the CLR implements a new feature for stack allocating an array of reference types.

The idea being that allowing for stackallocating an array of reference types, combined with params span, would allow for using params in an allocation free manner. I've contributed language features to Roslyn in the past, and would definitely do the compiler work to make this happen if the runtime work was done. I also think it reasonably likely the LDM would take this, @jaredpar?

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Memory untriaged New issue has not been triaged by the area owner labels Jul 1, 2020
@jkotas
Copy link
Member

jkotas commented Jul 1, 2020

Related / duplicate: #25423

@YairHalberstadt
Copy link
Contributor Author

When I remove the compiler error it generates the following:

CSharp:

class C
{
    static void Main()
    {
        Span<string> p = stackalloc string[] { ""Hello World"" };
        Console.WriteLine(p[0]);
    }
}

IL:

    {
      // Code size       40 (0x28)
      .maxstack  3
      .locals init (System.Span<string> V_0) //p
      IL_0000:  ldc.i4.1
      IL_0001:  conv.u
      IL_0002:  sizeof     ""string""
      IL_0008:  mul.ovf.un
      IL_0009:  localloc
      IL_000b:  dup
      IL_000c:  ldstr      ""Hello World""
      IL_0011:  stind.ref
      IL_0012:  ldc.i4.1
      IL_0013:  newobj     ""System.Span<string>..ctor(void*, int)""
      IL_0018:  stloc.0
      IL_0019:  ldloca.s   V_0
      IL_001b:  ldc.i4.0
      IL_001c:  call       ""ref string System.Span<string>.this[int].get""
      IL_0021:  ldind.ref
      IL_0022:  call       ""void System.Console.WriteLine(string)""
      IL_0027:  ret
    }

This fails when constructing the Span due to this line:

if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));

Is this IL safe? If so this will require removing that error from Span, and no runtime changes.

@jkotas
Copy link
Member

jkotas commented Jul 2, 2020

This IL does not work. Pointers stored in memory allocated using localloc are not tracked by the GC.

@jaredpar
Copy link
Member

jaredpar commented Jul 6, 2020

There are really two issues here:

  1. params Span<T>
  2. The call site optimizations we could in theory do around this feature

As for params Span<T> itself this has been roughly approved by LDM and we have developer resources scheduled to start work on it in 1-2 weeks. There are a number of supporting features here as well (like params IEnumerable<T>) but I expect all of them to get done fairly early in the post C# 9 time frame.

As for the call site optimizations these are more complex. The feature is designed such that we could safely stack alloc the Span<T> at the call site provided sufficient runtime APIs existed to allow that to happen. Immediately that would mean we could stack alloc params Span<int> since this is already supported in the runtime but would need some extra help to stack alloc params Span`.

The problem though is even if we can do this, should we do this? Yes this saves heap allocations but it also increases stack space. It's a virtual guarantee that if we blindly applied this optimization that we would break some customers because their programs, which previously were running very close to the edge of the stack size, now blew right past it.

In chatting with @davidwrighton about this a bit we decided to take a bit of a different approach. We'd move to have the runtime provide stack alloc APIs that the compiler treats as if they were stack alloced but the runtime makes a runtime decision on whether or not to actually stack alloc the memory. The runtime is best setup to determine if a stack alloc is safe or not, or at least it's much better setup than the compiler is. The compiler could then emit calls to this API at the call site of params Span<T> and the runtime could decide whether or not to apply this.

@jkotas
Copy link
Member

jkotas commented Jul 6, 2020

We'd move to have the runtime provide stack alloc APIs that the compiler treats as if they were stack alloced but the runtime makes a runtime decision on whether or not to actually stack alloc the memory.

This is exactly the API proposed by #25423 . Closing as duplicate.

@jkotas jkotas closed this as completed Jul 6, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
@tannergooding tannergooding removed the untriaged New issue has not been triaged by the area owner label Jun 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants