在PHP中需要一个类似于数组的结构,并且使用最less的内存
在我的PHP脚本中,我需要创build一个> 600k整数的数组。 不幸的是,我的web服务器memory_limit
被设置为32M,所以当初始化数组脚本中止消息
致命错误:在第8行的/home/www/myaccount/html/mem_test.php中允许的内存大小为33554432个字节(尽力分配71个字节)
我意识到这样的事实,即PHP不会将数组值存储为纯整数,而是将其存储为比纯整数值(我的64位系统上的8个字节)大得多的z值。 我写了一个小脚本来估计每个数组入口使用多less内存,事实certificate,它恰好是128个字节。 128! 我需要大于73M才能存储arrays。 不幸的是,networking服务器不在我的控制之下,所以我不能增加memory_limit
。
我的问题是,是否有可能在PHP中创build一个使用较less内存的类似数组的结构。 我不需要这个结构是关联的(普通的索引访问是足够的)。 它也不需要dynamicresize – 我知道这个数组到底有多大。 而且,所有的元素都是相同的types。 就像一个很好的旧Carrays。
编辑:这样欺骗的解决scheme与32位整数开箱即用。 但是,即使你在64位系统上, pack()似乎也不支持64位整数。 为了在我的数组中使用64位整数,我应用了一些位操作。 也许下面的片段将有助于某人:
function push_back(&$storage, $value) { // split the 64-bit value into two 32-bit chunks, then pass these to pack(). $storage .= pack('ll', ($value>>32), $value); } function get(&$storage, $idx) { // read two 32-bit chunks from $storage and glue them back together. return (current(unpack('l', substr($storage, $idx * 8, 4)))<<32 | current(unpack('l', substr($storage, $idx * 8+4, 4)))); }
您将获得的最高内存效率可能是将所有内容都存储在一个string中,并以二进制打包,然后使用手动build立索引。
$storage = ''; $storage .= pack('l', 42); // ... // get 10th entry $int = current(unpack('l', substr($storage, 9 * 4, 4)));
如果“arrays”初始化可以一举完成,并且您只是从结构中读取数据,这是可行的。 如果你需要大量附加到string,这将变得非常低效。 即使这可以通过使用资源句柄来完成:
$storage = fopen('php://memory', 'r+'); fwrite($storage, pack('l', 42)); ...
这非常有效。 然后,您可以将此缓冲区读回variables并将其用作string,也可以继续使用资源和fseek
。
PHP Judy数组将比标准PHP数组和SplFixedArray使用更less的内存。
我引用了“使用常规PHP数组结构的数组一百万个条目的数组需要200MB,SplFixedArray使用了大约90兆,Judy使用了8百万条,tradeoff在性能方面,Judy大约是常规PHP数组实现的两倍。
如果可能,你可以使用一个对象。 这些通常使用比数组less的内存。 另外SplFixedArray是一个不错的select。
但是这取决于你需要做的实现。 如果你需要一个函数来返回一个数组,并使用PHP 5.5。 您可以使用生成器良率来返回数组。
您可以尝试使用SplFixedArray ,速度更快,占用更less的内存(文档评论说减less了30%)。 在这里和这里testing。
我会把它存储在一个固定的偏移量的string,并使用substr来获得所需的一个。 快速写/快速读取,less内存,也许有点不雅,但完美的作品。 只要你能够承担intval(…)读取,当然。
600K是很多元素。 如果你打开其他方法,我个人会使用一个数据库。 然后使用标准的sql / nosqlselect语法来提取出来。 也许memcache或redis,如果你有一个容易的主机,如garantiadata.com。 也许APC。
根据你如何生成整数,你可能会使用PHP的生成器 ,假设你正在遍历数组,并使用单独的值做一些事情。
我通过@deceze得到了答案,并将其包装在一个可处理32位整数的类中。 它是仅附加的,但仍然可以将其用作简单的,内存优化的PHP Array,Queue或Heap。 AppendItem和ItemAt都是O(1),并且没有内存开销。 我添加了currentPosition / currentSize来避免不必要的fseek函数调用。 如果需要限制内存使用并自动切换到临时文件,请改用php:// temp 。
class MemoryOptimizedArray { private $_storage; private $_currentPosition; private $_currentSize; const BYTES_PER_ENTRY = 4; function __construct() { $this->_storage = fopen('php://memory', 'rw+'); $this->_currentPosition = 0; $this->_currentSize = 0; } function __destruct() { fclose($this->_storage); } function AppendItem($value) { if($this->_currentPosition != $this->_currentSize) { fseek($this->_storage, SEEK_END); } fwrite($this->_storage, pack('l', $value)); $this->_currentSize += self::BYTES_PER_ENTRY; $this->_currentPosition = $this->_currentSize; } function ItemAt($index) { $itemPosition = $index * self::BYTES_PER_ENTRY; if($this->_currentPosition != $itemPosition) { fseek($this->_storage, $itemPosition); } $binaryData = fread($this->_storage, self::BYTES_PER_ENTRY); $this->_currentPosition = $itemPosition + self::BYTES_PER_ENTRY; $unpackedElements = unpack('l', $binaryData); return $unpackedElements[1]; } } $arr = new MemoryOptimizedArray(); for($i = 0; $i < 3; $i++) { $v = rand(-2000000000,2000000000); $arr->AddToEnd($v); print("added $v\n"); } for($i = 0; $i < 3; $i++) { print($arr->ItemAt($i)."\n"); } for($i = 2; $i >=0; $i--) { print($arr->ItemAt($i)."\n"); }