Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MSTest" Version="3.10.4" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EPPlus.Fonts.OpenType\EPPlus.Fonts.OpenType.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="Fonts\Roboto-Regular.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
70 changes: 70 additions & 0 deletions src/EPPlus.Fonts.OpenType.Benchmarks/ExtractCharWidthsBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using BenchmarkDotNet.Attributes;
using EPPlus.Fonts.OpenType;
using EPPlus.Fonts.OpenType.TextShaping;
using OfficeOpenXml.Interfaces.Drawing.Text;

[MemoryDiagnoser]
[SimpleJob(warmupCount: 1, iterationCount: 3)]
public class ExtractCharWidthsBenchmark
{
private ITextShaper _shaper;
private string _shortText;
private string _mediumText;
private string _longText;
private ShapingOptions _options;

[GlobalSetup]
public void Setup()
{
var fontFolders = new List<string> { /* your font paths */ };
var font = OpenTypeFonts.GetFontData(fontFolders, "Calibri", FontSubFamily.Regular);
_shaper = new TextShaper(font);
_options = ShapingOptions.Default;

// Short: typical Excel cell
_shortText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; // 56 chars

// Medium: single paragraph
_mediumText = new string('x', 550); // Simulate 550 char paragraph

// Long: full 20 paragraphs
_longText = new string('x', 11000); // Simulate 11k chars
}

[Benchmark]
public double[] ExtractCharWidths_Short()
{
return _shaper.ExtractCharWidths(_shortText, 11f, _options);
}

[Benchmark]
public double[] ExtractCharWidths_Medium()
{
return _shaper.ExtractCharWidths(_mediumText, 11f, _options);
}

[Benchmark]
public double[] ExtractCharWidths_Long()
{
return _shaper.ExtractCharWidths(_longText, 11f, _options);
}

// For comparison: what does Shape() alone allocate?
[Benchmark]
public ShapedText ShapeOnly_Short()
{
return _shaper.Shape(_shortText, _options);
}

[Benchmark]
public ShapedText ShapeOnly_Medium()
{
return _shaper.Shape(_mediumText, _options);
}

[Benchmark]
public ShapedText ShapeOnly_Long()
{
return _shaper.Shape(_longText, _options);
}
}
56 changes: 56 additions & 0 deletions src/EPPlus.Fonts.OpenType.Benchmarks/FontCacheBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using BenchmarkDotNet.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EPPlus.Fonts.OpenType.Benchmarks
{
/// <summary>
/// Separate benchmark class to measure cache performance without ClearCache in IterationSetup
/// </summary>
[MemoryDiagnoser]
[SimpleJob(warmupCount: 3, iterationCount: 5)]
public class FontCacheBenchmarks
{
private List<string> _fontFolders;

[GlobalSetup]
public void Setup()
{
var fontsPath = Path.Combine(System.AppContext.BaseDirectory, "Fonts");

if (!Directory.Exists(fontsPath))
{
throw new DirectoryNotFoundException($"Fonts directory not found: {fontsPath}");
}

_fontFolders = new List<string> { fontsPath };

// Pre-load font into cache
OpenTypeFonts.ClearFontCache();
OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);
}

[Benchmark]
public OpenTypeFont Load_FromCache_SingleThread()
{
// This should be extremely fast - just cache lookup
return OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);
}

[Benchmark]
public OpenTypeFont[] Load_FromCache_MultipleFonts()
{
// Simulates loading multiple font styles (like for a document)
return new[]
{
OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular),
OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Bold),
OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Italic),
OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.BoldItalic)
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using BenchmarkDotNet.Attributes;
using EPPlus.Fonts.OpenType;

/// <summary>
/// Benchmarks for repeated cache clearing scenarios
/// </summary>
[MemoryDiagnoser]
[SimpleJob(warmupCount: 3, iterationCount: 5)]
public class FontCacheClearingBenchmarks
{
private List<string> _fontFolders;

[GlobalSetup]
public void Setup()
{
var fontsPath = Path.Combine(System.AppContext.BaseDirectory, "Fonts");

if (!Directory.Exists(fontsPath))
{
throw new DirectoryNotFoundException($"Fonts directory not found: {fontsPath}");
}

_fontFolders = new List<string> { fontsPath };
}

[Benchmark]
public OpenTypeFont Load_Clear_Load_Pattern()
{
// Simulates pattern where cache is cleared between operations
OpenTypeFonts.ClearFontCache();
var font1 = OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);

OpenTypeFonts.ClearFontCache();
var font2 = OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);

return font2;
}

[Benchmark]
public OpenTypeFont Load_Reuse_Pattern()
{
// Simulates pattern where cache is NOT cleared (optimal)
var font1 = OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);
var font2 = OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);

return font2;
}
}
74 changes: 74 additions & 0 deletions src/EPPlus.Fonts.OpenType.Benchmarks/FontLoadingBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*************************************************************************************************
Required Notice: Copyright (C) EPPlus Software AB.
This software is licensed under PolyForm Noncommercial License 1.0.0
and may only be used for noncommercial purposes
https://polyformproject.org/licenses/noncommercial/1.0.0/

A commercial license to use this software can be purchased at https://epplussoftware.com
*************************************************************************************************
Date Author Change
*************************************************************************************************
01/20/2025 EPPlus Software AB Initial implementation
*************************************************************************************************/
using BenchmarkDotNet.Attributes;
using EPPlus.Fonts.OpenType;
using System.Collections.Generic;
using System.IO;

namespace EPPlus.Fonts.Benchmarks
{
[MemoryDiagnoser]
[SimpleJob(warmupCount: 3, iterationCount: 5)]
public class FontLoadingBenchmarks
{
private List<string> _fontFolders;

[GlobalSetup]
public void Setup()
{
var fontsPath = Path.Combine(System.AppContext.BaseDirectory, "Fonts");

if (!Directory.Exists(fontsPath))
{
throw new DirectoryNotFoundException($"Fonts directory not found: {fontsPath}");
}

_fontFolders = new List<string> { fontsPath };
}

[Benchmark]
public OpenTypeFont Load_Roboto_Regular_ColdCache()
{
OpenTypeFonts.ClearFontCache(); // Clear INNE i benchmark
return OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);
}

[Benchmark]
public OpenTypeFont Load_Roboto_Regular_WarmCache()
{
// Load UTAN att cleara - använder cache från GlobalSetup eller warmup
return OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Regular);
}

[Benchmark]
public OpenTypeFont Load_Roboto_Bold_ColdCache()
{
OpenTypeFonts.ClearFontCache();
return OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Bold);
}

[Benchmark]
public OpenTypeFont Load_Roboto_Italic_ColdCache()
{
OpenTypeFonts.ClearFontCache();
return OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.Italic);
}

[Benchmark]
public OpenTypeFont Load_Roboto_BoldItalic_ColdCache()
{
OpenTypeFonts.ClearFontCache();
return OpenTypeFonts.GetFontData(_fontFolders, "Roboto", FontSubFamily.BoldItalic);
}
}
}
Binary file not shown.
14 changes: 14 additions & 0 deletions src/EPPlus.Fonts.OpenType.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using BenchmarkDotNet.Running;

namespace EPPlus.Fonts.OpenType.Benchmarks
{
// Program.cs - Entry point
public class Program
{
public static void Main(string[] args)
{
// Kör alla benchmark-klasser i assembly
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
}
}
}
Loading