Ellia.Duan 发表于 2024-6-21 17:38:00

GcExcel 性能优化(二)

本帖最后由 Ellia.Duan 于 2024-6-21 17:57 编辑

接上篇https://gcdn.grapecity.com.cn/showtopic-143102-1-1.html
我们简单回顾下上篇文章介绍的状态同步。

当一个单元格的值发生变化后,所有依赖它的单元格都必须重新计算。但GcExcel并不会立即触发这个计算过程,只是把所有依赖它的单元格的状态改变为Dirty,后面GetValue()时才计算,我们把这个过程叫做状态同步。
假设有这样一个表格:A1中存放一个数字10,B1中存放一个公式:=A1+2,C1中存放一个公式:=B1*2。所以当A1的值发生变化后,必须把B1的状态改为Dirty, B1变为Dirty后,又需要把C1的状态变为Dirty。

状态同步过程往往耗费时间较少,所以一般情况下,在SetValue()的时候不需要关闭计算引擎。只有当公式特别多,依赖链特别长的情况下,这个同步过程才会比较耗费时间。
我们接下来介绍下,当公式特别多,依赖链特别长的解决方案。

一、关闭计算引擎
在工作簿中进行大量setValue/setFormula时,可以关闭计算引擎,在设置后,开启计算引擎,大致代码如下:
workbook.setEnableCalculation(false);
setFormulas() // or setValue()
workbook.setEnableCalculation(true);待计算引擎打开后,对公式重计算
workbook.calculate();此方案优点在于,减少状态同步的过程,即减少重复标脏的过程。
当然,在一些场景下,使用上述方案也不是很好,如
1、当在工作簿中已经设置了大量的公式,当新增公式时,使用上述方案,会发现计算引擎关闭后,workbook.calculate()会对所有的工作重计算(包括之前设置好的公式),增加了计算过程,浪费了资源。
2、如下代码,在计算引擎关闭的基础上,getValue会出现结果不正确的问题。
workbook.setEnableCalculation(false);
setFormulas();
getValue()
workbook.setEnableCalculation(true);
针对上述两种场景,我们还有延迟标脏的方案。




二、设置延迟标脏
每次setValue/setFormula,都会进行状态同步。如果公式引用较复杂,状态同步的过程较长,此时可以设置延迟标脏,切断状态同步。等到设置好值/公式 后,延迟标脏结束,此时状态同步。
workbook.setDeferUpdateDirtyState(true);
setFormulas(columns);
workbook.setDeferUpdateDirtyState(false);


这个方案会减少setValue/setFormula的时间。
我们再来看看,前面提到的两种关闭计算引擎后不适用的两种场景,用延迟标脏方案是否可行?
场景一,在新增值/公式前,设置延迟标脏,设置好之后,延迟标脏结束,此时公式计算,由于脏值只有最新的数据,所以不会公式全计算,不会造成资源浪费,同时还减少了设置值/公式的时间
场景二、在getValue时,获取到脏值,由于此时计算引擎没有关闭,还是会参与公式计算,此时getValue()的值是正确的。


小tips:开启迭代计算,会影响延迟标脏的效果

Question:
如果有一个新的工作簿,开始对一些公式进行设置,然后获取值,此时上述两种性能好一点呢?
答案是一样的。上述两种方案优化的是状态同步的过程,而公式计算的时间是一致的。
即“禁用计算引擎,设置公式,开启计算引擎,重算,获取值”与“禁用迭代计算,设置脏标延迟更新,设置公式,关闭脏标延迟更新,获取值”的整体耗时大致一致。

如果在实际项目中,使用上述任意一种方案后,公式计算耗时仍然较长,可以考虑调整公式结构或者发帖求助联系技术顾问。







页: [1]
查看完整版本: GcExcel 性能优化(二)