{"id":4239,"date":"2012-08-20T19:51:13","date_gmt":"2012-08-20T19:51:13","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/inside-the-dlr-invoking-methods\/"},"modified":"2016-07-28T10:51:38","modified_gmt":"2016-07-28T10:51:38","slug":"inside-the-dlr-invoking-methods","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/inside-the-dlr-invoking-methods\/","title":{"rendered":"Inside the DLR &#8211; Invoking methods"},"content":{"rendered":"<p>So, we&#8217;ve looked at how a dynamic call is represented in a compiled assembly, and how the dynamic lookup is performed at runtime. The last piece of the puzzle is how the resolved method gets invoked, and that is the subject of this post.<\/p>\n<h4>Invoking methods<\/h4>\n<p>As discussed in my previous posts, doing a full lookup and bind at runtime each and every single time the callsite gets invoked would be far too slow to be usable. The results obtained from the callsite binder must to be cached, along with a series of conditions to determine whether the cached result can be reused.<\/p>\n<p>So, firstly, how are the conditions represented? These conditions can be <em>anything<\/em>; they are determined entirely by the semantics of the language the binder is representing. The binder has to be able to return arbitary code that is then executed to determine whether the conditions apply or not.<\/p>\n<p>Fortunately, .NET 4 has a neat way of representing arbitary code that can be easily combined with other code &#8211; expression trees. All the callsite binder has to return is an expression (called a &#8216;restriction&#8217;) that evaluates to a boolean, returning true when the restriction passes (indicating the corresponding method invocation can be used) and false when it does&#8217;t. If the bind result is also represented in an expression tree, these can be combined easily like so:<\/p>\n<pre>if ([restriction is true]) {\n    [invoke cached method]\n}<\/pre>\n<p>Take my example from my previous post:<\/p>\n<pre>public class ClassA\n{\n    public static void TestDynamic()\n    {\n        CallDynamic(new ClassA(), 10);\n        CallDynamic(new ClassA(), \"foo\");\n    }\n\n    public static void CallDynamic(dynamic d, object o)\n    {\n        d.Method(o);\n    }\n\n    public void Method(int i) {}\n    public void Method(string s) {}\n}<\/pre>\n<p>When the <code>Method(int)<\/code> method is first bound, along with an expression representing the result of the bind lookup, the C# binder will return the restrictions under which that bind can be reused. In this case, it can be reused if the types of the parameters are the same:<\/p>\n<pre>if (thisArg.GetType() == typeof(ClassA) &amp;&amp; arg1.GetType() == typeof(int)) {\n    thisClassA.Method(i);\n}<\/pre>\n<h4>Caching callsite results<\/h4>\n<p>So, now, it&#8217;s up to the callsite to link these expressions returned from the binder together in such a way that it can determine which one from the many it has cached it should use. This caching logic is all located in the <code>System.Dynamic.UpdateDelegates<\/code> class. It&#8217;ll help if you&#8217;ve got this type open in a decompiler to have a look yourself.<\/p>\n<p>For each callsite, there are 3 layers of caching involved:<\/p>\n<ol>\n<li>The last method invoked on the callsite.<\/li>\n<li>All methods that have ever been invoked on the callsite.<\/li>\n<li>All methods that have ever been invoked on any callsite of the same type.<\/li>\n<\/ol>\n<p>We&#8217;ll cover each of these layers in order<\/p>\n<h4>Level 1 cache: the last method called on the callsite<\/h4>\n<p>When a <code>CallSite&lt;T&gt;<\/code> object is first instantiated, the <code>Target<\/code> delegate field (containing the delegate that is called when the callsite is invoked) is set to one of the <code>UpdateAndExecute<\/code> generic methods in <code>UpdateDelegates<\/code>, corresponding to the number of parameters to the callsite, and the existance of any return value.<\/p>\n<p>These methods contain most of the caching, invoke, and binding logic for the callsite. The first time this method is invoked, the <code>UpdateAndExecute<\/code> method finds there aren&#8217;t any entries in the caches to reuse, and invokes the binder to resolve a new method.<\/p>\n<p>Once the callsite has the result from the binder, along with any restrictions, it stitches some extra expressions in, and replaces the <code>Target<\/code> field in the callsite with a compiled expression tree similar to this (in this example I&#8217;m assuming there&#8217;s no return value):<\/p>\n<pre>if ([restriction is true]) {\n    [invoke cached method]\n    return;\n}\n\nif (callSite._match) {\n    _match = false;\n    return;\n}\nelse {\n    UpdateAndExecute(callSite, arg0, arg1, ...);\n}<\/pre>\n<p>Woah. What&#8217;s going on here? Well, this resulting expression tree is actually the first level of caching. The <code>Target<\/code> field in the callsite, which contains the delegate to call when the callsite is invoked, is set to the above code compiled from the expression tree into IL, and then into native code by the JIT. This code checks whether the restrictions of the last method that was invoked on the callsite (the &#8216;primary&#8217; method) match, and if so, executes that method straight away.<\/p>\n<p>This means that, the next time the callsite is invoked, the first code that executes is the restriction check, executing as native code! This makes this restriction check on the primary cached delegate <em>very<\/em> fast.<\/p>\n<p>But what if the restrictions don&#8217;t match? In that case, the second part of the stitched expression tree is executed. What this section <em>should<\/em> be doing is calling back into the <code>UpdateAndExecute<\/code> method again to resolve a new method. But it&#8217;s slightly more complicated than that. To understand why, we need to understand the second and third level caches.<\/p>\n<h4>Level 2 cache: all methods that have ever been invoked on the callsite<\/h4>\n<p>When a binder has returned the result of a lookup, as well as updating the <code>Target<\/code> field with a compiled expression tree, stitched together as above, the callsite puts the same compiled expression tree in an internal list of delegates, called the rules list. This list acts as the level 2 cache.<\/p>\n<p>Why use the same delegate? Stitching together expression trees is an expensive operation. You don&#8217;t want to do it every time the callsite is invoked. Ideally, you would create one expression tree from the binder&#8217;s result, compile it, and then use the resulting delegate everywhere in the callsite.<\/p>\n<p>But, if the same delegate is used to invoke the callsite in the first place, and in the caches, that means each delegate needs two modes of operation. An &#8216;invoke&#8217; mode, for when the delegate is set as the value of the <code>Target<\/code> field, and a &#8216;match&#8217; mode, used when <code>UpdateAndExecute<\/code> is searching for a method in the callsite&#8217;s cache. Only in the invoke mode would the delegate call back into <code>UpdateAndExecute<\/code>. In match mode, it would simply return without doing anything.<\/p>\n<p>This mode is controlled by the <code>_match<\/code> field in <code>CallSite&lt;T&gt;<\/code>. The first time the callsite is invoked, <code>_match<\/code> is false, and so the <code>Target<\/code> delegate is called in invoke mode. Then, if the initial restriction check fails, the <code>Target<\/code> delegate calls back into <code>UpdateAndExecute<\/code>. This method sets <code>_match<\/code> to true, then calls all the cached delegates in the rules list in match mode to try and find one that passes its restrictions, and invokes it.<\/p>\n<p>However, there needs to be some way for each cached delegate to inform <code>UpdateAndExecute<\/code> whether it passed its restrictions or not. To do this, as you can see above, it simply re-uses <code>_match<\/code>, and sets it to false if it did not pass the restrictions. This allows the code within each <code>UpdateAndExecute<\/code> method to check for cache matches like so:<\/p>\n<pre>foreach (T cachedDelegate in Rules) {\n    callSite._match = true;\n    cachedDelegate();   \/\/ sets _match to false if restrictions do not pass\n    if (callSite._match) {\n        \/\/ passed restrictions, and the cached method was invoked\n        \/\/ set this delegate as the primary target to invoke next time\n        callSite.Target = cachedDelegate;\n        return;\n    }\n    \/\/ no luck, try the next one...\n}<\/pre>\n<h4>Level 3 cache: all methods that have ever been invoked on any callsite with the same signature<\/h4>\n<p>The reason for this cache should be clear &#8211; if a method has been invoked through a callsite in one place, then it is likely to be invoked on other callsites in the codebase with the same signature.<\/p>\n<p>Rather than living in the callsite, the &#8216;global&#8217; cache for callsite delegates lives in the <code>CallSiteBinder<\/code> class, in the <code>Cache<\/code> field. This is a dictionary, typed on the callsite delegate signature, providing a <code>RuleCache&lt;T&gt;<\/code> instance for each delegate signature. This is accessed in the same way as the level 2 callsite cache, by the <code>UpdateAndExecute<\/code> methods. When a method is matched in the global cache, it is copied into the callsite and Target cache before being executed.<\/p>\n<h4>Putting it all together<\/h4>\n<p>So, how does this all fit together? Like so (I&#8217;ve omitted some implementation &amp; performance details):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2196\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2012\/08\/DLR-3a2.png\" width=\"501\" height=\"462\" alt=\"DLR-3a2.png\" \/><\/p>\n<p>That, in essence, is how the DLR performs its dynamic calls nearly as fast as statically compiled IL code. Extensive use of expression trees, compiled to IL and then into native code. Multiple levels of caching, the first of which executes immediately when the dynamic callsite is invoked. And a clever re-use of compiled expression trees that can be used in completely different contexts without being recompiled. All in all, a very fast and very clever reflection caching mechanism.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So, we&#8217;ve looked at how a dynamic call is represented in a compiled assembly, and how the dynamic lookup is performed at runtime. The last piece of the puzzle is how the resolved method gets invoked, and that is the subject of this post. Invoking methods As discussed in my previous posts, doing a full&#8230;&hellip;<\/p>\n","protected":false},"author":186659,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[],"coauthors":[],"class_list":["post-4239","post","type-post","status-publish","format-standard","hentry","category-blogs"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/4239","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/186659"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=4239"}],"version-history":[{"count":14,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/4239\/revisions"}],"predecessor-version":[{"id":42213,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/4239\/revisions\/42213"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=4239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=4239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=4239"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=4239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}