Skip to content

Performance regression: cell.Calculate() with FollowDependencyChain=false #2265

@hershaw

Description

@hershaw

EPPlus usage

Commercial use (I have a commercial license)

Environment

OSX

Epplus version

8.4.1

Spreadsheet application

excel

Description

Performance regression: cell.Calculate() with FollowDependencyChain=false is ~3.5x slower in 8.4.1 vs 8.4.0

Description

ExcelRange.Calculate() with FollowDependencyChain = false is significantly slower in EPPlus 8.4.1 compared to 8.4.0. The regression is most pronounced on workbooks with cross-sheet INDEX/MATCH formulas and iterative calculation enabled (circular references).

In our production workload, real world spreadsheets went from ~1s to calculate to ~10s. We can't share those so we've reproduced an approximation of the issue here.

Reproduction

The attached standalone project programmatically builds a financial model with:

  • 5 sheets (Assumptions, Revenue, Costs, CashFlow, Summary)
  • Cross-sheet INDEX/MATCH lookups
  • Circular references in the CashFlow sheet (interest-on-balance pattern)
  • Iterative calculation enabled
  • ~2,400 formula cells total

It then benchmarks calling cell.Calculate() with FollowDependencyChain = false on every formula cell, twice (simulating an iterative convergence check).

Steps

epplus-841-perf-repro.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <!-- Change to 8.4.0 vs 8.4.1 to compare -->
    <PackageReference Include="EPPlus" Version="8.4.1" />
  </ItemGroup>
</Project>

Program.cs

  1. Copy the repro project (two files: Program.cs and epplus-841-perf-repro.csproj)
  2. Set EPPlus version to 8.4.0 in the .csproj:
    <PackageReference Include="EPPlus" Version="8.4.0" />
  3. Run: dotnet run -c Release
  4. Note the total time
  5. Change to 8.4.1:
    <PackageReference Include="EPPlus" Version="8.4.1" />
  6. Run again: dotnet run -c Release
  7. Compare total times

Results (Apple M1 Pro)

EPPlus Version Total Time Per-cell
8.4.0 ~480 ms ~100 us
8.4.1 ~1,700 ms ~350 us
Ratio ~3.5x slower

NOTE this repro shows 3.5x slower but our clients are experiencing closer to 10X slower on their workbooks.

Possible Cause

The 8.4.1 changelog mentions:

  • "Changing cell dependencies were not taken into account when recalculating dirty cells from dynamic array formulas"
  • "Cells containing formulas with the INDIRECT function are now always treated as dynamic"

These changes may have added overhead to the per-cell calculation path that is exercised heavily when calling cell.Calculate() individually with FollowDependencyChain = false. The full workbook.Calculate() path is not affected — only per-cell Calculate with FDC=false.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions