Copied to clipboard

Flag this post as spam?

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


  • Dan Diplo 1554 posts 6205 karma points MVP 6x c-trib
    Oct 12, 2011 @ 22:55
    Dan Diplo
    6

    Razor Benchmarks - Interesting Results

    I was curious to see how different Razor functions (using DynamicNode iterators) stacked up, so created a few benchmarks. I populated a bare 4.7.1 site with a large number of nested nodes (around 250) and then ran the benchmarks against this tree. Each benchmark was run a hundred times and the average time noted (using the System.Diagnostics.Stopwatch class).

    Here are some results:

    Counting All Nodes

    Model.AncestorOrSelf().DescendantsOrSelf().Count();  9 milliseconds
    Library.NodeById(-1).Descendants().Count();      103 milliseconds

    I was suprised to find that the first method is 10 times faster than the latter. I would have thought the latter would be the same, or even faster.

    Filtering By Alias

    In my tree I have just one umbHomepage (at root) and the rest are all umbTextpage

    Model.AncestorOrSelf().DescendantsOrSelf("umbHomepage").Count(); // 3 milliseconds
    Model.AncestorOrSelf().DescendantsOrSelf("umbTextpage").Count(); // 8 milliseconds

    So you can see that filtering by NodeTypeAlias can speed up traversal when there's a limited number of that type, but makes no difference if all nodes are of that alias.

    Conditions - Where

    Model.AncestorOrSelf().DescendantsOrSelf().Where("id != -1").Count(); // 10 milliseconds

    Filtering by a "native" property hardly adds any overhead at all.

    Model.AncestorOrSelf().DescendantsOrSelf().Where("Visible").Count(); 26 milliseconds

    Even though ALL my pages where visible (umbracoNaviHide not true) adding the where condition to a custom property adds a 2.5 x performance penalty. But that's not too bad, given the extra filtering that is going on.

    I'll add more benchmarks later!

  • Stefan Kip 1614 posts 4131 karma points c-trib
    Oct 12, 2011 @ 23:12
    Stefan Kip
    0

    Really interesting stuff! Can't wait for more benchmarks :-)

  • Dan Diplo 1554 posts 6205 karma points MVP 6x c-trib
    Oct 13, 2011 @ 00:21
    Dan Diplo
    2

    Some more benchmarks (but with more iterations), using the same set-up...

    Comparison of "native" methods and "dynamic" methods

    These functions do exactly the same thing, but with startingly different results:

    CurrentModel.ChildrenAsList.Count; 1 millisecond
    Model.Children.Count(); 21 milliseconds
    CurrentModel.GetChildrenAsList.Items.Count; 16 milliseconds

    So you can see there is quiet an overhead in using "dynamic" methods compared to "native" DynamicNode methods.

    And if we use Descendants() instead of Children (even when there is only 1 level of children) we get:

    Model.Descendants().Count();26 milliseconds

    So it seems that we should avoid using deep traversal methods like Ancestors() and Descendants() when we only need Child or Parent nodes.

    Up() vs Parent

    Model.Parent.Descendants().Count(); 27 milliseconds
    Model.Up().Descendants().Count(); 29 milliseconds
    Model.Up().Down().Up().Down().Up().Descendants().Count(); 30 milliseconds
    Model.Ancestors().First().Descendants().Count(); 371 milliseconds

    So we can see that there is little difference, and the penalty for going Up() and Down() is negligible. However, using Ancestors() can give quite a hit (which is understandable as it can potentialy traverse much deeper).

    Ordering Results

    Model.AncestorOrSelf().DescendantsOrSelf().OrderBy("id").Count(); 38 milliseconds
    Model.AncestorOrSelf().DescendantsOrSelf().OrderBy("id desc").Count(); 38 milliseconds

    So order direction makes no discernible difference, as you would expect. But splitting the OrderBy clause up does...

    Model.AncestorOrSelf().DescendantsOrSelf().
    OrderBy("Level, CreateDate").Count(); 64 milliseconds
    Model.AncestorOrSelf().DescendantsOrSelf().
    OrderBy("Level").OrderBy("CreateDate").Count(); 46 milliseconds

    However, not sure if the above give the same ordered results and are directly comparable? Anyone?

    Model.AncestorOrSelf().DescendantsOrSelf()
    .Where("id > -1 && id < 1100").Count(); 39 milliseconds
    Model.AncestorOrSelf().DescendantsOrSelf()
    .Where("id > -1").Where("id < 1100").Count(); 40 milliseconds

    Splitting and ANDed "where" clause into two doesn't seem to matter, which is what I'd expect.

  • Niels Hartvig 1951 posts 2391 karma points c-trib
    Oct 13, 2011 @ 14:17
    Niels Hartvig
    0

    Awesome! Would be interesting to actually package this up and we could use it to trace calls and see if we can improve performance. So the site along with a good range of DynamicNode calls could end up and a performance matrix. Game?

  • Dan Diplo 1554 posts 6205 karma points MVP 6x c-trib
    Oct 13, 2011 @ 22:34
    Dan Diplo
    0

    My basic benchmark Macro was just this:

    @inherits umbraco.MacroEngines.DynamicNodeContext
    @using System.Diagnostics;
    
    @{
        long millisecs = 0;
        int samples = 10;
        int iterations = 5;
        int count = 0;
    
        for (int i = 0; i < samples; i++)
        {
            TimeSpan elapsed = Benchmark(iterations, out count);
    
            <h1>Time: @elapsed.Duration().Milliseconds millseconds</h1>
    
            millisecs += elapsed.Duration().Milliseconds;
        }
    
        long average = millisecs / iterations;
    
        <h2>Average: @average milliseconds to count @count nodes @iterations times</h2>
    }
    
    @functions
    {
        TimeSpan Benchmark(int iterations, out int count)
        {
            count = 0;
    
            Stopwatch stopwatch = new Stopwatch();
    
            stopwatch.Start();
    
            for (int i = 0; i < iterations; i++)
            {
                count = Model.AncestorOrSelf().DescendantsOrSelf().Count();
            }
    
            stopwatch.Stop();
    
            return stopwatch.Elapsed.Duration();
        }
    }

    It really could do with a way of passing in the dynamic expression to the benchmark method, but I'm not clever enough to figure out how. Maybe Gareth can help? :)

Please Sign in or register to post replies

Write your reply to:

Draft