-
Notifications
You must be signed in to change notification settings - Fork 0
Page class that emphasizes performance #590
Description
I'm testing the performance of Kirby with large volumes of content. I have a page with 13,000 children and running:
$entries = site()->page('notes')->children();
...takes 1.14289
seconds on average. However, running:
$path = site()->root() . '/notes';
$entries = Dir::read($path);
...takes just 0.01498
seconds. That's a lot quicker and it got me thinking... if the file system is so fast, perhaps there's a way to cut some corners and preserve that speed?
I managed to get a huge performance boost by simplifying the internal inventory()
method via a custom page model:
<?php
use Kirby\Cms\Dir;
use Kirby\Cms\Page;
class NotesPage extends Page
{
public function inventory(): array
{
if ($this->inventory !== null) {
return $this->inventory;
}
$children = [];
$root = $this->root();
$listing = Dir::read($root);
foreach ($listing as $entry) {
// Ignore files and hidden folders.
if (strpos($entry, '.')) {
continue;
}
if (preg_match('/^([0-9]+)' . Dir::$numSeparator . '(.*)$/', $entry, $match)) {
$num = $match[1];
$slug = $match[2];
} else {
$num = null;
$slug = $entry;
}
$children[] = [
'dirname' => $entry,
'model' => null,
'num' => $num,
'root' => "$root/$entry",
'slug' => $slug,
];
}
return $this->inventory = [
'children' => $children,
'files' => [],
'template' => 'notes'
];
}
}
Now, running:
$entries = site()->page('notes')->children();
...takes just 0.17732
seconds. That's already 644% faster, while you don't lose much functionality. A simple is_dir()
call increases that time to 0.44538
...
To save even more time, I can change 'model' => null
to 'model' => 'note'
and assume that all child pages have the note
template. Then, in that model, I greatly simplify the constructor:
<?php
use Kirby\Cms\Page;
class NotePage extends Page
{
public function __construct(array $props)
{
$this->dirname = $props['dirname'] ?? null;
$this->setNum($props['num'] ?? null);
$this->root = $props['root'] ?? null;
$this->slug = $props['slug'] ?? null;
$this->parent = $props['parent'] ?? null;
$this->site = $props['site'] ?? null;
$this->isDraft = $props['isDraft'] ?? false;
}
}
...and this brings the time down to 0.03485
seconds!
I'm sure we could save a lot of time by skipping or hardcoding parts of the logic. Kirby could provide a QuickPage
class that functions the same way as the Page
class, but takes a lot more shortcuts to improve performance. Then, you can extend that class in your custom page models to get the benefit. Static properties could be used for configuration. For example:
<?php
use Kirby\Cms\QuickPage;
class NotesPage extends QuickPage
{
static $assumedTemplate = 'notes';
static $assumedChildTemplate = 'note';
// and so on...
}
Then, since child pages are assumed to be of template note
, you can use QuickPage
for them as well:
<?php
use Kirby\Cms\QuickPage;
class NotePage extends QuickPage
{
// Uses the simplified constructor.
}