如何使用PHPExcel改进公式计算?

时间:2016-03-17 00:39:44

标签: php excel phpexcel

我是PHPExcel的新手,我已经搜索了谷歌,但没有找到我的具体问题。我发现更新/评估工作表中的公式真的慢。

我有一个非常小的excel文件,我正在做一些非常基本的目标寻求(在PHP中完成目标搜索,在excel表中完成最终结果计算)。我已经准确地工作了,但速度绝对让我伤心。公式计算似乎应该归咎于 - 公式计算/更新如何加速?

不幸的是,我不能发布excel文件的副本,因为内容是我公司的商业秘密,但它并不是什么特别的。公式中的算法非常简单。我能想到的唯一可能产生影响的是,一些细胞依赖链可能有点长(15-ish依赖)。

从下面的输出中可以看出,我们只执行11次迭代以寻找目标,总共需要4-5秒。由于这将是一个AJAX服务,我真的需要它比这更快。

代码

这是非常快速和肮脏的概念证明代码,请耐心等待:

<?php

Stopwatch::start();

$inputFileType = PHPExcel_IOFactory::identify( './example.xlsx' );
var_dump( 'FileType: '.$inputFileType );
Stopwatch::rel( 'identify filetype' );

$objReader = PHPExcel_IOFactory::createReader( $inputFileType );
$objReader->setReadDataOnly( true );
$filterSubset = new ReadFilter( 1, 35, range( 'A', 'J' ));
$objReader->setReadFilter( $filterSubset );
Stopwatch::rel( 'create reader' );

$objPHPExcel = $objReader->load( $inputFileName );
Stopwatch::rel( 'load file' );

$data = $objPHPExcel->getSheetByName( 'Data' );
$inputCell  = $data->getCell( 'B9' );
$outputCell = $data->getCell( 'B35' );
Stopwatch::rel( 'get cells' );

goalSeek( $inputCell, $outputCell, '0.10', 1, 5 );







function goalSeek( $inputCell, $outputCell, $targetValue ) {
    $cellValue = function() use ( &$outputCell, $precision ) {
        return round( $outputCell->getCalculatedValue(), $precision );
    };

    $setValue = function( $value ) use ( &$inputCell, &$objPHPExcel, $cellValue ) {
        $inputCell->setValue( $value );
        PHPExcel_Calculation::getInstance( $objPHPExcel )->clearCalculationCache(); // -- clear cache so updates are calculated
        Stopwatch::rel( 'goal-seek' );
    };

    // -- very basic goal seeking psuedo-code
    while( $stillHunting ) { // -- outside tolerance
        $setValue( $newInputValue );
    }
};

class ReadFilter implements PHPExcel_Reader_IReadFilter {
    private $_startRow = 0;
    private $_endRow   = 0;
    private $_columns  = [];

    public function __construct( $startRow, $endRow, $columns ) {
        $this->_startRow = $startRow;
        $this->_endRow   = $endRow;
        $this->_columns  = $columns;
    }

    public function readCell( $column, $row, $worksheetName = '' ) {
        if( $row >= $this->_startRow && $row <= $this->_endRow ) { // -- valid row
            if( in_array( $column, $this->_columns )) { // -- valid column
                return true;
            }
        }
        // else (implicit)

        return false;
    }
}

输出

string 'FileType: Excel2007' (length=19)

array (size=2)
  'rel' => 
    array (size=17)
      'identify' => float 0.008597135543823242
      'create reader' => float 0.0001199245452880859
      'load file' => float 0.387645959854126
      'get cells' => float 5.292892456054688E-5
      'goal-seek' => float 0.4194750785827637
      'goal-seek2' => float 0.3829901218414307
      'goal-seek3' => float 0.3478608131408691
      'goal-seek4' => float 0.3471150398254395
      'goal-seek5' => float 0.3569440841674805
      'goal-seek6' => float 0.378180980682373
      'goal-seek7' => float 0.3683559894561768
      'goal-seek8' => float 0.3778479099273682
      'goal-seek9' => float 0.3664979934692383
      'goal-seek10' => float 0.4503841400146484
      '_avg' => float 0.2794940630594889
      '_untilStop' => float 0.5339441299438477
  'total' => float 4.726345062255859

1 个答案:

答案 0 :(得分:1)

好的,如果你重新计算相同的公式,但在相关单元格中使用不同的值,可能会加速一个可能的解决方案是解析公式一次,只解析一次,但执行多次。

getCalculatedValue()调用两种方法;第一个是parseFormula(),它接受​​公式作为字符串,并构建一个解析器堆栈(作为数组)执行该公式的步骤;第二个(私有方法,所以你需要在Calculation.php中将其更改为public)是processTokenStack(),它接受​​3个参数,即通过调用parseFormula()生成的标记栈,单元格ID (作为字符串)和单元格对象。

您可能只执行一次parseFormula()步骤,然后为每次迭代调用processTokenStack(),这将消除除第一次迭代之外的所有语法的解析步骤