Skip to content
This repository was archived by the owner on Jan 4, 2021. It is now read-only.
This repository was archived by the owner on Jan 4, 2021. It is now read-only.

Page class that emphasizes performance #590

@hdodov

Description

@hdodov

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.
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions