lihugang / mep2

The Math Editor for Photos Version 2
GNU General Public License v3.0
0 stars 0 forks source link

html2canvas performance improve #1

Closed lihugang closed 1 year ago

lihugang commented 1 year ago

Is your feature request related to a problem? Please describe. The library html2canvas clones all document nodes to iframe and chooses the selected nodes to convert into canvas, we have too many nodes in editor page, it causes extreme performance degradation, in the worst case, the fps goes down to 3-9. In addition, it I change font or color or size or add a new macro to codes, all text nodes will be redrawed. It usually takes tens of seconds to complete the redrawing. Oh, it's too long!

Describe the solution you'd like According to the design of thread pools, I have an idea. Request to pop up a new window, use postmessage api to clone nodes to the window, because v8 engine is isolated, clone nodes and convert them into canvas will not affect the editor page, performance will be improved. We could open a set of pages to make a thread pool, after using, not close them but to keep. When a new request receives, choose a freest window to send. But browser will block them if we pops up too many windows. So we can add a option to settings, users can decide whether they enable this function.

Describe alternatives you've considered Backup solution: remake html2canvas with OffscreenCanvas, the documentation is here https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas, there is an issue already, https://github.com/niklasvh/html2canvas/issues/2880, but it seems to be abandoned. There are many performance problems in html2canvas but no responses. https://www.npmjs.com/package/html-to-image html-to-image will also be considered.

lihugang commented 1 year ago

I run a benchmark of different libraries which are converting texts to images. Here's a report. (With KaTeX fonts)

empty dom html2canvas x 8.59 ops/sec ±4.63% (43 runs sampled)
empty dom html-to-image x 427 ops/sec ±4.34% (75 runs sampled)
empty dom dom-to-image x 8.28 ops/sec ±1.57% (41 runs sampled)
empty dom dom-to-image-more x 544 ops/sec ±85.39% (73 runs sampled)
empty dom dom-to-image-next x 526 ops/sec ±91.12% (72 runs sampled)
94 lengths of text html2canvas x 9.30 ops/sec ±5.56% (46 runs sampled)
94 lengths of text html-to-image x 214 ops/sec ±3.83% (73 runs sampled)
94 lengths of text dom-to-image x 8.26 ops/sec ±1.48% (40 runs sampled)
94 lengths of text dom-to-image-more x 229 ops/sec ±42.79% (74 runs sampled)
94 lengths of text dom-to-image-next x 226 ops/sec ±42.09% (73 runs sampled)
9000 lengths of text html2canvas x 3.55 ops/sec ±2.72% (22 runs sampled)
9000 lengths of text html-to-image x 5.63 ops/sec ±3.30% (31 runs sampled)
9000 lengths of text dom-to-image x 3.65 ops/sec ±2.92% (22 runs sampled)
9000 lengths of text dom-to-image-more x 9.50 ops/sec ±3.32% (47 runs sampled)
9000 lengths of text dom-to-image-next x 9.29 ops/sec ±3.29% (47 runs sampled)
LaTeX html2canvas x 5.63 ops/sec ±6.87% (30 runs sampled)
LaTeX html-to-image x 3.78 ops/sec ±10.92% (24 runs sampled)
LaTeX dom-to-image x 1.75 ops/sec ±4.22% (13 runs sampled)
LaTeX dom-to-image-more x 3.38 ops/sec ±4.91% (21 runs sampled)
LaTeX dom-to-image-next x 3.14 ops/sec ±4.74% (20 runs sampled)
empty dom (with 20000 nodes) html2canvas x 0.90 ops/sec ±5.22% (9 runs sampled)
empty dom (with 20000 nodes) html-to-image x 58.65 ops/sec ±12.66% (66 runs sampled)
empty dom (with 20000 nodes) dom-to-image x 3.99 ops/sec ±3.14% (23 runs sampled)
empty dom (with 20000 nodes) dom-to-image-more x 6.15 ops/sec ±8.09% (32 runs sampled)
empty dom (with 20000 nodes) dom-to-image-next x 5.46 ops/sec ±7.58% (30 runs sampled)
94 lengths of text (with 20000 nodes) html2canvas x 0.84 ops/sec ±6.65% (9 runs sampled)
94 lengths of text (with 20000 nodes) html-to-image x 44.59 ops/sec ±12.54% (65 runs sampled)
94 lengths of text (with 20000 nodes) dom-to-image x 3.50 ops/sec ±3.56% (21 runs sampled)
94 lengths of text (with 20000 nodes) dom-to-image-more x 5.14 ops/sec ±8.57% (28 runs sampled)
94 lengths of text (with 20000 nodes) dom-to-image-next x 4.67 ops/sec ±10.31% (27 runs sampled)
9000 lengths of text (with 20000 nodes) html2canvas x 0.81 ops/sec ±7.31% (8 runs sampled)
9000 lengths of text (with 20000 nodes) html-to-image x 56.06 ops/sec ±9.71% (62 runs sampled)
9000 lengths of text (with 20000 nodes) dom-to-image x 3.38 ops/sec ±6.27% (20 runs sampled)
9000 lengths of text (with 20000 nodes) dom-to-image-more x 4.62 ops/sec ±7.15% (26 runs sampled)
9000 lengths of text (with 20000 nodes) dom-to-image-next x 4.11 ops/sec ±8.18% (24 runs sampled)
LaTeX (with 20000 nodes) html2canvas x 0.62 ops/sec ±4.57% (7 runs sampled)
LaTeX (with 20000 nodes) html-to-image x 3.15 ops/sec ±10.32% (20 runs sampled)
LaTeX (with 20000 nodes) dom-to-image x 1.31 ops/sec ±2.18% (11 runs sampled)
LaTeX (with 20000 nodes) dom-to-image-more x 2.06 ops/sec ±8.43% (14 runs sampled)
LaTeX (with 20000 nodes) dom-to-image-next x 2.07 ops/sec ±5.82% (15 runs sampled)

Although there's a little performance advantage by using html-to-image, but it requests fonts file as many as calling counts, it cannot be accepted. So the lib which we choose is html2canvas. (The test code is following

<body>
    <div id="emptyDom"></div>
    <div id="container"></div>
    <div id="text100Ref">
        Grow old along with me! The best is yet to be. The last of life, for which the first was made.
    </div>
    <div id="text9000Ref"></div>
    <div id="mathContainer"></div>
   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js"
        integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4"
        crossorigin="anonymous"></script>

</body>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.4/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/platform@1.3.3/platform.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/benchmark@2.1.4/benchmark.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<script type="module">
    import * as htmlToImage from 'https://cdn.jsdelivr.net/npm/html-to-image@1.11.4/+esm'
    import domToImage from 'https://cdn.jsdelivr.net/npm/dom-to-image@2.6.0/+esm'
    import domToImageMore from 'https://cdn.jsdelivr.net/npm/dom-to-image-more@2.14.0/+esm'
    import domToImageNext from 'https://cdn.jsdelivr.net/npm/dom-to-image-next@2.11.7/+esm'

    const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
    const idle = () => new Promise((resolve) => requestIdleCallback(resolve));

    (async () => {
        const emptyDom = document.querySelector('#emptyDom');
        await runTest('empty dom', emptyDom);

        const text100Ref = document.querySelector('#text100Ref');
        await runTest('94 lengths of text', text100Ref);

        const text9000Ref = document.querySelector('#text9000Ref');
        text9000Ref.innerText = get9000Text();

        await idle();
        await runTest('9000 lengths of text', text9000Ref);

        text9000Ref.innerText = '';
        await idle();

        const mathContainer = document.querySelector('#mathContainer');
        mathContainer.innerHTML = katex.renderToString("x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}");

        await idle();
        await runTest('LaTeX', mathContainer);

        mathContainer.innerHTML = '';
        await idle();

        const doms = [];
        const container = document.querySelector('#container');
        for (let i = 0; i < 20000; ++i) {
            const dom = document.createElement('div');
            dom.innerHTML = 'Benchmark';
            container.appendChild(dom);
            doms.push(dom);
        }
        await idle();
        await runTest('empty dom (with 20000 nodes)', emptyDom);

        await runTest('94 lengths of text (with 20000 nodes)', text100Ref);

        await idle();
        await runTest('9000 lengths of text (with 20000 nodes)', text9000Ref);

        text9000Ref.innerText = '';
        await idle();

        mathContainer.innerHTML = katex.renderToString("x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}");

        await idle();
        await runTest('LaTeX (with 20000 nodes)', mathContainer);

        mathContainer.innerHTML = '';
        await idle();

        doms.forEach(dom => container.removeChild(dom));
        await idle();

    })();

    function runTest(name, dom) {
        return new Promise((resolve) => {
            const suite = new Benchmark.Suite;
            suite.add('html2canvas', {
                defer: true,
                async fn(deferred) {
                    html2canvas(dom, {
                        logging: false
                    }).then((canvas) => {
                        deferred.resolve(canvas.toDataURL('image/png'))
                    });
                }
            })
                .add('html-to-image', {
                    defer: true,
                    async fn(deferred) {
                        htmlToImage.toPng(dom).then(() => deferred.resolve())
                    }
                })
                .add('dom-to-image', {
                    defer: true,
                    async fn(deferred) {
                        domToImage.toPng(dom).then(() => deferred.resolve())
                    }
                })
                .add('dom-to-image-more', {
                    defer: true,
                    async fn(deferred) {
                        domToImageNext.toPng(dom).then(() => deferred.resolve())
                    }
                })
                .add('dom-to-image-next', {
                    defer: true,
                    async fn(deferred) {
                        domToImageMore.toPng(dom).then(() => deferred.resolve())
                    }
                })
                .on('cycle', (e) => {
                    console.log(name, String(e.target));
                })
                .on('complete', () => {
                    resolve();
                })
                .run();
        })
    }

    function get9000Text() {
        return `Terms of Use
Terms and conditions of use of United Nations websites

Disclaimers    Country and area nomenclature    Privacy notice    Preservation of Immunities    General

The use of this web site constitutes agreement with the following terms and conditions:

(a) The United Nations maintains this web site (the “Site”) as a courtesy to those who may choose to access the Site (“Users”). The information presented herein is for informative purposes only. The United Nations grants permission to Users to visit the Site and to download and copy the information, documents and materials (collectively, “Materials”) from the Site for the User’s personal, non-commercial use, without any right to resell or redistribute them or to compile or create derivative works therefrom, subject to the terms and conditions outlined below, and also subject to more specific restrictions that may apply to specific Material within this Site.

(b) The United Nations administers this Site. All Material on this Site from the United Nations appears subject to the present Terms and Conditions.

(c) Unless expressly stated otherwise, the findings, interpretations and conclusions expressed in the Materials on this Site are those of the various United Nations staff members, consultants and advisers to the United Nations Secretariat who prepared the work and do not necessarily represent the views of the United Nations or its Member States.

Disclaimers
Materials provided on this Site are provided “as is”, without warranty of any kind, either express or implied, including, without limitation, warranties of merchantability, fitness for a particular purpose and non-infringement. The United Nations specifically does not make any warranties or representations as to the accuracy or completeness of any such Materials. The United Nations periodically adds, changes, improves or updates the Materials on this Site without notice. Under no circumstances shall the United Nations be liable for any loss, damage, liability or expense incurred or suffered that is claimed to have resulted from the use of this Site, including, without limitation, any fault, error, omission, interruption or delay with respect thereto. The use of this Site is at the User’s sole risk. Under no circumstances, including but not limited to negligence, shall the United Nations or its affiliates be liable for any direct, indirect, incidental, special or consequential damages, even if the United Nations has been advised of the possibility of such damages.

The User specifically acknowledges and agrees that the United Nations is not liable for any conduct of any User.

This site may contain advice, opinions and statements of various information providers. The United Nations does not represent or endorse the accuracy or reliability of any advice, opinion, statement or other information provided by any information provider, any User of this Site or any other person or entity. Reliance upon any such advice, opinion, statement, or other information shall also be at the User’s own risk. Neither the United Nations nor its affiliates, nor any of their respective agents, employees, information providers or content providers, shall be liable to any User or anyone else for any inaccuracy, error, omission, interruption, deletion, defect, alteration of or use of any content herein, or for its timeliness or completeness, nor shall they be liable for any failure of performance, computer virus or communication line failure, regardless of cause, or for any damages resulting therefrom.

As a condition of use of this Site, the User agrees to indemnify the United Nations and its affiliates from and against any and all actions, claims, losses, damages, liabilities and expenses (including reasonable attorneys’ fees) arising out of the User’s use of this Site, including, without limitation, any claims alleging facts that if true would constitute a breach by the User of these Terms and Conditions. If the User is dissatisfied with any Material on this Site or with any of its Terms and Conditions of Use, the User’s sole and exclusive remedy is to discontinue using the Site.

This Site may contain links and references to third-party web sites. The linked sites are not under the control of the United Nations, and the United Nations is not responsible for the content of any linked site or any link contained in a linked site. The United Nations provides these links only as a convenience, and the inclusion of a link or reference does not imply the endorsement of the linked site by the United Nations.

If this Site contains bulletin boards, chat rooms, access to mailing lists or other message or communication facilities (collectively, “Forums”), the User agrees to use the Forums only to send and receive messages and materials that are proper and related to the particular Forum. By way of example and not as a limitation, the User agrees that when using a Forum, he or she shall not do any of the following:

(a) Defame, abuse, harass, stalk, threaten or otherwise violate the legal rights (such as rights of privacy and publicity) of others;

(b) Publish, post, distribute or disseminate any defamatory, infringing, obscene, indecent or unlawful material or information;

(c) Upload or attach files that contain software or other material protected by intellectual property laws (or by rights of privacy and publicity) unless the User owns or controls the rights thereto or has received all consents therefor as may be required by law;

(d) Upload or attach files that contain viruses, corrupted files or any other similar software or programs that may damage the operation of another’s computer;

(e) Delete any author attributions, legal notices or proprietary designations or labels in any file that is uploaded;

(f) Falsify the origin or source of software or other material contained in a file that is uploaded;

(g) Advertise or offer to sell any goods or services, or conduct or forward surveys, contests or chain letters, or download any file posted by another user of a Forum that the User knows, or reasonably should know, cannot be legally distributed in such manner.

The User acknowledges that all Forums and discussion groups are public and not private communications. Further, the User acknowledges that chats, postings, conferences, e-mails and other communications by other Users are not endorsed by the United Nations, and that such communications shall not be considered to have been reviewed, screened or approved by the United Nations. The United Nations reserves the right to remove, for any reason and without notice, any content of the Forums received from Users, including, without limitation, e-mail and bulletin board postings.

The boundaries and names shown and the designations used on the maps on this site do not imply official endorsement or acceptance by the United Nations.

Country and area nomenclature

The designations employed and the presentation of material in this website do not imply the expression of any opinion whatsoever on the part of the Secretariat of the United Nations concerning the legal status of any country, territory, city or area or of its authorities, or concerning the delimitation of its frontiers or boundaries. The term “country” as used in this material also refers, as appropriate, to territories or areas.

Privacy notice
By accessing this site, certain information about the User, such as Internet protocol (IP) addresses, navigation through the Site, the software used and the time spent, along with other similar information, will be stored on United Nations servers. These will not specifically identify the User. The information will be used internally only for web site traffic analysis. If the User provides unique identifying information, such as name, address and other information on forms stored on this Site, such information will be used only for statistical purposes and will not be published for general access. The United Nations, however, assumes no responsibility for the security of this information.

Preservation of immunities
Nothing herein shall constitute or be considered to be a limitation upon or a waiver of the privileges and immunities of the United Nations, which are specifically reserved.

General
The United Nations reserves its exclusive right in its sole discretion to alter, limit or discontinue the Site or any Materials in any respect. The United Nations shall have no obligation to take the needs of any User into consideration in connection therewith.

The United Nations reserves the right to deny in its sole discretion any user access to this Site or any portion thereof without notice.

No waiver by the United Nations of any provision of these Terms and Conditions shall be binding except as set forth in writing and signed by its duly authorized representative.`;
    }
</script>

)

lihugang commented 1 year ago

After enabling experimental features--render thread pool, fps when converting has been increased to 55, in the 2.0.0 version, the old number is 30. Rendering threads pool works well. For further information, please see 2.0.1 version changelog and release log.