Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Threads massively slows down linting / re-linting #52

Closed
eek opened this issue Nov 27, 2020 · 19 comments · Fixed by #55
Closed

Threads massively slows down linting / re-linting #52

eek opened this issue Nov 27, 2020 · 19 comments · Fixed by #55

Comments

@eek
Copy link

eek commented Nov 27, 2020

  • Operating System: Windows 10
  • Node Version: v14.15.1
  • YARN Version: 1.22.4
  • webpack Version: 4.44.2
  • eslint-webpack-plugin Version: 2.4.0

This is a problem that was experienced in react-scripts / create-react-app v4.0.1 and v4.0.2

Compiling and Re-compiling changes seems extremely slow compared to pre v4 version. After digging a bit, it seems the culprit is threads: true pointed out by @radoi90 here - #50 (comment)

Expected Behavior

Fast compiling

Actual Behavior

Actual very slow compiling / recompiling.

I dug a bit deeper to see if there's a small issue with the threads and it might be 2 issues, don't know if it's actually the same.

On the first compile:

  • Linting a particular file seems fast but it takes a while to move to a new file - anywhere between 20ms to a few seconds.

I've added before this line:

  console.timeEnd('start lint next');
  console.time('linted in');

  // Queue file for linting.
  lint(file);

  console.timeEnd('linted in');
  console.time('start lint next');

and my results look like this:

start lint next: 19.038ms
files to lint: E:\project\src\components\layout\aFile.tsx
linted in: 0.702ms

start lint next: 133.277ms
files to lint: E:\project\src\components\settings\bFile.tsx
linted in: 1.106ms

start lint next: 577.165ms
files to lint: E:\project\src\stores\cFile.ts
linted in: 1.451ms

start lint next: 98.303ms
files to lint: E:\project\src\stores\dFile.ts
linted in: 1.143ms

start lint next: 148.661ms
files to lint: E:\project\src\stores\eFile.ts
linted in: 0.999ms

start lint next: 901.431ms
files to lint: E:\project\src\stores\fFile.ts
linted in: 1.096ms

start lint next: 13.968ms
files to lint: E:\project\src\functions\gFile.ts
linted in: 0.657ms

start lint next: 86.591ms
files to lint: E:\project\src\stores\thumb-gen\hFile.ts
linted in: 1.701ms

(this is a small excerpt of 300+ files that need to be linted at first run)

I've also measured:

const { errors, warnings, generateReportAsset } = await report();

which took awaitReport: 29.909ms

and

await generateReportAsset(compilation);

generateReportAsset: 0.177ms

then I've edited just a single file to see what happens there and it looks something like this:

files to lint: E:\project\src\components\layout\Header.tsx
linted in: 3.163ms
awaitReport: 16.754s // 16 seconds!!


files to lint: E:\project\src\components\layout\Header.tsx
linted in: 1.515ms
awaitReport: 11.264s // 11 seconds!!

Code

https://github.com/facebook/create-react-app/blob/9b08e3c9b365ac790546a3d5027d24f264b42613/packages/react-scripts/config/webpack.config.js#L753-L771

setting threads: false as here facebook/create-react-app#10154 (comment)
solves the slow lint / relint

// additional code, HEY YO remove this block if you don't need it

How Do We Reproduce?

I guess try it on a newly created React project with create-react-app - facebook/create-react-app#10145 (comment)

@alexander-akait
Copy link
Member

@ricardogobbosouza I think we should set threads: false by default

@alexander-akait
Copy link
Member

This makes sense on large projects.

@eek
Copy link
Author

eek commented Nov 27, 2020

@ricardogobbosouza I think we should set threads: false by default

I agree, should be an option for people that really need it.

For now, I also don't see a lot of improvements in our build time - it's 5 seconds faster with threads than without threads enabled - 64.66s vs 69.05s (and we have 297 files and 7440 3rd party files), but having 10s+ delay of each file change is too much.

I've also upgraded from node v14 to node v15 to see if there's a difference, but there wasn't one.

@jsg2021
Copy link
Contributor

jsg2021 commented Nov 30, 2020

Just out of curiosity, what is your cpu? I imaging form hardware to hardware this will vary. The report should be only issued once per compile and will take the same amount of time threaded or not.

The linting ... reading/parsing/validating happens in workers.

I'm quite surprised the recompile time you're seeing. I haven't noticed that locally. Is your cpu under heavy load?

@jsg2021
Copy link
Contributor

jsg2021 commented Nov 30, 2020

and we have 297 files and 7440 3rd party files), but having 10s+ delay of each file change is too much.

I sure hope you aren't linting third party code. A 10s delay sounds like a problem and is not expected behavior.

I maintain a very large project that has thousands of files (ignoring third party files) and editing and recompiling is nearly imperceptible. So I'm curious what could be different in your config?

@jsg2021
Copy link
Contributor

jsg2021 commented Nov 30, 2020

I've added before this line:

Your time measurement here is measuring time between invocations... which is not really measuring the speed of this block. Webpack is calling this repeatedly on all the files in the graph. It only makes it into that block when we see a file we want to lint. And then, we kick off an asynchronous call to have it linted, but return synchronously to not block the compiler.

The report starts at then end of compiling and waits for the lint queue to finish. (this will take some time depending on how many files are still being worked on... similar to how long running eslint command line on your project)

@jsg2021
Copy link
Contributor

jsg2021 commented Nov 30, 2020

@ricardogobbosouza I think we should set threads: false by default

it may be a bug. it shouldn't ever be slower. It turns itself off if there are 2 or less cores reported. on 4+ core systems it should be a net speed up.

@eek
Copy link
Author

eek commented Nov 30, 2020

and we have 297 files and 7440 3rd party files), but having 10s+ delay of each file change is too much.

I sure hope you aren't linting third party code. A 10s delay sounds like a problem and is not expected behavior.

I maintain a very large project that has thousands of files (ignoring third party files) and editing and recompiling is nearly imperceptible. So I'm curious what could be different in your config?

I'm definitely not linting 3rd party code (especially since I don't have this problem with threads: off)

On just a file I've added some timings above, it takes 3ms to lint but then it takes awaitReport around 11-16s to complete.

I don't have a slow machine. It's a Dell XPS 9560 - i7-7700HQ, 32gb RAM, and an NVME SSD (on Windows). And the same problem happens to my colleague as well, who runs a Dell Area 51 laptop with (i9-10980HK I think) on PopOS

Since this also happens to a lot of people that run react-create-app, definitely a bug somewhere, probably related to the config file or linting rules?

This guy says it's the same issue on blank projects as well - facebook/create-react-app#10145 (comment)

I will give it a try and see if I have the same issue on a newly created React project and come back.

@jsg2021
Copy link
Contributor

jsg2021 commented Nov 30, 2020

@eek I will try to reproduce as well in the morning.

@eek
Copy link
Author

eek commented Nov 30, 2020

@eek I will try to reproduce as well in the morning.

Great.

I've also tested it now and what @imsandez reported here - facebook/create-react-app#10145 (comment) is true.

if you create a new react app:

npx create-react-app test-app --template typescript

(I've created a TS one to be closer to what I use)

I takes between 3-6s to compile a change in App.tsx (closer to 3s rather than 6s) but once you downgrate the eslint-webpack-plugin by adding resolutions at the end of package.json:

  "resolutions": {
    "react-scripts/eslint-webpack-plugin": "2.3.0"
  }

and yarn again. (don't know if npm supports resolutions)

Compile will take maximum 0.5s.

With my custom eslintConfig:
  "eslintConfig": {
    "parser": "@typescript-eslint/parser",
    "extends": [
      "airbnb-typescript",
      "plugin:@typescript-eslint/eslint-recommended"
    ],
    "parserOptions": {
      "project": "./tsconfig.json",
      "ecmaVersion": 8,
      "ecmaFeatures": {
        "experimentalObjectRestSpread": true
      }
    },
    "plugins": [
      "@typescript-eslint",
      "import",
      "react"
    ],
    "env": {
      "browser": true
    },
    "globals": {
      "gtag": true,
      "isNaN": true
    },
    "settings": {
      "import/resolver": {
        "node": {
          "extensions": [
            ".js",
            ".jsx",
            ".ts",
            ".tsx"
          ],
          "moduleDirectory": [
            "node_modules",
            "src/"
          ]
        }
      }
    }
  },

on the blank project it always takes 6s. Once I downgrade to the previous eslint-webpack-plugin it takes 0.5s tops.

@ricardogobbosouza
Copy link
Collaborator

I agree that it should be deactivated by default, in most projects that are usually small, it does not make sense to have it activated.

We forgot to put this option in the documentation 😟
And a note recommending to be activated for large projects

WTDY @alexander-akait ?

@eek
Copy link
Author

eek commented Nov 30, 2020

I agree that it should be deactivated by default, in most projects that are usually small, it does not make sense to have it activated.

We forgot to put this option in the documentation 😟
And a note recommending to be activated for large projects

WTDY @alexander-akait ?

@ricardogobbosouza, I also understand what @jsg2021 says as well. If it wouldn't be for this weird bug that's affecting recompile time, it would be a performance improvement for all projects.

@ricardogobbosouza
Copy link
Collaborator

@jsg2021
In my view the problem is here, I think
https://github.com/webpack-contrib/eslint-webpack-plugin/blob/master/src/getESLint.js#L58
Is carrying the eslint twice

@jsg2021
Copy link
Contributor

jsg2021 commented Nov 30, 2020

Using the defaults provided by npx create-react-app test-app --template typescript I'm getting 1-2ms recompile time editing App.tsx. Adding your lint config makes it substantially slower and exposes what the slow down is.

The jest-worker doesn't auto-warm up the pool. So until you hit max-pool size each lint request is paying the eslint init cost. After each compile, we cleanup the pool. So, each recompile rebuilds the pool... and since incremental edits often are one file... that takes a pool init + module init + ESLint setup AND run... So, my first stab at this is to disable the pool after the first build. All rebuilds would be on the main process and not respin the pool.

I can go further to preserve the pool (and warmup) such that re-compiles don't start over. Dispatching seems to eat ~150-200ms. So recompiles will always be slower by that much minimum. Would some lint configs warrant always run off thread? or would just running lint on the main thread after the first compile be sufficient? @ricardogobbosouza @eek @alexander-akait

@eek
Copy link
Author

eek commented Dec 1, 2020

@jsg2021 just running the lint on the main thread after the first compile I think it's sufficient.

Updated now to 2.4.1 and things are no longer slow 🙌 thanks! 😁

@fangbinwei
Copy link

my first stab at this is to disable the pool after the first build. All rebuilds would be on the main process and not respin the pool

After pr #55, we can feel free to set threads: true ? For a large project, linting files through jest-worker in the first build is meaningful. Any suggestion?

@alexander-akait
Copy link
Member

I think best value is false right now, in future we can revisit it

@alexander-akait
Copy link
Member

The main problem is initial workers start and we should not ending threads in watch mode, I think we need webpack API for this

@jsg2021
Copy link
Contributor

jsg2021 commented Dec 1, 2020

my first stab at this is to disable the pool after the first build. All rebuilds would be on the main process and not respin the pool

After pr #55, we can feel free to set threads: true ? For a large project, linting files through jest-worker in the first build is meaningful. Any suggestion?

we didn't take it away. it's just not default. you may turn it on in your config. that's what i have done for my project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants