PHP中矩阵的内存高效数据结构

时间:2013-05-25 14:25:32

标签: php performance memory matrix

我的内存不足,在PHP中使用80k x 20矩阵(数组)的整数值。有解决方案吗?

背景

我有一个PHP应用程序,它收集数据并将其存储到数据库中。数据收集在不同的域中(> 20k)。变量的数量因域而异(主要是无限制的),所以我必须在我的MySQL数据库中存储以逗号分隔的列表(在版本5之前)。这表现得很好。

在某些时候,用户需要下载数据。下载功能必须执行一些规范化,因此它需要每个变量的中位数(不是平均值!)(实际上是变量子集的中位数)。通常我可以轻松地从数据库中读取数据,爆炸()逗号分隔数据并将中值相关数据存储到数组[var] [row]中。我可以排序()数组,我得到了中位数。

但是,有一个域没有100或1000个数据记录(行),但是80K。给定20个中值相关变量,这是1.6M整数值(32位)或51 MB原始整数数据(可能是两倍,因为我正在使用64位Linux机器)。到目前为止,这么好 - 但阵列结构有一些开销,所以它变得比128 MB大得多。这就是我的PHP内存不足的地方。

我不想做的事

当然,我可以增加每个PHP脚本的内存限制。出于各种原因,我想避免这种情况。

还有一些算法不需要存储n个值来计算中位数,但是对n / 2(+ x)​​感到满意,但是将内存负载减少到50%+ X可能不足以解决问题

我还可以计算每个变量的中位数变量。但这需要我从数据库加载80K行数据20次并再次执行explode()。这将大大增加脚本运行时间。

[编辑]数据库当前未规范化(使用每个数据行的CSV数据)。出于性能原因,这是有意和必要的。因此,我不喜欢规范化数据库,因为这将导致一个包含100M条目和巨大索引的表。

我想做什么

我们所说的不超过51 MB的原始32位整数值。是否有任何变化可以将开销减少到几个百分点?甚至可能在64位机器上?

我知道自PHP 5.0.0以来可用的SPL扩展,但我还没有找到解决方案如何使用此扩展来节省内存。任何人都可以给我一个提示 - 通过SPL或使用其他解决方案(理想情况下在PHP中可用)?

示例代码

private function retrieveReferences() {
    $query = $this->getResultsQuery(true);
    $times = array();
    $tp = -1; // Length of $times - 1
    while ($row = $query->fetchArray()) {
        $timeSrc = explode(',', $row['times']);

        // Store the times per page
        foreach ($timeSrc as $p=>$s) {
            // Should be faster than checking isset $times[$p] all the time
            while ($p > $tp) {
                $times[] = array();
                $tp = count($times) - 1;
            }
            $times[$p][] = (int)$s;
        }
    }
    // Compute median for each $times[$p]
    // <snip>
}

3 个答案:

答案 0 :(得分:0)

  • 数组至少消耗104 bytes,因此数组越少,内存使用量就越少。

    切换到1维数组会节省一些内存。只需使用$times[$item*$n_items + $sub_item]而非$times[$item][$sub_item]计算的索引访问元素。

  • 完成后,很容易切换到SplFixedArrays,这几乎可以消除标准数组的内存开销(标准数组每个元素最多消耗90 bytes)。

  • 你甚至可以通过打包字符串中的所有整数来消除zval开销

答案 1 :(得分:0)

http://we-love-php.blogspot.de/2012/06/php-memory-consumption-with-arrays.html上找到的解决方案实际上证明非常有用。我认为值得,发布在这里:

  

PHP中最有效的数组类型是字符串。

实际上在字符串中存储大量整数值(每个整数4个字节或字符)可以节省超过80%(!)的内存。当然有很多缺点:你需要对整数进行编码/解码,排序需要自定义函数等。因此,只有当内存真正重要时,这个解决方案才有意义。

就我而言,16位整数就足够了,所以我可以用2个字符对每个整数进行编码。以下是我使用的示例代码:

private function retrieveReferences() {
    $query = $this->getResultsQuery(true);
    $timesSE = array();
    $tp = -1; // Length of $times - 1
    while ($row = $query->fetchArray()) {
        $timeSrc = explode(',', $row['times']);

        // Store the times per page
        foreach ($timeSrc as $p=>$s) {
            // Should be faster than checking isset $times[$p] all the time
            while ($p > $tp) {
                $timesSE[] = '';
                $tp = count($timesSE) - 1;
            }
            // Save to limit to 16bit (in this specific case)
            $i = (int)$s;
            if ($i > 0xFFFF) {
                $i = 0xFFFF;
            }
            $timesSE[$p].= chr(($i & 0xFF00) >> 8).chr($i & 0x00FF);
        }
    }
    // Compute median for each $times[$p]
    // <snip>
}

答案 2 :(得分:0)

您可能会达到内存限制,因为数据最低为6.5M

需要看看你的php.ini文件。最初默认为8M

请参阅http://www.php.net/manual/en/ini.core.php#ini.memory-limit

相关问题