CSharpRepl
CSharpRepl copied to clipboard
Add "Execute on Release" feature
Feature Description
Proposal
You can already use F9
and CTRL+F9
to see the generated MSIL
in Debug and Release, respectively.
However as of C# REPL 0.6.3
, it is currently not possible to also evaluate code with Release optimizations.
Reasons this would be useful is quite simple: Being able to see the differences between optimized and unoptimized outputs, and using functions that only work with inlining.
Implementation
There are many binds on Enter
, so introducing a toggle (e.g. F2
) that switches from Debug
to Release
sounds like the way to go. The only problem with this idea is that perhaps a person may forget that they are in one or the other, as with any modal system it can be easy to get lost or to be unaware of the current state.
I would also personally add a CLI arg "--release
" to start the REPL automatically in release mode.
Example
It is not possible to experiment with the way the following functions get optimized:
> Unsafe.SkipInit(out int i);
> i
0
> string.Join(", ", stackalloc byte[10].ToArray())
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
└── 🟡 Length: 28
> [MethodImpl(MethodImplOptions.AggressiveInlining)]
string CurrentMethodName() => MethodBase.GetCurrentMethod().Name;
> CurrentMethodName()
CurrentMethodName
└── 🟡 Length: 17
This seems to suggest that all of these functions are zero-initialized and have no amount of inlining, though of course this guarantee is dropped if we are to copy-paste this code into our IDE and run it in Release mode.
Edgecases
It is important to also consider the following edgecase.
> #if DEBUG
void Foo() => throw new();
#else
void Foo() { }
#endif
> Foo();
It should be expected that Foo
throws when you run it in Debug
and no-op (or even inlined) on Release
. This means that you require two compilation contexts.
This is an interesting idea. I just manually converted CSharpRepl to compile/evaluate everything in Release mode (OptimizationLevel.Release
), and it looks like the optimizations you're expecting still don't take place. So it's possible that the underlying APIs we're using don't apply the same optimizations that we'd get in a "real" release mode. Needs a bit of digging I think.
Hi, turns out I was wrong about the behavior of the C# runtime.
The first one is explained by the lack of SkipLocalsInitAttribute
. I forget that by default I have this enabled via [module: SkipLocalsInit]
.
The second one is explained by the fact that AggressiveInlining
will still not inline your function if there is an observable difference. Since the method uses reflection to refer to itself, no inlining occurs.
That being said, is there a way to annotate attributes to the current module/assembly?