Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    9.1d: Calendar performance of daylane view

    Hi,
    I am encountering performance problems with the daylane view with 29 lanes and 3 events on a day. Navigating to a day with this amount of events is sluggish and slow.

    I tested the same in the timelineview and there it seemed that the events where rendered when scrolling through the lanes, the timelineview rendered the example code below in a much faster/better way.

    In the daylaneview it seems that events are not rendered on demand. This has a large performance impact it seems.

    Why does the daylaneview not also support renderondemand? Or does it do this somehow already using a different property?

    Are there other properties which I can set or unset to get a better performance on the daylaneview (maybe related to overlap or anything else).

    gr. Martin

    Here is some code to try out to see the performance I mean:
    Code:
    var lanes = [
        { name: "charlesMadigen", title: "Charles Madigen", width: 200 },
        { name: "tamaraKane", title: "Tamara Kane", width: 200 },
        { name: "darcyFeeney", title: "Darcy Feeney", width: 200 },
        { name: "kaiKong", title: "Kai Kong", width: 200 },
        { name: "charlesMadigen1", title: "Charles Madigen1", width: 200 },
        { name: "tamaraKane1", title: "Tamara Kane", width: 200 },
        { name: "darcyFeeney1", title: "Darcy Feeney", width: 200 },
        { name: "kaiKong1", title: "Kai Kong", width: 200 },
        { name: "charlesMadigen2", title: "Charles Madigen", width: 200 },
        { name: "tamaraKane2", title: "Tamara Kane", width: 200 },
        { name: "darcyFeeney2", title: "Darcy Feeney", width: 200 },
        { name: "kaiKong2", title: "Kai Kong", width: 200 },
        { name: "charlesMadigen3", title: "Charles Madigen", width: 200 },
        { name: "tamaraKane3", title: "Tamara Kane", width: 200 },
        { name: "darcyFeeney3", title: "Darcy Feeney", width: 200 },
        { name: "kaiKong3", title: "Kai Kong", width: 200 },
        { name: "charlesMadigen4", title: "Charles Madigen", width: 200 },
        { name: "tamaraKane4", title: "Tamara Kane", width: 200 },
        { name: "darcyFeeney4", title: "Darcy Feeney", width: 200 },
        { name: "kaiKong4", title: "Kai Kong", width: 200 },
        { name: "charlesMadigen5", title: "Charles Madigen", width: 200 },
        { name: "tamaraKane5", title: "Tamara Kane", width: 200 },
        { name: "darcyFeeney5", title: "Darcy Feeney", width: 200 },
        { name: "kaiKong5", title: "Kai Kong", width: 200 },
        { name: "charlesMadigen6", title: "Charles Madigen", width: 200 },
        { name: "tamaraKane6", title: "Tamara Kane", width: 200 },
        { name: "darcyFeeney6", title: "Darcy Feeney", width: 200 },
        { name: "kaiKong6", title: "Kai Kong", width: 200 },
        { name: "shellyFewel", title: "Shelly Fewel", width: 200 }
    ];
    
    var _today = new Date();
    var y = _today.getFullYear();
    var m = _today.getMonth();
    var d = _today.getDate();
    
    var dayLaneData = [];
    for (var i = 0; i < 1; i++) {
    var day = d + (i%10);
    for (var j = 0; j < lanes.length; j++) {
      dayLaneData.add({
    	eventId: i,
    	startDate: new Date(y, m, day, 7, 0), 
    	endDate: new Date(y, m, day, 8, 0), 
    	name: "Development Meeting " + i,
        description: "Development meeting " + i,
    	lane: lanes[j].name
    });
      dayLaneData.add({
    	eventId: i,
    	startDate: new Date(y, m, day, 12, 0), 
    	endDate: new Date(y, m, day, 13, 0), 
    	name: "Development Meeting " + i,
        description: "Development meeting " + i,
    	lane: lanes[j].name
    });
      dayLaneData.add({
    	eventId: i,
    	startDate: new Date(y, m, day, 14, 0), 
    	endDate: new Date(y, m, day, 15, 0), 
    	name: "Development Meeting " + i,
        description: "Development meeting " + i,
    	lane: lanes[j].name
    });
    }
    }
    
    isc.Calendar.create({
        ID: "calendar", 
        data: dayLaneData,
        lanes: lanes,
        showWeekView: false,
        showMonthView: false,
        showTimelineView: false,
    
        chosenDate: new Date(),
    
        showDayLanes: true,
        canEditLane: true
        
    });

    #2
    renderOnDemand has never been supported in day view - that said, we could never add 29 columns to a day view until now, and a quick test shows potential for significant performance increase when there are many vertical lanes, so we'll most likely add something in this area - we'll update this thread when we have more information

    Comment


      #3
      Hi,
      Thanks, I was wondering/curious, what improvements do you see?
      My main thought was that renderondemand would help, with the daylanes features, renderondemand (or something similar) becomes much more relevant, almost a necessity for more than 5-10 lanes, probably the same reason as why it is there for timelines also.

      Just of out of interest, do you have an idea when these performance improvements would be available for testing? We are quite interested/eager to try it out.

      Thanks a lot!

      gr. Martin

      Comment


        #4
        We tested your sample code, but rendering only the events in the viewport, and we see the improvement you'd expect - navigating from day to day takes maybe a few tenths of a second, rather than the several seconds it takes to process all 29 lanes.

        So, we'll add something to deal with that, most likely renderEventsOnDemand, a la Timeline - no exact ETA, but something should be in place in the next few days.

        Comment


          #5
          Great to hear, thanks!

          gr. Martin

          Comment


            #6
            Ok, we've added renderEventsOnDemand for day and week views, default true - so, now, only events in the current viewport are rendered - in fact, we made a number of other optimizations here too, including fewer redraws and an extensible filtering mechanism - now, data-bound views will only load the events that sit in the current date-range (the area you can scroll to in a given view), and data will be refetched when the range changes - note that this feature is still undocumented for the moment (API names may change).

            In our tests, your example now renders in maybe a couple of tenths of a second as you scroll around and when you change the date-range, eg, via the next and previous buttons.

            All of these changes are available in today's 9.1d build - let us know if you still see issues.
            Last edited by Isomorphic; 11 Nov 2013, 21:08.

            Comment


              #7
              Hi,
              Thanks for the update! I see a clear performance improvement but I also see a few things which do not seem to work, or at least are less handy in my case.

              1) I get an undefined error when I do poolEventWindows: false, it seems that the _eventCanvasPool is not initialized with an empty array. See the attached screenshot.
              2) When having a dayview with lanes, when I try to create an event in the first lane, the startdate/enddate of the event can not be determined. See the attached screenshot, the col variable is computed as being 0, so no getCellDate is called on the view.
              3) My main trouble a bit is with the event windows being pooled, my event windows are of different types (a break event versus a standard appointment), it is difficult to mix and re-use one type of window for another event. I am neither sure if the current canvaspool is always in line with the current set of events for the calendar. When I do setData the canvaspool does not seem to be reset. So 2 things: 1) it would be great if I somehow can control the pooling of windows in more detail (with a canPool on the eventwindow for example, or a method where I can validate/prevent pooling of certain eventwindows for specific events), 2) it seems that the canvaspool is not inline with the current dataset.

              Images:
              http://postimg.org/image/47z41nhel/

              http://postimg.org/image/9lfw4c4nb/

              Hope this helps, thanks for your quick responses.

              gr. Martin

              Comment


                #8
                We've made changes to address 1) and 2).

                For 3), we're not sure what difficulties you're having with pooled eventWindows.

                Can you show code that demonstrates what you're doing, and what difficulties it brings that mean you want to switch off event pooling?

                Comment


                  #9
                  Hi,
                  Thanks! Regarding point 3, I see 2 things:
                  1) a specific one to my situation: I use a different body constructor for different types of events, for example breaks are visualized different from standard appointments, also the closebutton is not shown for breaks for example. So when a non-break event re-uses a standard appointment event window this whole body and other parts need to be recreated. I can workaround this but wanted to share the usecase, if the window pool usage is somehow controllable (for example with a overrideable method: getPooledWindow(event), I can let break windows only be re-used by break events.
                  2) Looking at the calendar code I am not sure if the eventindex is the right parameter to index into the window pool. To illustrate what I mean, here is the callstack I see:

                  When working with the calendar and creating events the _eventCanvasPool is getting filled up, the events are indexed in the order they are created. Then when I call refreshEvent (or it gets called because the date or data changes) the following happens:

                  refreshEvents --> builds a new events array (so each event has a new index in this events array).

                  refreshVisibleEvents --> uses this new events array and calls addevent with the index of the event in the new event array, this call then _getNewEventWindow with the new index

                  _getNewEventWindow --> uses this new index to look in the event canvas pool to check if there is a canvas there, there could be one, the change it is a different one is very large (the event pool is indexed using the old event index which is not valid anymore), also the function does not check if the _availableForUse is false (so a window already in use can be re-used by a new event).
                  There is a line commented out in the _getnewEventWindow which could be of help:
                  canvas // = view.getCurrentEventCanvas(event),

                  but the 'issue' I see with the new eventIndex being applied on an old-indexed canvaspool is even then still valid (I think).

                  Hope this helps to give an idea on what I mean: all imho ofcourse, the above is just what I see but I don't know the deeper design concepts and ideas behind the current code ofcourse.

                  gr. Martin

                  Comment


                    #10
                    You're poking about in partially completed and, thus, internal and undocumented subsystems here - even poolEventWindows itself is undocumented.

                    Still, on your point 1), since poolEventWindows is actually being used, despite not being documented, we've made some changes so that things work as expected with poolEventWindows:false - note that you're going to see event refreshing slow down again, somewhat, by using that.

                    On your point 2) - this is most definitely internal code, and the index you refer to is for temporary use, in certain circumstances...

                    Comment


                      #11
                      Separately, there is a plan for a new lightweight EventCanvas class for SmartClient 10.0 - this will replace the EventWindow by default, but its based on isc.Canvas, not isc.Window.

                      With that in mind, can you give us an idea of any other isc.Window specific APIs you might be making use of?

                      Comment


                        #12
                        thanks for your reply, for 2) I just wanted to share my observations when using pooleventwindows: true, afaics for a certain (common) scenario this index does not seem to work and results in re-use of existing already used event windows.

                        But as I said my observations are not limited by or based on any deep knowledge, probably/maybe my remark is wrong and incorrect, but still I could not stop and wanted to share my thoughts as they could be valuable for a person working on this code.

                        And I should proof my point with a testcase (I know :-), but maybe my remarks above already ring a bell.

                        gr. Martin

                        Comment


                          #13
                          My reply/your reply crossed.

                          For your last question, I don't use window specific api's afaics.

                          I have 2 types of events currently in the calendar:
                          - breaks: some of then can be moved, some not, each lane has one or two breaks a day.
                          - appointments which show title, a coloured side bar and a set of icons with hovers, by clicking on icons a separate window popups.

                          So when reusing event windows the main event window body needs to be redrawn as each event has its own colour and icons.

                          But not sure if this answers your question.

                          gr. Martin
                          Attached Files

                          Comment


                            #14
                            You said you're using different bodyConstructors, right?

                            bodyConstructor is an attribute of isc.Window, and is not available on isc.Canvas - that's the sort of thing we meant.

                            Comment


                              #15
                              Hi,
                              Yes, I checked but other than bodyconstructor, the only thing on the window is that I have a context menu (which I think canvases support also).

                              As a side note, when doing eventwindowpooling it would be great if there is a more expressive api/function to implement so that a window knows for which event it is being re-used. Currently I override the setProperties to capture the re-use happening.

                              Also, I looked at the _getNewEventWindow function (can't stop myself) some more and if I have renderEventsOnDemand on true event window pooling automatically happens it seems, see the else, which always adds the eventwindow to the pool, and the if before that which uses it when renderEventsOnDemand==true. Also the if does not check on _availableForUse (if it is for a different event).

                              Code:
                                  var canvasPool = view._eventCanvasPool,
                                      canvas // = view.getCurrentEventCanvas(event),
                                  ;
                              
                                  // there's a current event window, or we're recycling them and we have one available...
                                  if (canvas || (this.renderEventsOnDemand && canvasPool[eventIndex])) {
                                      if (canvas) {
                                          var existingWinIndex = canvasPool.indexOf(canvas);
                                          if (existingWinIndex != eventIndex) {
                                              var moveThisWin = canvasPool[eventIndex];
                                              canvasPool[eventIndex] = canvas;
                                              canvasPool[existingWinIndex] = moveThisWin;
                                          }
                                      } else {
                                          // ...reclaim the event from the event bin
                                          canvas = canvasPool[eventIndex];
                                          canvas.event = event;
                                          canvas.VSnapOrigin = 0;
                                          reclaimed = true;
                                          canvas.setStyleName(event[this.eventWindowStyleField] || this.eventWindowStyle);
                                      }
                                      canvas.setProperties(props);
                                  } else {
                                      // create eventWindow as an autoChild so it can be customized.
                                      canvas = this.createAutoChild("eventWindow", props, this.eventCanvasConstructor);
                                      view._eventCanvasPool.add(canvas);
                                  }
                              gr. Martin
                              Last edited by martintaal; 13 Nov 2013, 04:39.

                              Comment

                              Working...
                              X