Runtime: Intrinsify RuntimeInformation.IsOSPlatform

Created on 10 Dec 2019  路  10Comments  路  Source: dotnet/runtime

The result of RuntimeInformation.IsOSPlatform does not change during the application run lifecycle. It would be a nice optimization if JIT intrinsically generate a better code for this method.

For example, for the following method:

```c#
private static bool CheckIfOSIsWindows() =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

JIT produces this asm (with VS2019 and .NET Core 3.1 in Release configuration):

```asm
           RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
00007FF810DC0FB2  sub         esp,30h  
00007FF810DC0FB5  lea         rbp,[rsp+30h]  
00007FF810DC0FBA  xor         eax,eax  
00007FF810DC0FBC  mov         qword ptr [rbp-8],rax  
00007FF810DC0FC0  call        00007FF810DC0670  
00007FF810DC0FC5  mov         qword ptr [rbp-8],rax  
00007FF810DC0FC9  mov         rcx,qword ptr [rbp-8]  
00007FF810DC0FCD  call        00007FF810DC0778  
00007FF810DC0FD2  nop  
00007FF810DC0FD3  lea         rsp,[rbp]  
00007FF810DC0FD7  pop         rbp  
00007FF810DC0FD8  ret  

Instead, it could just produce:

xor     eax, 1
ret

on Windows, and on Unix:

xor     eax, eax
ret
area-System.Runtime.InteropServices tenet-performance

All 10 comments

Presumably that could potentially enable the JIT to remove the code as dead.

How common is it to check this though, and is it normally a perf concern?

I think it belongs to the same category as branch elimination due to Avx.IsSupported etc. (today, which is not there yet as well: https://github.com/dotnet/coreclr/issues/27935). While profiling some code today, I found some noise cause by RuntimeInformation branches.

branch elimination due to Avx.IsSupported

These APIs were specifically designed to be friendly to JIT optimizations

RuntimeInformation.IsOSPlatform

This API is not friendly to JIT optimizations. It can be optimized quite a bit by having a better implementation, without any help from the JIT. These optimizations should be done first.

How common is it to check this though, and is it normally a perf concern?

It can reduce code size in common scenarios. Platform specific code may be large when calling a sequence of P/Invoke.

It can be optimized quite a bit by having a better implementation, without any help from the JIT. These optimizations should be done first.

@jkotas, do you have a suggestion on how to do this?

The implementation today is trivial and just compares the input struct with the static readonly instance member for the current platform:
https://github.com/dotnet/runtime/blob/c9789de3d8840a5d69c8f8745dda91332b4b2f6b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.Windows.cs#L17-L20

Which ultimately just compares the internal string member using string.Equals(_osPlatform, other, StringComparison.Ordinal);.

I can't think of any existing JIT optimizations that would allow us to improve this so that it could be constant folded for the common case of IsOSPlatform(OSPlatform.Windows), etc.

Another case where this would help is in AOT situations. If I was compiling for linux-x64, and if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) was turned into if (false), then whole chunks of code wouldn't need to be compiled and could be eliminated in the final assembly. For example:

C# if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // bring in a whole new dependency that is Windows-specific }

It could eliminate a whole dependency completely in a linux AOT/crossgen'd version.

Note that this is the pattern we recommend to developers to use for switching on Operating Systems. There currently isn't a built-in way to pivot your C# compilation based on target OS.

There is an effort to do this in the linker: https://github.com/mono/linker/issues/607 . Doing this in the linker will strip it from both IL and potential AOT image. Stripping it from both is better than stripping it from just AOT image.

I can't think of any existing JIT optimizations that would allow us to improve this

For example, we can normalize the string into int in the struct constructor (keeping a global table with all OS strings) and then the equals method will become a comparison of two ints. The JIT may need fixes to do the constant prop for these two ints, but that would be a reasonable general-purpose optimization to have.

This approach has trade-offs so it is not obvious that we should do it. I am just trying to demonstrate that there are options.

// bring in a whole new dependency that is Windows-specific

To avoid bringing in the whole new dependency, the code should better be moved into a separate method. Even if we intrinsify this in the JIT, the JIT would still bring in the inline dependency in in the common case before optimizing it out.

Note that this is the pattern we recommend to developers to use for switching on Operating Systems.

Do we have it written anywhere? This pattern is a non-starter in many cases given the current performance characteristics of this API. RuntimeInformation is a slow type. One has to cache the results locally in a readonly static if it is used for anything on a frequently executed path.

Do we have it written anywhere?

Yes:

https://github.com/dotnet/platform-compat/blob/master/docs/DE0007.md

DE0007: Platform ID shouldn't be used

...
Code that compares Environment.OSVersion.Platform to PlatformID.MacOSX
should use the newer RuntimeInformation.IsOSPlatform(OSPlatform.OSX) method instead.

https://github.com/dotnet/platform-compat/blob/master/docs/DE0009.md

DE0009: Environment.OSVersion shouldn't be used

...
Use the RuntimeInformation.IsOSPlatform method to identify the OS platform. Avoid writing code dependent on reported OS version and instead check for availability of the needed features.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GitAntoinee picture GitAntoinee  路  3Comments

EgorBo picture EgorBo  路  3Comments

iCodeWebApps picture iCodeWebApps  路  3Comments

v0l picture v0l  路  3Comments

aggieben picture aggieben  路  3Comments