Skip to content

Add SkipLocalsInit #26598

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

Closed
wants to merge 2 commits into from
Closed

Add SkipLocalsInit #26598

wants to merge 2 commits into from

Conversation

JamesNK
Copy link
Member

@JamesNK JamesNK commented Oct 5, 2020

Addresses #26586

Enable SkipLocalsInit almost everywhere in Kestrel, HTTP, hosting, routing. Selected because they're the "hot path" assemblies when it comes to startup time and per-request performance.

Test to see what breaks.

@davidfowl
Copy link
Member

I'm not sure we'd easily be able to tell what breaks. I'd hate to cause an unexpected security bug because we enabled it assembly wide.

@JamesNK
Copy link
Member Author

JamesNK commented Oct 5, 2020

Stackallocs review. These are the stackallocs used in the assemblies with the attribute applied. I haven't reviewed any from code that is synced with runtime. They are already working with attribute applied.

@davidfowl
Copy link
Member

cc @GrabYourPitchforks Can you help us out here with an audit?

@GrabYourPitchforks
Copy link
Member

TBH I was actually going to try to pull back on our use of SkipLocalsInit within the runtime repo, targeting specifically crypto and serialization and other "sensitive" assemblies.

@@ -15,6 +16,8 @@
<Reference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" />
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />

<Compile Include="$(SharedSourceRoot)SkipLocalsInit.cs" LinkBase="Shared\SkipLocalsInit.cs" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hosting.Abstractions is not on a perf-critical code path and from your review it doesn't contain any stackallocs. Why did you mark this assembly? The same goes for Server.Abstractions and likely some of the others.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added it to all projects referenced by Kestrel.Core. Want them removed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it get us if we're not using stackallocs in these assemblies?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub Is there much benefit to SkipLocalsInit outside of stackalloc usages?

Copy link
Member

@stephentoub stephentoub Oct 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benefit isn't limited to stackalloc, but is proportional to how much state is on the stack that needs to be cleared (and where clearing can be skipped). As such, it generally is much more impactful with stackalloc, but it can show up as a peanut butter cost elsewhere.

[SkipLocalsInit]
[Benchmark]
public unsafe int WithSkipLocal()
{
    Guid g1, g2, g3, g4, g5, g6;

    // Do something cheap that let's the benchmark compile and stuff not get optimized away
    Unsafe.SkipInit(out g1);
    Unsafe.SkipInit(out g2);
    Unsafe.SkipInit(out g3);
    Unsafe.SkipInit(out g4);
    Unsafe.SkipInit(out g5);
    Unsafe.SkipInit(out g6);
    return *(int*)&g1 + *(int*)&g2 + *(int*)&g3 + *(int*)&g4 + *(int*)&g5 + *(int*)&g6;
}

[Benchmark]
public unsafe int WithoutSkipLocal()
{
    Guid g1, g2, g3, g4, g5, g6;

    // Do something cheap that let's the benchmark compile and stuff not get optimized away
    Unsafe.SkipInit(out g1);
    Unsafe.SkipInit(out g2);
    Unsafe.SkipInit(out g3);
    Unsafe.SkipInit(out g4);
    Unsafe.SkipInit(out g5);
    Unsafe.SkipInit(out g6);
    return *(int*)&g1 + *(int*)&g2 + *(int*)&g3 + *(int*)&g4 + *(int*)&g5 + *(int*)&g6;
}
Method Mean Error StdDev Code Size
WithSkipLocal 0.2983 ns 0.0324 ns 0.0303 ns 33 B
WithoutSkipLocal 0.7273 ns 0.0015 ns 0.0013 ns 79 B

If the goal was biggest bang for the buck, though, it's definitely where larger stackallocs are being employed.

Copy link
Member

@stephentoub stephentoub Oct 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another place it can show up impactfully is with fixed-size buffers, since as with stackallocs, they can often represent large regions of stack space that need to be cleared, e.g.

[SkipLocalsInit]
[Benchmark]
public unsafe int WithSkipLocal()
{
    Data d;
    Unsafe.SkipInit(out d);
    return d.Ptr[0];
}

[Benchmark]
public unsafe int WithoutSkipLocal()
{
    Data d;
    Unsafe.SkipInit(out d);
    return d.Ptr[0];
}

unsafe struct Data
{
    public fixed byte Ptr[1024];
}
Method Mean Error StdDev Code Size
WithSkipLocal 0.5009 ns 0.0047 ns 0.0041 ns 64 B
WithoutSkipLocal 14.8256 ns 0.0411 ns 0.0343 ns 132 B

Copy link
Member

@Tratcher Tratcher Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given it comes with a degree of risk, it seems better not to add it unless here's a demonstrated benefit. I also assume you'd be targeting the hot paths like per-request processing. Hosting doesn't have many of those.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @Tratcher but we also don't clear the array from the array pool so I don't see how this is different. Still I'd prefer doing this to some key placed in Kestrel rather than the entire assembly. That'll build more confidence.

@JamesNK JamesNK force-pushed the jamesnk/skiplocalsinit branch from 4ae24c1 to a6b288a Compare December 1, 2020 02:28
@JamesNK JamesNK force-pushed the jamesnk/skiplocalsinit branch from a6b288a to ac10f7d Compare December 1, 2020 02:39
Base automatically changed from master to main January 22, 2021 01:33
@davidfowl
Copy link
Member

@JamesNK are we doing this PR?

@JamesNK
Copy link
Member Author

JamesNK commented Apr 7, 2021

Do you want it?

I investigated how we're using stackalloc to make sure they're safe, but it sounds like a deeper review is required and there is little enthusiasm to do it.

@davidfowl
Copy link
Member

I don't want it assembly wide, I want to put it on a few hot methods in kestrel to start.

@JamesNK
Copy link
Member Author

JamesNK commented May 12, 2021

Replaced by explicit PR.

@JamesNK JamesNK closed this May 12, 2021
@JamesNK JamesNK deleted the jamesnk/skiplocalsinit branch July 24, 2021 21:11
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants