Achieving compile-time performance with Reflection in C#

Поділитися
Вставка
  • Опубліковано 6 вер 2024

КОМЕНТАРІ • 70

  • @user-pu4qu9my5j
    @user-pu4qu9my5j 3 роки тому +23

    You also just can use Expressions. Look: you can get methodInfo from property getter and then create compiled function from Exprression.Call with method info in parameters. At the end you need to create Expression.Lambda and cast it to Func. Then it expression must be compiled and saved into static field as delegate. It can shows similar performance with simple delegate creating.

    • @JobertoDiniz
      @JobertoDiniz 3 роки тому

      He could extend in another video to benchmark using expressions

    • @user-pu4qu9my5j
      @user-pu4qu9my5j 3 роки тому +1

      @@JobertoDiniz honestly it's perf is like IL emit. It's not really something new :)

    • @SebGruch
      @SebGruch 3 роки тому

      Interesting... Do you have any sample gist fur such syntax?

    • @user-pu4qu9my5j
      @user-pu4qu9my5j 3 роки тому

      @@SebGruch me not really. But you can inspect open source project Automapper. They have the space on github. This module have mapping logic where result of reflection using in expressions to compile mapping delegates and store in the concurrent dictionary.

    • @SebGruch
      @SebGruch 3 роки тому

      @@user-pu4qu9my5j Thanks. Usually I used to cache Get/SetMethod info for frequently serialized objects, but I thought about improving the performance. And actually, I've been playing with this until succeeded ;-) Yet gonna look into AM code, for sure.
      static void TestCode()
      {
      var instance = new SearchParameters()
      {
      FileNamePattern = "adsf asdf"
      };
      var refType = typeof(SearchParameters);
      var refProp = refType.GetProperty(nameof(SearchParameters.FileNamePattern),
      BindingFlags.Instance | BindingFlags.Public);
      var objParameterExpr = Expression.Parameter(typeof(SearchParameters), "instance");
      var valueExpression = Expression.Parameter(typeof(string));
      var getterCall = Expression.Call(objParameterExpr, refProp.GetMethod);
      var setterCall = Expression.Call(objParameterExpr, refProp.SetMethod, valueExpression);
      var getter = Expression.Lambda(getterCall, objParameterExpr).Compile();
      var setter = Expression.Lambda(setterCall, objParameterExpr, valueExpression).Compile();
      setter.Invoke(instance, getter.Invoke(instance) + " 111111");
      Debug.WriteLine(instance.FileNamePattern);
      }

  • @hoisinholdup
    @hoisinholdup 3 роки тому +41

    Would love a vid on Expression Trees my guy

    • @sigma_z
      @sigma_z 3 роки тому +6

      I would second that. Especially on performance tuning Expression Trees.

    • @nickchapsas
      @nickchapsas  3 роки тому +8

      Expression Trees are such an interesting topic to me but it's so hard to talk about a tree of any type without having a way to visualizing it as you're going

    • @X39
      @X39 2 роки тому

      They are also a way to get that stuff done too
      But much more complex to setup

    • @davidrogers8352
      @davidrogers8352 2 роки тому

      You would need a series on Expression Trees. The fundamentals themselves are not briefly conveyed. There's quite a bit to them.

    • @chriscardoz
      @chriscardoz 2 роки тому

      @@nickchapsas I've recently implemented Expression trees (compiled expression) in a custom json serializer to mask or remove any secure data from the logs in my gRPC interceptor. Probably a topic as such could help create a video.

  • @tobyjacobs1310
    @tobyjacobs1310 3 роки тому +5

    War story: I once had to address an issue in a library I had no control of. I worked out that I could use ILSpy to get at the internal class and inject the logic I needed, but then I had to make sure it was actually used instead of the actual internal class. Expression trees + a lot of static constructors and extension methods later I had a customised way of using the other library. With basically zero performance penalty... So fun!

  • @sps014
    @sps014 3 роки тому +2

    This video is pure gold, thanks ,
    never thought reflections can be made this much faster.

  • @emanuelvintila42
    @emanuelvintila42 3 роки тому +3

    Nick, I absolutely love your videos, and the way you go in depth about the topic at hand. Also, I'm a big fan of reflection and C#, so this video pleases me profoundly. Keep up the work, you're doing fantastic! 😄

  • @rafaelocariz1384
    @rafaelocariz1384 3 роки тому +1

    Your videos are pure gold! Besides that, I must admit: I see this stuff as some kind of sorcery hahaha.
    Thanks a lot for the magnificent content you provide us

  • @dotanoob466
    @dotanoob466 3 роки тому

    Dude. You have the best videos. On my way to sr dev because of youre taking my game to the next level. Thank you!!

  • @utubekade
    @utubekade 3 роки тому +1

    I had to hook to an event which was internal to another library. I ended up using IL emiting approach, only wrote the IL manually

  • @addkim7889
    @addkim7889 2 роки тому

    Absolutely love the video you made.Since my company has code using reflection, I have been looking for a way to speed up the existing code base and found your video. One thing to note though, and had been pointed out by other comment(s), is that the Expression API do the same thing as Sigil, with less confusing syntax(IMO) and according to my bench marking, also faster than Sigil, this is a small sample and I have not tested it against pure IL Emitter code but just something to consider.
    | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated |
    | IlGenerator | 69.39 us | 0.233 us | 0.218 us | 6.1035 | 0.1221 | 38 KB |
    | ExpressionGenerate | 42.82 us | 0.848 us | 1.103 us | 0.7324 | 0.3662 | 5 KB |

  • @johnathankrausrig9237
    @johnathankrausrig9237 2 роки тому

    I ised to have a issue on my Rest library where i could solve the problem by caching the propinfo. But just getting the getter is genius. I need to implement it! Thanks for sharing it with us.

    • @TheMonk72
      @TheMonk72 2 роки тому

      You can use LINQ Expressions to build a function that gets the property value, then cache that.

    • @johnathankrausrig9237
      @johnathankrausrig9237 2 роки тому +1

      @@TheMonk72 the reflection is required because i use attributes to Mark properties which are used in the request. I dont know how this could be achiveable by using linq expressions. How you mean it?

  • @MaximGorbatyk
    @MaximGorbatyk 3 роки тому

    Really useful video. Thank you for the demo of how the reflection works

  • @aughey
    @aughey 3 роки тому

    Dynamic assemblies at run-time are life-changing. I wrote a program that drove dynamic execution through various traditional techniques such as data-driven calls through virtual methods. Even pre-caching using Action/Func was still 15 times slower than native. Using techniques from this video, the differences between compiled vs run-time compiled are imperceivable because it's literally the same code.

    • @renauddanniau676
      @renauddanniau676 2 роки тому

      Can you elaborate ? It seems interesting to me :)

    • @aughey
      @aughey 2 роки тому +1

      @@renauddanniau676 The applicaiton was essentially unrolling a graph of logically connected nodes that perform computation. Like the visual node graphs that you'll see in applications like Unreal Engine or Houdini. Through reflection or a lot action indirection, this graph can be executed in an interpreted sort of way. Applications like simulink will generic code that is compiled, however using C# and building IL code on the fly, I can unroll the node graph at runtime and generate native-speed type-safe code without extra boxing or runtime interpretation.

    • @renauddanniau676
      @renauddanniau676 2 роки тому

      @@aughey You mean that your users generate some codes, then precompiled some assemblies and finally inject them in your application ? And thanks to the IL-emitted code you are able to run their codes in similar native performance that is ?
      I thought that you could use the activator class to get the desired performance by reading via reflection the name of the classes inside the assemblies injected. And when you have found the classes that could potentially derived from your interfaces, you just use the activator class, create the instances and cast them to your interfaces. And you should have native performance with this trick. Am I wrong ?
      Is your code visible anywhere ?

    • @aughey
      @aughey 2 роки тому

      ​@@renauddanniau676 The generic application is a node graph architecture Wikipedia(Node_graph_architecture) where nodes are precompiled static methods or method instances of an object. The "user" can manipulate the graph to create extensive interconnected networks of nodes that represent a computation. The DAG is then unrolled into a linear execution where nodes consume and generate data.
      Through reflection I can create an executor by inspecting the MethodInfo to extract parameters and ultimately call the function using MethodInfo.Invoke. Depending on the types, IDisposable overhead is incurred as well as boxing and unboxing of valuetypes and managing the storage. This is what happens through roughly minute 7 of Nick's video.
      The IL generated version of the executor still uses reflection to inspect the types, but then generates the straightline code (IL) to manage data and execute methods without runtime reflection overhead. I've benchmarked the IL generated runroll vs unrolling it by hand and they are equivalent.

    • @phizc
      @phizc 2 роки тому

      @@aughey this might be similar to something I did a couple of years ago.. I wanted to parse the data files for Daz Studio's 3d models, which are json files that are several megabytes in size. I found that the json deserializer in dotnet was too slow.. can't remember if it was dotnet's built-in or Newtonsoft's or both, but anyway I decided to write my own. It analyzes the C# classes and generates IL for the deserialization. My best version (out of 31) was more than 3 times faster. than the other methods.
      It was originally in .net framework (4.7 or so), but I managed to get it to work in dotnet core too.. Just can't export the generated assembly to a dll, so it's a little slower on first run.

  • @WarrenLeggatt
    @WarrenLeggatt 3 роки тому

    I have a love/hate with reflection. The way I see is if you are using to get to private data in an object then that is a bad code smell. If you are accessing public then lambda, expression and also something like a Haskel Lens system are better and more performant. The lenses also compose so give a great means to get/set data fast

  • @KoScosss
    @KoScosss 3 роки тому

    Thanks, never heard of that.

  • @user-tk2jy8xr8b
    @user-tk2jy8xr8b Рік тому

    Wow, used IL-generated delegates, but didn't know there was GetGetMethod+CreateDelegate approach

  • @kamyker
    @kamyker Рік тому

    Dno if this is a bug in net standard 2.0 but I had to use Func delegate to make it bind. 8:30

  • @matthewkupriyanov6504
    @matthewkupriyanov6504 2 роки тому

    Mind blowing!

  • @X39
    @X39 2 роки тому

    Did not know about that Delegate utility method.
    Would have loved to see linq expression too (instead of emit eg. As that really is dangerous territory and can be F up horribly easily)

  • @msafari1964
    @msafari1964 11 місяців тому

    🎉🎉very good 💯

  • @T___Brown
    @T___Brown 2 роки тому

    I fall into the wow'd group.

  • @js6pak
    @js6pak 3 роки тому

    you could create the instance with IL aswell

  • @berylliosis5250
    @berylliosis5250 3 роки тому +1

    I don't know .NET IL and calling conventions well; are there any specific dangers to watch out for? Presumably you can wreck the stack and mess with the wrong registers (?); anything else?
    Also, in this case, since you've already written the code, it's probably pretty safe to copy (unless the requirements for safe IL change frequentlyish).

  • @ibnfpv
    @ibnfpv 3 роки тому

    Great content!

  • @AFE-GmdG
    @AFE-GmdG 3 роки тому

    That's a WOW!

  • @shadowkras
    @shadowkras 3 роки тому

    My Voodoo skill just leveled up.

  • @JustArion
    @JustArion 3 роки тому +1

    Hey, intermediate level here and shower thought: What would be the performance impact of static vs instance? ofc Rider suggests static and my assumption would be that static is faster.
    I've looked into the IL Code and there's not much difference and BenchmarkDotNet throws "Benchmarked method `something` is static. Benchmarks MUST be instance methods, static methods are not supported.
    " As I would like to know exactly how much of a performance impact both has.

    • @nickchapsas
      @nickchapsas  3 роки тому

      Static doesn't really affect performance. You shouldn't worry about it.

  • @smikkelbeert
    @smikkelbeert 2 роки тому

    Hi Nick, you are saying that you used this on a production level. Could you explain to me some situations where you might need reflection?

  • @FromBeaverton
    @FromBeaverton 3 роки тому +3

    Thank you, amazing video! However, if you need to access private properties, you need refactoring (if it is your library) or a new vendor (if it is third-party), not reflection.
    There is a valid use-case for reflection, though - enumerating (public) properties or fields of a class and passing them to some type-specific properties or methods in your class. This is handy in serialization. Another case when it is handy is when a generic base class is getting a generic type from a derived class and needs to do something with each field of a class of template type.
    I wonder whether this delegate approach is applicable.

    • @ya4eburashka
      @ya4eburashka 3 роки тому

      Generated private stuff still need reflection to access them.

  • @TheAceInfinity
    @TheAceInfinity 2 роки тому

    You can do the same with Expressions in C#

  • @Marko-the-Beast-Master
    @Marko-the-Beast-Master 3 роки тому

    Hello, And what if I don't want to call method name from some instance but inside a specific class. Dont want to call new VeryPublicClass, just call it from the VeryPublicClass in some method.

  • @cedricsmit8794
    @cedricsmit8794 3 роки тому +1

    Would like to see a video about quartz with .net core 5 from you.

  • @bluedev6304
    @bluedev6304 2 роки тому

    I wanna access all the members with a pirticular attribute and i'm using reflection for it which take 5 sec. will this emitted version improve it? btw i want to be able to find the attribute even if it's on another assembly

  • @DamianWalczak
    @DamianWalczak 3 роки тому +1

    hey qucik question, are you sponsored by JetBrains ?

    • @nickchapsas
      @nickchapsas  3 роки тому

      I am not. If I was I would have to disclose it. That being said I am a Microsoft MVP which means I get JetBrains products for free but I was paying for them before the award and I would pay for them after the award.

    • @mosth8ed
      @mosth8ed 3 роки тому

      @@nickchapsas +100 on that. I had a student version of the Jetbrains suite for a while, I pay for the personal suite now and for the last few years. Worth every. single. penny.

  • @TankersonWoT
    @TankersonWoT 3 роки тому

    I have a question - rider constantly prompts making methods that are not dependent on object's fields static. It results in having bunch of private methods that just process some strings or values being static. Is this recommended or not? From my point of view it can cause bottleneck and issues with parallel execution when many objects refer to the same method. What is your opinion?

    • @nickchapsas
      @nickchapsas  3 роки тому +1

      It’s basically the same. You can safely make them static. This depends on what the method is doing but the use case you describe makes sense

    • @TankersonWoT
      @TankersonWoT 3 роки тому

      @@nickchapsas Thanks for the reply

    • @agsystems8220
      @agsystems8220 3 роки тому +4

      In a sense all methods are static, with instance methods being shorthand for methods defined in a type where the first argument is of that type. Extension methods do this explicitly. When the compiler sees ObjectInstance.Method(argument) it first treats it like Object'sType.Method(ObjectInstance, argument). By making it an instance method you are effectively just giving it a redundant argument.
      As for why it doesn't cause problems, methods are immutable (and this is a also good lesson on why immutability is awesome for parallel programming). That means that the method doesn't need to be synced up. You can have as many copies of a method as you like without caring, and as many readers as you like. Instances of objects don't actually carry the methods around with them, instead just a reference to their type, which is where all the methods live (usually, exceptions apply). The only implementation difference between static and instance methods is whether it takes an instance as an argument or not.
      When you see that static variables live in the type, while instance variables live in the object, it is natural to assume that static methods live in the type, while instance methods live in the object. Natural, but wrong. They actually both live in the type.

  • @yehudamakarov
    @yehudamakarov 3 роки тому

    Maybe soon you’ll burn out from c# and do some higher level stuff? Or maybe some rust or go? 😁

  • @oldfish3059
    @oldfish3059 2 роки тому

    We hope to increase Alipay by payment

  • @neralem
    @neralem 3 роки тому

    I know this makes no difference but you actually get the VeryPrivateProperty in your CachedInternalProperty. Copy paste failure.

    • @nickchapsas
      @nickchapsas  3 роки тому

      That's what I wanted to get. Internal refers to the class on this one not the property. Should have given a more descriptive name tho

    • @neralem
      @neralem 3 роки тому

      @@nickchapsas Oh of course...excuse my brainfart. Haven't slept very well^^

  • @mathboy8188
    @mathboy8188 2 роки тому

    His Benchmark video: "Benchmarking C# code using BenchmarkDotNet" ua-cam.com/video/EWmufbVF2A4/v-deo.html