mfp's two cents

...on Dynamics AX Development!
  • mfp's two cents

    Memory consumption of tables variables


    I recently made an interesting discovery, that I want to share with you. I was investigating an recursive algorithm that scaled nicely; until it ran out of memory. For each level of the recursion a new class instance was created – this class contained multiple member variables of type Table (as in CustTable). I decided to measure the memory footprint of this construct, and I was much surprised by my findings.

    Consider these 3 classes – that each stores the same amount of data, but in different ways:

    /// <summary> 
    /// Test class containing a table buffer and setting 20 fields 
    /// </summary> 
    class Class1 
        BOMCalcTrans bomCalcTrans; 
        public void new() 
            bomCalcTrans.ConsumptionVariable = 50; 
            bomCalcTrans.ConsumptionConstant = 50; 
            bomCalcTrans.CostPriceQty = 50; 
            bomCalcTrans.CostPrice = 50; 
            bomCalcTrans.CostPriceQtySecCur_RU = 50; 
            bomCalcTrans.CostMarkup = 50; 
            bomCalcTrans.NumOfSeries = 50; 
            bomCalcTrans.SalesPriceQty = 50; 
            bomCalcTrans.SalesMarkupQty = 50; 
            bomCalcTrans.CostMarkupQty = 50; 
            bomCalcTrans.CostMarkupQtySecCur_RU = 50; 
            bomCalcTrans.CostPriceSecCur_RU = 50; 
            bomCalcTrans.CostMarkupSecCur_RU = 50; 
            bomCalcTrans.NetWeightQty = 50; 
            bomCalcTrans.CostPriceUnit = 50; 
            bomCalcTrans.SalesPrice = 50; 
            bomCalcTrans.SalesMarkup = 50; 
            bomCalcTrans.SalesPriceUnit = 50; 
            bomCalcTrans.SalesPriceFallBackVersion = 'abc'; 
            bomCalcTrans.CostPriceFallBackVersion = 'def'; 
    /// <summary> 
    /// Test class containing a hashTable with 20 key/value pairs 
    /// </summary> 
    class Class2 
        System.Collections.Hashtable hashTable; 
        public void new() 
            hashTable = new System.Collections.Hashtable(); 
            hashTable.Add(fieldNum(bomCalcTrans,ConsumptionVariable), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,ConsumptionConstant), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPrice), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceQtySecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkup), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,NumOfSeries), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesMarkupQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupQtySecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceSecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostMarkupSecCur_RU), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,NetWeightQty), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceUnit), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPrice), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesMarkup), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceUnit), 50); 
            hashTable.Add(fieldNum(bomCalcTrans,SalesPriceFallBackVersion), 'abc'); 
            hashTable.Add(fieldNum(bomCalcTrans,CostPriceFallBackVersion), 'def'); 
    /// <summary> 
    /// Test class containing 20 member variables with values 
    /// </summary> 
    class Class3 
        InventQty bomCalcTransConsumptionVariable; 
        InventQty bomCalcTransConsumptionConstant; 
        CostPrice bomCalcTransCostPriceQty; 
        CostPrice bomCalcTransCostPrice; 
        CostPriceSecCur_RU bomCalcTransCostPriceQtySecCur_RU; 
        CostPrice bomCalcTransCostMarkup; 
        InventQty bomCalcTransNumOfSeries; 
        InventSalesPrice bomCalcTransSalesPriceQty; 
        InventSalesMarkup bomCalcTransSalesMarkupQty; 
        CostMarkup bomCalcTransCostMarkupQty; 
        InventPriceMarkupSecCur_RU bomCalcTransCostMarkupQtySecCur_RU; 
        CostPriceSecCur_RU bomCalcTransCostPriceSecCur_RU; 
        CostPriceSecCur_RU bomCalcTransCostMarkupSecCur_RU; 
        ItemNetWeight bomCalcTransNetWeightQty; 
        PriceUnit bomCalcTransCostPriceUnit; 
        CostingVersionId bomCalcTransCostPriceFallBackVersion; 
        InventSalesPrice bomCalcTransSalesPrice; 
        InventSalesMarkup bomCalcTransSalesMarkup; 
        PriceUnit bomCalcTransSalesPriceUnit; 
        CostingVersionId bomCalcTransSalesPriceFallBackVersion; 
        public void new() 
            bomCalcTransConsumptionVariable = 50; 
            bomCalcTransConsumptionConstant = 50; 
            bomCalcTransCostPriceQty = 50; 
            bomCalcTransCostPrice = 50; 
            bomCalcTransCostPriceQtySecCur_RU = 50; 
            bomCalcTransCostMarkup = 50; 
            bomCalcTransNumOfSeries = 50; 
            bomCalcTransSalesPriceQty = 50; 
            bomCalcTransSalesMarkupQty = 50; 
            bomCalcTransCostMarkupQty = 50; 
            bomCalcTransCostMarkupQtySecCur_RU = 50; 
            bomCalcTransCostPriceSecCur_RU = 50; 
            bomCalcTransCostMarkupSecCur_RU = 50; 
            bomCalcTransNetWeightQty = 50; 
            bomCalcTransCostPriceUnit = 50; 
            bomCalcTransSalesPrice = 50; 
            bomCalcTransSalesMarkup = 50; 
            bomCalcTransSalesPriceUnit = 50; 
            bomCalcTransSalesPriceFallBackVersion = 'abc';  
            bomCalcTransCostPriceFallBackVersion = 'def'; 

    Now; let us create 1,000,000 instances of each and store them all in a List(Types::Class), and measure the memory footprint of each type of the 3 classes – running as IL and running as pcode.


    Notice that using a table as a member variable consumes 6x as much memory as having each field as a member – apparently there is a huge overhead associated with the xRecord+Common functionality.

    My conclusion – apparently you cannot have nice code and low memory consumption at the same time. My advice – be careful when using tables as member variables, especially in batch scenarios, i.e. classes derived from RunBaseBatch or used by same.

  • mfp's two cents

    Microsoft Dynamics AX Technical Conference 2012


    Take advantage of this unique chance to see how Microsoft Dynamics AX can help provide you with innovative solutions for your business at the Microsoft Dynamics AX Technical Conference 2012. Whether you are new to Microsoft Dynamics AX, or are looking to keep your edge by going deeper and staying up-to-date on the new capabilities in the upcoming release of Microsoft Dynamics AX 2012 R2, there will be content that fits you!

    Sign-up for email alerts today and be one of the first people to receive event updates on registration opening, agenda & content updates, and more!

  • mfp's two cents

    We’re kicking it all the way to awesome


    Make sure to visit for the new fireside chat videos on the Microsoft Dynamics vision.

  • mfp's two cents

    CombineXPOs official version now in beta


    If you are using a version control system and want an automated build process, I have some great news for you. Microsoft has just published a beta version of CombineXPOs, which is a small utility required in the build process. For more information see here:

  • mfp's two cents

    The compare tool–and running X++ code as IL


    Running X++ as IL has some huge performance benefits, but you have to be careful. Here is a write up of the changes we went through for the code compare tool in AX 2012 to increase its performance and reduce some IL side effects.

    In Dynamics AX 2012 the compare tool has been through two significant changes to improve performance.

    New algorithm

    The first change was a re-implementation of the algorithm. In AX 2009 (and previous) the algorithm was “home-grown” – in AX 2012 it was changed to use the Longest Common Subsequence algorithm. For texts that were nearly identical this yielded little improvements – but for vastly different texts it became significant. To measure the performance improvement a “typical” text was defined as a text where 10% of all lines contained random text. The new algorithm (orange) could compare two typical texts each with 1100 lines in 10 seconds – the original algorithm (red) could “only” handle 650 lines in 10 seconds.

    Running as IL

    As the algorithm is pure CPU extensive– there is no database involvement- it could benefit significantly by running the code as IL. Switching over to run X++ code as IL is quiet simple in X++. By running the code as IL we can compare 4300 lines in 10 seconds. (green).

    To change the compare code to run as IL, here is what we had to do:

    1. Change the SysCompareText class to run on server tier.
    2. Create a static method that returns a container with results and takes a container as input:

      server static container runCIL(container _inputContainer)
          // Simple variable names used to make correctness obvious.
          str s1, s2;
          boolean b1, b2, b3, b4, b5;

          // Extract parameters from container
          [s1, s2, b1, b2, b3, b4, b5] = _inputContainer;

          // Pass parameters to the compare engine
          return SysCompareText::runInternal(s1, s2, b1, b2, b3, b4, b5);
    3. Call the static method via the runClassMethodIL() API

      XppILExecutePermission perm =

      new XppILExecutePermission();
      return runClassMethodIL(classStr(SysCompareText), staticMethodStr(SysCompareText, runCIL),
      [_text1, _text2, _caseSensitive, _suppressWhiteSpace, _lineNumbers, _singleLine, _alternateLines]);

    The pitfalls

    1. The generated code may not be what you expect.
      The compare algorithm uses a 2-dimensional array. In X++ that was implemented as an int[] (the offset for the indexer was calculated as x+y*width). However; the resulting CLR code uses an Dictionary<int, int> – which consumes 8x as much memory as an System.Int32[] array. This extensive memory consumption could cause AX to run out of memory, when comparing large files – e.g. comparing two 10,000 lines files would consume >3GB memory. The performance of System.Int32[] should also be better, so it was decided to change the int[] to System.Int32[,] and the benchmark was run again.  Much to my surprise the performance decreased (purple). After a while of looking at IL disassembly the reason was clear. The only way to access the contents of an CLR array in X++ is via the get_Item() method, which is a “slow” virtual method call. If just the X++ compiler converted an int[] to a System.Int32[] instead of Dictionary<int,int> then the lightning fast IL array  indexers could be used (and a factor >30x gained.) Yet, it was decided to use the System.Int32[,] array due to the lower memory consumption – even though it was somewhat slower.
    2. The user may disable IL
      If the user deselects “Execute business operations in CIL” under Tools | Options, then the code will execute as regular “slow” pcode. And in the compare case it will run even slower, as we decided to explicitly use System.Int32[,] instead of int[]. This causes a lot of “slow” interop calls into CLR. (blue)


    Compare benchmark


    By changing to a better algorithm and running the compare algorithm as IL, we have increased the size of texts that can be compared in 10 seconds from 650 lines to 3400 lines – a factor of 5. For small texts the difference is insignificant – the larger the text, the larger the gain.  However; if the user disables IL ,then gain from running IL is gone, and a penalty is paid for the IL specific optimizations implemented. In this case it was an optimization to reduce memory consumption.

  • mfp's two cents

    Microsoft Dynamics AX 2012 Development Cookbook


    A new AX development book has just been published. It is an AX 2012 version of Mindaugas Pocius’ development cookbook for AX 2009.

    I’m looking forward to reading this new book and writing a review.

    You can download an eBook version of the book today, or order an hard copy here.

  • mfp's two cents

    TechTalk @ MDCC : Project Roslyn - Compilers and Services for the Masses by Brian Rasmussen


    If you are in the neighborhood and can spare a morning,  you are most welcome at Microsoft Developer Center Copenhagen for a TechTalk by Brian Rasmussen. It is free – but remember to sign up.


    The goal of the Roslyn project is not only to deliver the next generation C# and Visual Basic compilers and language services. Roslyn also provides a rich set of APIs for interacting with the entire compiler pipeline and data model. The Roslyn APIs make parsing source code, querying semantic information, analyzing data and control flows as well as scripting easy. In this talk we look at several examples of how to use the APIs to enhance tools and applications.

    About Brian Rasmussen

    Brian is a Senior SDET at Microsoft working on the next generation C# and Visual Basic language services in Roslyn. Before joining Microsoft Brian was a Microsoft MVP for Visual C# for four years. Brian is blogging on the C# FAQ ( and can be found on Twitter (@kodehoved).

    When:             May 24, 2012 at 09.30 – 10.30

    Where:            Microsoft Development Center Copenhagen, Frydenlunds Alle 6, 2950 Vedbaek

    Sign up:          Here

  • mfp's two cents

    An AX first

    image I just had a first experience with AX. After being a developer on the product for most of my professional career, I was suddenly experiencing AX from a new perspective: The end user’s. Microsoft’s internal expense management system is now powered by Microsoft Dynamics AX 2012, and I had an expense to file.

    Filing my report took only a few minutes, and the experience was familiar to me. It didn’t require any training and the tool was even integrated with AMEX, so I could easily link my expense to the charge on my credit card. The UI was familiar as as I’m used to working with Share Point, ribbons and, of course, Dynamics AX. The experience offers a vast improvement over the old system, which was Excel based, with ever changing templates, required stapling receipts on to paper and printing confirmations. I now feel a little prouder in my daily work.

    To learn more about the Microsoft Dynamics AX 2012 expense management system internally at Microsoft go here.

    In case you want to know, I was expensing the purchase of Pavel Hruby’s book: Model-Drive Design Using Business Patterns.

  • mfp's two cents

    Houston, I have a problem


    Convergence 2012 is over. I didn’t attend. What did I miss? Probably a whole lot – based on all the traffic on Twitter and my past Convergence experiences. Luckily Microsoft has created a Virtual Convergence where attendees can see all recorded sessions on-demand. But even better: Virtual Conference is also for me – even though I didn’t attend the conference. I can stream the keynotes and the general sessions – I’ve already enjoyed Kirill Tatarinov’s keynote and Sri Srinivasan’s general session on Microsoft Dynamics AX 2012. Next time I’ll be sure to go.


  • mfp's two cents

    Dynamics AX 2012 for Retail Virtual Launch Event


    Register for the Microsoft Dynamics AX 2012 for Retail Virtual Launch Event on February 1st, 2012 at 9:00 a.m. Pacific Time (US)

    Microsoft leaders, customers, and retail industry experts will talk about what it means to be a Dynamic Retailer in today’s retail environment while providing a first-look at the powerful retail capabilities being introduced as part of Microsoft Dynamics AX 2012.

    Register now.


Page 5 of 19 (189 items) «34567»

mfp's two cents

...on Dynamics AX Development!