Skip to content

Commit

Permalink
feat(frontend): support sorting in profiler "Top functions" table
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk committed Jul 6, 2024
1 parent f008fcd commit a123cd6
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 2 deletions.
15 changes: 13 additions & 2 deletions src/Module/Frontend/Module/Profiler/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Buggregator\Trap\Module\Frontend\Module\Profiler\Message\CallGraph;
use Buggregator\Trap\Module\Frontend\Module\Profiler\Message\FlameChart;
use Buggregator\Trap\Module\Frontend\Module\Profiler\Message\TopFunctions;
use Buggregator\Trap\Module\Profiler\Struct\Branch;
use Buggregator\Trap\Proto\Frame\Profiler\Payload as ProfilerPayload;

/**
Expand Down Expand Up @@ -38,8 +39,18 @@ public function topFunctions(Event $event, string $metric): TopFunctions

// Get top
$top = [];
foreach ($profile->calls->all as $branch) {
// todo: limit with 100 and sort
$topBranches = $profile->calls->top(
100,
match ($metric) {
'ct' => static fn(Branch $a, Branch $b): int => $b->item->cost->ct <=> $a->item->cost->ct,
'cpu' => static fn(Branch $a, Branch $b): int => $b->item->cost->cpu <=> $a->item->cost->cpu,
'mu' => static fn(Branch $a, Branch $b): int => $b->item->cost->mu <=> $a->item->cost->mu,
'pmu' => static fn(Branch $a, Branch $b): int => $b->item->cost->pmu <=> $a->item->cost->pmu,
default => static fn(Branch $a, Branch $b): int => $b->item->cost->wt <=> $a->item->cost->wt,
}
);

foreach ($topBranches as $branch) {
$top[] = TopFunctions\Func::fromEdge($branch->item);
}

Expand Down
38 changes: 38 additions & 0 deletions src/Module/Profiler/Struct/Tree.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,44 @@ public static function fromEdgesList(array $edges, callable $getCurrent, callabl
return $tree;
}

/**
* Get the top N branches by a custom sorting.
*
* @param int<1, max> $limit
* @param callable(Branch<TItem>, Branch<TItem>): int $sorter
*
* @return list<Branch>
*/
public function top(int $limit, callable $sorter): array
{
// Get N branches and sort it
$all = \array_values($this->all);
$result = \array_slice($all, 0, $limit);
\usort($result, $sorter);

// Compare next item with the last of the top
// and resort the top one by one
$end = \array_key_last($result);
$next = $limit - 1;

while (++$next < \count($all)) {
if ($sorter($all[$next], $result[$end]) > 0) {
continue;
}

// Add the next item to the top
$result[$end] = $all[$next];

// Sort
$current = $end;
while (--$current >= 0 && $sorter($result[$current], $result[$current + 1]) > 0) {
[$result[$current], $result[$current + 1]] = [$result[$current + 1], $result[$current]];
}
}

return $result;
}

/**
* @param non-empty-string $id
* @param non-empty-string|null $parentId
Expand Down
70 changes: 70 additions & 0 deletions tests/Unit/Module/Profiler/Struct/TreeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Tests\Unit\Module\Profiler\Struct;

use Buggregator\Trap\Module\Profiler\Struct\Branch;
use Buggregator\Trap\Module\Profiler\Struct\Cost;
use Buggregator\Trap\Module\Profiler\Struct\Edge;
use Buggregator\Trap\Module\Profiler\Struct\Tree;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class TreeTest extends TestCase
{
public static function topNumbers(): iterable
{
yield [1];
yield [5];
yield [10];
yield [100];
yield [1000];
}

#[DataProvider('topNumbers')]
public function testTop(int $top): void
{
$edges = $this->generateEdges();
$tree = Tree::fromEdgesList(
$edges,
static fn(Edge $edge) => $edge->callee,
static fn(Edge $edge) => $edge->caller,
);

$result = $tree->top($top, static fn(Branch $a,Branch $b) => $b->item->cost->wt <=> $a->item->cost->wt);

self::assertCount(\min($top, \count($edges)), $result);

$topUnoptimized = \array_map(static fn (Edge $e) => $e->cost->wt, $edges);
\rsort($topUnoptimized);
$topUnoptimized = \array_slice($topUnoptimized, 0, $top);
$topVals = \array_map(static fn (Branch $b) => $b->item->cost->wt, $result);
self::assertSame($topUnoptimized, $topVals);
}

/**
* @return array<Edge>
*/
private function generateEdges(int $multiplier = 20): array
{
$result = [];

for ($i = 0; $i < $multiplier; $i++) {
for ($j = 0; $j <= $i; $j++) {
$result[] = new Edge(
$i === 0 ? null : 'item-' . ($i - 1) . "-$j", "item-$i-$j",
new Cost(
ct: (int) $i**$i,
wt: ($multiplier - $i) * 1000 + $j,
cpu: ($multiplier - $i) * 10,
mu: (int) $i * 1000,
pmu: (int) $i * 1000,
),
);
}
}

return $result;
}
}

0 comments on commit a123cd6

Please sign in to comment.