How To: Optimize Performance of Section Report
Sometimes when a very large report is previewed or run, Report authors and developers may experience an "Out Of Memory" exception . Before we go further, let's understand that ActiveReports is designed to run reports as quickly as possible and each time a new page is rendered it is stored in memory. This means that there is a linear correlation between the number of pages in a report and the amount of memory required to hold that document in memory. To resolve this, we have a CacheToDisk property, which can be used to optimize the amount of memory consumed by the document. As the name suggests, this property caches the report to the disk instead of holding it in memory. However, before we start using this property, we might need to consider the internal settings of this property as mentioned in this article. To add further, when setting CacheToDisk property to true, a temporary file is created only after the report engine allocates 500 images in memory, and some additional images are swapped to memory (to reduce GDI resource usage) resulting in memory usage of 100 MB or more. What if the report does not allocate 500 images in memory and still throws an OutOfMemory exception? Is there any solution for this? Yes, there is. In some cases (e.g. if report does not have a GrandTotal summary field in the Report Header, Page Range etc.), the report pages can be cached by saving the specified number of pages in a new file and once all the pages have been saved, then merge all the individual files in to a single document. This is comparatively faster and results in less overhead as well. Report in the image below has 300 pages.
In the given example, a new file is created for every 50 pages in the report (since there are 300 pages in the report then 6 new files would be created) and then all of these individual files are merged in to a single document. Note that we are using the PageEnd event to keep track of the number of pages to be added and the ReportEnd event to create the temporary files in the root folder.
private void SectionReport1_PageEnd(object sender, EventArgs e)
{
if (this.Document.Pages.Count > 52)
{
Doc = new SectionDocument();
for (int j = 0; j < 50; j++)
{
Doc.Pages.Add(this.Document.Pages[0].Clone() as Page);
this.Document.Pages.RemoveAt(0);
}
Doc.Save(System.Windows.Forms.Application.StartupPath + "\\\doc_" + docs_count.ToString() + ".ddd");
docs_count++;
Doc.Pages.Clear();
Doc.Dispose();
}
}
private void SectionReport1_ReportEnd(object sender, EventArgs e)
{
SectionDocument DocTemp = new SectionDocument();
Doc = new SectionDocument();
for (int j = 0; j < docs_count; j++)
{
DocTemp.Load(System.Windows.Forms.Application.StartupPath + "\\\doc_" + j.ToString() + ".ddd");
Doc.Pages.AddRange(DocTemp.Pages.Clone() as PagesCollection);
DocTemp.Pages.Clear();
DocTemp.Dispose();
}
foreach (Page p in this.Document.Pages)
{
Doc.Pages.Add(p.Clone() as Page);
}
this.Document.Pages.Clear();
this.Document.Pages.AddRange(Doc.Pages);
}
}
A sample application demonstrating this approach can be downloaded in both C# and VB.NET from the links given below. CacheReport_CSharp CacheReport_VB.NET