Skip to content

Use of Reflection.Emit in ML.NET #1736

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
MichalStrehovsky opened this issue Nov 27, 2018 · 7 comments
Closed

Use of Reflection.Emit in ML.NET #1736

MichalStrehovsky opened this issue Nov 27, 2018 · 7 comments
Labels
question Further information is requested

Comments

@MichalStrehovsky
Copy link
Member

Can ML.NET be used on platforms that don't support Reflection.Emit, such as Windows UWP or Xamarin iOS?

I'm helping a customer get ML.NET running on UWP, but seeing there are code paths that might end up using Reflection.Emit worry me that I'm just wasting time.

@TomFinley
Copy link
Contributor

Hi @MichalStrehovsky thanks for the question... however my understanding, which may be flawed, is that UWP supports .NET Standard 2.0 (I see this blog was written by @terrajobst so perhaps he can comment), and Xamarin seems to also support .NET Standard 2.0. Also if I search our issues page for "UWP" I see examples of people with questions, but it's clear they were seemingly able to successfully use ML.NET.

(Maybe Xamarin iOS specifically still uses PCL? I honestly have no experience with that.)

@TomFinley TomFinley added the question Further information is requested label Nov 27, 2018
@MichalStrehovsky
Copy link
Member Author

Thanks @TomFinley. My question is specifically about the code paths in this file: https://github.com/dotnet/machinelearning/blob/1e7b8be855210f2bd8adbd532396a1840a20541d/src/Microsoft.ML.Api/ApiUtils.cs, such as this:

var mb = new DynamicMethod("Peek", null, args, typeof(TOwn), true);
var il = mb.GetILGenerator();
il.Emit(OpCodes.Ldarg_3); // push arg3
il.Emit(OpCodes.Ldarg_1); // push arg1
il.Emit(OpCodes.Ldfld, fieldInfo); // push [stack top].[fieldInfo]
// Stobj needs to coupled with a type.
if (assignmentOpCode == OpCodes.Stobj) // [stack top-1] = [stack top]
il.Emit(assignmentOpCode, fieldInfo.FieldType);
else
il.Emit(assignmentOpCode);
il.Emit(OpCodes.Ret); // ret

DynamicMethod is not part of NetStandard 2.0: https://apisof.net/catalog/System.Reflection.Emit.DynamicMethod

Xamarin on iOS has documented limitations around Reflection.Emit and so does UWP, so I would assume code that calls into this will not work.

@TomFinley
Copy link
Contributor

Well, it's like many other things we use not part of .NET Standard 2.0 itself, but rather a System.Refection.Emit.LightWeight nuget which is a .NET Standard 2.0 library. Whether that works in UWP or Xamarin is I'm afraid more than I know. If I open a UWP project and try to link that nuget and play around with it, it seems to work.

The Xamarin.iOS restriction is more serious than merely not supporting something like System.Reflection.Emit. According to the document you linked, assuming that's current information, they cannot do any code generation at all, even the very puny stuff around instantiating generic methods of a particular type. We rely on that a lot, in many places. We have a data pipeline that works over generic types that are often known to the components only at runtime -- for that reason we often call generic methods with types known only at runtime (indeed we have a commonly used utility for this Utils.MarshalInvoke we use internally, which is used dozens of times). This is both for keeping the code small (so we don't have to enumerate every conceivable type everytime we want to do anything, even if that were possible to do), as well as for perf reasons (for value types, the JIT will actually generate new assembly code, which is great). But in a world where "there is no JIT," ML.NET can't really work either.

UWP though, I'm not sure why that wouldn't work?

@terrajobst
Copy link

The ref-emit packages are broken, don't use them.

We've added reflection emit and LCG (lighweight code gen, i.e. via DynamicMethod) to the upcoming version of .NET Standard 2.1. However, not all platforms will support ref-emit. So we've also added two capability APIs that will allow you to check whether code is supported (via RuntimeFeature.IsDynamicCodeSupported and RuntimeFeature.IsDynamicCodeCompiled).

Xamarin will implement an IL interpreter which will allow full-refemit (i.e. generation of types) but the code won't be efficient. So on iOS ref-emit is useful for, e.g. mocking by generating proxy types on the fly, but you wouldn't use ref-emit to speed things up. You could, however, use expression trees which are more efficient to interpret than general IL interpretation. AFAIK expression trees are already supported everywhere.

Does this help?

@MichalStrehovsky
Copy link
Member Author

If I open a UWP project and try to link that nuget and play around with it, it seems to work.

You need to switch to the Release configuration to see Ref.Emit being broken. Debug configuration runs on top of CoreCLR that supports Ref.Emit, but .NET Native doesn't. .NET Native is a requirement to ship to the Windows Store. UWP spans platforms that disallow runtime code generation (Xbox), so we can't really JIT.

According to the document you linked, assuming that's current information, they cannot do any code generation at all, even the very puny stuff around instantiating generic methods of a particular type.

AFAIK, Xamarin on iOS will fall back to this tech to get MakeGenericMethod/MakeGenericType working. In .NET Native we have a similar solution. The code such instantiations end up running won't win any prizes for perf, but it will run. Given the limitations of the underlying platform, it's the best that can be done...

Looking at the use of Ref.Emit in ML.NET, it seems to be specifically for perf. Once the capability APIs are available to use, ML.NET can use them to switch to regular reflection. It will be better than hitting an interpreted Ref.Emit, or a Ref.Emit that doesn't work at all.

If the code path with Ref.Emit is reachable in a UWP scenario, I would suggest wrapping it in a catch (PlatformNotSupportedException) and falling back to normal reflection.

@terrajobst
Copy link

Once the capability APIs are available to use, ML.NET can use them to switch to regular reflection. It will be better than hitting an interpreted Ref.Emit, or a Ref.Emit that doesn't work at all.

Agreed.

If the code path with Ref.Emit is reachable in a UWP scenario, I would suggest wrapping it in a catch (PlatformNotSupportedException) and falling back to normal reflection.

That works, but capability APIs (if available) would be preferable (but UWP doesn't have them).

@CESARDELATORRE
Copy link
Contributor

Adding a related sample app which mentions the same issues:
https://xamlbrewer.wordpress.com/2019/01/25/machine-learning-with-ml-net-in-uwp-clustering/

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants