Mad about .NET A blog from Jose Fco Bonnin


"Types may declare locations that are associated with the type rather than any particular value of the type. Such locations are static fields of the type. As such, static fields declare a location that is shared by all values of the type. Just like non-static (instance) fields, a static field is typed and that type never changes. Static fields are always restricted to a single application domain basis, but they may also be allocated on a per-thread basis."

The paragraph above is the definition of static field extracted from the CLI specification. The text means that when we create a static field "f" inside a class "C", the value will not belong to any of the instances we create of C, instead its value is shared across all the instances and therefore "belongs" to the type C itself.

I've created a very simple class with one static and two instance fields, which you can see below.

   1: public class BusinessLogic
   2: {
   3:     public int instanceField1;
   4:     public int instanceField2;
   5:  
   6:     public static int staticField3 = 3;
   7:  
   8:     public BusinessLogic()
   9:     {
  10:         instanceField1 = 1;
  11:         instanceField2 = 2;
  12:     }
  13: }

I've run the code and I've created 5 instances of the class above, which we will check with WinDBG. To do it we can execute the next command:

!DumpHeap -type BusinessLogic

That will show something similar to the next:

Address               MT     Size
01dfa98c      006668c4      16    
01dfe898     006668c4       16    
01e027a4     006668c4       16    
01e0668c     006668c4       16    
01e13104     006668c4       16    
total 5 objects
Statistics:
      MT    Count    TotalSize   Class Name
006668c4        5           80        BusinessLogic
Total 5 objects

Once we have the address of the 5 instances we created we can dump the objects one by one by using the command !DumpObj [Address]. If we take the first one it will display something similar to:

Name: BusinessLogic
MethodTable: 001c68bc
EEClass: 00281abc
Size: 16(0x10) bytes
Fields:
          MT      Field   Offset   Type                VT   Attr         Value    Name
6f642b38  4000001        4      System.Int32     1    instance  1           instanceField1
6f642b38  4000002        8      System.Int32     1    instance  2           instanceField2
6f642b38  4000003       1c     System.Int32     1    static       3           staticField3

What we have done with this command is to examine the fields of one of the instances in memory of BusinessLogic. We can see that the object has the fields instanceField1, instanceField2 and staticField3 and its values are 1,2 and 3 respectively. So, all is as expected.

Lets focus now on the cool thing of static fields. We can see how "staticField3" is shared across al the instances we create of BusinessLogic by examing the EEClass of the objects. The 5 instances we created before have the same EEClass: "00281abc". If we examine it with the command !DumpClass 00281abc we will see that "staticField3" is present at EEClass level and its value is already initialized.

Class Name: BusinessLogic
mdToken: 02000002
Parent Class: 6f3d3ef0
Module: 001c64f0
Method Table: 001c68bc
Vtable Slots: 4
Total Method Slots: 6
Class Attributes: 100001 
NumInstanceFields: 2
NumStaticFields: 1
      MT          Field  Offset   Type                 VT    Attr         Value   Name
6f642b38    4000001        4      System.Int32   1     instance              instanceField1
6f642b38    4000002        8      System.Int32   1     instance              instanceField2
6f642b38  4000003      1c    System.Int32   1     static           3     staticField3

At this point we have demonstrated how the static fields and their values are shared across all the instances we create of a class.

In this sample we have used value types for the static field, but this also applies for reference types. This is very cool, because it allow us creating class designs where objects with a heavy load construction can be instantiated just once  i.e.

static object staticField;
....
if (staticField == null)
       staticField = new object();

This simple code will allow sharing the same instance of staticField across the entire application domain helping with the performance if staticField is hard to construct.

This does not mean that from now you must create all your "hard to construct" fields as static, because like always the gold hammer does not exist and static fields have a downside.

As we have seen the static field values are related to the EEClass, which are allocated on the loader heaps that are AppDomain specific, so this means they will be in memory until the AppDomain is unloaded.

Some time ago I read the .NET 3.5 Service Pack 1 would bring some nice improvements in the JIT compiler for the x86. Among them there were some modifications regarding how the JIT inlines methods (see my previous post about inlining).

Today I've seen this post from Vance Morrison where describes the heuristic used to determine when a candidate method must be inlined or not. Which according to him looks like follows:

"1. Estimate the size of the call site if the method were not inlined.

2. Estimate the size of the call site if it were inlined (this is an estimate based on the IL, we employ a simple state machine (Markov Model), created using lots of real data to form this estimator logic)

3. Compute a multiplier. By default it is 1

4. Increase the multiplier if the code is in a loop (the current heuristic bumps it to 5 in a loop)

5. Increase the multiplier if it looks like struct optimizations will kick in.

6. If InlineSize <= NonInlineSize * Multiplier do the inlining."

I invite you to read the entire post, I've found it very interesting.

One of the features we have in .NET is the ability to execute IL code generated dynamically, in fact there is a complete namespace to generate code at runtime called System.Reflection.Emit

Prior to .NET Framework 2.0 if we wanted to execute the IL code generated we needed to deal with a dynamic assembly making use of classes like the AssemblyBuilder, ModuleBuilder, etc. together with the IL code we can also emit symbolic information making possible to debug the dynamic code even within Visual Studio.

The main downside of this way of generating the IL code is (by design) that the generated code can not be garbage collected and therefore the memory is not released until the AppDomain is unload. This created some controversy about if this was a memory leak or not, which in any case is avoidable by loading everything in a separated AppDomain that can be unloaded when it's not used anymore.

With .NET Framework 2.0 we got the LCG (Lightweight Code Generation), which allows generating code at run time, without having to define the dynamic assembly nor type to contain the methods we create. In addition, the IL and the data structures related to the code generation are allocated on the managed, meaning that they can be garbage collected when there are no more references to the DynamicMethod class that is the main class of LCG.

Below you can see some simple code that dynamically emits the IL for a method "Talk" that concatenates two strings.

   1: class Demo
   2: {
   3:     public void RunDynamicCode()
   4:     {
   5:         MethodInfo concatMethodInfo = typeof(string).GetMethod("Concat", 
   6:             new Type[] { typeof(string), typeof(string) });
   7:  
   8:         DynamicMethod dm = new DynamicMethod("Talk", typeof(string), 
   9:             new Type[] { typeof(string) }, this.GetType(), true);
  10:  
  11:         ILGenerator generator = dm.GetILGenerator();
  12:         generator.Emit(OpCodes.Ldstr, "Hello ");
  13:         generator.Emit(OpCodes.Ldarg_0);
  14:         generator.EmitCall(OpCodes.Call, concatMethodInfo, null);
  15:         generator.Emit(OpCodes.Ret);
  16:  
  17:         Func<string,string> talkDelegate =
  18:             (Func<string, string>)dm.CreateDelegate(typeof(Func<string, string>));
  19:  
  20:         string result = talkDelegate("Jose Bonnin");
  21:  
  22:         Console.Write(result);
  23:         Console.ReadKey();
  24:     }
  25: }

Line 8 instantiates the DynamicMethod and specifies the name, the return type, an array with the parameters received and the type owner. The C# signature would be similar to "string Talk(string);"

Lines from 11 to 15 is where we write the IL code making use of the ILGenerator class. Note that in the line 16 we are emitting a call to the MethodInfo for the method String.Concat we obtained in the line 5.

Finally, in line 17 we do the coolest, we obtain a delegate for the method we have just generated that is callable from our C# code.

The main problem we experience with DynamicMethod is that we do not have the ability to generate debugging info for LCG, since the debugging API is based on metadata that the LCG does not have. In any case, not all is lost since we can continue debugging with WinDBG.

If we attach WinDBG to the code above we can effectively check that we have created a dynamic method and see the IL generated. To do it we just need to obtain a pointer to the DynamicMethod which can be obtained in different ways, an easy one for demo purposes is to the do a !DumpHeap -stat to obtain a list of the different objects grouped by type, then we just need to locate in the list the MT address of the type System.Reflection.Emit.DynamicMethod and run the command !DumpHeap -mt [address] over it. Which will show something similar to this:

0:003> !DumpHeap -mt 6fbf026c
Address      MT         Size
01d0e028   6fbf026c    56
total 1 objects
Statistics:
MT         Count   TotalSize    Class Name
6fbf041c        1            56     System.Reflection.Emit.DynamicMethod
Total 1 objects

Now that we have obtained the address (01d0e028) we can run the command DumpIL to see the dynamic IL generated.

0:003> !DumpIL 01d0e028
This is dynamic IL. Exception info is not reported at this time.
If a token is unresolved, run "!do " on the addr given
in parenthesis. You can also look at the token table yourself, by
running "!DumpArray 01d0e62c".

IL_0000: ldstr 70000002 "Hello "
IL_0005: ldarg.0
IL_0006: call a000003 (01d0e560)
IL_000b: ret

In this post we have seen how we do not need to look to "new" technologies like WPF, Silverlight or WCF to find cool things within .NET

Method inlining is an optimization technique that consists of putting the body of a method inside the body of all the caller methods and removing the original method. Let's suppose the next sample:

   1: class Demo
   2: {
   3:     public void Talk()
   4:     {
   5:         SayHello();
   6:  
   7:         WaitUserAction();
   8:     }
   9:  
  10:     private void SayHello()
  11:     {
  12:         Console.WriteLine("Hello!");
  13:     }
  14:  
  15:     ...
  16: }

To inline the method SayHello, we should refactor our code in the next way:

   1: class Demo
   2: {
   3:     public void Talk()
   4:     {
   5:         Console.WriteLine("Hello!");
   6:  
   7:         WaitUserAction();
   8:     }
   9:     ...
  10: }

As you can appreciate we have introduced "Console.WriteLine("Hello!");" directly inside the method Talk.

This technique provides some performance benefits by reducing the overhead associated to a new method call. There are many characteristics that make a method a good candidate to be inlined like: reduced size and complexity, methods frequently used, etc. Fortunately this is not something we should think ourselves, most compilers include this optimization when compile our code.

.NET Framework is not an exception and although it does not have an explicit "inline" clause to suggest its use, as you can find in C++, the JIT (Just in Time) Compiler performs inline optimizations when it considers that can help to improve the performance.

If we debug the first version of the code we can appreciate how the method "Talk" and "SayHello" are inlined. The code must be compiled in Release mode and run out of Visual Studio in order the jitter applies the optimizations, so a possible way you can see it is debugging with WinDBG. To do it just run the code and attach the executable, load the Son of Strike extension and run the command !EEStack -EE this will display something similar to:

0015ee28 793e8e33 (MethodDesc 0x79259c00 +0x7  System.Console.ReadKey())
0015ee2c 009100ea (MethodDesc 0x3030a0   +0x32  Demo.WaitUserAction())
0015ee3c 009100a7 (MethodDesc 0x303028   +0x37  Program.Main(System.String[]))

As we see the stack does not display the methods "Talk" and "SayHello" and if we run the command !DumpMT -MD [address of method table] we will see how SOS does not reflect the methods as jitted even if we know the code has been executed. This is because, after inline those methods, its body has been expanded within the Main method but they do "not exist" at all.

79371278   7914b928   PreJIT System.Object.ToString()
7936b3b0   7914b930   PreJIT System.Object.Equals(System.Object)
7936b3d0   7914b948   PreJIT System.Object.GetHashCode()
793624d0   7914b950   PreJIT System.Object.Finalize()
0030c035   00303090   NONE Demo.Talk()
0030c039   00303098   NONE Demo.SayHello()

009100b8   003030a0   JIT Demo.WaitUserAction()
0030c041   003030a8   NONE Demo..ctor()

Just for fun we can change the code and force the jitter to do not inline our methods. This is accomplished adding the attribute [MethodImpl(MethodImplOptions.NoInlining)] on top of the method definition for "Talk" and "SayHello". If we recompile and debug with WinDBG again we will see the differences.

Now the stack shows the calls to the methods "Talk" and "SayHello" and both methods appear as jitted.

0021ed10 793e8e33 (MethodDesc 0x79259c00 +0x7 System.Console.ReadKey())
0021ed14 009a0132 (MethodDesc 0x8830a0 +0x32 Demo.WaitUserAction())
0021ed24 009a00ed (MethodDesc 0x883098 +0x2d Demo.SayHello())
0021ed28 009a00a6 (MethodDesc 0x883090 +0x6 Demo.Talk())

0021ed2c 009a0084 (MethodDesc 0x883028 +0x14 Program.Main(System.String[]))

79371278 7914b928 PreJIT System.Object.ToString()
7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)
7936b3d0 7914b948 PreJIT System.Object.GetHashCode()
793624d0 7914b950 PreJIT System.Object.Finalize()
009a00a0 00883090 JIT Demo.Talk()
009a00c0 00883098 JIT Demo.SayHello()
009a0100 008830a0 JIT Demo.WaitUserAction()
0088c041 008830a8 NONE Demo..ctor()

You can go one step further checking not only the stacks, but also how the code produced contains the inline instructions. If we use the command !u within WindDBG for each method "Main", "Talk" and "SayHello" when they are not inlined we obtain something similar to this:

Program.Main(System.String[])
Begin 009a0070, size 15
009a0070 b9b0308800 mov ecx,8830B0h (MT: Demo)
009a0075 e8a21f77ff call 0011201c (JitHelp: CORINFO_HELP_NEWSFAST)
009a007a 8bc8 mov ecx,eax
009a007c 3909 cmp dword ptr [ecx],ecx
009a007e ff15e8308800 call dword ptr ds:[8830E8h] (Demo.Talk(), mdToken: 06000003)
009a0084 c3 ret

Demo.Talk()
Begin 009a00a0, size 7
009a00a0 ff15ec308800 call dword ptr ds:[8830ECh] (Demo.SayHello(), mdToken: 06000004)
009a00a6 c3 ret

Demo.SayHello()
Begin 002500c0, size 2e
002500c0 833d8c10f20200 cmp dword ptr ds:[2F2108Ch],0
002500c7 750a jne 002500d3
002500c9 b901000000 mov ecx,1
002500ce e891581179 call mscorlib_ni+0x2a5964 (79365964) (System.Console.InitializeStdOutError(Boolean), mdToken: 06000770)
002500d3 8b0d8c10f202 mov ecx,dword ptr ds:[2F2108Ch] (Object: System.IO.TextWriter+SyncTextWriter)
002500d9 8b153c30f202 mov edx,dword ptr ds:[2F2303Ch] ("Hello!")
002500df 8b01 mov eax,dword ptr [ecx]
002500e1 ff90d8000000 call dword ptr [eax+0D8h]
002500e7 ff15f0301b00 call dword ptr ds:[1B30F0h] (Demo.WaitUserAction(), mdToken: 06000005)
002500ed c3 ret

When the methods are inlined we cannot run anymore the command !u for them, but if we do it for the Main method we can see without being an expert on native code how the code contains now the SayHello instructions.

Program.Main(System.String[])
Begin 00970070, size 38
00970070 b9b0303a00 mov ecx,3A30B0h (MT: Demo)
00970075 e8a21fa2ff call 0039201c (JitHelp: CORINFO_HELP_NEWSFAST)
0097007a 833d8c10c80200 cmp dword ptr ds:[2C8108Ch],0
00970081 750a jne 0097008d
00970083 b901000000 mov ecx,1
00970088 e8d7589f78 call mscorlib_ni+0x2a5964 (79365964) (System. Console.InitializeStdOutError(Boolean), mdToken: 06000770)
0097008d 8b0d8c10c802 mov ecx,dword ptr ds:[2C8108Ch] (Object: System. IO.TextWriter+SyncTextWriter)
00970093 8b153c30c802 mov edx,dword ptr ds:[2C8303Ch] ("Hello!")
00970099 8b01 mov eax,dword ptr [ecx]
0097009b ff90d8000000 call dword ptr [eax+0D8h]
009700a1 ff15f0303a00 call dword ptr ds:[3A30F0h] (Demo.WaitUserAction(), mdToken: 06000005)
009700a7 c3 ret

As you can see there are many things to explore and get fun with WinDBG.

In the last TTT we had a session to discuss about several things regarding the .NET User Groups and one of the subjects was the MSDN forums.

Microsoft told us that they were investing some efforts to improve the Spanish MSDN forums and immediately after that we talked about the local forums each .NUG has in their sites. The idea proposed was to remove the local forums and to support the MSDN forums, this would benefit the developer community since instead of having microsites we can sum our efforts to have a centralized help site.

In Baleares on .NET we thought that the best was to have our own forums in order to provide better assistance to our community, we wanted to be sure that the .NET developers of Baleares had answers to their questions. Because of that some "volunteers" are monitoring the forums to provide solutions, but as the community grows we can give less support and the idea to post the questions to bigger communities does not seem to be a bad idea.

I must say that I'm not 100% convinced yet that this is best solution for our community, since I trust the "closeness" concept we tried to offer and that had good results until now.

Anyway I've decided to give a try and to introduce progressively some of the MSDN forums in order our users know them. I wanted not only to insert a simple link to the MSDN forums, but to display some of the posts in our website in line with the current appearance. 

To do it, we can save lot of time taking profit of the Community Server platform to add the latest posts in our home page in the same way the Recent Post List appears. If we take a look about how CS displays the recent posts, we can see that it uses the control IndexPostList, which basically consists of display the results of a search done when the page inits. Below you can see the code the guys from CS use:

   1: List<IndexPost> recentPosts = CSCache.Get("HomePageSearch-" + CurrentCSContext.User.RoleKey) as List<IndexPost>;
   2: if (recentPosts == null)
   3: {
   4:     SearchQuery query = new SearchQuery();
   5:     query.StartDate = DateTime.Now.AddDays(-10);
   6:     query.EndDate = DateTime.Now.AddDays(1);
   7:     query.PageSize = 5;
   8:  
   9:     recentPosts = CSSearch.Search(query).Posts;
  10:     CSCache.Insert("HomePageSearch-" + CurrentCSContext.User.RoleKey, recentPosts, CSCache.MinuteFactor * 5);
  11: }
  12: RecentPostList.DataSource = recentPosts;

It doesn't seem too complicated, basically we need to introduce a new IndexPostList control in the main page and set the DataSource property with our own List of IndexPost. All the MSDN ES forums allow to syndicate the content, this means that we can access it via RSS. The easiest way to do this with .NET is to use a XmlTextReader and read the contents inside a DataSet.

As you can see in the code below we also make use of the XslCompiledTransform, this is done because the feeds contain two comments elements, even if they have different namespaces the ReadXml method of the dataset will throw an exception, since it will consider both of them as the same column. I got the idea to use to Transform from the next post, where the author got into the same troubles when loading two "comments" elements from the feed.

   1: XslCompiledTransform rssTransform = new XslCompiledTransform();
   2: rssTransform.Load(xsltPath);
   3:  
   4:  
   5: using (XmlTextReader xmlTextReader = new XmlTextReader(feeds[i].Url))
   6: {
   7:     using (StringWriter stringWriter = new StringWriter())
   8:     {
   9:         XPathDocument rssXPath = new System.Xml.XPath.XPathDocument(xmlTextReader);
  10:         rssTransform.Transform(rssXPath, null, stringWriter);
  11:  
  12:         DataSet ds = new DataSet();
  13:         using (System.IO.StringReader reader = new System.IO.StringReader(stringWriter.ToString()))
  14:         {
  15:             ds.ReadXml(reader);
  16:         }
  17:  
  18:         DataTable dt = ds.Tables["item"];
  19:         int maxItems = feeds[i].MaxItems;
  20:         DateTime maxDate = DateTime.Now.AddDays(feeds[i].MaxDays * -1);
  21:         for (int idx = 0; idx < maxItems && idx < dt.Rows.Count; idx++)
  22:         {
  23:             DateTime postDate = DateTime.Parse((string)dt.Rows[idx]["date"]);
  24:  
  25:             if (postDate >= maxDate)
  26:             {
  27:                 IndexPost post = new IndexPost();
  28:                 post.ApplicationKey = feeds[i].Name;
  29:                 post.ApplicationType = ApplicationType.Forum;
  30:                 post.ApplicationUrl = feeds[i].Url;
  31:                 post.FormattedBody = (string)dt.Rows[idx]["description"];
  32:                 post.PostDate = postDate;
  33:                 post.SectionName = feeds[i].Name;
  34:                 post.Title = (string)dt.Rows[idx]["title"];
  35:                 post.Url = (string)dt.Rows[idx]["link"];
  36:                 post.UserName = (string)dt.Rows[idx]["creator"];
  37:  
  38:                 items.Add(post);
  39:             }
  40:         }
  41:  
  42:     }
  43: }

From lines 27 to 36 we initialize the class IndexPost with the values obtained from the feed, this values are then used by the IndexPostList control and allow some interaction with them like providing links to the post and forum URLs.

Aside loading the XML you will see that I added some extra features to be able to configure a few parameters regarding the posts we will display. This is done via a custom section I added to the web.config that is handled by the classes SectionHandler, FeedConfigurationCollection and FeedConfiguration. These classes will allow us configuring the feeds from which we will get the posts, the maximum number of posts to display per feed and the oldness of them. You can download the source code to see the full implementation. Do not forget to place the new assembly in the bin folder of CS and to set the configuration in the web.config.

Now that we can configure the feeds and to obtain the desired posts we need to modify the page home.aspx for each of the themes supported on our CS. First of all is to add our IndexPostList control with the information to show.

   1: <CSControl:IndexPostList runat="server" ShowHeaderFooterOnNone="false" ID="RecentMsdnPostList">
   2:     <HeaderTemplate>
   3:         <p />
   4:         <h2 class="CommonTitle"><CSControl:ResourceControl ResourceName="default_homepage_msdnrecentposts" runat="server" /></h2>
   5:         <div class="CommonContent">
   6:         <ul class="CommonSearchResultList">
   7:     </HeaderTemplate>
   8:     <ItemTemplate>
   9:         <li>
  10:         <CSControl:IndexPostData runat="server" Property="ApplicationType" Text="&lt;div class=&quot;CommonSearchResultArea Msdn&quot;&gt;" />
  11:             <CSControl:IndexPostData runat="server" Property="Title" LinkTo="Post" Tag="h4" CssClass="CommonSearchResultName" />
  12:             <div class="CommonSearchResult">
  13:                 <CSControl:IndexPostData Property="FormattedBody" runat="server" TruncateAt="350" />
  14:             </div>
  15:             <div class="CommonSearchResultDetails">
  16:                 <CSControl:ResourceControl runat="server" ResourceName="SearchResults_PostTo" />
  17:                 <CSControl:IndexPostData Property="SectionName" LinkTo="Section" runat="server" />
  18:                 <CSControl:IndexPostData Property="ApplicationType" LinkTo="Application" Text="({0})" runat="server" />
  19:                 <CSControl:ResourceControl runat="server" ResourceName="SearchResults_By" />
  20:                 <CSControl:IndexPostData Property="UserName" LinkTo="Nothing" runat="server" />
  21:                 
  22:                 <CSControl:IndexPostData Property="PostDate" runat="server" />
  23:             </div>
  24:         </div>
  25:         </li>
  26:     </ItemTemplate>
  27:     <FooterTemplate>
  28:         </ul>
  29:         </div>
  30:     </FooterTemplate>
  31:     <NoneTemplate></NoneTemplate>
  32: </CSControl:IndexPostList>

The main things to have in consideration is that we added the a new resource default_homepage_msdnrecentposts (file resources.xml of each language) to support several languages and a new css style CommonSearchResultArea.Msdn (file common.css of each theme), with this style we will modify the background image that appears left to the post.

The next step is to load the feeds and to bind the list of posts retrieved to the control, this is done in the OnInit event of the home page.

   1: List<IndexPost> msdnPosts = CSCache.Get("HomePageMsdn-" + CurrentCSContext.User.RoleKey) as List<IndexPost>;
   2: if (msdnPosts == null)
   3: {
   4:     BalearesOnNet.Rss.FeedHelper helper = new BalearesOnNet.Rss.FeedHelper();
   5:     msdnPosts = helper.LoadFeeds(Request.PhysicalApplicationPath + "rssToDataSet.xslt");
   6:     if (msdnPosts.Count > 0)
   7:         CSCache.Insert("HomePageMsdn-" + CurrentCSContext.User.RoleKey, msdnPosts, CSCache.HourFactor);
   8: }
   9: RecentMsdnPostList.DataSource = msdnPosts;

As you can see we also add the retrieved posts to the cache to avoid loading them from MSDN forums each time a user connects to the page, you can play with several factors to find which one fits better with your site.

Below you can see a screenshot of how the posts are displayed or even better just go to Baleares on .NET

 

That's all I hope you liked it.

BalearesOnNetRss.zip (6.73 kb)