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