vuepress / core

Vue-Powered Static Site Generator
https://vuepress.vuejs.org
MIT License
2.35k stars 922 forks source link

[Bug report] Compiling & rendering too too too much slower than VitePress #994

Open lslzl3000 opened 2 years ago

lslzl3000 commented 2 years ago

Description

Try to build with 1000 .md files Vuepress takes more than 10min to compile and render, in both vite-bundler and webpack-bundler

Meanwhile, VitePress@1.0.0-alpha.4 only takes 30s

The core part of build-bundler - Vite is the same, so the problem may be how VuePress processes or organises .md and .vue. What is the difference between VitePress and VuePress during compiling & rendering?

Used Package Manager

npm

System Info

System:
    OS: macOS 12.4
    CPU: (8) arm64 Apple M2
    Memory: 6.52 GB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.15.1 - /usr/local/bin/node
    Yarn: Not Found
    npm: 8.11.0 - /usr/local/bin/npm
  Utilities:
    Git: 2.32.1 - /usr/bin/git
  Browsers:
    Chrome: 103.0.5060.134
    Edge: Not Found
    Firefox: Not Found
    Safari: 15.5
  npmPackages:
System:
    OS: macOS 12.4
    CPU: (8) arm64 Apple M2
    Memory: 6.52 GB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.15.1 - /usr/local/bin/node
    Yarn: Not Found
    npm: 8.11.0 - /usr/local/bin/npm
  Utilities:
    Git: 2.32.1 - /usr/bin/git
  Browsers:
    Chrome: 103.0.5060.134
    Edge: Not Found
    Firefox: Not Found
    Safari: 15.5
  npmPackages:
    @vuepress/bundler-vite: ^2.0.0-beta.49 => 2.0.0-beta.49 
    @vuepress/bundler-webpack: ^2.0.0-beta.49 => 2.0.0-beta.49 
    @vuepress/cli:  2.0.0-beta.49 
    @vuepress/client:  2.0.0-beta.49 
    @vuepress/core:  2.0.0-beta.49 
    @vuepress/markdown:  2.0.0-beta.49 
    @vuepress/plugin-active-header-links:  2.0.0-beta.49 
    @vuepress/plugin-back-to-top:  2.0.0-beta.49 
    @vuepress/plugin-container:  2.0.0-beta.49 
    @vuepress/plugin-docsearch: ^2.0.0-beta.49 => 2.0.0-beta.49 
    @vuepress/plugin-external-link-icon:  2.0.0-beta.49 
    @vuepress/plugin-git:  2.0.0-beta.49 
    @vuepress/plugin-google-analytics: ^2.0.0-beta.49 => 2.0.0-beta.49 
    @vuepress/plugin-medium-zoom:  2.0.0-beta.49 
    @vuepress/plugin-nprogress:  2.0.0-beta.49 
    @vuepress/plugin-palette:  2.0.0-beta.49 
    @vuepress/plugin-prismjs:  2.0.0-beta.49 
    @vuepress/plugin-pwa: Not Found
    @vuepress/plugin-pwa-popup: Not Found
    @vuepress/plugin-register-components: ^2.0.0-beta.49 => 2.0.0-beta.49 
    @vuepress/plugin-search: Not Found
    @vuepress/plugin-shiki: ^2.0.0-beta.49 => 2.0.0-beta.49 
    @vuepress/plugin-theme-data:  2.0.0-beta.49 
    @vuepress/plugin-toc: Not Found
    @vuepress/shared:  2.0.0-beta.49 
    @vuepress/theme-default:  2.0.0-beta.49 
    @vuepress/utils:  2.0.0-beta.49 
    vue:  3.2.37 
    vue-loader:  17.0.0 
    vue-router:  4.0.16 
    vuepress: ^2.0.0-beta.49 => 2.0.0-beta.49 
    vuepress-vite:  2.0.0-beta.49
github-actions[bot] commented 2 years ago

Hello @lslzl3000. Please provide a minimal reproduction using a GitHub repository or v2.vuepress.vuejs.org/new. Issues marked with need reproduction will be closed if they have no activity within 3 days.

meteorlxy commented 2 years ago

It would be helpful if you could provide a reproduction repo.

lslzl3000 commented 2 years ago

@meteorlxy I have created a simple repo to test VitePress and VuePress https://github.com/lslzl3000/vuepressVSvitepress

It has about 1000 .md files, just simple doc generated from babylon.js you can try build with VitePress and VuePress

  1. You will see how much longer the VuePress will take
    In my Macbook Pro 2022 with M2, VuePress: 13mins+ vs VitePress:60-70s

  2. VuePress also use much much more memory than VitePress It has to build with NODE_OPTIONS=--max_old_space_size=20480, otherwise node process will exit by JavaScript heap out of memory

If build with full VuePress theme and other plugins, it will take more time

lslzl3000 commented 2 years ago

@meteorlxy any progress? closed by bot

github-actions[bot] commented 2 years ago

This issue is marked as stale because it has not had recent activity. Issues marked with stale will be closed if they have no activity within 3 days.

github-actions[bot] commented 2 years ago

This issue is marked as stale because it has not had recent activity. Issues marked with stale will be closed if they have no activity within 3 days.

Mister-Hope commented 2 years ago

I think we should add a new 'need review' label to stop issue like these closed by bot @meteorlxy

I started to be annoyed about this stale bot

hustcer commented 2 years ago

Any update on this issue? 1500+ md files, build time 23min+: https://github.com/nushell/nushell.github.io/actions/runs/3036416348

hustcer commented 2 years ago

这个问题能不能加急处理下?或者有没有其他什么替代方案?谢谢

Mister-Hope commented 2 years ago

I am focusing this issue these days.

Vitepress loads markdown files directly through a converter, and there is no global "Pages" scope (access other page infomation from one page), which means that vitepress directly luanch a app.

Meanwhile, vuepress have lifecyles, it's tranforming all markdown files to vue compoent and provides "pages data" to let plugins and theme proceed them, also, vuepress is registing all routes at start up and loads pages data with promises.


Besides the structure, one of the reasons which slows down vuepress ssr speed is that we are transforming almost every packages when generating ssr:

https://github.com/vuepress/vuepress-next/blob/bcf6033ce2acf2b98dede4a4e580fe4f39222517/packages/bundler-vite/src/plugins/mainPlugin.ts#L97

So that we will take more time to generate a ssr bundle before proceeing SSG. This is caused by pnpm support. But I do curious about why SSG speed is donzens times slower then vitepress, probably caused by taking much more memory with the "pages map full of promises" .

Mister-Hope commented 2 years ago

@lslzl3000 Would you please run another test with your repo after we migrate to ESM?

Mister-Hope commented 2 years ago

@meteorlxy I have a deeper dig in to our code, pageData only changes during router navigation, but SSG doesn't need those at all, so maybe it could be possible for us to stop loading the whole @internal/pageData, and just load that page Data instead during SSG, I think that will probably reduce memory usage and speed up SSG. Loading all page data through promise when rending any page surely has negative effects during SSG

sr2ds commented 1 year ago

Hello, I'm looking for some solution as well. On my case I have around 2K posts and is impossible do a build, with 10GB on max_old_space_size does not works. Have someone more with this issue? We can try to fix and open the PR or is better go to VitePress?

favoyang commented 1 year ago

I used to bring up the issue of VuePress scalability on its GitHub repository (https://github.com/vuejs/vuepress/issues/2689). In my project (https://github.com/openupm/openupm-next), I have around 3000 pages generated by a custom VuePress plugin's onInitialized method. The API allows you to provide all pages through app.pages.push(page) instead of using a generator that yields items individually. The intentional design of VuePress leads to keeping everything in memory. With each page consuming approximately 1 MB of memory (the estimation could be totally wrong), the total memory usage for 3000 pages amounts to about 3 GB, which may not sound excessive.

My project was initially built with VuePress v1, but I encountered some difficulties during a partial migration to VuePress v2 using --max_old_space_size=14000. It's worth noting that a significant number of efforts have been made to address such issues in VuePress v2, which drives my curiosity about the bottleneck.

For illustration, let's consider @lslzl3000's repository (https://github.com/lslzl3000/vuepressVSvitepress), which contains 914 markdown files, each having an average file size of 22KB. Conducting a quick test yielded the following results:

Profiling 1: 914 pages Peak Memory Time
vuepress dev 868 MB 10s
vitepress dev 209 MB Instantly
vuepress build 12.2 GB + swap Out of memory during compiling with vite
vitepress build 5.9 GB 1m59s

A few additional notes:

Let's double the page numbers by duplicating pages under the docs folder.

Profiling 2: 1828 pages Peak Memory Time
vuepress dev 1.3 GB 20s
vitepress dev 209 MB Instantly
vuepress build 12.2 GB + swap Out of memory during compiling with vite
vitepress build 8.6 GB 4m51s

My major takeaways are two points:

  1. Vuepress build costs much more memory than Vitepress. But it's comparing apples and oranges for different feature sets.

  2. The memory usage of both Vuepress and Vitepress grows almost linearly with the number of pages. For vitepress, it costs 4.8 MB/page.

That is a bit counterintuitive - I assume the build time grows linearly with the number of pages, not the memory usage. The behavior is not friendly with large websites that rely on cloud-based build services (GitHub actions for example). We can suffer with more build time, but a large memory VM is usually very expensive or unnecessary for generating a static website with thousands of pages, think about a 10K page website that requires a 48 GB memory VM.

After reading https://github.com/vuepress/vuepress-next/issues/1262's proposal, it seems hard to improve:

@Mister-Hope:

During the build process, this cannot be done, webpack and vite all need to load and analyze all codes to pack the application, this means the more you put inside a project, the more codes they need to handle, so the compiling stage needs a linear space of memory comparing to content. These memory are taken by bundler, and it's not quite related to VuePress itselfs, unless we do not require a bundler to pack our spa anymore.

On the vite community it's also a hot topic: https://github.com/vitejs/vite/issues/2433, I'm processing it slowly and hopefully find some clues.

It seems a pitfall you're going hit when you grow to a larger scale, and the solution is not very obvious here.

Refs #994, #1262

favoyang commented 1 year ago

I also conducted a quick test on @hustcer's repository (https://github.com/nushell/nushell.github.io), which comprises 676 markdown files, each with an average file size of 6 KB.

Here are the profiling results for the test:

Profile Pages Peak Memory Time
vuepress build 676 pages 1.7 GB 1m16s (with Git plugin on)

The outcome is entirely acceptable. The primary distinction when compared with lslzl3000/vuepressVSvitepress is that the average markdown file size of nutshell.github.io is significantly smaller (22KB vs. 6 KB).

This implies that before delving into vite bundle options, the most immediate area to address is reducing the overall project size. While this approach may not be suitable for a purely markdown-based VuePress site, it might prove beneficial for some dynamically generated pages. One potential strategy could be deferring the loading of heavy data to runtime, such as fetching a JSON file from the public folder.

meteorlxy commented 1 year ago

@favoyang Thanks for your investigation.

Some problems of performance IMO:

  1. Transforming all md files to vue files and collect page data in prepare stage, and store page data in memory. This is what make our dev server start slowly and consume more memory. If we change to only transform md -> vue on demand when visiting the page, it would be much faster in dev. But the drawback is we'll lose our current auto sidebar functionality.

  2. vue-router might be the killer of build. According to #1353, vue-router is too expensive for static site. That should be the reason why VitePress implements a custom lightweight router instead. If we change to use a lightweight router, it might be much faster in build.

  3. SPA might not be the solution of static site, and that's why we have to bundle all files together and causing linear memory usage increase in build.


I've been wanting to try these things for a while, but don't have enough time. TBH it should be faster after those changes, but it would also make VuePress more like VitePress. Thus I also don't have much motivation to do so. 😞

I have put a lot of effort into this project, although it is not perfect. I am grateful to have you who are still using this project. ❤️

favoyang commented 1 year ago

@meteorlxy, I truly appreciate your consistent assistance.

I am aware that the terms "performance" and "scalability" can have diverse meanings depending on the context. They may refer to:

  1. The memory footprint during the build process.
  2. The time taken for the build process.
  3. The initial loading speed of the development server.
  4. The initial loading time of the website in the browser.

In my previous comments, I addressed the first three aspects. Currently, my primary focus lies on the memory footprint, as it could potentially impede a smooth production build. By implementing the strategy of deferring the loading of heavy data to runtime and relocating large data from frontmatter to JSON files hosted at public/, I have observed a significant reduction in the memory footprint. Essentially, the idea involves separating data and code once again, which might stand in contrast to the traditional Static Site Generation (SSG) approach.

To clarify my use case further, envision a blog containing an extensive number of pages. The idea is to fetch and render markdown format content at runtime, catering to a scenario where the number of pages is substantial.

Feedback on other ideas

Transforming all md files to vue files and collect page data in prepare stage, and store page data in memory. This is what make our dev server start slowly and consume more memory. If we change to only transform md -> vue on demand when visiting the page, it would be much faster in dev. But the drawback is we'll lose our current auto sidebar functionality.

If the auto sidebar is the sole feature that necessitates such extensive data processing, it could possibly be deferred for computation until later stages during runtime to exchange a better dev experience.

vue-router might be the killer of build. According to https://github.com/vuepress/vuepress-next/pull/1353, vue-router is too expensive for static site. That should be the reason why VitePress implements a custom lightweight router instead. If we change to use a lightweight router, it might be much faster in build.

I'm not entirely sure about the extent to which this optimization has contributed to the build time. Although conducting optimizations is crucial, it's essential to run a benchmark on a sample project and analyze the results to ensure that everyone has a clear understanding of the impact.

However, it seems the PR itself can also influence runtime performance, which could be a reasonable consideration.

SPA might not be the solution of static site, and that's why we have to bundle all files together and causing linear memory usage increase in build.

My initial intuition about Static Site Generation (SSG) is that common parts of the website, such as the core functionality and layout, are bundled together during the build process. These common parts typically form the main framework of the website and are known as the "static core."

On the other hand, the actual content and data of each page are treated as individual entities. These pages are analyzed and generated separately, resulting in specific JavaScript chunks for each page. These chunks are then loaded dynamically at runtime as needed.

The beauty of this approach is that, regardless of how large the project grows in terms of content and pages, the build memory footprint remains relatively constant and does not scale with the size of the website. The build time, too, scales linearly with the number of pages or content items, which makes it an efficient and scalable solution for generating static sites.

Perhaps my intuition is mistaken. That many details results in pushing all converted files to the vite compiler together - which drives more memory footprint scaled linearly with the number of pages.


Once again, thank you for your unwavering dedication and hard work. While I might not be familiar with the internal details of VuePress and VitePress, one advantage of being an independent project is the ability to mold it according to your vision and goals. Every framework possesses its own set of strengths and weaknesses, and it's not necessary for one to perfectly align with another in all aspects.

ricolau commented 1 year ago

mark

Mister-Hope commented 1 year ago

Updates here, we have a new pr which avoids heavy cost on router.resolve

https://github.com/vuepress/vuepress-next/pull/1433

This would speed up build and webpaage performance, but little on memory cost.

sr2ds commented 11 months ago

Hello guys,

I was able to enhance the performance of my builds significantly with Vitepress after implementing a few improvements. Currently, my blog features around 2300 articles and is running smoothly on Netlify's free hosting platform 🎉.

If you're interested in giving it a try, take a look at my Vitepress repository clone. I've created a branch with a 'prepare' command in the package.json file to ensure compatibility for direct use in my projects. You can simply add the branch version directly to your package.json file for experimentation. However, please note that I don't plan to maintain this branch after the pull request is merged; it's intended for testing purposes only.

If you find this enhancement helpful or if it suits your needs, please consider leaving a like on my open pull request (PR) here to expedite the merge process. Your support would be greatly appreciated, and it can contribute to having these improvements in the upcoming versions.

meteorlxy commented 9 months ago

Some updates:

Since v2.0.0-rc.3, we have replaced vue-router's original routes with lightweight custom routes to get better performance.


Some results from @Mister-Hope :

Case A: 4m41s → 1m28s Case B: 4m22s → 1m29s


For the case provided here

VuePress build time improved from 13min to 4min.

image

To answer the original question - the differences between VuePress and Vitepress - in short, the bottleneck is vue-router.

VuePress is a typical SPA project with vue-router. Imagine that if you build a project with thousands of pages, you would have to define all the routes - and it would be slow to build for sure.

In contrast, VitePress totally drops vue-router, so that we don't need to define all the routes. Instead, VitePress implemented a super light weight router with the help of vite.

Thus, for VuePress: