mfp's two cents

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

    Inside Microsoft Dynamics AX 2012 @ the printer

    • 4 Comments

    The book is now being printed.

    Read more here:

    http://blogs.msdn.com/b/microsoft_press/archive/2012/10/10/rtm-d-today-inside-microsoft-dynamics-ax-2012.aspx

  • mfp's two cents

    Don't miss the upcoming Microsoft Dynamics AX Technical Conference

    • 0 Comments

    View the personal invitation to the Microsoft Dynamics AX Technical Conference from CVP of Dynamics ERP Research and Development, Hal Howard and Technical Fellow, Mike Ehrenberg to learn more about what you will experience at the upcoming conference. Register today!

  • mfp's two cents

    Inside Microsoft Dynamics AX 2012 RTM

    • 2 Comments
    Today the team of authors and editors handed over the last chapter to MS Press. The book is now in the process of being printed.

    It is scheduled to hit the shelves October 24th 2012.

     

    This book project started exactly one year ago. I remember a morning in early October last year on a hotel’s patio in downtown Seattle where the first draft of the table of contents got discussed. Despite local traffic, rain, and jetlag we created a straw-man and table of contents. Now, one year later, and with thousands of iterations on contents, structures, phrases, wordings, samples, graphics and with the contribution of over 60 individuals the book is finally a reality. I’m delighted to have been part of the team, and I hope you will benefit from (and perhaps even enjoy) reading this book.

    THIS POST IS PROVIDED AS-IS AND CONFERS NO RIGHTS.

  • mfp's two cents

    Pre-order Inside Microsoft Dynamics AX 2012

    • 2 Comments

    We are heading for a true power demonstration from Microsoft in the fall of 2012. A long line of exciting products will be released, including Windows 8, Windows Server 2012 and Microsoft Surface

    …and the probably most anticipated of them all:
    Inside Dynamics AX 2012.

    Pre-order your copy today.

    Abstract:

    Dig into the architecture and internals of Microsoft Dynamics AX 2012 - with firsthand insights from the team that designed and developed it. Targeted for solution developers and system implementers, this guide focuses on programming and customization capabilities - including key architectural principles, the application model, framework, and tools. Topics include: Architecture and development environment, including MorphX Microsoft Visual Studio tools for Microsoft Dynamics AX X++ programming language Microsoft SQL Server reporting and analytics Models Core development concepts Extending and customizing Microsoft Dynamics AX Performance and security considerations Workflow Best practices Note: Readers should have working knowledge of SQL and OOP concepts to gain max benefit from this book.

  • mfp's two cents

    Memory consumption of tables variables

    • 3 Comments

    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.

    mem

    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

    • 0 Comments

    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

    • 0 Comments

    Make sure to visit http://dynamicbusiness.microsoft.com for the new fireside chat videos on the Microsoft Dynamics vision.

  • mfp's two cents

    CombineXPOs official version now in beta

    • 3 Comments

    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: http://blogs.msdn.com/b/axtools/archive/2012/06/29/combinexpos-new-version-official-beta-has-started.aspx

  • mfp's two cents

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

    • 2 Comments

    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:

      private
      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();
      perm.assert();
      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

    Conclusion

    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

    • 0 Comments

    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.

Page 4 of 19 (183 items) «23456»

mfp's two cents

...on Dynamics AX Development!