8 ms·
Why is .NET reflection slow?
- taspeotis 10y agoHi Matt, thanks for your in-depth .NET articles. They do a good job casting light on CLR internals.
- matthewwarren 10y agoThanks, glad you like them and find them useful!
- themihai 10y agoI think reflection is "slow" is most languages unless you use a kind of preprocessor. Is there any exception?
- camus2 10y agoThat's why macros like the Crystal implementation are often the way to go : https://crystal-lang.org/docs/syntax_and_semantics/macros.html https://crystal-lang.org/docs/syntax_and_semantics/macros.ht... It's a meta language that works at compile time. It's not just text substitution like with GCC, so the syntax can be validated. And no need for "go gen" like silly tricks. Languages that have reflection should do everything so that using reflection is the exception, not the norm. Some languages which boast about simplicity are often the worst offenders in practice, when it comes to reflection, just look at Go code out there, full of reflection everywhere, because the language is way too simplistic for a lot of use cases. Go even allows adhoc type definition at run time, out of thin air. Is this really what gophers mean by "simplicity" ? because the maintainers refuse to add any new syntax they now basically cram everything they need into the reflect package. Then dare say its use is "not idiomatic" when they abuse reflection themselves ?
- eropple 10y agoCrystal's implementation wouldn't work for something like C#, though, because C# supports dynamic assembly loading at runtime and must provide reflective facilities for operating on types loaded from them. (I once wrote an extensibility system[1] and on-the-fly compiler for a game engine that took advantage of this, it was neat.) Macros don't help you here and limiting yourself to "I can compile the whole blob at once" cuts off a lot of avenues of extensibility. [1] - https://github.com/eropple/exor https://github.com/eropple/exor
- themihai 10y ago>> because the maintainers refuse to add any new syntax they now basically cram everything they need into the reflect package. I think they want to try different avenues before to decide what features to add. Generators("go generate") are used a lot too but they are a pain to develop. Most of them still use templates or print statements instead of specilised packages such go/ast , go/types, go/parser etc to actually generate/print code. Usually you should use reflection to prototype and switch to generators only where the performance is really a big issue or where reflection can't help(i.e define new named types, import/use arbitrary packages/functions)
- eropple 10y agoAlternatively, you build a type system that can express what developers actually need it to express and solve the problem without reflection at all. =)
- themihai 10y agoThat's easier said than done. There are tradeoffs on expressive type systems too. That being said I wouldn't mind more expressivity in the Go type system. I'm actually looking to implement my own small features(i.e optionals, variant types) in a Go superset.
- camus2 10y agoWhere do you think struct tags fit in all this ? do you really think go designers took the time to consider the tradeoffs of such feature ? I don't think so.
- DrJokepu 10y agoAny sufficiently advanced type system is indistinguishable from magic.
- camus2 10y agoBut putting struct tags in Go was a good idea that promote simplicity ... I prefer the magic of generics or pattern matching or covariance to insane features like this, thank you.
- spullara 10y agoThe JVM has very fast reflective invocations. Lookup is reasonable.
- matthewwarren 10y agoFrom what I understand, the JVM uses 'inflation' to make reflection faster. See https://blogs.oracle.com/buck/entry/inflation_system_properties https://blogs.oracle.com/buck/entry/inflation_system_propert... and http://stackoverflow.com/questions/10082523/java-what-is-jitcs-reflection-inflation http://stackoverflow.com/questions/10082523/java-what-is-jit... for more info
- chrisseaton 10y ago> I think reflection is "slow" is most languages unless you use a kind of preprocessor. Is there any exception? Yes! I wrote a PhD on making reflection in Ruby fast. It is possible to make most reflection operations run with no peak time performance overhead at all compared to normal operations. http://chrisseaton.com/phd/ http://chrisseaton.com/phd/ The main techniques needed are dynamic code compilation with speculation and deoptimisation, polymorphic inline caching, splitting and method inlining. Unfortunately I think the .NET JIT isn't dynamic (might be mistaken, not an expert on .NET) so can't do speculative optimisations. Basically you see what method names have been used last time in a reflective call, and create a little call that's more like a conventional call using those names. Each time you do the reflective call you check the name against the list of names you have created conventional calls for, and use them if you can. You need to make string comparison fast, which can be done using a rope data structure. Then you need to be able to remove the check entirely if the string comes from somewhere you can control, like a constant. Then you need to inline the reflection method so you are just left with the synthetic conventional call. So it isn't easy! Which is why it isn't usually done. But it's possible. In Ruby you need to make it fast no matter how hard it is, as Ruby libraries tend to use reflection (they call it metaprogramming) in inner-loop operations.
- Locke1689 10y ago> It is possible to make most reflection operations run with no peak time performance overhead at all compared to normal operations. Hmm... but my discussions with people who have worked on Ruby execution engines say that Ruby's normal operations are slow (because Ruby is constrained to do many slow things for normal invocations). How does Ruby reflection compare to .NET reflection and how does Ruby invocation compare to .NET invocation?
- chrisseaton 10y agoI mean that for Ruby it is possible to compile at runtime a + b And a.send('+', b) Into the same thing - machine code like add ra, rb, rx jo Except for the jo to detect overflow, this is as good as you will get from any compiler including .NET, Java, C and so on. Ruby really isn't constrained to do anything slow for normal method invocations - you can optimise I think literally all of them away entirely if you have a JIT. For normal method invocations this all all solved back in the early 1990s in the research on making Smalltalk fast, but wasn't fully applied to Ruby until my work (and Topaz, Ruby on PyPy achieved similar results around the same time actually). I extended it to work with reflection (metaprogramming).
- __old_dude__ 10y agoJava 7 has introduced a very specific API (java.lang.invoke) to be able to inline reflection calls. So lookups are quite slow, but invocations are fully inlined. java.lang.invoke API is fast to the point that for doing faster string concatenation in Java 9, Aleksey Shipilev has used this API instead of introducing a new opcode in the JVM [1]. [1] https://youtu.be/wIyeOaitmWM?list=PL2ekzZZrxVUmBXtrwS7pLabUu8v-FCE8O&t=2278 https://youtu.be/wIyeOaitmWM?list=PL2ekzZZrxVUmBXtrwS7pLabUu...
- wmccullough 10y agoI think it's nice to see this type of analysis. We all know that reflection is slow for most languages, but understanding the why gives me a great sense of how interesting the task of reflection is.
- matthewwarren 10y agoThis was exactly my reason for writing it :-)
- doubleorseven 10y ago3265 words until you reach the part when the site is no longer mobile friendly, this is where i stopped. Learned some new stuff hopes it got better.
- foota 10y agoIt seems a bit silly to me that the author uses number of method calls (which is only like 4?) and lines of codes as a metric for how slow the function is.
- recursive 10y agoHe also uses time.
- taspeotis 10y agoAnd other complementary metrics, like memory allocations.
- matthewwarren 10y agoYeah that's just to illustrate it's doing a lot of work compared to calling the property without reflection (which isn't even a method call as its inlined)
- thepumpkin1979 10y agoIn .NET, You're supposed to use reflection for discovery and then generate the code, at runtime, in a Assembly which is loaded and JIT'Ted by the runtime just like any other library.
- taspeotis 10y agoWell I think the benchmarks show that there's quite a bit of nuance to it. Reflection generated code to get a property's value from its get accessor ends up 8.82x slower than what RyuJit does, because you're stuck with a method call rather than method inlining. ("GetViaProperty" is just reading from a field.) That doesn't mean you can't reflect even more and get the backing field and generate code to access it, but now you've got your optimizing compiler hat on! And even then, I think you still can't get 1:1 performance because you have to call the generated method vs. having it inlined.
- ori_b 10y agoWhy can't the jit inline the generated code?
- taspeotis 10y agoStatic dispatch vs. dynamic dispatch. Assuming the functions and types are known in advance, no virtuals etc: given something like `Main` calling `DoSomething` calling `GetPropertyValue` which is public int GetPropertyValue(MyObject myObject) { return myObject.PropertyValue; } When RyuJit compiles your application, it can see these methods and how they're called. `PropertyValue` can be inlined into a field access, `GetPropertyValue` can be inlined into `DoSomething` which in turn can be inlined into `Main`. When you do something like the article: Func<TestClass, string> getDelegate = (Func<TestClass, string>)Delegate.CreateDelegate( typeof(Func<TestClass, string>), property.GetGetMethod(nonPublic: true)); [Benchmark] public string GetViaDelegate() { return getDelegate(testClass); } RyuJit can't see that `getDelegate` is the equivalent of `return myObject.PropertyValue`. It's a reference to some function. RyuJit has to compile calling an arbitrary method vs. calling `GetPropertyValue`.
- __s 10y agoIt'd be nice if reflection could use generics. Often passing around typeof(T) into reflection
- n00b101 10y agoI'm not sure what you are asking for, but you can find the generic type arguments of any object at runtime: Code snippet: public class Foo<T>{} Foo<int> foo = new Foo<int>(); Console.WriteLine(foo.GetType().GetGenericArguments()[0]); Output: System.Int32 Complete code: http://rextester.com/RAQO69918 http://rextester.com/RAQO69918
- eropple 10y agoI think he may mean that a lot of reflection methods take Type rather than a generic parameter? A lot of those have generic overloads now though.
- naasking 10y agoWhat would really make reflection fast is the dual operation of typeof(T) -> Type, that is, Type -> T. Then you can do all kinds of neat but efficient things by exploiting the CLR's runtime generics [1]. This is something that would be easily done in the CLR itself, a sort of hidden universal method that could perform a single, maybe double, dispatch into the correct generic overload, but alas, we are left only to simulate this using poor imitations of inline caches. [1] https://github.com/naasking/Dynamics.NET https://github.com/naasking/Dynamics.NET
- matthewwarren 10y agoCool, Dynamics.NET look like a cool library, I'd not heard of it before, thanks
- __s 10y agoThat is what I meant. I may be a bit outdated with C# versions, still working with 4.5
- halestock 10y agoDoes anyone have any comparable analyses for Java? I would assume the performance penalties are similar. There are many popular libraries which rely on annotations (JAX-RS, Jackson, Guice/Spring DI) and this presumably uses reflection heavily, so I've often wondered what kind of impact this has vs not using annotations.
- winteriscoming 10y agoThere are libraries out there, like Jandex[1] which use indexes to avoid repeated reflection for annotations. [1] https://github.com/wildfly/jandex https://github.com/wildfly/jandex
- DonHopkins 10y agoI've been researching JSON serialization/deserialization libraries for Unity, and I'm writing a spreadsheet comparing the different alternatives. Unity JSON Libraries: https://docs.google.com/spreadsheets/d/1NoIYASJR9uUT-kZxjb54zWX2Lr3EXp8F7t7N62yo7J8/edit?usp=sharing https://docs.google.com/spreadsheets/d/1NoIYASJR9uUT-kZxjb54... Unity is limited by the fact that it uses an older version of .NET 2.0, or even preferably a .NET 2.0 subset. It can also be optimized by disabling exceptions. And most importantly on AOT-compiled platforms like iOS and consoles, it doesn't support dynamic JIT compilation and interfaces like Reflection.Emit, which some of the high performance JSON serialization libraries use for optimization. So the question of "Why is reflection so slow?" is very interesting to me as a Unity iOS developer, since I'm forced to use it without all the fancy optimizations (or figure out how to avoid using it). Some of the big fancy luxurious all dancing all singing JSON libraries intended for less restricted CLR platforms like Windows do all kinds of amazing tricks to reduce and optimize their use of reflection. Some of those have been stripped down and ported to Unity, but others haven't. JSON.Net is one of the most advanced feature-rich C# JSON libraries, and it is free, but it is not the fastest. The free version doesn't run on Unity out of the box, but there is a striped down simplified version that runs on Unity available on the Unity Asset Store for $25. JSON.NET home page: http://www.newtonsoft.com/json http://www.newtonsoft.com/json JSON.Net source code: https://github.com/JamesNK/Newtonsoft.Json https://github.com/JamesNK/Newtonsoft.Json Unity JSON.Net port: https://www.assetstore.unity3d.com/#!/content/11347 https://www.assetstore.unity3d.com/#!/content/11347 FastJSON is quite fast true to its name, free, has a lot of advanced features, is deeply customizable, is highly optimized and benchmarked against other libraries, and there's a great in-depth article comparing it to other libraries and explaining its optimization techniques. It hasn't been ported to Unity, but that might be worth doing, since it's such a nice piece of work. I asked the author about it, and he said that its speed was the result of dynamic code generation features that Unity doesn't support. That would sacrifice some of the optimizations and advanced features, but still might be worth doing. FastJSON article and benchmarks: http://www.codeproject.com/Articles/159450/fastJSON http://www.codeproject.com/Articles/159450/fastJSON FastJSON source: https://github.com/mgholam/fastJSON https://github.com/mgholam/fastJSON Full Serializer is a well written free JSON library with lots of useful features that is specifically designed for Unity from the start, which avoids using anything that will limit optimization, like anything higher than .Net 2.0 subset features, advanced reflection features, LINQ, code generation and even exceptions. So far, it seems like the best balanced compromise I've found between Unity's limitations and a full set of useful features, and it looks to me like well written but not overly complex code. FullSerializer source: https://github.com/jacobdufault/fullserializer https://github.com/jacobdufault/fullserializer Thread asking about using FastJSON on Unity, where the FullSerializer author chimes in: https://www.reddit.com/r/Unity3D/comments/2a4c7p/using_fastjson_in_unity/ https://www.reddit.com/r/Unity3D/comments/2a4c7p/using_fastj... JSONObject is another free simple JSON library written for Unity, which I've been using for years, and while it works for basic stuff, I'm not very happy with it (which is why I'm looking around for alternatives). It's not particularly efficient (in speed or runtime size), has a terrible API, few advanced features, and some embarrassing bugs. (It doesn't correctly handle string escapes like \r \n \u1234 etc -- c'mon, there's a very simple explicit JSON spec that defines every bit of the syntax: follow it!) JSONObject source: https://github.com/mtschoen/JSONObject https://github.com/mtschoen/JSONObject JSONObject on the Asset Store: https://www.assetstore.unity3d.com/en/#!/content/710 https://www.assetstore.unity3d.com/en/#!/content/710 Unity now has its own JSONUtility module that at first glance seems like it might be useful, but once you try to actually use it for anything in the real or virtual world, you hit a wall, because it's a wolf in sheep's clothing. Even worse than a wolf: it's layered directly on top of Unity's infamous serialization system, and inherits all its bizarre quirks and limitations, making it practically useless (and probably slow). https://docs.unity3d.com/ScriptReference/JsonUtility.html https://docs.unity3d.com/ScriptReference/JsonUtility.html https://docs.unity3d.com/Manual/JSONSerialization.html https://docs.unity3d.com/Manual/JSONSerialization.html First, in order to begin to understand how truly fucked up Unity's JSONUtility library truly is, you have to understand how truly fucked up Unity's serialization system is, and this great classic blog posting by Lucas Meijer bravely and honestly touches the surface, then falls off into the deep end: Serialization in Unity: https://blogs.unity3d.com/2014/06/24/serialization-in-unity/ https://blogs.unity3d.com/2014/06/24/serialization-in-unity/ While this reddit discussion goes to the heart of the matter: Unity Serialization is truly FUBAR: https://www.reddit.com/r/Unity3D/comments/2e9vlg/unity_serialization_is_truly_fu https://www.reddit.com/r/Unity3D/comments/2e9vlg/unity_seria... For many Unity developers, an important reason for using a JSON serialization library is to avoid using Unity's serialization system. So the idea of JSONUtility being layered on top of the system they were trying to get away from doesn't appeal to them. "This is why I am moving away from scriptable objects style services and into using static clr objects that load their configuration from csv / json txt files. I am loosing editor integration, but Im gaining peace of mind that unity wont blow up and corrupt my array of 100 items, levels, whatever." [–]nventimiglia Expert 2 points 2 years ago https://www.reddit.com/r/Unity3D/comments/2e9vlg/unity_serialization_is_truly_fubar/cjy86ho/ https://www.reddit.com/r/Unity3D/comments/2e9vlg/unity_seria... I'd prefer to build on top of Unity's built-in optimized JSON parser if that were possible, layering a system like Full Serialization on top of it without tangling with Unity Serialization, but the current JSONUtility API makes that impossible. Unity should publish the source for JSONUtility to the community (and throw in Serialization too please, so we can at least understand how it works when we're forced to interact with it), and accept pushes, please!
- DonHopkins 10y agoHere's an article about optimizing reflection on Unity AOT platforms like iOS: Mike Talbots Blog: Faster Invoke for reflected property access and method invocation with AOT compilation Article: https://web-beta.archive.org/web/20120626190355/http://whydoidoit.com/2012/04/18/faster-invoke-for-reflected-property-access-and-method-invocation-with-aot-compilation https://web-beta.archive.org/web/20120626190355/http://whydo... Source: https://web-beta.archive.org/web/20161005070240/http://www.whydoidoit.net/Downloads/DelegateSupport.cs https://web-beta.archive.org/web/20161005070240/http://www.w... Newer version: https://gitgud.io/TheSniperFan/unityserializer-ng/blob/master/Assets/Plugins/OpenUnityTools/unityserializer-ng/Radical/System/Delegates/DelegateSupport.cs https://gitgud.io/TheSniperFan/unityserializer-ng/blob/maste... "The bane of the iOS programmers life, when working with reflection in Mono, is that you cant go around making up new generic types to ensure that your reflected properties and methods get called at decent speed. This is because Mono on iOS is fully Ahead Of Time compiled and simply cant make up new stuff as you go along. That coupled with the dire performance of Invoke when using reflected properties lead me to construct a helper class." "This works by registering a series of method signatures with the compiler, so that they are available to code running on the device. In my tests property access was 4.5x faster and method access with one parameters was 2.4x faster. Not earth shattering but every little helps. If you knew what you wanted ahead of time, then you could probably do a lot better. See here for info:" "You have to register signatures inside each class Im afraid. Nothing I can do about that." http://stackoverflow.com/questions/1116073/can-delegate-dynamicinvoke-be-avoided-in-this-generic-code http://stackoverflow.com/questions/1116073/can-delegate-dyna... If you need to extend this to wrapping member invocations from classes without using Reflection.Emit you can do so by creating a series of compiler hints that can map a class and a function parameter list or return type. Basically you need to create lambdas that take objects as parameters and return an object. Then use a generic function that the compiler sees AOT to create a cache of suitable methods to call the member and cast the parameters. The trick is to create open delegates and pass them through a second lamda to get to the underlying hint at runtime. You do have to provide the hints for every class and signature (but not every method or property). I've worked up a class here that does this, it's a bit too long to list out in this post. In performance testing it's no where near as good as the example above, but it is generic which means it works in the circumstances I needed. Performance around 4.5x on reading a property compared to Invoke.