Merge pull request #4609 from Fuzuki785/fix/4607

Performance improvement when working with large amounts of cells
This commit is contained in:
oleibman
2025-09-07 06:52:12 +00:00
committed by GitHub
2 changed files with 91 additions and 5 deletions
+70 -5
View File
@@ -43,6 +43,25 @@ class Cells
*/
private array $index = [];
/**
* Flag to avoid sorting the index every time.
*/
private bool $indexSorted = false;
/**
* Index keys cache to avoid recalculating on large arrays.
*
* @var null|string[]
*/
private ?array $indexKeysCache = null;
/**
* Index values cache to avoid recalculating on large arrays.
*
* @var null|int[]
*/
private ?array $indexValuesCache = null;
/**
* Prefix used to uniquely identify cache data for this worksheet.
*/
@@ -112,6 +131,10 @@ class Cells
unset($this->index[$cellCoordinate]);
// Clear index caches
$this->indexKeysCache = null;
$this->indexValuesCache = null;
// Delete the entry from cache
$this->cache->delete($this->cachePrefix . $cellCoordinate);
}
@@ -123,7 +146,12 @@ class Cells
*/
public function getCoordinates(): array
{
return array_keys($this->index);
// Build or rebuild index keys cache
if ($this->indexKeysCache === null) {
$this->indexKeysCache = array_keys($this->index);
}
return $this->indexKeysCache;
}
/**
@@ -133,9 +161,21 @@ class Cells
*/
public function getSortedCoordinates(): array
{
asort($this->index);
// Sort only when required
if (!$this->indexSorted) {
asort($this->index);
$this->indexSorted = true;
// Clear unsorted cache
$this->indexKeysCache = null;
$this->indexValuesCache = null;
}
return array_keys($this->index);
// Build or rebuild index keys cache
if ($this->indexKeysCache === null) {
$this->indexKeysCache = array_keys($this->index);
}
return $this->indexKeysCache;
}
/**
@@ -145,9 +185,19 @@ class Cells
*/
public function getSortedCoordinatesInt(): array
{
asort($this->index);
if (!$this->indexSorted) {
asort($this->index);
$this->indexSorted = true;
// Clear unsorted cache
$this->indexKeysCache = null;
$this->indexValuesCache = null;
}
return array_values($this->index);
if ($this->indexValuesCache === null) {
$this->indexValuesCache = array_values($this->index);
}
return $this->indexValuesCache;
}
/**
@@ -300,6 +350,11 @@ class Cells
}
}
// Clear index sorted flag and index caches
$newCollection->indexSorted = false;
$newCollection->indexKeysCache = null;
$newCollection->indexValuesCache = null;
return $newCollection;
}
@@ -390,6 +445,11 @@ class Cells
/** @var int $row */
$this->index[$cellCoordinate] = (--$row * self::MAX_COLUMN_ID) + Coordinate::columnIndexFromString((string) $column);
// Clear index sorted flag and index caches
$this->indexSorted = false;
$this->indexKeysCache = null;
$this->indexValuesCache = null;
$this->currentCoordinate = $cellCoordinate;
$this->currentCell = $cell;
$this->currentCellIsDirty = true;
@@ -444,6 +504,11 @@ class Cells
$this->index = [];
// Clear index sorted flag and index caches
$this->indexSorted = false;
$this->indexKeysCache = null;
$this->indexValuesCache = null;
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
@@ -3100,9 +3100,30 @@ class Worksheet
$index = ($row - 1) * AddressRange::MAX_COLUMN_INT + 1;
$indexPlus = $index + AddressRange::MAX_COLUMN_INT - 1;
// Binary search to quickly approach the correct index
$keyIndex = intdiv($keysCount, 2);
$boundLow = 0;
$boundHigh = $keysCount - 1;
while ($boundLow <= $boundHigh) {
$keyIndex = intdiv($boundLow + $boundHigh, 2);
if ($keys[$keyIndex] < $index) {
$boundLow = $keyIndex + 1;
} elseif ($keys[$keyIndex] > $index) {
$boundHigh = $keyIndex - 1;
} else {
break;
}
}
// Realign to the proper index value
while ($keyIndex > 0 && $keys[$keyIndex] > $index) {
--$keyIndex;
}
while ($keyIndex < $keysCount && $keys[$keyIndex] < $index) {
++$keyIndex;
}
while ($keyIndex < $keysCount && $keys[$keyIndex] <= $indexPlus) {
$key = $keys[$keyIndex];
$thisRow = intdiv($key - 1, AddressRange::MAX_COLUMN_INT) + 1;