Replies: 38 comments
-
I'm definitely in favor of the proposal, but not the |
Beta Was this translation helpful? Give feedback.
-
I am not hardline on any particular name. The only thing it needs to convey is that you aren't getting what most would consider "normal" behaviour in .net. So it needs to be clear. If you turn the data into a struct for instance you could get anything... (ahh found c memories) :P I am not sure fixed conveys that but I don't thing nonzero is right either.. Naming is hard ;) |
Beta Was this translation helpful? Give feedback.
-
I kinda like I can't see reusing a keyword here, none make any sense. :-( |
Beta Was this translation helpful? Give feedback.
-
And maybe one day we'll get to use |
Beta Was this translation helpful? Give feedback.
-
I was thinking rather than what your are or are not doing a description is better... actually dirty is what I thought of as well... plus there would be a small smile on my face to write Byte* b = stackalloc dirty byte [10] |
Beta Was this translation helpful? Give feedback.
-
The bigger challenge might be coming up with the name of the IL modifier required to make this possible. Currently |
Beta Was this translation helpful? Give feedback.
-
Collisions should be rare and it's pretty clear that it's an extra word there isn't more possible modifiers in a stackalloc case. If there is a better way I am all ears! |
Beta Was this translation helpful? Give feedback.
-
@jnm2, even with contextual, I don't think the collision risk exists.
In either case, I think the bigger question is, how is this expected to work on the CoreCLR side, without breaking other expected behavior in C#. Currently (at least according to the ECMA spec), Currently (as best as I can tell) C# always sets the init flag, even though it also tries to error if you haven't initialized the field first. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour, there shouldn't need to be a CLR change here (unless it currently sometimes initializes memory to zero, even if the |
Beta Was this translation helpful? Give feedback.
-
Take a look at the second issue at the bottom of my initial comment (sorry on phone now or I would sort a link) but that's exactly what they are discussing. So if they sort it out there it would be good to have a way to access it rather than it being special magic. |
Beta Was this translation helpful? Give feedback.
-
First issue sorry. |
Beta Was this translation helpful? Give feedback.
-
Also, regardless of whether removing the init flag guarantees uninitialized memory on the CLR side, I'm wondering if removing the Today, the following code is disallowed in C#, due to the assigned/unassigned tracking: unsafe static void Main(string[] args)
{
int x;
Method(out x);
}
static void Method(out int x)
{
if (x == 0)
{
}
} However, regardless of C# saying 'x' is unassigned in |
Beta Was this translation helpful? Give feedback.
-
Is the init locals for an entire method only? As a quick aside the ecma spec says stackalloc memory contents is undefined |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
It may be interesting to note the following:
|
Beta Was this translation helpful? Give feedback.
-
@Drawaes, it would be treated as intrinsic by the runtime and be emitted as equivalent to a In any case, for normal C# semantics (where variables have to be initialized before use), you can probably just strip the If you want to strip the flags, here is a simple program to do so: private static void StripLocalsInit(IEnumerable<string> assemblyPaths)
{
foreach (var assemblyPath in assemblyPaths)
{
var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
Console.WriteLine($"Stripping the locals `init` flag for {assembly.FullName}");
foreach (var module in assembly.Modules)
{
if (module.HasTypes)
{
foreach (var type in module.Types)
{
Process(type);
}
}
}
assembly.Write(assemblyPath + ".opt");
}
}
private static void Process(TypeDefinition type)
{
if (type.HasNestedTypes)
{
foreach (var nestedType in type.NestedTypes)
{
Process(nestedType);
}
}
if (type.HasMethods)
{
foreach (var method in type.Methods)
{
Process(method);
}
}
if (type.HasProperties)
{
foreach (var property in type.Properties)
{
Process(property.GetMethod);
Process(property.SetMethod);
if (property.HasOtherMethods)
{
foreach (var otherMethod in property.OtherMethods)
{
Process(otherMethod);
}
}
}
}
if (type.HasEvents)
{
foreach (var @event in type.Events)
{
Process(@event.AddMethod);
Process(@event.InvokeMethod);
Process(@event.RemoveMethod);
if (@event.HasOtherMethods)
{
foreach (var otherMethod in @event.OtherMethods)
{
Process(otherMethod);
}
}
}
}
}
private static void Process(MethodDefinition method)
{
if ((method != null) && method.HasBody)
{
var body = method.Body;
if (body.InitLocals)
{
Console.WriteLine($" {method.FullName}");
body.InitLocals = false;
}
}
} Should just need to add a dependency on Mono.Cecil and it should work. |
Beta Was this translation helpful? Give feedback.
-
I don't think that's a good idea. Since this is a method-wide flag that impacts both local initialization and stack allocation indiscriminately I think that appearing as an API would only make it really confusing as to what kind of impact the pseudo-call would have on other code. It would have to be illegal to combine both |
Beta Was this translation helpful? Give feedback.
-
Agreed on that. The example I have above (calling schannel) is the prime place that could catch you out where you have an array of structs elsewhere |
Beta Was this translation helpful? Give feedback.
-
How so, since it is an intrinsic, rather than an IL instruction it can have whatever semantics its contract defines. If it states that it behaves as the C/C++ alloca method and zeros the memory, or not, based on some It would require runtime support, but it would be completely non-breaking and would be no different from calling |
Beta Was this translation helpful? Give feedback.
-
To be clear, I am stating this would be a runtime intrinsic, not a compiler intrinsic. The compiler would continue emitting the The runtime would recognize the |
Beta Was this translation helpful? Give feedback.
-
I see. I assumed that the compiler would recognize the call and omit the Are there any precedents for such "runtime intrinstics" like this? |
Beta Was this translation helpful? Give feedback.
-
The hardware intrinsics proposal is somewhat similar, although it is adding runtime intrinsics for Intel SIMD instructions. There have always been some functions treated as intrinsic by the runtime ( Some other members of the |
Beta Was this translation helpful? Give feedback.
-
So, runtime intrinsics aren't really new, and are still being used in places, but I am not sure if there is something "exactly" like this. |
Beta Was this translation helpful? Give feedback.
-
FYI I like your "temp" fix above and will probably be using it 👍 So to give an example of a simple method static unsafe void TestMethod()
{
var b = stackalloc dirty byte[3];
var c = stackalloc byte[4];
} It's unverifiable today so we win/lose nothing by breaking verifiability (is that a word?) So if we modify the IL like below then there should be no change in existing behavior and thus little risk Most of the values we need are in the stack anyway (size and address) but might need a little shuffle.. not sure anyway it should be cheap as we would have payed it for the second stackalloc anyway (and the first). |
Beta Was this translation helpful? Give feedback.
-
That sounds about right. As an aside, I've been wanting a tool that does post IL optimization on an assembly for sometime and I just created a repo to do this: https://github.com/tannergooding/ilopt. Right now it just exposes a It does not currently insert |
Beta Was this translation helpful? Give feedback.
-
I think you are trying to tempt me into doing this aren't you :p |
Beta Was this translation helpful? Give feedback.
-
Only if you have time 😄; Otherwise, I'll take a look tonight or this weekend. I have a few simple optimizations I want to add anyways (things that the C# compiler doesn't do and that can be done trivially before the JIT gets it, like shortcutting comparisons, or using shift to multiply, etc) and this will be my long weekend project. |
Beta Was this translation helpful? Give feedback.
-
I will see u on gitter;) |
Beta Was this translation helpful? Give feedback.
-
Tagging @VSadov @OmarTawfik |
Beta Was this translation helpful? Give feedback.
-
This proposal is almost completely obsoleted by the already approved #1738. This will add an attribute named SkipLocalsInitAttribute. This attribute can be assembly, class, or member level. And causes any stackalloc to skip zeroing, by omitting the This is technically not a change in the language, only a change in the runtime, and in the compiler. I don't think that is actually a problem in practice. I feel like this can be closed as rejected in favor of #1738. |
Beta Was this translation helpful? Give feedback.
-
Motivation
Stackalloc is often used in performance critical code where every operation counts. Today you have to pay the price of zeroing the memory even if you know you will overwrite the entire space, paying the memory write price 2x.
Current Situation
While the current documentation for stackalloc states you should not rely on it being zero'd I imagine that plenty of code has been written relying on it. As this code often has few checks and balances for performance reasons I don't think the current behaviour can be changed without risk.
Proposed Solution
A keyword could be added to modify the stackalloc command. This should be simple to parse because it will only occur after the stackalloc keyword. This would leave existing behaviour as it and allow an opt in to not zeroing the memory.
e.g.
byte* = stackalloc dirty byte[16];
If a runtime couldn't support this or hadn't been updated it could just be ignored and the zeroing would occur with no harm (other than performance).
Related issues
CoreClr 1279
CorerClr 8542
Potential implementation
If any method contains a dirty stackalloc the init flag will be left unset and the compiler would emit manual initialization for all other local vars and stackallocs in the method.
This would not require a CLR change, or extra instructions and as the method contains unsafe anyway it is already not verifiable.
Updates
Beta Was this translation helpful? Give feedback.
All reactions