box / spout-使用十六进制颜色与预定义颜色时的大量内存使用情况

问题描述 投票:0回答:1

[我正在使用Box/Spout库,似乎与使用自定义十六进制颜色(例如,蓝色为0000FF)一起使用StyleBuilder相比,使用诸如Color :: BLUE之类的预定义颜色会占用大量内存。为什么会这样?

相关代码段:

//LOW MEMORY
$row[] = WriterEntityFactory::createCell('test', (new StyleBuilder())->setFontColor(Color::BLUE)->build());

//HIGH MEMORY
$row[] = WriterEntityFactory::createCell('test', (new StyleBuilder())->setFontColor($colorHex)->build());

输出:

setFontColor(Color :: BLUE):Peak memory usage: 1666 KB

setFontColor($ colorHex):Peak memory usage: 189436 KB

完整代码:

(出于演示目的,我正在加载一个小的250x150图像以提供多种颜色值)

<?php

    require_once 'Spout/Autoloader/autoload.php';
    use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
    use Box\Spout\Common\Entity\Style\Color;
    use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;

    //load an image
    $img = imagecreatefrompng('input/test250x150.png');

    $writer = WriterEntityFactory::createXLSXWriter();
    $writer->openToFile('output/MyExcel.xlsx');

    //height of the image
    for($y=0; $y<150; $y++) {

        //create or reset array to hold this row's cells
        $row = [];

        //width of the image
        for($x=0; $x<250; $x++) {

            //gets the pixel color
            $index = imagecolorat($img, $x, $y);
            $colorRGBArr = imagecolorsforindex($img, $index);
            $colorHex = sprintf("%02x%02x%02x", $colorRGBArr['red'], $colorRGBArr['green'], $colorRGBArr['blue']);

            //LOW MEMORY
            //$row[] = WriterEntityFactory::createCell('test', (new StyleBuilder())->setFontColor(Color::BLUE)->build());
            //HIGH MEMORY
            $row[] = WriterEntityFactory::createCell('test', (new StyleBuilder())->setFontColor($colorHex)->build());

        }
        $writer->addRow(WriterEntityFactory::createRow($row));
    }

    $writer->close();

    echo 'Peak memory usage: '.round(memory_get_peak_usage() / 1024).' KB';
?>
php phpexcel spout
1个回答
0
投票

tl; dr

虽然Sprout可以改进,Excel isn't designed for large quantities of styles,所以这并不是库的缺陷(您可能想要rescind the issue

故事

[好吧,这里有一些事情在起作用。.我用来测试的代码在我的帖子的底部-与颜色相关的关键功能是jonEgjonEgQuant(这是其中的$by变量可以调整)

  • Excel中的样式有点像HTML + CSS。样式最初是在文件中(在标题块中)定义的,然后在工作表中按名称(类)进行引用。从根本上讲,这表明Excel存储格式不是为多种样式设计的,而只是少数在许多单元格上可重复使用的样式]]

  • [Sprout向用户隐藏了样式定义的复杂性-如果已经定义了样式(即已经存在确切的样式

  • ),那么它将重用该定义,否则将定义新样式] >
  • Sprout通过样式对象serializing构建所有样式的索引,并将其存储在数组中。每种样式大约1749字节,此索引消耗内存(除了它在内存中用于生成文件的实际样式对象之外)-可以在Sprout

  • 中进行改进
  • 使用此代码,我保证每个单元格都有不同的样式(使用行/列位置定义颜色的红色和绿色分量)-比您的图像颜色选择要极端,但可能不多

    (没有示例图像我无法分辨)。 RGB三联体有1600万种颜色可供选择。.但我们的眼睛无法始终察觉到几个点的差异。例如,将在红色渐变中清楚地看到255,254,253 ... 128看起来很平滑,但是随机分布的255,254,253的单个块可能看起来像是单一颜色。对于除原色以外的任何颜色,此差异现在适用于三个维度(r,g和b)。 JPEG format利用了这一点(重建过程中压缩和噪声的组合)-因此每次看起来像统一块的颜色选择仍将返回略有不同的值
  • 当您使用jonEg运行我的代码时,您将获得(100 * 150 + 1)15001样式,仅Sprout中的索引就需要(15001 * 1749/1024/1024)〜25MB的内存。再次需要该索引来防止Excel中的每个单元格都有其自己的样式(但是,当然,在这个人为设计的示例中,我已确保它毫无意义-每个单元格都具有其自己的样式)-此使用〜100MB

    的内存
  • 当您使用jonEgQuant运行我的代码(离开$by=16;时)仅需要71种样式(但这是非常极端的舍入,总共只允许4096种颜色),-使用〜2MB

    内存
  • 当您设置$by=4(在ColorBuilder::jonEgQuant内部)时-您现在有多达一百万种颜色,需要使用989种样式,并使用〜7MB

    内存(更重要的是,在Excel中打开时类似于jonEg
  • [Sprout包括一种将RGB十进制值转换为字符串Color::rgb($r,$g,$b)的方法-但这不会改变结果(sproutDoc方法)

  • 样式的尺寸远不止于此-我使用了背景色,但有前景(您的示例),下划线,粗体,斜体,字体,字体大小-所有这些都会使样式数量相乘(减少此处的色彩空间解决了您记录的问题,但是可以通过更改其他样式组件来撤消)

  • 带走
    • 减少您使用的样式数量-Excel期望数量不多,Sprout同样没有为此优化。压缩颜色(通过与jonEgQuant一样四舍五入的值)是一种途径

  • 改进Sprout样式索引机制,但请注意,索引仅消耗了部分内存-这不小,但不是唯一的贡献者-每个样式对象需要

    生成结果。删除此样式高速缓存(对于这个人为的示例而言,这无济于事),使内存使用量增加到40MB(并生成相同的Excel文件)。 60%的节省并不是什么,但是更多的样式会占用更多的内存,更大的Excel文件,以及打开/渲染这些文件的速度较慢]]
  • 测试笔记
    • 我在测试时放弃了图像读取/色彩收集,因为它可能会增加内存使用量,但不会增加问题的实质性

  • 使用$colCount=250;(在您的示例中),您超出了PHP中128MB的默认内存限制,最好将其设置为$colCount=100;,并且可以运行所有测试而无需更改该设置

  • 我切换为设置背景色,在Excel中打开时更容​​易看到差异

  • 对于每个颜色生成器(jonEgsproutDocfixedEgjonEgQuant),都会生成一个不同的XLSX(有助于查看文件大小和渲染差异)

  • 文件大小与Excel复杂度相匹配-jonEg是179KB的文件(并且Excel很难打开),而jonEgQuant是43KB

  • ]

    代码
    <?php
    
    require_once 'Spout/Autoloader/autoload.php';
    use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
    use Box\Spout\Common\Entity\Style\Color;
    use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
    
    // -- -- Set this to one of the method names on ColorBuilder (that isn't a helper)
    $choice='jonEg';
    // -- -- 
    
    class ColorBuilder {
        static $defaultBlue=255;
        static function jonEg($x,$y) {return sprintf("%02x%02x%02x", $x, $y, static::$defaultBlue);}
        static function sproutDoc($x,$y) {return Color::rgb($x, $y, static::$defaultBlue);}
        static function fixedEg($x,$y) {return Color::BLUE;}
        static function jonEgQuant($x,$y) {$by=16;return sprintf("%02x%02x%02x", static::_quantize($x,$by),static::_quantize($y,$by), static::_quantize(static::$defaultBlue,$by));}
    
        //Helpers - don't use these for choice
        static function validate(string $name):bool {
            if ($name==null) return false;//Null or empty
            if (substr($name,0,1)=='_') return false;//Private by convention
            if ($name==='validate') return false;//Not the function you seek
            return method_exists('ColorBuilder',$name);
        }
        private static function _quantize(int $i,int $by=16):int {return round($i/$by)*$by;}
    }
    
    function createRow($y,$color) {
        $colCount=100;
        $row = [];
    
        for($x=0; $x<$colCount; $x++) {
            $row[] = WriterEntityFactory::createCell('*', (new StyleBuilder())->setBackgroundColor(ColorBuilder::$color($x,$y))->build());
        }
        return $row;
    }
    
    function buildSheet($name) {
        if (!ColorBuilder::validate($name)) {throw new Error('Invalid color provider');}
    
        $writer = WriterEntityFactory::createXLSXWriter();
        $writer->openToFile('output/'.$name.'.xlsx');
    
        for($y=0; $y<150; $y++) {
            $writer->addRow(WriterEntityFactory::createRow(createRow($y,$name)));
        }
    
        $writer->close();
    }
    
    buildSheet($choice);
    echo 'Peak memory usage: '.round(memory_get_peak_usage() / 1024).' KB';
    

    Tech:

PHP 7.4.2 CLI, Sprout: 3.1.0, Win: 7 x64 (I know), Coffee: Venti Dark
© www.soinside.com 2019 - 2024. All rights reserved.