我正在计算大量日期对(开始和结束)列表,并且遇到了内存问题。 我目前的表示是基于字符串的,效率仍然很低:
目前我将两个日期序列化为 28 字节的manual-YYYY-MM-DD-YYYY-MM-DD
。 我可以通过切换到 16 将其降低到YYYYMMDDYYYYMMDD
.
但是,如果我现在要更改格式,我想知道是否有更有效的解决方案。理论上没有太多的日期,所以一个 4 字节的 int 应该足够了,让我减少到 8 个字节。
我最初决定使用字符串表示,因为它(a)人类可读,(b)易于重复数据删除(想想这样的句点和in_array
的数组)
为了给出数字的概念:我正在设置一个周期数组数组,因此一个包含 813.000 个数组的列表,总共包含 6500 万个此类项目(许多重复项,大多数时候都是从头到尾,例如 2021-05-01-2021-05-31)。所以我也很感兴趣,如果一些查找字典解决方案可能会有所帮助......
为了节省内存,您可以将日期存储为int
,表示自参考日期(例如最早日期)以来的天数,如果您事先知道它是什么,或者可能早于整个数据集的日期。
int
占用一个zend_value
的大小,PHP>= 7中的8个字节,因为它直接存储为zend_value
本身的一部分(参见zend_value
,这是_zval_struct
的一部分,又名zval
),因此根本没有内存开销。
这是一个值可以具有的最低内存占用,因为除了int
(lval
zend_value
,zend_long
)或double
(dval
在zend_value
中,double
)之外的任何内容都存储为指向单独分配的值的指针(*str
,*arr
,*obj
zend_value
等)。 它占用指针的大小(等于zend_value
的大小)和它所指向的结构的大小。
zend_string
) 采用:8 字节(gc
的zend_refcounted_h
大小)+ 8 字节(h
的zend_ulong
大小)+ 8 字节(len
的size_t
大小)+ 1 字节(val
的char
大小)= 25 字节的开销 + 8 字节(zend_value
的大小)= 空字符串的 33 个字节 + 每个字符多一个字节(或多字节的多个字节)字符串)。
您可以在空字符串占用的内存中容纳 4int
秒。
如果要准确计算 PHP 值占用的内存量,请记住,您需要考虑包含zend_value
的zval
增加的开销,这可能因 PHP 版本而异。
由于您有日期范围,您还可以考虑将范围表示为int
(类似于$first_day * 1000000 + $last_day
),从而节省用两个zval
来表示两个int
的开销。
查找表也应该有所帮助,特别是如果您有很多相似的日期。
您可以设置日期数组和索引数组。当你遇到一个日期时,查看index[date]是否存在,如果存在,那么该值就是日期数组中的索引,如果没有,则将其插入日期数组并存储索引。像这样:
if (!isset($index[$date])) {
$dates []= $date;
$index[$date] = count($dates) - 1;
}
$date_index = $index[$date];
现在,您可以存储$date_index
es而不是完整日期,并且可以在需要时引用$index
来检索日期。
日期不必在内存中是人类可读的,只需在显示它们时即可。
因此,您可以编写一个显示函数来执行以人类可读形式显示数据所需的一切:使用$date_index
从$dates
获取日期,然后将其从其内部表示形式转换为可读字符串。
我也鼓励你考虑El_Vanja的评论。这听起来像是一个长时间运行的任务,而不是用户需要看到的对请求的响应,所以也许你可以以较小的批次处理数据,而不是一次处理所有数据。
如果输入没有更改,因此计算结果也不会更改,则还可以考虑缓存结果,这样您就不必再次执行所有这些操作。在这种情况下,您还可以考虑在大量数据条目可用后立即执行计算和缓存结果,这应该使用更少的处理能力,并且结果也更快可用。