VB.NET... what are you doing?

by Moridin8 3. December 2009 13:45

Just a small post after a long time not posting (been moving between countries… it kinda takes up your spare time you know?)

 

Recently I have been working on a mostly VB.NET project in a more hands-on development capacity than I have been used to in recent years.  Needs must when you have a family to feed - especially in the current economy.

 

Whilst C# is by far my preferred language to work with (hence the name of this site) with Java and C++/CLI closely following, despite my general complaints, I have been using and retaining familiarity with VB.NET over the years as it’s always a good idea to ‘know your enemy’ so to speak.

 

There are many reasons I don’t like VB/BASIC as a language, but at least you would hope that no matter what, the VB.NET compiler team would at least do some basic optimisation on certain operations.  But it would seem that forcing integer division operations needlessly through the FPU is par for course…

 

This is a small peeve of mine… especially as it’s such an obvious and easy optimization.

 

In VB.NET, ‘CType’ is the quickest cast/conversion operation from a double to an Int32 in VB.NET the follow expression compiles to this (yes, all optimisations are ON):  [NB: ! prefix = unrequired)

 

! ldc.r8    100 // r8 = double ==> FPU (should be ldc.i4.s)

  ldarg.1

  callvirt  (blah..).get_Count()

! conv.r8   // r8 = double ==> FPU

  div

! call float64 (blah..).Math::Round(float64) // ==> JIT to FPU instructions

! conv.i4   // this will also be a couple of FPU instructions as return value is r8

  stloc.1

 

C# does *exactly* the same thing thus:

 

ldc.i4.s 100

ldarg.1

callvirt (blah..).get_Count()

div

stloc.1

 

Understandably the VB.NET version is ~2.2 times slower mostly due to needlessly forced FPU operations.

 

Funny, you would have thought this:

 

  DirectCast(100/distroItems.Count,Int32)

would be like:   (int)(100/distroItems.Count)

 

But the directcast operation is illegal...

 

Tags: , , ,

Articles | Blog

Generic 'new T()' Performance Issue and a Solution

by Moridin8 11. June 2009 00:00

Generics is one of the best things to ever feature in the CLR.  The advantages of Generics feature are myriad and to be quite honest, I couldn’t imagine life without it.  It is efficient, fast and flexible with few exceptions.  However, one of those exceptions is in the area of Dynamic type instantiation:

 T tmp = new T();

This instruction is syntax sugar for the following:

 T tmp = Activator.CreateInstance<T>();

The reason Activator is used, is because the CLR can not instantiate an unknown type (the CLR uses the CIL instruction ‘NewObj’ for this task.)

The problem with Activator.CreateInstance, is it is so VERY slow.  Around 95 TIMES slower than a simple ‘new’.  

That’s quite a figure!  And in some cases highly unacceptable – like in a couple of projects I have been drafted into in an attempt to identify and enhance performance.  Sometimes, throwing new Hardware at an application can only take you so far.

One such application I worked on heavily used ‘new T()’ syntax for reasons beyond this text.  What is more poignant is that the hardware was purchased, the software was deployed (although not published) and was failing SLA targets under moderate load (there were other issues, but again, not important here).   After a little analysis and a quick set of tests, I confidently told them that I could increase the speed of their software between 500 to 800% with very little work.

Interestingly the development team on the project didn’t believe me and tried to call my bluff.  Two days later and a little bit of find-and-replace I was able to demonstrate the principle.  The code actually ran at about 850% faster... 

I now wish I had bet some big dollar cash on it....  :(

Essentially the solution lies in the abilities of Reflection and Delegates to do some very flexible run-time code generation and execution.  But rather than explain it I’ll let the example code below do the explaining for me.

If I have more time in the next day or so, I will maybe flesh out the explanation. Here follows an example of proof of concept I produced along side the two day work load....

Here are the stats I quickly generated a few minutes ago (Code follows).  Quickest methods first (green), Dynamic methods second (Yellow) and the usual 'new T()' last.    Please note that the 'D1' and 'D2'  examples also use the Dynamic code examples, however they are not very flexible:  You can't always get everything you want ;)

--

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace ConsoleApplication2
{
    internal delegate object DynamicActivator();

    internal abstract class SomeBaseClass { }
    internal class SomeClassA : SomeBaseClass { }
    internal class SomeClassB : SomeBaseClass { }

    internal class SomeFactory
    {
        private const BindingFlags BFLAGS = BindingFlags.Instance
                                            | BindingFlags.Public
                                            | BindingFlags.NonPublic
                                            | BindingFlags.ExactBinding;

        private static readonly Dictionary<Type, DynamicActivator> createSomeClass;

        public static readonly DynamicActivator SomeClassA_D1;
        public static readonly DynamicActivator SomeClassB_D2;

        static SomeFactory()
        {
            createSomeClass = new Dictionary<Type, DynamicActivator>();

            // boot-strap the DynamicActivators for test
            SomeClassA_D1 = DynamicNew<SomeClassA>();
            SomeClassB_D2 = DynamicNew<SomeClassB>();

            CacheDynamicActivator<SomeClassA>();
            CacheDynamicActivator<SomeClassB>();
        }

        public static SomeClassA SomeClassA()
        {
            return new SomeClassA();
        }

        public static SomeClassB SomeClassB()
        {
            return new SomeClassB();
        }

        // B1 and B2 are the same
        public static T SomeClassB1<T>()
            where T : SomeBaseClass, new()
        {
            return new T(); // compiles to "Activator.CreateInstance<T>()"
        }

        public static T SomeClassB2<T>()
            where T : SomeBaseClass, new()
        {
            return Activator.CreateInstance<T>();
        }

        // This version for JIT caching
        public static T SomeClassC1<T>()
            where T : SomeBaseClass, new()
        {
            CacheDynamicActivator<T>();

            return (T)createSomeClass[typeof(T)]();
        }

        // The DynamicActivator solution to 'new T()'
        public static T SomeClassC2<T>()
            where T : SomeBaseClass, new()
        {
            return (T)createSomeClass[typeof(T)]();
        }

        // Caches the DynamicActivator.
        private static void CacheDynamicActivator<T>()
            where T : class, new()
        {
            Type TT = typeof(T);

            if (!createSomeClass.ContainsKey(TT))
                createSomeClass.Add(TT, DynamicNew<T>());
        }

        // Generates a Dynamic method that can act as a more performant version of 'new T();'
        // 
        // Note: this is a slow operation, hence the bootstrap or JIT generation.
        // <typeparam name="T">Class to create</typeparam>
        // <returns>delegate for invocation</returns>
        private static DynamicActivator DynamicNew<T>()
            where T : class, new()
        {
            Type TT = typeof(T);

            DynamicMethod dm = new DynamicMethod(Guid.NewGuid().ToString(), TT, null, TT.Module);

            ILGenerator il = dm.GetILGenerator();

            il.Emit(OpCodes.Newobj, TT.GetConstructor(BFLAGS, null, Type.EmptyTypes, null));
            il.Emit(OpCodes.Ret);

            return (DynamicActivator)dm.CreateDelegate(typeof(DynamicActivator));
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            const int len = 10000000;

            SomeClassA tmpA;
            SomeClassB tmpB;

            Thread.Sleep(2000);

            Stopwatch sp = new Stopwatch();

            for (int j = 0; j < 1; j++)
            {
                Console.WriteLine();

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpA = new SomeClassA();
                sp.Stop();
                Console.WriteLine("                      new SomeClassA() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpA = SomeFactory.SomeClassA();
                sp.Stop();
                Console.WriteLine("              SomeFactory.SomeClassA() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpA = SomeFactory.SomeClassB1<SomeClassA>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassB1<SomeClassA>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpA = SomeFactory.SomeClassB2<SomeClassA>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassB2<SomeClassA>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpA = SomeFactory.SomeClassC1<SomeClassA>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassC1<SomeClassA>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpA = SomeFactory.SomeClassC1<SomeClassA>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassC2<SomeClassA>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpA = SomeFactory.SomeClassA_D1() as SomeClassA;
                sp.Stop();
                Console.WriteLine("           SomeFactory.SomeClassA_D1() = {0}", sp.Elapsed);

                Console.WriteLine();

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpB = new SomeClassB();
                sp.Stop();
                Console.WriteLine("                      new SomeClassB() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpB = SomeFactory.SomeClassB();
                sp.Stop();
                Console.WriteLine("              SomeFactory.SomeClassB() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpB = SomeFactory.SomeClassB1<SomeClassB>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassB1<SomeClassB>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpB = SomeFactory.SomeClassB1<SomeClassB>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassB2<SomeClassB>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpB = SomeFactory.SomeClassC1<SomeClassB>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassC1<SomeClassB>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpB = SomeFactory.SomeClassC1<SomeClassB>();
                sp.Stop();
                Console.WriteLine(" SomeFactory.SomeClassC2<SomeClassB>() = {0}", sp.Elapsed);

                sp.Reset(); sp.Start();
                for (int i = 0; i < len; i++)
                    tmpB = SomeFactory.SomeClassB_D2() as SomeClassB;
                sp.Stop();
                Console.WriteLine("           SomeFactory.SomeClassB_D2() = {0}", sp.Elapsed);
            }

            Console.ReadLine();
        }
    }
}

--

 

Tags: , , , , ,

Articles

WCF and the Cyclical Object Referencing Problem

by Moridin8 28. April 2008 20:10

As usual, when you need something done right, you sometimes just can not rely on MSDN or its forums, google or any form of current literature - or even a number of Microsoft employee's on my contact lists. 

Thats just the way of it sometimes.

What am I talking about? DataContractSerialization in WCF - in reference to the sometimes essential need to send cyclical object graphs across the wire.  WCF just does not support it.  At all - Something to do with an Xml Serialization standard or some such.  Which is fine in the general run of the mill situations involved with third party SOA services publically published - well... no, not in my oppinion... it's just not good enough.

But I digress.

Ok, some of you may be asking, "What do you mean by cyclical object graphs?",

Here is crude example:

[DataContract]
public class Example
{
    [DataMember]
    public Example MeMyselfAndI { get; set; }

    public Example()
    {
        this.MeMyselfAndI = this;
    }
}

Notice the (bad example) of the MeMyselfAndI property referencing itself?  When you serialize the object any serializer object will usually spit out an exception.

But wait...  Doesn't WCF use the DataContractSerializer class?  And doesn't that support cyclical object graphs?  Yes it does, which is great.  It's just that WCF doesn't generate the DataContractSerializer with the PreserveReferences feature enabled, which can be a major issue on services hosted in IIS that use the .svc files, and auto-generated client proxies and/or in situations where you have absolutely no choice but to use WCF configuration files - the M$ recommended route - and rightly so.

There are plenty of examples on the web demonstrating how to use the DataContractSerializer/PreserveReferences purely on the client (and only the client so far as I can find) when building your WCF client manually sans config.  This however only solves half of the problem because you just can not get the server to play the same way with these examples if you need cyclical object graphs returned, and the lack of a configuration support flies in the face of all reason.

MSDN is simply no help, neither are the forums, neither are a couple of highly skilled Microsoft employed architects I know.  *grin* they will choke when I send them the link to this.  I tend to enjoy these moments...

So, here is the solution... here is how to get WCF to support PreserveReferences... and still maintain the configurational aspect of WCF.

First, the server

The mechanics of WCF hosted in IIS involves the use of the well known '.svc' file.  Behind the scenes within WCF each page is generated within a factory that generates a ServiceHost instance.  The service host handles the requested calls to your service as well as all the appropriate discriptive information required during the lifetime of the service request. 

Usually, the ServiceHost is completely handled by the activation classes within the the ASP.NET model (Microsoft say it's handled by IIS... not true... but makes things easy to explain I suppose).  The ServiceHost contains all the information about the service contracts etc.  However, the ServiceHost isn't generally available under normal circumstances - and we need the ServiceHost.

So, to get access to the ServiceHost, we need to create our own ServiceHost factory and tell the '.svc' to it instead of the default factory supplied by WCF (code follows shortly).  To tell the '.svc' file to use the custom factory we do the following:

Remove the codebehind attribute and replace it with the Factory attribute thus...

<%@ ServiceHost Language="C#" Debug="true" Service="TestWCF.ManagerService" Factory="TestWCF.ManagerServiceHostFactory"  %>

Don't worry about what visual studio says about the Factory attribute not being valid, it is supported by WCF in the internal System.ServiceModel.Activation.ServiceParser class, which is more important.

The custom ServiceHostFactory, ServiceHost and a minor DataContactSerializer replacement are as follows:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;
using System.Xml;

public class ManagerServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, 
Uri[] baseAddresses) { ManagerServiceCustomHost customServiceHost
= new ManagerServiceCustomHost(serviceType, baseAddresses);


return customServiceHost; } } public class ManagerServiceCustomHost : ServiceHost { public ManagerServiceCustomHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) { } protected override void ApplyConfiguration() { base.ApplyConfiguration(); RefPreserveDataContractSerializerOperationBehavior.Reconfigure
(base.ImplementedContracts["TestWCF.IManagerService"].Operations); } } public class RefPreserveDataContractSerializerOperationBehavior
: DataContractSerializerOperationBehavior { public static void Reconfigure(OperationDescriptionCollection odc) { foreach (OperationDescription oper in odc) { for (int i = 0; i < oper.Behaviors.Count; i++) { if (oper.Behaviors[i] is DataContractSerializerOperationBehavior) { DataContractSerializerOperationBehavior tmp
= oper.Behaviors[i]
as DataContractSerializerOperationBehavior; // replace out the DataContractSerializerOperationBehavior
// with our own...
RefPreserveDataContractSerializerOperationBehavior tmp2
= new RefPreserveDataContractSerializerOperationBehavior
(oper, tmp.DataContractFormatAttribute); tmp2.DataContractSurrogate = tmp.DataContractSurrogate; tmp2.IgnoreExtensionDataObject = tmp.IgnoreExtensionDataObject; tmp2.MaxItemsInObjectGraph = tmp.MaxItemsInObjectGraph; oper.Behaviors[i] = tmp2; break; } } } } public RefPreserveDataContractSerializerOperationBehavior
(OperationDescription operationDescription) : this(operationDescription, null) { } public RefPreserveDataContractSerializerOperationBehavior
(OperationDescription operation,
DataContractFormatAttribute
dataContractFormatAttribute
) : base(operation, dataContractFormatAttribute) { } public override XmlObjectSerializer CreateSerializer
(Type type, string name, string ns, IList<Type> knownTypes) { return CreateDataContractSerializer(type, name, ns, knownTypes); } private static XmlObjectSerializer CreateDataContractSerializer
(Type type, string name, string ns, IList<Type> knownTypes) { return CreateDataContractSerializer(type, name, ns, knownTypes); } public override XmlObjectSerializer CreateSerializer
(Type type, XmlDictionaryString name, XmlDictionaryString ns,
IList<Type> knownTypes) { return new DataContractSerializer
(type, name, ns, knownTypes, int.MaxValue,
false,
true /* PreserveReferences*/,
null); } }

You should notice in the DataContractSerializer the 'static void Reconfigure(OperationDescriptionCollection odc)' method.  This is what does the work to ensure that WCF gives us that nice cyclical reference feature we are after.  Also please notice the contructor parameters used in the CreateSerializer method (please see MSDN for more info).

[client work pending... need to eat... ;)]

(didn't get chance to complete.... sorry)

Tags: , , , , , , ,

Articles

Powered by BlogEngine.NET 1.5.0.7

About Matt R.Warren

MeMy name is Matt and I am the current tenant of this small corner of the internet. I mostly architect, design and prototype applications that use .NET with C# and a little C++/CLI for Enterprise although I am aware of and enjoy fully embracing Java based solutions and alternatives such as Mono/Linux.  

I have worked on projects ranging from small tools to large distributed real-time Enterprise systems ranging from EPOS and real-time/JIT stock management systems, to distributed applications for National/International Utility, Healthcare, Insurance and Finance  in the private sector in both the USA and the EU.

My LinkedIn Profile (Opens new window/tab)

“Matt is one of the brightest people I've worked with. His in-depth knowledge of the .NET frameworks has been a tremendous benefit to nVISIA and our clients. His knowledge of software architecture in general allows him to architect systems for the best fit to his client's needs.” 
Dan Christopherson , Technical Director , nVISIA

“I had the distinct pleasure of working with Matt at nVisia. Matt's understanding of the Microsoft Technical space is outstanding. He is constantly working on improving his technical skills and rapidly masters any new technology that he encounters. He is an excellent teacher and a wonderful asset for any size team.” 
Jim Harnden , Senior Technical Architect , nVISIA

“Matt Warren is a very talented developer with great capacity for self study, investigation and adapts to new languages and frameworks with ease. He has an excellent grasp of software architecture and modern development principles. He has proven himself time and time again to be a hard worker and someone who can get the job done when you're in a tight spot.” 
Andrew Jump , Partner, C# Developer , Contegra

This website represents some of my spare time.  My small presence on the web between my family and my career.  I hope over time you find many useful things here.