box/spout - 使用十六进制颜色与预定义颜色时会占用大量内存
box/spout - Massive memory usage when using hex color vs pre-defined colors
我正在使用 Box/Spout 库,与使用预定义颜色(例如 [=36)相比,使用具有自定义十六进制颜色(例如 0000FF 表示蓝色)的 StyleBuilder 似乎会占用大量内存=].为什么会这样?
相关片段:
//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';
?>
tl;博士
虽然 Spout
可以改进,Excel isn't designed for large quantities of styles, so this isn't really a flaw of the library (you might want to rescind the issue)
故事
好吧,这里有一些东西在起作用..我用来测试的代码在我的 post 的底部 - 关键相关的颜色函数是 jonEg
和 jonEgQuant
(里面是一个可以调的$by
变量)
Excel 中的样式有点像 HTML+CSS。样式最初在文件中定义(在 header 块中),然后在工作表中通过名称 (class) 引用。从根本上说,这表明 Excel 存储格式并不是为过多的样式而设计的,而只是为许多单元重复使用的少数样式
Spout
向用户隐藏 style-definition 复杂性 - 如果样式已经定义(即 一个确切的样式已经存在 ) 然后它会重用该定义,否则会定义一个新样式
Spout
建立所有样式的索引,通过serializing样式object存储在数组中。在每个样式大约 1749 字节的情况下,该索引会占用内存(除了它在内存中生成文件的实际样式 objects 之外)- 这可以在 Spout
[ 中得到改进=124=]
使用这段代码,我保证每个单元格都有不同的样式(使用 row/column 位置来定义颜色的红色和绿色分量)- 比你的 image-color-picking 更极端,但可能不多 (没有样本图像我无法判断)。 RGB 三元组有 1600 万种颜色可用。但我们的眼睛不能总是检测到几个点的差异。例如,在红色渐变中,255,254,253...128 看起来很平滑,但随机分布的单个块 255,254,253 可能看起来像单一颜色。对于除原色以外的任何颜色,此差异现在适用于三个维度(r、g 和 b)。 JPEG format 利用了这一点(压缩和重建噪声的组合)——所以颜色选择可能看起来像一个统一的块仍然 return 每次
[=121] 略有不同的值=]
当你 运行 我的代码 jonEg
你得到 (100*150+1) 15001 种样式,需要 (15001*1749/1024/1024) ~25MB仅 Spout
中索引的内存。同样需要这个索引来防止 Excel 中的每个单元格都有自己的样式(当然,在这个人为的例子中我已经确保它没有意义 - 每个单元格都有自己的样式) - 这使用 ~100MB 内存
当你 运行 我的代码 jonEgQuant
(留下 $by=16;
)时,只需要 71 种样式(但这是非常极端的四舍五入,只允许 4096 种颜色总共),- 这使用 ~2MB 内存
当你设置$by=4
时(在ColorBuilder::jonEgQuant
内)-你现在有四分之一百万种颜色,需要989种样式,使用~ 7MB 内存(更重要的是,在 Excel 中打开时这看起来类似于 jonEg
)
Spout
包含一种将 RGB 十进制值转换为字符串的方法 Color::rgb($r,$g,$b)
- 但这不会改变结果(spoutDoc
方法)
一种样式的尺寸比这多得多 - 我使用了背景颜色,但有前景(你的例子)、下划线、粗体、斜体、字体,font-size - 全部这些乘以样式数量(减少颜色 space 此处解决了您记录的问题,但可以通过更改其他样式组件来撤消)
外卖
问题不在于使用十六进制代码、RGB 值或常数,而是您使用的样式数量
减少您使用的样式数量 - Excel 预计数量不会很大,Spout
同样没有为此进行优化。压缩颜色(通过四舍五入值 jonEgQuant
)是一种途径
改进Spout
样式索引机制,但注意索引只消耗了一些内存——它不小,但不是唯一的贡献者——每个样式object 需要来生成结果。删除此样式缓存(对于这个人为的示例没有帮助)会使内存使用量达到 40MB(并生成相同的 Excel 文件)。 60% 的节省并不是什么,但更多的样式会导致更多的内存使用、更大的 Excel 文件和更慢的 opening/rendering 这些文件
测试笔记
我在测试时放弃了图像 reading/color 收获,因为它可能会增加内存使用量,但不会增加问题的实质
使用$colCount=250;
(从你的例子)你超过了PHP中的128MB默认内存限制,最好设置它$colCount=100;
并且你可以运行 不改变设置的所有测试
我切换到设置background-color,在Excel
中打开更容易看出区别
对于每个颜色生成器(jonEg
、spoutDoc
、fixedEg
、jonEgQuant
)一个不同的 XLSX 是基因ate(有助于查看文件大小和渲染差异)
文件大小与 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 spoutDoc($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';
技术: PHP 7.4.2 CLI, Spout: 3.1.0, Win: 7 x64 (I know), Coffee: Venti Dark
我正在使用 Box/Spout 库,与使用预定义颜色(例如 [=36)相比,使用具有自定义十六进制颜色(例如 0000FF 表示蓝色)的 StyleBuilder 似乎会占用大量内存=].为什么会这样?
相关片段:
//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';
?>
tl;博士
虽然 Spout
可以改进,Excel isn't designed for large quantities of styles, so this isn't really a flaw of the library (you might want to rescind the issue)
故事
好吧,这里有一些东西在起作用..我用来测试的代码在我的 post 的底部 - 关键相关的颜色函数是 jonEg
和 jonEgQuant
(里面是一个可以调的$by
变量)
Excel 中的样式有点像 HTML+CSS。样式最初在文件中定义(在 header 块中),然后在工作表中通过名称 (class) 引用。从根本上说,这表明 Excel 存储格式并不是为过多的样式而设计的,而只是为许多单元重复使用的少数样式
Spout
向用户隐藏 style-definition 复杂性 - 如果样式已经定义(即 一个确切的样式已经存在 ) 然后它会重用该定义,否则会定义一个新样式Spout
建立所有样式的索引,通过serializing样式object存储在数组中。在每个样式大约 1749 字节的情况下,该索引会占用内存(除了它在内存中生成文件的实际样式 objects 之外)- 这可以在Spout
[ 中得到改进=124=]使用这段代码,我保证每个单元格都有不同的样式(使用 row/column 位置来定义颜色的红色和绿色分量)- 比你的 image-color-picking 更极端,但可能不多 (没有样本图像我无法判断)。 RGB 三元组有 1600 万种颜色可用。但我们的眼睛不能总是检测到几个点的差异。例如,在红色渐变中,255,254,253...128 看起来很平滑,但随机分布的单个块 255,254,253 可能看起来像单一颜色。对于除原色以外的任何颜色,此差异现在适用于三个维度(r、g 和 b)。 JPEG format 利用了这一点(压缩和重建噪声的组合)——所以颜色选择可能看起来像一个统一的块仍然 return 每次
[=121] 略有不同的值=]当你 运行 我的代码
jonEg
你得到 (100*150+1) 15001 种样式,需要 (15001*1749/1024/1024) ~25MB仅Spout
中索引的内存。同样需要这个索引来防止 Excel 中的每个单元格都有自己的样式(当然,在这个人为的例子中我已经确保它没有意义 - 每个单元格都有自己的样式) - 这使用 ~100MB 内存当你 运行 我的代码
jonEgQuant
(留下$by=16;
)时,只需要 71 种样式(但这是非常极端的四舍五入,只允许 4096 种颜色总共),- 这使用 ~2MB 内存当你设置
$by=4
时(在ColorBuilder::jonEgQuant
内)-你现在有四分之一百万种颜色,需要989种样式,使用~ 7MB 内存(更重要的是,在 Excel 中打开时这看起来类似于jonEg
)Spout
包含一种将 RGB 十进制值转换为字符串的方法Color::rgb($r,$g,$b)
- 但这不会改变结果(spoutDoc
方法)一种样式的尺寸比这多得多 - 我使用了背景颜色,但有前景(你的例子)、下划线、粗体、斜体、字体,font-size - 全部这些乘以样式数量(减少颜色 space 此处解决了您记录的问题,但可以通过更改其他样式组件来撤消)
外卖
问题不在于使用十六进制代码、RGB 值或常数,而是您使用的样式数量
减少您使用的样式数量 - Excel 预计数量不会很大,
Spout
同样没有为此进行优化。压缩颜色(通过四舍五入值jonEgQuant
)是一种途径改进
Spout
样式索引机制,但注意索引只消耗了一些内存——它不小,但不是唯一的贡献者——每个样式object 需要来生成结果。删除此样式缓存(对于这个人为的示例没有帮助)会使内存使用量达到 40MB(并生成相同的 Excel 文件)。 60% 的节省并不是什么,但更多的样式会导致更多的内存使用、更大的 Excel 文件和更慢的 opening/rendering 这些文件
测试笔记
我在测试时放弃了图像 reading/color 收获,因为它可能会增加内存使用量,但不会增加问题的实质
使用
$colCount=250;
(从你的例子)你超过了PHP中的128MB默认内存限制,最好设置它$colCount=100;
并且你可以运行 不改变设置的所有测试我切换到设置background-color,在Excel
中打开更容易看出区别
对于每个颜色生成器(
jonEg
、spoutDoc
、fixedEg
、jonEgQuant
)一个不同的 XLSX 是基因ate(有助于查看文件大小和渲染差异)文件大小与 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 spoutDoc($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';
技术: PHP 7.4.2 CLI, Spout: 3.1.0, Win: 7 x64 (I know), Coffee: Venti Dark