CSharpRepl icon indicating copy to clipboard operation
CSharpRepl copied to clipboard

Add "Execute on Release" feature

Open Emik03 opened this issue 1 year ago • 2 comments

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.

Emik03 avatar Jul 08 '23 09:07 Emik03

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.

waf avatar Jul 10 '23 15:07 waf

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].

image

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?

image

Emik03 avatar Nov 03 '23 00:11 Emik03