matsuyoshi30 / harbor

Simple and minimal personal blog theme.
MIT License
187 stars 68 forks source link

Improve search performance when there are many search results #145

Closed kaakaa closed 1 year ago

kaakaa commented 1 year ago

Issue

When there are many search results, searching is very slow.

In the case of the following, searching freezes the page for nearly 20 seconds.

https://github.com/matsuyoshi30/harbor/assets/1453749/86281056-82ae-4855-9b35-354cac038a44

Fixes

Since the issue is caused by the way to append html element, this PR fix it. With this fix, performance in the same situation is improved.

https://github.com/matsuyoshi30/harbor/assets/1453749/7a7e03a6-4198-4efa-9712-7bce4a8f779a

matsuyoshi30 commented 1 year ago

@kaakaa First of all, thank you for your contribution!

I believe it's better to use the DOM API, like createElement, for generating DOM elements rather than generating them directly from a string. In this case, the issue likely arises from creating elements with createElement and appending them within each loop.

When adding a large number of elements to the DOM at once, we can reduce the number of reflows and repaints by first adding them to a DocumentFragment, rather than adding each element individually. Afterward, the entire DocumentFragment can be added to the DOM all at once. Could you check what the result would be when using createDocumentFragment?

kaakaa commented 1 year ago

Thanks @matsuyoshi30 ! With DocumentFragment, there is a little lag, but I think that it's acceptable. I fixed my code to use DocumentFragment, so please take a look again.

https://github.com/matsuyoshi30/harbor/assets/1453749/d446663f-b78a-4bc4-bfb7-7a574eaf05a1


FYI: I compared the time of each method by inserting a few lines using performance API.

    ...
    // Display search results
    const start = performance.now();
    renderResults(index.search(query, 10, { enrich: true }));
    searchResultsArea.style.display = 'block'
    const end = performance.now();
    console.log(query, ': ', end - start);
    ...
query master[ms] string [ms] Fragment [ms]
ma 9082.599999904633 2.0999999046325684 64.10000014305115
mat 8725.599999904633 0.09999990463256836 67.39999985694885
matt 8337.700000047684 0.2999999523162842 56.5
matte 10371.599999904633 0.2999999523162842 68.20000004768372
matter 9084.799999952316 0.20000004768371582 47
matterm 8448.799999952316 0.40000009536743164 90.19999980926514
mattermo 8870.700000047684 0.20000004768371582 287.7999999523163
mattermos 8338 0.2999999523162842 105
mattermost 8986.200000047684 0.2999999523162842 97.70000004768372

Generating DOM from string is the best from a performance perspective, but I understand that using the DOM API is more polite way and a better practice than string way as you said. So I have no objection to adopt DocumentFragment approach. (Of course, I can revert to string way if you like it.)

matsuyoshi30 commented 1 year ago

@kaakaa Great work! Thank you :+1: