我有一个长时间运行的PHP守护进程,它有一个扩展ArrayIterator的集合类。这包含一组自定义Column对象,通常小于1000个。通过xdebug分析器运行它,我发现我的find方法消耗了大约35%的循环的35%。
如何以优化的方式在内部迭代这些项?
class ColumnCollection extends \ArrayIterator
{
public function find($name)
{
$return = null;
$name = trim(strtolower($name));
$this->rewind();
while ($this->valid()) {
/** @var Column $column */
$column = $this->current();
if (strtolower($column->name) === $name) {
$return = $column;
break;
}
$this->next();
}
$this->rewind();
return $return;
}
}发布于 2017-03-09 15:21:01
您的find()方法显然只是返回带有查询$name的第一个列对象。在这种情况下,按名称对Array进行索引可能是有意义的,例如,将对象的名称存储为键。然后,查找变成O(1)调用。
ArrayIterator实现了ArrayAccess。这意味着您可以向集合中添加如下新项:
$collection = new ColumnCollection;
$collection[$someCollectionObject->name] = $someCollectionObject;并通过方括号符号检索它们:
$someCollectionObject = $collection["foo"];如果不想更改客户端代码,只需在offsetSet中重写ColumnCollection:
public function offsetSet($index, $newValue)
{
if ($index === null && $newValue instanceof Column) {
return parent::offsetSet($newValue->name, $newValue);
}
return parent::offsetSet($index, $newValue);
}这样,执行$collection[] = $column将自动按名称添加$column。有关演示,请参见http://codepad.org/egAchYpk。
如果使用append()方法添加新元素,只需将其更改为:
public function append($newValue)
{
parent::offsetSet($newValue->name, $newValue);
}但是,ArrayAccess比本机数组访问慢,因此您可能需要将ColumnCollection更改为如下所示:
class ColumnCollection implements IteratorAggregate
{
private $columns = []; // or SplObjectStorage
public function add(Column $column) {
$this->columns[$column->name] = $column;
}
public function find($name) {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
public function getIterator()
{
return new ArrayIterator($this->data);
}
}发布于 2017-03-09 14:15:13
我将迭代器方法调用替换为数组副本上的一个循环。我认为这可以直接访问内部存储,因为PHP实现了复制即写。本机foreach比调用rewind()、valid()、current()和next()快得多。此外,预计算列对象上的strtolower也有所帮助。这使得性能从35%的循环下降到0.14%的。
public function find($name)
{
$return = null;
$name = trim(strtolower($name));
/** @var Column $column */
foreach ($this->getArrayCopy() as $column) {
if ($column->nameLower === $name) {
$return = $column;
break;
}
}
return $return;
}此外,还尝试使用@Gordon的建议,使用以名称为键的数组,而不是使用内部存储。以上是一个简单的插入替换的良好工作。
https://stackoverflow.com/questions/42697550
复制相似问题