snippetcsharpModerate
My Calendar generation code is slow. How do I improve it?
Viewed 0 times
slowimprovegenerationhowcodecalendar
Problem
I have a model I have created to generate iCal and the necessary JSON for the jQuery calendar plugin Full Calendar to work. When I display the page, the JSON generation takes about 7 seconds. The query itself has been running in only a few microseconds. So I know my code is the problem.
I was trying to get the format like what Full Calendar expects thus why I wrote the code how I did. My guess is that the below is the problem. Any thoughts on where I can improve this?
```
#region Event
public class Event
{
public string Title { set; get; }
public string Description { set; get; }
public string URL { set; get; }
public string UID { set; get; }
public DateTime DateTimeStamp { set; get; }
public DateTime CreatedDateTime { set; get; }
public DateTime Start { set; get; }
public DateTime End { set; get; }
public bool AllDay { set; get; }
}
#endregion
public class Calendar
{
#region Model
public string CalendarName { set; get; }
public string Product { set; get; }
public string TimeZone { set; get; }
public List Events { private set; get; }
#endregion
///
/// Creates a calendar json string.
///
///
public string ToFullCalendarJsonString()
{
StringBuilder sb = new StringBuilder();
sb.Append("[");
int count = Events.Count;
int i = 0;
foreach (Event eventItem in Events)
{
sb.Append("{");
sb.AppendFormat("\"title\":\"{0}\",", eventItem.Title);
sb.AppendFormat("\"allDay\":\"{0}\",", eventItem.AllDay);
if (eventItem.AllDay)
{
sb.AppendFormat("\"start\":\"{0}T00:00:00\",", eventItem.Start.ToString("yyyy-MM-dd"));
sb.AppendFormat("\"end\":\"{0}T00:00:00\",", eventItem.End.ToString("yyyy-MM-dd"));
}
else
{
sb.AppendFormat("\"start\":\"{0}T{1}\",", eventItem.Start.ToString("yyyy-MM-dd"), eventItem.Sta
I was trying to get the format like what Full Calendar expects thus why I wrote the code how I did. My guess is that the below is the problem. Any thoughts on where I can improve this?
```
#region Event
public class Event
{
public string Title { set; get; }
public string Description { set; get; }
public string URL { set; get; }
public string UID { set; get; }
public DateTime DateTimeStamp { set; get; }
public DateTime CreatedDateTime { set; get; }
public DateTime Start { set; get; }
public DateTime End { set; get; }
public bool AllDay { set; get; }
}
#endregion
public class Calendar
{
#region Model
public string CalendarName { set; get; }
public string Product { set; get; }
public string TimeZone { set; get; }
public List Events { private set; get; }
#endregion
///
/// Creates a calendar json string.
///
///
public string ToFullCalendarJsonString()
{
StringBuilder sb = new StringBuilder();
sb.Append("[");
int count = Events.Count;
int i = 0;
foreach (Event eventItem in Events)
{
sb.Append("{");
sb.AppendFormat("\"title\":\"{0}\",", eventItem.Title);
sb.AppendFormat("\"allDay\":\"{0}\",", eventItem.AllDay);
if (eventItem.AllDay)
{
sb.AppendFormat("\"start\":\"{0}T00:00:00\",", eventItem.Start.ToString("yyyy-MM-dd"));
sb.AppendFormat("\"end\":\"{0}T00:00:00\",", eventItem.End.ToString("yyyy-MM-dd"));
}
else
{
sb.AppendFormat("\"start\":\"{0}T{1}\",", eventItem.Start.ToString("yyyy-MM-dd"), eventItem.Sta
Solution
I just read somewhere that
For 1,000,000 empty events the times required are:
That is a considerable difference of 11%!
I'm guessing this is due to the lookup of the arguments and such. It would be nice if
This is the code:
By also applying Snowbear's earlier replies:
In total, after applying all changes, the code is 22% faster. :)
Bottomline is, there probably isn't a 'magic' solution which can make it go instant with so many events, but you can improve the speed considerably. I suggest you run the processing on a
... this is getting even more ridiculous. Changing the date formatting to the following:
... gives another speed increase and makes it 34% faster in total. Might slow down
again if you need more specific formatting.
Beware: this last update is probably erronous. I asked a question about proper usage of PLINQ.
I haven't used Parallel LINQ (PLINQ) yet. But this seemed a nice use for it. After replacing the for by:
I get a total speed increase of 43%, again 9% faster. :) This is on a dual core processor. PC's with more cores should perform better. I don't know how PLINQ works exactly, but I would think it could work even faster if one iteration doesn't need to wait on another. The
AppendFormat() can be slower than simple Append() calls. Being shocked by reading this, I decided to investigate.For 1,000,000 empty events the times required are:
- With AppendFormat: 9297 ticks
- Without AppendFormat: 8268 ticks
That is a considerable difference of 11%!
I'm guessing this is due to the lookup of the arguments and such. It would be nice if
AppendFormat() would be recompiled to Append() calls only by default.This is the code:
///
/// Creates a calendar json string.
///
///
public string ToFullCalendarJsonStringFaster()
{
StringBuilder sb = new StringBuilder();
sb.Append( "[" );
int count = Events.Count;
int i = 0;
foreach ( Event eventItem in Events )
{
sb.Append( "{" );
sb.Append("\"title\":\"");
sb.Append(eventItem.Title);
sb.Append("\",");
sb.Append("\"allDay\":\"");
sb.Append(eventItem.AllDay);
sb.Append("\",");
if ( eventItem.AllDay )
{
// My test never comes here, so I left it out.
}
else
{
sb.Append("\"start\":\"");
sb.Append(eventItem.Start.ToString("yyyy-MM-dd"));
sb.Append("T");
sb.Append(eventItem.Start.ToString("HH:mm:ss"));
sb.Append("\",");
sb.Append("\"end\":\"");
sb.Append(eventItem.End.ToString("yyyy-MM-dd"));
sb.Append("T");
sb.Append(eventItem.End.ToString("HH:mm:ss"));
sb.Append("\",");
}
sb.Append("\"url\":\"");
sb.Append(eventItem.URL);
sb.Append("\"");
sb.Append( "}" );
i++;
if ( i < count )
{
sb.Append( "," );
}
}
sb.Append( "]" );
return sb.ToString();
}By also applying Snowbear's earlier replies:
- Using
yyyy-MM-ddTHH:mm:ssas format strings, gives an extra speed difference of 5%
- Preallocating
StringBuildersize, gives an extra speed difference of 6%
In total, after applying all changes, the code is 22% faster. :)
Bottomline is, there probably isn't a 'magic' solution which can make it go instant with so many events, but you can improve the speed considerably. I suggest you run the processing on a
BackgroundWorker.... this is getting even more ridiculous. Changing the date formatting to the following:
//sb.Append(eventItem.Start.ToString( "yyyy-MM-ddTHH:mm:ss" ) );
sb.Append(eventItem.Start.Year);
sb.Append("-");
sb.Append(eventItem.Start.Month);
sb.Append("-");
sb.Append(eventItem.Start.Day);
sb.Append("T");
sb.Append(eventItem.Start.Hour);
sb.Append(":");
sb.Append(eventItem.Start.Minute);
sb.Append(":");
sb.Append(eventItem.Start.Second);
sb.Append("\",");... gives another speed increase and makes it 34% faster in total. Might slow down
again if you need more specific formatting.
Beware: this last update is probably erronous. I asked a question about proper usage of PLINQ.
I haven't used Parallel LINQ (PLINQ) yet. But this seemed a nice use for it. After replacing the for by:
Events.AsParallel().AsOrdered().ForAll( eventItem =>
{
...
} );I get a total speed increase of 43%, again 9% faster. :) This is on a dual core processor. PC's with more cores should perform better. I don't know how PLINQ works exactly, but I would think it could work even faster if one iteration doesn't need to wait on another. The
StringBuilder and i are exposed as closures. Anyone got any better approaches than ForAll()?Code Snippets
/// <summary>
/// Creates a calendar json string.
/// </summary>
/// <returns></returns>
public string ToFullCalendarJsonStringFaster()
{
StringBuilder sb = new StringBuilder();
sb.Append( "[" );
int count = Events.Count;
int i = 0;
foreach ( Event eventItem in Events )
{
sb.Append( "{" );
sb.Append("\"title\":\"");
sb.Append(eventItem.Title);
sb.Append("\",");
sb.Append("\"allDay\":\"");
sb.Append(eventItem.AllDay);
sb.Append("\",");
if ( eventItem.AllDay )
{
// My test never comes here, so I left it out.
}
else
{
sb.Append("\"start\":\"");
sb.Append(eventItem.Start.ToString("yyyy-MM-dd"));
sb.Append("T");
sb.Append(eventItem.Start.ToString("HH:mm:ss"));
sb.Append("\",");
sb.Append("\"end\":\"");
sb.Append(eventItem.End.ToString("yyyy-MM-dd"));
sb.Append("T");
sb.Append(eventItem.End.ToString("HH:mm:ss"));
sb.Append("\",");
}
sb.Append("\"url\":\"");
sb.Append(eventItem.URL);
sb.Append("\"");
sb.Append( "}" );
i++;
if ( i < count )
{
sb.Append( "," );
}
}
sb.Append( "]" );
return sb.ToString();
}//sb.Append(eventItem.Start.ToString( "yyyy-MM-ddTHH:mm:ss" ) );
sb.Append(eventItem.Start.Year);
sb.Append("-");
sb.Append(eventItem.Start.Month);
sb.Append("-");
sb.Append(eventItem.Start.Day);
sb.Append("T");
sb.Append(eventItem.Start.Hour);
sb.Append(":");
sb.Append(eventItem.Start.Minute);
sb.Append(":");
sb.Append(eventItem.Start.Second);
sb.Append("\",");Events.AsParallel().AsOrdered().ForAll( eventItem =>
{
...
} );Context
StackExchange Code Review Q#1330, answer score: 11
Revisions (0)
No revisions yet.