【发布时间】:2019-12-12 14:49:16
【问题描述】:
嘿,很棒的 Stackoverflow 人
我目前正在评估是否应该将 iText 7.1.9 用于 Java 或 C#。 为此,我创建了一个测试用例,在其中我编写了一个包含一堆页面的 PDF,每个页面都包含一个大表格(代码如下)。
在 Java 中,创建包含 x 页的 PDF 会产生以下结果:
- 1 页:0 秒
- 10 页:1 秒
- 100 页:5 秒
- 1000 页:23 秒
这是合理的表现。然而,当我将完全相同的代码移植到 C# .Net 时,我感到非常震惊:
- 1 页:0 秒
- 10 页:1 秒
- 100 页:10 秒
- 1000 页:96 秒
使用 Java 创建的 PDF 与 C# 的大小相同,文件看起来完全相同。 然而,C# 似乎可以线性扩展,而 Java 设法优化更大的 PDF。
由于我们更愿意使用 C#,所以问题是:
- 到底是怎么回事?
- 我需要做些什么才能获得与 Java 大致相同的性能吗?
Java 代码:
package pcm;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import java.io.FileInputStream;
import java.io.IOException;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfAConformanceLevel;
import com.itextpdf.kernel.pdf.PdfOutputIntent;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.PdfViewerPreferences;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.AreaBreakType;
import com.itextpdf.layout.property.UnitValue;
import com.itextpdf.pdfa.PdfADocument;
public class PcmSimple {
public static void main(String[] args) {
long startTime, elapsedTime;
try {
for(int i=1; i<=1000; i*=10) {
startTime = System.nanoTime();
createPdf("D:\\Pcm Test", i);
elapsedTime = System.nanoTime() - startTime;
System.out.println(String.format("%04d pages: %02d sec", i, NANOSECONDS.toSeconds(elapsedTime)));
}
} catch(Exception ex) {
System.out.println(ex.getMessage() + ": " + ex.getStackTrace());
}
}
private static void createPdf(String path, int numberOfPages) throws IOException {
PdfFont fontBold = PdfFontFactory.createFont("resources/fonts/OpenSans-Bold.ttf", true);
PdfFont fontComic = PdfFontFactory.createFont("resources/fonts/comic-sans-ms_[allfont.de].ttf", true);
// Set up the document.
PdfADocument pdfDocument = new PdfADocument(
new PdfWriter(String.format("%s\\java_%d_pages.pdf", path, numberOfPages)),
PdfAConformanceLevel.PDF_A_3B,
new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1",
new FileInputStream("resources/color/sRGB_CS_profile.icm")));
pdfDocument.setTagged();
pdfDocument.getDocumentInfo().setTitle("Reference Document");
pdfDocument.getCatalog().setViewerPreferences(new PdfViewerPreferences().setDisplayDocTitle(true));
pdfDocument.getCatalog().setLang(new PdfString("en-US"));
Document document = new Document(pdfDocument);
// Add a table to every page.
for (int i = 0; i < numberOfPages; i++) {
Table table = new Table(5);
table.setWidth(UnitValue.createPercentValue(100));
for (int j = 0; j < 5; j++) {
Cell cell = new Cell(2, 1)
.add(new Paragraph("Header " + j).setMultipliedLeading(0.5f))
.setFont(fontBold)
.setFontSize(20)
.setBackgroundColor(ColorConstants.CYAN);
table.addHeaderCell(cell);
}
for(int j=0; j<225; j++) {
Cell cell = new Cell()
.add(new Paragraph("Test " + j).setMultipliedLeading(0.5f))
.setFont(fontComic)
.setPaddingTop(4.1f);
table.addCell(cell);
}
document.add(table);
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
document.close();
}
}
C# .Net 代码:
using iText.Kernel.Colors;
using iText.Kernel.Font;
using iText.Kernel.Pdf;
using iText.Layout;
using iText.Layout.Element;
using iText.Layout.Properties;
using iText.Pdfa;
using System;
using System.IO;
namespace PCM_Performance_Test_Console
{
class Program
{
static void Main(string[] args)
{
DateTime startTime;
long elapsedTime;
try
{
for (int i = 1; i <= 1000; i *= 10)
{
startTime = DateTime.Now;
CreatePdf("D:\\Pcm Test", i);
elapsedTime = (long)(DateTime.Now - startTime).TotalSeconds;
Console.WriteLine($"{i:d04} pages: {elapsedTime:d02} sec");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ": " + ex.StackTrace);
}
finally
{
Console.Read();
}
}
private static void CreatePdf(String path, int numberOfPages)
{
PdfFont fontBold = PdfFontFactory.CreateFont("resources/fonts/OpenSans-Bold.ttf", true);
PdfFont fontComic = PdfFontFactory.CreateFont("resources/fonts/comic-sans-ms_[allfont.de].ttf", true);
// Set up the document.
PdfADocument pdfDocument = new PdfADocument(
new PdfWriter($"{path}\\csharp_{numberOfPages}_pages.pdf"),
PdfAConformanceLevel.PDF_A_3B,
new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1",
new FileStream("resources/color/sRGB_CS_profile.icm", FileMode.Open, FileAccess.Read)));
pdfDocument.SetTagged();
pdfDocument.GetDocumentInfo().SetTitle("Reference Document");
pdfDocument.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true));
pdfDocument.GetCatalog().SetLang(new PdfString("en-US"));
Document document = new Document(pdfDocument);
// Add a table to every page.
for (int i = 0; i < numberOfPages; i++)
{
Table table = new Table(5);
table.SetWidth(UnitValue.CreatePercentValue(100));
for (int j = 0; j < 5; j++)
{
Cell cell = new Cell(2, 1)
.Add(new Paragraph("Header " + j).SetMultipliedLeading(0.5f))
.SetFont(fontBold)
.SetFontSize(20)
.SetBackgroundColor(ColorConstants.CYAN);
table.AddHeaderCell(cell);
}
for (int j = 0; j < 225; j++)
{
Cell cell = new Cell()
.Add(new Paragraph("Test " + j).SetMultipliedLeading(0.5f))
.SetFont(fontComic)
.SetPaddingTop(4.1f);
table.AddCell(cell);
}
document.Add(table);
document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
document.Close();
}
}
}
【问题讨论】:
-
如果您可以在没有
try...catch的情况下运行它,那么 1000 页的时间是多少?将try...catch放在需要的地方总是更有效,并且只捕获您希望使用Exception捕获的错误类型,而不是所有的错误类型 -
考虑到内容越多情况越糟糕(系数从 1 上升到超过 4),这看起来像是集合访问或系统资源的问题。前者应该由 iText 人看,后者请特别检查内存设置和使用情况。
-
@Amedee 我想我已经弄清楚发生了什么。在我那糟糕的 2 核工作计算机上并不清楚,但我现在已经在我的 i9-9900K@4.7GHz 上运行了测试。 C# 测试按预期运行:它们消耗了一个逻辑内核的 100%。 Java 测试很有趣:它们正常启动,但在 10,000 页期间的某个时间点(是的,我已经提高了数量),Java 切换到多线程,使用我所有的 16 个逻辑内核,消耗了我总 CPU 功率的 80% ,这解释了 Java 如何比线性扩展更好!那么,这是一个奇怪的 Java 功能还是 iText for Java 做到了这一点?
-
我还不知道,我还在喝早上的咖啡。 :) Java 和 .NET 版本的源代码尽可能保持相同,尽可能使用称为 Sharpen (medium.com/@pauldbau/…) 的(高度定制的)转换工具将 Java 代码自动转换为 C#。这样做是为了可维护性。在 iText 5 中,我们仍然需要手动从 Java 移植到 C#,而 C# 版本通常比 Java 版本晚数周。现在只落后了几分钟。 (续)
-
@Amedee 是的,没关系,对不起。它是需要 6.5 GB 内存的 Java。由于我的第一次测试只分配了 4 GB,GC 最终使用了所有 CPU 来尝试释放一些资源。当使用 8 GB (-Xmx8g) 执行时,它使用 1 个 CPU。 Java 需要 133 秒来处理 10'000 个页面,而 C# 需要 691 秒。我想我们只会使用 Java ;)
标签: c# .net performance itext7