Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Carlos 338 posts 472 karma points
    Oct 12, 2012 @ 20:02
    Carlos
    0

    Razor Performance issue compared to XSLT?

    We have tried an experiment where we are using Razor and XSLT as a test. We have over 2000 nodes in our website and some are tagged with a property from a dropdown list.  It is cross promotions. So if the nodes are tagged with the same categories they will show up on each other's pages.  We are querying from the root down the tree because no all the property is in multiple DocTypes.  

    Our issue is, we want to switch our XSLT file to Razor, but when we did that, did the query looking for the property using Razor, etc and we stuck the Macro in the template, all of a sudden out website started lagging and our CPU started spiking at 100%.  

    So my question is:  What is different between the way XSLT is getting the data and nodes and the way Razor is getting the nodes.  Because XSLT seems to have no problems rendering it's Macro on the page and the Razor version (using uQuery and Razor) has some severe performance issues.  Why might the CPU be spiking when querying with Razor vs XSLT. 

    Is there something we can do to speed up the query or even keep out CPU from spiking?  

    BTW: we are using 4.7.1.

     

    Thanks in advance.

  • Jeremy Pyne 106 posts 246 karma points MVP c-trib
    Oct 12, 2012 @ 20:45
    Jeremy Pyne
    0

    My recomendation: Use the Macro Cacheing fetures uf Umbraco.  You can set a cache period for how long you want to macro to rebuild itsself.  This won't per say speed up the razor macro execution time but it will greatly improve proformance for every user after the first request.  The macro timout will affect the preformance and also how long it takes for changes to show up.

    NOTE: In your use case your macro is generating different output based on the page that is calling it so be sure to check the Cache by Page option so that each version is cached.

    NOTE: You should alwayse use cacheing where available but some thing's won't work in a cache.  Reading/writting Session/query string state in .NET code for example wont work becosue the .net code is not being run every time.  This could be done in javascript/cookies however.  Also Backend calls that talk to a databse will only run the first time themacor is called and the results cached.  In this case one could use XML calls to /base in the macro to get the desired cacheing but also have dynamic data.

  • Carlos 338 posts 472 karma points
    Oct 12, 2012 @ 20:53
    Carlos
    0

    Hmm, doesn't quite solve our issue.  We have actually tried caching the Razor version of the Macro before and the issue still persisted.  The thing is, it slows the WHOLE site down, not just the page. What would be the difference in the way the XSLT is executed in Umbraco and Razor? My only guess is that XSLT is executed quicker in Umbraco than in Razor.  But I am not certain of this.

    It still seemed to really slow the site down anyways.  My guess is because of the amount of nodes we have, the execution is much slower with Razor (for some reason).  

     

    Any other suggestions?

  • Jeremy Pyne 106 posts 246 karma points MVP c-trib
    Oct 12, 2012 @ 21:15
    Jeremy Pyne
    0

    Ok, is the razor macro running the tree walk differently then the xlt version was, checking more nodes, loading extra property data?  If you use the DynamicNode or @Modle.GetNodById methods or creating new Document() calls?  If using @Model is should be checking the cache and not hitting the DB to tre walk, maby check the sql server ands see if thats gettign > queries durring the macro.

     

    Are you filtering out trypes of nodes that cant have categories, IE blog posts or other doc types.

  • Carlos 338 posts 472 karma points
    Oct 12, 2012 @ 21:52
    Carlos
    0

    This filtering is not much different between the XSLT and the uQuery Razor.

    This is what we have for our uQuery code. I looked at the StackTrace and the uQuery Razor code it is taking 3.4 seconds and jacks with the CPU on our server.  The XSLT code only takes 1.9 seconds and does barely anything to the CPU.  Code examples are below.  If anything, I think we are going to just stick with the XSLT version for now.


    @{
        var mystring string.Empty;
        int numberOfResults int.Parse(@Parameter.numberOfResults);
        var Category @Model.crossPromotionCategories;
        var uRootNode uQuery.GetRootNode();
        
        var uNodes uRootNode.GetDescendantNodes().Where(=x.GetPropertyAsString("crossPromotionCategories"== Category &x.Id != @Model.Id);
        
        var lNodes GetRandomNodesAndShuffle(uNodes.ToList()numberOfResults).ToList()

        if(!String.IsNullOrWhiteSpace(Category)){
        if(@uNodes.Count(0){
         <div id="crossPromoHolder">
              @if(@numberOfResults >= &@Model._customPromoBoxTitle == ""){
             <h4>YOU MAY ALSO LIKE:</h4>
              }

            @if(@numberOfResults == &@Model._customPromoBoxTitle != ""){
             <div id="customPromo" class="crossPromoBox">
               <h3>@Model._customPromoBoxTitle</h3>
               @Model._customPromoBoxContent                                                          
                  <href="@Model.NodeById(@Model._customPromoBoxLink).Url" class="smallGreenBtn">@Model._customPromoBoxButtonTitle</a
                @Model._crossPromoBoxBottomContent
               </div
             }
                                
           @*SPECIFIC CROSS PROMOS LOGIC*@

              @if(@Category != String.Empty &@Model.HasProperty("specificCrossPromos"&@Model.GetProperty("specificCrossPromos").Value != String.Empty){
                
              var nodeItem @Model.specificCrossPromos;
              
              foreach(var in nodeItem){
               var promoNode @Model.NodeById(x.InnerText);
              <div class="crossPromoBox">
               
                                         
              @if(@promoNode.HasValue("landingPageImage")){
               var imgPathItem @promoNode.landingPageImage;
               var imgPath Library.MediaById(imgPathItem);
               var imgAltTag imgPath.altTag;
                  <href="@promoNode.Url">
                    <img src="@imgPath.Url" alt="@imgPath.imgAltTag" title="@imgPath.imgAltTag" />
                  </a>
               }
               else
               {
                  <p>No Image selected</p>
               }
               <div class="crossPromoBoxContent">
                    <h3>
                     <href="@promoNode.Url">
                      @if (@promoNode.HasValue("altPageTitle"))
                    {
                       @promoNode.altPageTitle
                    }
                    else
                    {
                       @promoNode.Name
                    }
                        </a>
                    </h3>
                    @promoNode.landingPageContent
                    <div class="crossPromoBtnContainer">
                        <href="@promoNode.Url" class="learnMoreBtn">View Details</a>
                        @if(@promoNode.HasValue("buyNowLink")){
                            <href="@promoNode.buyNowLink" class="buyNowBtn">Buy Now</a>
                      }
                    </div>
                  </div>
                </div>
                  
                  }                                   
                }
           @*--END SPECIFIC CROSS PROMO--*@
                                
           @*RANDOM CROSS PROMOS LOGIC*@
            @{var new Random();}
            @foreach (var item in @lNodes)
              {
             if(!@Model.HasProperty("specificCrossPromos"|(@Model.HasProperty("specificCrossPromos"&@Model.GetProperty("specificCrossPromos").Value == String.Empty))
               {
              <div class="crossPromoBox">
               
              @if(@item.HasProperty("landingPageImage")){                
                 
               var imgPathItem @item.GetProperty("landingPageImage");
                var imgPath Library.MediaById(@imgPathItem.Value);
               var imgAltTag imgPath.altTag;
                  <href="@item.Url">
                    <img src="@imgPath.umbracoFile" alt="@imgPath.imgAltTag" title="@imgPath.imgAltTag" />
                  </a>
               }
               else
               {
                  <p>No Image selected</p>
               }
              <div class="crossPromoBoxContent">
               <h3>
                <href="@item.Url">
                  @if (@item.GetProperty("altPageTitle").Value != String.Empty)
                    {
                       @item.GetProperty("altPageTitle").Value
                    }
                    else
                    {
                       @item.Name
                    }
                 </a>
                </h3>
                    @Html.Raw(@item.GetProperty("landingPageContent").Value.ToString())
                
                    <div class="crossPromoBtnContainer">
                        <href="@item.Url" class="learnMoreBtn">View Details</a>
                        @if(@item.GetProperty("buyNowLink").Value != String.Empty){
                            <href='@item.GetProperty("buyNowLink").Value' class="buyNowBtn">Buy Now</a>
                      }
                    </div>
                </div>
                </div>
                 }
              }
         </div>
      }
     }                                             
    }

    @functions{

      private List<NodeShuffleList(List<NodeinputList,int max)
      {
         List<NoderandomList new List<Node>();
         Random new Random();
         int randomIndex 0;
         int counter inputList.Count;
         for(int 0maxi++)
         {
            randomIndex r.Next(0counter--);
            randomList.Add(inputList[randomIndex]);
         }
        
        
        
         return randomList
      }
               
      //<summary>
      //This method takes all of the root nodesgrabs nodes randomly and checks for appropriate qualificationand only returns the necessary number 
      //</summary>
      //<returns></returns>
      public List<NodeGetRandomNodesAndShuffle(IEnumerable<NodeinputListint maxCount)
      {
         return inputList.RandomOrder().Take(maxCount).ToList();
      }

               
      public void testFunction(IEnumerable<NodeinputList)
      {
      }
    }

    This is our XSLT

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:stylesheet [
      <!ENTITY nbsp "&#x00A0;">
    ]>
    <xsl:stylesheet
            version="1.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:msxml="urn:schemas-microsoft-com:xslt"
            xmlns:umbraco.library="urn:umbraco.library"
            xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon"
            xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes"
            xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath"
            xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions"
            xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings"
            xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets"
            xmlns:ferinet="urn:ferinet"
            xmlns:random="urn:random"
            exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets ferinet">

      <xsl:output method="xml" omit-xml-declaration="yes"/>
      <xsl:param name="currentPage"/>
      <xsl:variable name="numberOfResults" select="/macro/numberOfResults"/>
      
      <msxml:script implements-prefix="random" language="C#">
        <msxml:assembly name="umbraco"/>
        <msxml:using namespace="umbraco"/>
        <![CDATA[
            public int GetRandom(int minValue, int maxValue)
            {
              return umbraco.library.GetRandom().Next(minValue, maxValue);
            }
        ]]>
      </msxml:script>

      <xsl:template match="/">
          
        <xsl:variable name="Category" select="$currentPage/crossPromotionCategories"/>
           
        <xsl:variable name="stringCount" select="string-length($Category)"/>

        <xsl:if test="$stringCount &gt; 0">

          <xsl:variable name="AllNodes" select="umbraco.library:GetXmlAll()/descendant-or-self::* [@isDoc and crossPromotionCategories = $Category and @id != $currentPage/@id]"/>

          <xsl:if test="count($AllNodes) &gt; 0">
            <div id="crossPromoHolder">
              <h4>YOU MAY ALSO LIKE</h4>
              <xsl:for-each select="$AllNodes">
                <xsl:sort select="random:GetRandom(1, count($AllNodes))" order="ascending" />
                <xsl:if test="position() &lt; $numberOfResults + 1">
                  <div class="crossPromoBox">
                    <!--Landing Page Image-->
                    
                    <xsl:if test="landingPageImage &gt; 0">
                     
                      <xsl:variable name="imgPath" select="umbraco.library:GetMedia(landingPageImage , '0')/umbracoFile" />
                      <xsl:variable name="imgAltTag" select="umbraco.library:GetMedia(landingPageImage , '0')/altTag" />
                       <href="{umbraco.library:NiceUrl(@id)}">
                      <img src="{$imgPath}" alt="{$imgAltTag}" />
                       </a>
                    </xsl:if>
                    <!--Title-->
    <h3>
    <href="{umbraco.library:NiceUrl(@id)}">
       <xsl:choose>
         <xsl:when test="altPageTitle = ''">
            <xsl:value-of select="@nodeName"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="altPageTitle"/>
        </xsl:otherwise>
      </xsl:choose>
    </a>
    </h3>
                    <!--Landing Page Blurb-->
                    <xsl:value-of select="landingPageContent" disable-output-escaping="yes"/>
                   <div class="crossPromoBtnContainer">
                    <href="{umbraco.library:NiceUrl(@id)}" class="learnMoreBtn">View Details</a>
                    <xsl:if test="buyNowLink != ''">
                      <href="{buyNowLink}" class="buyNowBtn">Buy Now</a>
                      
                    </xsl:if>
                    </div>
                  </div>
                  <!--End Cross Promo Box-->
                </xsl:if>
              </xsl:for-each>
            </div>
          </xsl:if>

        </xsl:if>
      </xsl:template>
    </xsl:stylesheet>
     
  • Jeremy Pyne 106 posts 246 karma points MVP c-trib
    Oct 12, 2012 @ 22:25
    Jeremy Pyne
    0

    You could try this, it may run faster, not sure, but it's not using uQuery.

     

     

    var uRootNode = @Model.AncestorOrSelf(1);
    Dictionary<string,objectvalues new Dictionary<string,object>();
    values.Add("NodeId"Model.Id);
    values.Add("Category", 
    Category);

    var uNodes = uRootNode.Children.Where("crossPromotionCategories = Category && Id != NodeId"), values).Items;    

     

  • Carlos 338 posts 472 karma points
    Oct 12, 2012 @ 23:57
    Carlos
    0

    Thanks for the suggestion. We will give it a try.  

    We found that our site had a lot of items caching. Actually taking the caching off of the Macros sped up the page load.  We were also using ImageGen, which in theory should be fine, but seemed to also be slowing down the site. Our site is pretty high traffic with over 2000 pages with tons of images and media, so out theory right now is that there was Macros being called inside other Macros and there was a caching conflict.  
    And ImageGen was doing it's process at the same time (even though it has it's own caching). So we are not sure.

    On Monday when we get back to the office, we are giong to go back and try again with the uQuery version, if that still has an issue, we will try just straight Razor and try your way Jeremy.

    Thanks again. 

Please Sign in or register to post replies

Write your reply to:

Draft