1
0
Fork 0
mirror of https://github.com/composer/composer synced 2025-05-10 09:02:59 +00:00

Improve memory usage resolving dependencies

It is known that composer update takes a lot of memory: #5915, #5902,

I am playing with a profiler (@blackfireio) to make a demo in my local
PHP meetup (@phpvigo) and I found out a way to use less memory. These
are my first tests:

* Private project using PHP 5.6:
  * Memory: from 1.31GB to 1.07GB
  * Wall Time: from 2min 8s to 1min 33s

* symfony-demo using PHP 7.1 in my old mac book:
  * Memory: from 667MB to 523MB
  * Wall Time: from  5min 29s to 5min 28s

Not use an array inside conflict rules is this improvement main idea:

```php
<?php
//Memory 38MB
gc_collect_cycles();
gc_disable();

class Rule
{
    public $literals;

    public function __construct(array $literals)
    {
        $this->literals = $literals;
    }
}

$rules = array();

$i = 0;
while ($i<80000){ //
    $i++;

    $array = array(-$i, $i);
    $rule = new Rule($array);
    $rules[] = $rule;
}
```

```php
<?php
//Memory 11.1MB
gc_collect_cycles();
gc_disable();

class Rule2Literals
{
    public $literal1;
    public $literal2;

    public function __construct($literal1, $literal2)
    {
        $this->literal1 = $literal1;
        $this->literal2 = $literal2;
    }
}

$rules = array();

$i = 0;
while ($i<80000){ //
    $i++;

    $rule = new ConflictRule(-$i, $i);
    $rules[] = $rule;
}
```

More info https://github.com/composer/composer/pull/6168
This commit is contained in:
rubenrua 2017-02-16 22:00:57 +00:00
parent 363bab90fa
commit 4e1887a721
10 changed files with 263 additions and 126 deletions

View file

@ -13,13 +13,12 @@
namespace Composer\DependencyResolver;
use Composer\Package\CompletePackage;
use Composer\Package\PackageInterface;
use Composer\Package\Link;
/**
* @author Nils Adermann <naderman@naderman.de>
* @author Ruben Gonzalez <rubenrua@gmail.com>
*/
class Rule
abstract class Rule
{
// reason constants
const RULE_INTERNAL_ALLOW_UPDATE = 1;
@ -39,27 +38,16 @@ class Rule
const BITFIELD_REASON = 8;
const BITFIELD_DISABLED = 16;
/**
* READ-ONLY: The literals this rule consists of.
* @var array
*/
public $literals;
protected $bitfield;
protected $reasonData;
/**
* @param array $literals
* @param int $reason A RULE_* constant describing the reason for generating this rule
* @param Link|PackageInterface $reasonData
* @param array $job The job this rule was created from
*/
public function __construct(array $literals, $reason, $reasonData, $job = null)
public function __construct($reason, $reasonData, $job = null)
{
// sort all packages ascending by id
sort($literals);
$this->literals = $literals;
$this->reasonData = $reasonData;
if ($job) {
@ -71,18 +59,17 @@ class Rule
(255 << self::BITFIELD_TYPE);
}
public function getHash()
{
$data = unpack('ihash', md5(implode(',', $this->literals), true));
abstract public function getLiterals();
return $data['hash'];
}
abstract public function getHash();
public function getJob()
{
return isset($this->job) ? $this->job : null;
}
abstract public function equals(Rule $rule);
public function getReason()
{
return ($this->bitfield & (255 << self::BITFIELD_REASON)) >> self::BITFIELD_REASON;
@ -104,29 +91,6 @@ class Rule
}
}
/**
* Checks if this rule is equal to another one
*
* Ignores whether either of the rules is disabled.
*
* @param Rule $rule The rule to check against
* @return bool Whether the rules are equal
*/
public function equals(Rule $rule)
{
if (count($this->literals) != count($rule->literals)) {
return false;
}
for ($i = 0, $n = count($this->literals); $i < $n; $i++) {
if ($this->literals[$i] !== $rule->literals[$i]) {
return false;
}
}
return true;
}
public function setType($type)
{
$this->bitfield = ($this->bitfield & ~(255 << self::BITFIELD_TYPE)) | ((255 & $type) << self::BITFIELD_TYPE);
@ -157,15 +121,14 @@ class Rule
return !(($this->bitfield & (255 << self::BITFIELD_DISABLED)) >> self::BITFIELD_DISABLED);
}
public function isAssertion()
{
return 1 === count($this->literals);
}
abstract public function isAssertion();
public function getPrettyString(Pool $pool, array $installedMap = array())
{
$literals = $this->getLiterals();
$ruleText = '';
foreach ($this->literals as $i => $literal) {
foreach ($literals as $i => $literal) {
if ($i != 0) {
$ruleText .= '|';
}
@ -183,13 +146,12 @@ class Rule
return "Remove command rule ($ruleText)";
case self::RULE_PACKAGE_CONFLICT:
$package1 = $pool->literalToPackage($this->literals[0]);
$package2 = $pool->literalToPackage($this->literals[1]);
$package1 = $pool->literalToPackage($literals[0]);
$package2 = $pool->literalToPackage($literals[1]);
return $package1->getPrettyString().' conflicts with '.$this->formatPackagesUnique($pool, array($package2)).'.';
case self::RULE_PACKAGE_REQUIRES:
$literals = $this->literals;
$sourceLiteral = array_shift($literals);
$sourcePackage = $pool->literalToPackage($sourceLiteral);
@ -261,7 +223,7 @@ class Rule
case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
return $ruleText;
case self::RULE_PACKAGE_SAME_NAME:
return 'Can only install one of: ' . $this->formatPackagesUnique($pool, $this->literals) . '.';
return 'Can only install one of: ' . $this->formatPackagesUnique($pool, $literals) . '.';
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
return $ruleText;
case self::RULE_LEARNED:
@ -289,25 +251,4 @@ class Rule
return implode(', ', $prepared);
}
/**
* Formats a rule as a string of the format (Literal1|Literal2|...)
*
* @return string
*/
public function __toString()
{
$result = ($this->isDisabled()) ? 'disabled(' : '(';
foreach ($this->literals as $i => $literal) {
if ($i != 0) {
$result .= '|';
}
$result .= $literal;
}
$result .= ')';
return $result;
}
}