This article is intended to provide a general overview of what kind of performance you can expect from the MailMerge class in various typical situations. This document can be used as a starting point for evaluating your own performance tests.
Test Environment
To perform these tests, we used a midrange PC with an AMD Ryzen 7 2700X eight-core 3.70 GHz processor (comparable to Azure D-Series VMs) and 16GB of memory.
The test application is a .NET Core Console App using the classes Mail
╰ DocumentServer Namespace
╰ MailMerge Class
The MailMerge class is a .NET component that can be used to effortlessly merge template documents with database content in .NET projects, such as ASP.NET web applications, web services or Windows services. and Server
╰ TXTextControl Namespace
╰ ServerTextControl Class
The ServerTextControl class implements a component that provide high-level text processing features for server-based applications. from TX Text Control .NET Server for ASP.NET 31.0 SP2.
MailMerge Template
As a template, we used a simple but typical invoice template with a header and SVG logo, some static data, an address merge field block, and a repeating block that lists the invoice line items.
As a data source, the following C# object is used with different number of line items and invoices resulting in different number of pages from 1 page to hundreds of generated pages.
public class Invoice { | |
public Buyer Buyer { get; set; } | |
public List<LineItem> LineItems { get; set; } | |
public float Total { | |
get { | |
var total = 0F; | |
foreach (var item in LineItems) { | |
total += item.LineTotal; | |
} | |
return total; | |
} | |
} | |
} | |
public class LineItem { | |
public string ProductID { get; set; } | |
public string Name { get; set; } | |
public int Quantity { get; set; } | |
public float Price { get; set; } | |
public float LineTotal { | |
get { return Price * Quantity; } | |
} | |
} | |
public class Buyer { | |
public string Name { get; set; } | |
public string ContactName { get; set; } | |
public string Street { get; set; } | |
public string City { get; set; } | |
} |
The following code shows the basic merge process, including the export to PDF.
using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl()) { | |
tx.Create(); | |
tx.Load("invoice-template.tx", TXTextControl.StreamType.InternalUnicodeFormat); | |
using (MailMerge mailMerge = new MailMerge()) { | |
mailMerge.TextComponent = tx; | |
mailMerge.MergeObjects(invoices, false); | |
} | |
tx.Save("results.pdf", TXTextControl.StreamType.AdobePDF); | |
} |
The invoices data object is an object of type List<Invoice> that is generated prior to the benchmark merge process. Each Invoice is represented as a Document in the following diagrams.
The following screenshot shows the resulting document of scenario 1, where 1 invoice with 5 block data rows is created.
Test Scenarios
All test scenarios are executed across different test loads using the same test environment.
Scenario 1: Small Documents
This scenario shows the performance of simple templates with 5 line items (merge block rows):
- One page template
- 1 merge block with 5 data rows
- Number of pages per invoice: 1
- Variable: Number of documents (invoices)
Duration for a single document: 0.35 seconds
As you can see in the chart above, a single document is generated in less than 0.35 seconds and increases linearly. 80 documents are generated in about 16 seconds.
Scenario 2: Typical Documents
This scenario shows the performance of simple templates with 100 line items (merge block rows):
- One page template
- 1 merge block with 100 data rows
- Number of pages per invoice: 6
- Variable: Number of documents (invoices)
Duration for a single document: 3.2 seconds
As you can see in the chart above, a single document with 100 merge blocks rows is generated in 3.2 seconds and increases linearly. In the last run, the resulting document contains generated 480 pages with 80000 merge block rows.
Scenario 3: Increasing Merge Block Rows
This scenario shows the performance of simple templates with an increasing number of merge block rows:
- One page template
- 1 merge block
- Number of pages per invoice: Variable
- Number of invoices: 1
- Variable: Number of data rows
As visualized in the chart above, a single document with 1 merge blocks row is generated in 0.23 seconds and increases exponentially. In the last run, the resulting document contains generated 59 pages with 1280 merge block rows.
Scenario 4: Two Merge Blocks
This scenario shows the performance of simple templates with an increasing number of merge block rows for 2 merge blocks:
- One page template
- 2 merge blocks
- Number of pages per invoice: Variable
- Number of invoices: 1
- Variable: Number of data rows
As visualized in the chart above, a single document with 1 merge blocks row is generated in 0.34 seconds and increases exponentially. In the last run, the resulting document contains generated 8 pages with 160 merge block rows in two merge blocks.
What can be observed is that the same number of merge block rows divided into 2 merge blocks is faster than a single table.
Scenario 5: SaveSettings PDF Features
This scenario shows the performance of typical PDF features such as encryption, digital signatures and embedded files.
- One page template
- 1 merge block with 10 rows
- Number of pages per invoice: 1
- Number of invoices: 5
- Variable: Different SaveSettings features
The following features are used for this test:
- Encryption: The document is encrypted with a user and master password.
- Embedded Files: A typical XML (ZUGFeRD) is embedded in the PDF document.
- Digital Signature: The entire document is encrypted and signed with a PFX certificate.
- SignatureField: A single signature field is replaced with a signature image (SVG) and signed with a PFX certificate.
It can be observed that additional features such as encryption doesn't take significant more time to process. The test Embedded Files depends on the size of the to be embedded document.
Scenario 6: Formulas vs. Static Data
This scenario illustrates the differences between calculating dynamic values based on formulas in a merge block and merging static data from a data source.
- One page template
- 1 merge block
- 2 formulas (line total and total sum)
- Number of invoices: 1
- Variable: Merge block rows
The results show that the use of formulas is acceptable in shorter tables (low number of merge rows). The more rows are used, the more often formulas are calculated.
Conclusion
These benchmarks show the performance of the MailMerge class in a single-threaded merge process. If you plan to use the MailMerge class in a high-performance environment where documents are created in parallel (such as in a Web API), a multi-process approach is recommended.
Learn More
This article shows how to generate documents concurrently by creating multiple, truly parallel processes.