Skip to content

Commit

Permalink
Memory and CPU performance improvements
Browse files Browse the repository at this point in the history
 - Add helper priorityCount() to TimeOrderedStorageInterface, TimeOrderArray can provide this information without needing to clone the queue making sliceCount() quick
 - TimeOrderedArray needs to be sorted before cloning to ensure we don't continuously sort when peeking ahead.

PHP8.1/8.2 deprecation notices fixed up
  • Loading branch information
lucasnetau committed Jun 16, 2023
1 parent b1c8dfe commit 58a0bff
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 21 deletions.
6 changes: 3 additions & 3 deletions src/TimeBucket.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ public function count() : int
*/
public function sliceCount() : int
{
$iter = $this->getIterator(); //Perform this action on a copy of the queue to ensure we don't modify it
$iter->setExtractFlags(SplPriorityQueue::EXTR_PRIORITY);
return $iter->isEmpty() ? 0 : count(array_unique(iterator_to_array($iter)));
return $this->innerQueue->priorityCount();
}

/**
Expand Down Expand Up @@ -167,6 +165,7 @@ public function insert($datum, $priority)
*/
public function getIterator() : TimeOrderedStorageInterface
{
$this->innerQueue->beforeClone();
return clone $this->innerQueue;
}

Expand Down Expand Up @@ -416,6 +415,7 @@ public function unserialize($data)
*/
function __clone()
{
$this->innerQueue->beforeClone();
$this->innerQueue = clone $this->innerQueue;
}
}
26 changes: 19 additions & 7 deletions src/TimeOrderedArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class TimeOrderedArray implements TimeOrderedStorageInterface {
*
* @var ?int|string
*/
protected $top = null;
protected string|int|null $top = null;

/**
* Total elements contained in the queue
Expand Down Expand Up @@ -94,15 +94,15 @@ public function compare($priority1, $priority2) : int
* @param mixed $value Element to insert
* @param string|int $priority Priority can be any key acceptable to a PHP array (so int|string)
*/
public function insert($value, $priority)
public function insert($value, $priority) : void
{
if (!array_key_exists($priority, $this->priorities))
{
$this->values[] = [];
$newIndex = array_key_last($this->values);
$this->priorities[$priority] = $newIndex;
$this->prioritiesUnsorted = true;
if (null === $this->top || 1 == $this->compare($priority, $this->top)) {
if (null === $this->top || 1 === $this->compare($priority, $this->top)) {
$this->top = $priority;
}
}
Expand All @@ -115,7 +115,7 @@ public function insert($value, $priority)
*
* @return mixed
*/
public function extract()
public function extract() : mixed
{
if (!$this->valid()) {
return false;
Expand All @@ -130,12 +130,12 @@ public function extract()
*
* @return mixed
*/
public function top()
public function top() : mixed
{
if ($this->isEmpty()) {
return false;
}
return$this->current();
return $this->current();
}

/**
Expand Down Expand Up @@ -235,7 +235,7 @@ public function isEmpty() : bool
* Set the extraction flag for the queue. Priority / Data / Both
* @param int $flag
*/
public function setExtractFlags(int $flag)
public function setExtractFlags(int $flag): void
{
$this->mode = $flag;
}
Expand All @@ -248,4 +248,16 @@ public function getExtractFlags() : int
{
return $this->mode;
}

public function beforeClone() : void {
if ($this->prioritiesUnsorted) {
uksort($this->priorities, array($this, 'compare'));
$this->prioritiesUnsorted = false;
}
}

public function priorityCount(): int
{
return count($this->priorities);
}
}
23 changes: 20 additions & 3 deletions src/TimeOrderedQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
namespace EdgeTelemetrics\TimeBucket;

use SplPriorityQueue;
use function array_unique;
use function count;
use function iterator_to_array;

class TimeOrderedQueue extends SplPriorityQueue implements TimeOrderedStorageInterface {
/**
* @var int
*/
protected int $serial = PHP_INT_MIN;

public function insert($value, $priority)
public function insert($value, $priority) : void
{
if (! is_array($priority)) {
$priority = [$priority, $this->serial++];
Expand Down Expand Up @@ -55,15 +58,29 @@ public function current() : mixed
return $this->fixPriority($extract);
}

public function extract()
public function extract() : mixed
{
$extract = parent::extract();
return $this->fixPriority($extract);
}

public function top()
public function top() : mixed
{
$extract = parent::top();
return $this->fixPriority($extract);
}

public function beforeClone() : void {
//NOOP
}

public function priorityCount(): int
{
if ($this->isEmpty()) {
return 0;
}
$iter = clone $this;
$iter->setExtractFlags(SplPriorityQueue::EXTR_PRIORITY);
return count(array_unique(iterator_to_array($iter)));
}
}
22 changes: 14 additions & 8 deletions src/TimeOrderedStorageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@
* @package EdgeTelemetrics\TimeBucket
*/
interface TimeOrderedStorageInterface extends Iterator, Countable {
public function insert($value, $priority);
/**
* @return int
*/
public function count() : int;
public function insert($value, $priority) : void;

public function current() : mixed;
public function compare($priority1, $priority2) : int;
public function extract();
public function top();
public function isEmpty() : bool;
public function extract() : mixed;
public function top() : mixed;

/**
* @return bool
*/
public function isEmpty();

public function priorityCount() : int;

/** Perform any tasks on parent object prior to clone being called */
public function beforeClone() : void;
}
5 changes: 5 additions & 0 deletions tests/timeBucketOrderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,14 @@ public function __construct($array)

echo '**** Validate getTimeSlices()' . PHP_EOL;
$totalDatapoints = 0;
echo $prevTime = null;
foreach($bucket->getTimeSlices() as ['time' => $time, 'data' => $data])
{
$totalDatapoints += count($data);
if ($prevTime !== null && $time < $prevTime) {
echo '***ERROR*** Time out of order. ' . PHP_EOL;
}
$prevTime = $time;
// echo 'Slice ' . $time . " contains " . count($data) . " datapoints" . PHP_EOL;
}
echo 'Bucket contains ' . $totalDatapoints . " datapoints." . PHP_EOL;
Expand Down

0 comments on commit 58a0bff

Please sign in to comment.