minor #23582 [Console] Improve Table performance (ro0NL)

This PR was submitted for the 3.4 branch but it was merged into the 4.1-dev branch instead (closes #23582).

Discussion
----------

[Console] Improve Table performance

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | might
| New feature?  | might as well
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!--highly recommended for new features-->

Little memory gain for console tables :)

`setRows(array_fill(0, 1000000, [str_repeat('x', 100)]))`

Before: +-20ms / +- 900mb
After: +- 20ms / +- 530mb

Next step could be to open public API for `iterable`, although we pre-iterate rows for calculation interally, it could be a nice feature :)

Commits
-------

2c9922e8af [Console] Improve Table performance
This commit is contained in:
Fabien Potencier 2018-02-07 07:27:49 +01:00
commit c44a894258
2 changed files with 71 additions and 34 deletions

View File

@ -275,29 +275,40 @@ class Table
*/
public function render()
{
$this->calculateNumberOfColumns();
$rows = $this->buildTableRows($this->rows);
$headers = $this->buildTableRows($this->headers);
$rows = array_merge($this->headers, array($divider = new TableSeparator()), $this->rows);
$this->calculateNumberOfColumns($rows);
$this->calculateColumnsWidth(array_merge($headers, $rows));
$rows = $this->buildTableRows($rows);
$this->calculateColumnsWidth($rows);
$this->renderRowSeparator();
if (!empty($headers)) {
foreach ($headers as $header) {
$this->renderRow($header, $this->style->getCellHeaderFormat());
$this->renderRowSeparator();
}
}
$isHeader = true;
$isFirstRow = false;
foreach ($rows as $row) {
if ($divider === $row) {
$isHeader = false;
$isFirstRow = true;
continue;
}
if ($row instanceof TableSeparator) {
$this->renderRowSeparator();
} else {
$this->renderRow($row, $this->style->getCellRowFormat());
continue;
}
if (!$row) {
continue;
}
if ($isHeader || $isFirstRow) {
$this->renderRowSeparator();
if ($isFirstRow) {
$isFirstRow = false;
}
}
$this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat());
}
if (!empty($rows)) {
$this->renderRowSeparator();
}
$this->renderRowSeparator();
$this->cleanup();
}
@ -340,10 +351,6 @@ class Table
*/
private function renderRow(array $row, string $cellFormat)
{
if (empty($row)) {
return;
}
$rowContent = $this->renderColumnSeparator();
foreach ($this->getRowColumns($row) as $column) {
$rowContent .= $this->renderCell($row, $column, $cellFormat);
@ -386,14 +393,10 @@ class Table
/**
* Calculate number of columns for this table.
*/
private function calculateNumberOfColumns()
private function calculateNumberOfColumns($rows)
{
if (null !== $this->numberOfColumns) {
return;
}
$columns = array(0);
foreach (array_merge($this->headers, $this->rows) as $row) {
foreach ($rows as $row) {
if ($row instanceof TableSeparator) {
continue;
}
@ -429,15 +432,17 @@ class Table
}
}
$tableRows = array();
foreach ($rows as $rowKey => $row) {
$tableRows[] = $this->fillCells($row);
if (isset($unmergedRows[$rowKey])) {
$tableRows = array_merge($tableRows, $unmergedRows[$rowKey]);
}
}
return new TableRows(function () use ($rows, $unmergedRows) {
foreach ($rows as $rowKey => $row) {
yield $this->fillCells($row);
return $tableRows;
if (isset($unmergedRows[$rowKey])) {
foreach ($unmergedRows[$rowKey] as $row) {
yield $row;
}
}
}
});
}
/**

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Helper;
/**
* @internal
*/
class TableRows implements \IteratorAggregate
{
private $generator;
public function __construct(callable $generator)
{
$this->generator = $generator;
}
public function getIterator()
{
$g = $this->generator;
return $g();
}
}