在PHP中需要一个类似数组的结构,并且内存使用量最小

在PHP中需要一个类似数组的结构,并且内存使用量最小,php,arrays,memory-management,out-of-memory,Php,Arrays,Memory Management,Out Of Memory,在我的PHP脚本中,我需要创建一个大于600k整数的数组。不幸的是,我的Web服务器内存\u限制设置为32M,因此在初始化数组时,脚本将中止并显示消息 致命错误:允许的内存大小为33554432字节,在/home/www/myaccount/html/mem_test.php在线8 我知道,PHP不会将数组值存储为普通整数,而是存储为比普通整数值大得多的zvalue(在我的64位系统中为8字节)。我写了一个小脚本来估计每个数组条目使用了多少内存,结果是,它正好是128字节。128!!! 我需要>

在我的PHP脚本中,我需要创建一个大于600k整数的数组。不幸的是,我的Web服务器
内存\u限制
设置为32M,因此在初始化数组时,脚本将中止并显示消息

致命错误:允许的内存大小为33554432字节,在/home/www/myaccount/html/mem_test.php在线8

我知道,PHP不会将数组值存储为普通整数,而是存储为比普通整数值大得多的zvalue(在我的64位系统中为8字节)。我写了一个小脚本来估计每个数组条目使用了多少内存,结果是,它正好是128字节。128!!! 我需要>73M才能存储阵列。不幸的是,Web服务器不在我的控制之下,因此我无法增加
内存限制

我的问题是,在PHP中是否有可能创建一个使用更少内存的类似数组的结构。我不需要这个结构是关联的(普通索引访问就足够了)。它也不需要动态调整大小-我确切地知道阵列有多大。而且,所有元素的类型都相同。就像一个很好的旧C数组


编辑: 因此,我们的解决方案是用32位整数开箱即用。但即使您在64位系统上,也不支持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))));
}
功能推回(&$storage,$value)
{
//将64位值拆分为两个32位块,然后将它们传递给pack()。
$storage.=pack('ll',($value>>32),$value);
}
函数get(&$storage,$idx)
{
//从$storage读取两个32位块,并将它们粘在一起。

return(current(unpack('l',substr($storage,$idx*8,4))您可以尝试使用a,它更快,占用的内存更少(文档注释说大约减少30%)。如果可能的话,可以使用对象。它们通常比数组占用的内存更少。 这也是一个不错的选择

但这实际上取决于您需要执行的实现。如果您需要一个函数来返回数组,并且正在使用PHP 5.5,则可以使用来流式返回数组。

A将使用比标准PHP数组和SplFixedArray少得多的内存


我引用“使用常规PHP数组数据结构的具有100万个条目的数组需要200MB。SplFixedArray需要大约90兆字节。Judy需要8兆字节。折衷是性能,Judy需要大约两倍于常规PHP数组实现的时间。”

您将获得的最有效的内存可能是将所有内容存储在一个字符串中,以二进制格式打包,并使用手动索引

$storage = '';

$storage .= pack('l', 42);

// ...

// get 10th entry
$int = current(unpack('l', substr($storage, 9 * 4, 4)));
如果“数组”初始化可以一次性完成,并且您只是从结构中读取,那么这是可行的。如果您需要大量的字符串附加,这将变得非常低效。即使使用资源句柄也可以做到:

$storage = fopen('php://memory', 'r+');
fwrite($storage, pack('l', 42));
...

这是非常有效的。然后,您可以将此缓冲区读回变量并将其用作字符串,或者您可以继续使用资源并使用字符串(我想应该是16或20位)并使用substr获取所需的数据。极快的写/读、超级简单和600.000个整数只需约12M即可存储

base\u convert()-如果您需要更紧凑的东西,但需要尽可能少的努力,请将整数转换为base-36而不是base-10;在这种情况下,14位数字将存储在9个字母数字字符中。您需要两个64位整数,但我确信这不是问题。(我会把它们分成9位数的块,在这里转换为6个字符的版本。)


pack()/unpack()-二进制压缩也是一回事,效率更高。如果没有其他方法可以使用,请使用它;将数字拆分为两个32位的部分。

根据生成整数的方式,您可以潜在地使用它,假设您正在遍历数组并使用单个值执行某些操作。

600K是一个非常复杂的过程元素。如果你对其他方法持开放态度,我个人会使用数据库。然后使用标准sql/nosql select语法来解决问题。如果你有一个简单的主机,比如garantiaadata.com,可能是memcache或redis。可能是APC。

我用@deceze给出了答案,并将其封装在一个可以处理32位整数的类中它是仅附加的,但您仍然可以将其用作一个简单的、内存优化的PHP数组、队列或堆,并且没有内存开销。我添加了currentPosition/currentSize以避免不必要的fseek函数调用。如果需要限制内存使用并自动切换到临时文件,请改用

类内存优化阵列
{
私有存储;
私人$\u当前职位;
私有$currentSize;
const BYTES_PER_ENTRY=4;
函数_u构造()
{
$this->_storage=fopen('php://memory","rw+",;
$this->_currentPosition=0;
$this->_currentSize=0;
}
函数_udestruct()
{
fclose($this->\u存储);
}
函数AppendItem($value)
{
如果($this->\u currentPosition!=$this->\u currentSize)
{
fseek($this->\u存储,SEEK\u结束);
}
fwrite($this->_存储,pack('l',$value));
$this->\u currentSize+=self::BYTES\u每个\u条目;
$this->\u currentPosition=$this->\u currentSize;
}
函数ItemAt($index)
{
$itemPosition=$index*self::BYTES\u PER\u条目;
如果($this->\u currentPosition!=$itemPosition)
{
fseek($this->\u storage,$itemPosition);
}
$binaryData=fread($this->\u存储,self::BYTES\P
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");
}