Performance Difference between Admin-defined Types and Hard-coded

Topics: General, Troubleshooting, Writing modules
Feb 24 at 10:34 PM
Edited Mar 30 at 12:01 AM
I have an Orchard CMS application with a Book Type. At present Book is defined using the standard Orchard Admin Content Type Definition facility. Books are frequently displayed as Summary lists and for this I use Queries and Projections, with customized Shapes defined with templates (eg a Shortlist.cshtml, driving a custom Content-Book.Summary.cshtml).

The above works just fine, but I would like to improve the performance. Can anyone provide guidance on whether writing a module to define a hard-coded Model and custom Drivers would make much difference to speed?

This issue is now a couple of months old. The problem still exists for me and I really would appreciate comments from anyone who has insight into Orchard performance.
Apr 23 at 12:07 AM
Results are unacceptable in production. Best performance is 49s to render 89 items on an otherwise unloaded machine.

Looking at Mini Profiler (first few lines only):
SAMSUNG on Fri, 22 Apr 2016 22:48:08 GMT
duration (ms)   from start (ms) query time (ms)
http://localhost:30321/OrchardLocal/notables-...    96.2    +0.0    4 sql   18.2
  Controller: Contents.ItemController.Display   655.7   +48.1   ! 11 sql    255.1
  Result: System.Web.Mvc.ViewResult 363.2   +704.6  ! 36 sql    97.0
   Shape Display: Shortlist (Detail) (Projection... 38752.1 +1066.7 ! 751 sql   705.9
      Shape Display: Content (Summary) (Book)   830.4   +1343.2 ! 1 sql 0.7
      Shape Display: Content (Summary) (Book)   79.4    +2608.5 ! 1 sql 2.2
      Shape Display: Content (Summary) (Book)   45.9    +3025.2 ! 1 sql 0.7
The line that worries me is obviously the 751 repetitions within the Shortlist shape display and the duration of 38 seconds.
The Book display times hover around 80ms each, for a total of 80 * 89 = 7 seconds. Not great, but still...

The problem is how shortlist.cshtml works:
@using Orchard.ContentManagement
    var cm = WorkContext.Resolve<IContentManager>();

<div class="row">
        @foreach (var contentItem in Model.ContentItems)
            var shape = cm.BuildDisplay(contentItem, "Summary");
            shape.Style = "Shortlist";
            <div class="col-lg-4 col-sm-6" >
Calling BuildDisplay on each item in the projection results is not the best way to go about rendering each book (content item). I also, now suspect it's not a question of data design but the way I have written this shape.

Can anyone suggest a better way to go?

As far as I can tell, the only list provided to a Shape by a Projection is on Model.ContentItems, which means I need to convert each ContentItem to a shape to render it, or of course I can just render it in place. But since I already have a Book summary shape, I would prefer to use that.

OK I have now rewritten Shortlist to render the items inplace, without and further calls to Display. Render time has gone down from 49s to 6s. Still not fantastic, but much better. The original question still stands: Would I get any better performance from hard-coding the Book ContentType?
May 11 at 6:44 PM
I am not sure what you mean by "hard-coding a content type", because a content type by definition is stored as information in the database, describing its name, the attached parts and fields. Even if you implement a "BookPart" and "BookPartDriver" for example, it would make no difference: the content type is still assembled from parts (and potentially fields).

Rendering content items in-place is definitely faster than invoking BuildDisplay on each item for obvious reasons: for each item, all content handlers and drivers are invoked, creating shapes, which then need to be displayed (which is also not very fast - the more shapes to render, the worse the performance).

To increase performance of rendering content items in-line, make sure that all items' content parts are loaded. If all parts are "shifted" (i.o.w. using InfoSet storage) then it should be fine. Otherwise you may very well be suffering from the n+1 issue, where a DB query or other expensive operation happens on each iteration.

Ultimately you probably should enable Output Caching.
May 11 at 9:56 PM
Edited May 11 at 9:57 PM
Shapes are certainly very expensive to create. By bringing everything into the list template, including eliminating the use of Media Profiles, I have been able to decrease the render time to about 6 seconds, not including the book cover images themselves. This is still unnacceptably slow.

The final tasks was to enable Output Caching. This too proved problematic. Many users are logged in as Members. There are thousands of members and caching doesn't work for authenticated users. If you enable Output Caching for authenticated users then, at a minimum, content such as the user name will be displayed incorrectly (authenticated users will appear to be logged in as someone else -- whoever's page last got cached).

I even tried Donut Caching using the experimental IDeliverable.Widgets module. It worked but led to other display problems and we abandoned it.

Our only solution at this point is to create a static version of the page. In fact I am considering developing a 'static cache' module that will need to be manually refreshed whenever a change is made, but this is the only way I can see Orchard being fast enough.