Closed lukashavrlant closed 3 years ago
I have also experienced this issue when running alsatian tests after upgrading to ts-node v7. I've found I can resolve the issue by specifying the --files
CLI flag.
The problem seems to stem from the fact that the test framework require
s each .test.ts
file in turn, and each call to require
appears to compile every module in that file's dependency tree from scratch. Specifying --files
reverts to the old behaviour of loading all files on startup (and presumably caching them in memory).
@blakeembrey does this sound like a plausible diagnosis, and can you see any better solution?
Have you tried --transpile-only
mode?
I've run into similar issue today. After ts-node upgrade from 7.0.1 to 8.0.1 my mocha test suite became very slow (32s vs 4s). Adding TS_NODE_TRANSPILE_ONLY=true resolved my issue. Thanks @cspotcode
--transpile-only
does speed things up considerably for me. I don't want to lose the type checking though!
I tried all the proposed solutions. The files: true
makes execution significantly faster! Turning off type checking also made things faster. I just had to turn off typeCheck and turn on transpileOnly (typeCheck: false, transpileOnly: true
). This is good enough workaround, but it seems that the default behavior is still weird.
That's intereseting, files: false
is definitely meant to make things faster rather than slower. Does anyone have a large enough project that's running slow and want to either 1. investigate if the reason @davidgruar mentions appears true or 2. allow me temporary access to look into further myself?
Ideally files: false
loads a single file and spiders out so it is possible that this is a slight issue with performance. It's also possible that the previous version was hitting the cache heavily, if you upgraded from v7 to v8 (v8 removed the cache because of type issues). We could also investigate adding caching back but it needs to be much smarter than the previous version to solve transitive type changes.
I've hit this issue too after updating from 7.0.1 to 8.0.1.
From what I can tell when files: false
the tsconfig.json
file gets changed in memory to remove the files
and includes
attributes.
This seems to create an invalid config (when using TypeScript 3.2.4):
error TS18002: The 'files' list in config file 'tsconfig.json' is empty.
Removing the files
and includes
attributes altogether however seems to resolve the issue.
If anyone can give me temporary access to a slow repo, let me know! I can quickly take a look into the issue and happy to sign any NDA, etc you need. If not, can I get a sense of peoples tsconfig.json
that's slow - are you relying on files
, include
, exclude
, rootDir
or other to compile with TypeScript?
Let's try with reactions:
files
include
exclude
rootDir
8.0.2 hasn't made any difference for me. The fact it's adding files: []
in to the tsconfig.json
still kills performance.
I can't share our project unfortunately - but here's our tsconfig.json
:
{
"compileOnSave": true,
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"lib": [
"es2017"
],
"module": "commonjs",
"moduleResolution": "node",
"newLine": "LF",
"noEmitOnError": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": false,
"outDir": "dist",
"removeComments": true,
"rootDir": ".",
"sourceMap": true,
"strict": true,
"strictFunctionTypes": false,
"suppressImplicitAnyIndexErrors": true,
"target": "es6"
}
}
@garethflowers Are you using mocha
or something else that makes the explanation in https://github.com/TypeStrong/ts-node/issues/754#issuecomment-456788194 make sense?
Alternatively, can you try populating files = ['one.ts file here']
so it's valid, but still overridden, and let me know if that changes anything?
@blakeembrey I've made a minimal repro at https://github.com/davidgruar/tsnode-perf-repro - please check it out and have a play. Loading just three test files with fairly few dependencies takes more than a second, and the more imports you add, the longer it takes.
It doesn't seem like a huge overhead when you time the entire program, instead of just requires:
There is a difference, however, so for testing it might make the most sense to enable --files
whereas running an application it doesn't - there's only a single input and TypeScript pre-processes everything anyway. I'll look into it more though because the difference is clear and it would be nice to bring the dynamic files number down.
Ok, perfect. It does look like TypeScript makes 4x the number of requests to the filesystem when files: false
. Looking into whether there's some issue with how TypeScript is functioning here and how this could be improved.
i've found several settings contributing to slow compile times, im pretty sure the issue is related to settings that will delete the default setting of ignore: node_modules. IE: allowJs. Others that may also (just from my memory of guess/check) are removeComments, emitDecoratorMetadata, and a few others. I've also noticed that if you glob without file ext like include: src/*/ instead of src/*/.ts it can cause slowdowns , idk if its chokadir watching fontfiles that may be in there or something. using isolatedModules is the sure-fire way to speed things up though.
@jeremy-coleman Sorry, I couldn't quite follow. Can you please let me know who you/what are responding to here?
Edit: I don't think any of those are related to the root cause of issues mentioned in this thread, that's all.
@blakeembrey Hi, I also created an example: https://github.com/lukashavrlant/ts-node-perf But I guess it is similar to the previous example. Basically compiling TS files to JS files and then running mocha tests using the generated JS files is faster then using ts-node directly. See readme for more details please. The more imports I use is test files, the slower it gets. For the sake of example I used zero imports in tests files.
Unfortunately this definitely seems to be the case. I'm not 100% sure why in the language services this is so expensive. It appears that changing rootFiles
results in the "project out of date" and it starts re-resolving all types and traversing node_modules
. This is very bad in the test loading case - I need to find a way to use the previously cached resolutions over trying to re-resolve everything when the root files change. cc @TypeStrong/typescript-team @weswigham in case I'm doing something wrong.
@sheetalkamat would you know what would need to be done to allow API implementers to persist the resolution cache across builds?
When host doesn't have resolveModuleName it creates the cache per program. So whenever program changes the new resolution cache is created. (Note this is different from internal resolution cache that tsc and tsserver use that are made aware of which files have changed and hence which resolutions to invalidate) I don't think ModuleResolutionCache
that compiler has, is equippd to handle the changes and hence its per program.
I think better option would be to use our WatchAPI instead as that is equipped to handle changes in file and hence invalidating partial resolutions.
I'm also having issues with speed. I'm having a big Angular Application where I am running protractor tests with the jasmine framework. When I start the tests with protractor flag --troubleshoot i can see where it gets to the point where the files get processed. Usually that takes like +- 3 seconds and after that jasmine starts. Having version 8.0.0 and above its taking up to almost 2 Minutes until the files are processed and jasmine starts.
Sadly I don't know where I could try out the solution with file: false. Any help is appreciated.
It's not a solution, but downgrade to 6.2.0 helped me currently with the test performace issues.
For now, please try using the environment flag from https://github.com/TypeStrong/ts-node#cli-and-programmatic-options - TS_NODE_FILES=true
. I'll attempt refactoring to the newer TypeScript watch API and see if it improves performance in the coming weeks.
ts-node 7.0 -- 2.5s ts-node 8.0 -- 5.2s
same environment.
@miao17game By same environment, is this a test suite, a script or something else?
Hi -- for what it's worth --
We have a rather large code base (no way to know really...)
It is on the order of 450 Mocha tests.
7.x was not "snappy" to startup - but it was tolerable.
I upgraded from 7.0.2 to 8.0.1 and the tests will actually never finish on Windows. They work fine on Linux (like 7.x).
After reading through this, I was trying to use the --files true
flag. On a smaller codebase it made a big different (the tests ran).
On the code base with 450 tests... it simply never finished. The strange thing is if I set the environment variable TS_NODE_FILES -- set TS_NODE_FILES=true
; the tests start almost immediately.
If it helps:
mocha --recursive --require ts-node/register -R spec ./**/*.spec.ts --no-timeouts --exit
tsconfig.json
{
"compilerOptions": {
"target": "es6",
"outDir": "dist",
"rootDir": "src",
"moduleResolution": "node",
"module": "commonjs",
"declaration": true,
"importHelpers": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"listFiles": false,
"traceResolution": false,
"pretty": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny" : true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"es6",
"dom",
"dom.iterable"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules/**"
],
"compileOnSave": false
}
package.json
"ts-node": "^8.0.2",
"typescript": "3.3.3"
"tslib": "^1.9.3",
We have just started running into this problem after upgrading our Angular app to 7.2.5 and at the same time we also upgraded ts-node to 8.0.2. Since the upgrade running the protractor e2e tests always took about 2 minutes before they would start running. After some investigation I found that it was taking a long time loading the spec files. I've had to downgrade ts-node back to 5.0.1 which fixes it but just wondering if anyone else has had the same problem and if they were able to solve it with the latest ts-node version.
I'm facing this issue when writing tests using mocha + proxyquire. My solution so far is to mock as many dependencies with proxyquire as possible and use these three options for ts-node (they all seem to positively affect the start-up speed):
transpileOnly: true,
typeCheck: false,
files: false
@kaiza set this environment variable -> set TS_NODE_FILES=true
It should make your tests 'go fast'
I was wondering if this is fixed in the new release (8.0.3). We have a test set of nearly 8000 tests and with version 7 it only takes a few moments before it starts, but when I updated to version 8, it would take more than 30 seconds before the tests would start.
For reference: the codebase is about 70k lines of code, test codebase is something over 110k lines of code. With a total of almost 1800 files.
@chadbr I'm having an issue with this too, I tried your suggested fix of adding the environment variable which unfortunately didn't work running ts-node 8.0.3
@blakeembrey I'm hitting this problem with ts-node@8.0.3
in my monorepo project (https://github.com/just-dandi/dandi). I updated from 7.0.1 to 8.0.3 in this branch: https://github.com/just-dandi/dandi/tree/feature/9-pg-db-tests, and every time I run my test suite, it now takes around 30s. By comparison, on 7.0.1, it would run in ~6s once ts-node's cache was warmed.
I tried using TS_NODE_FILES=true
, but it didn't seem to do anything.
If you'd like to try checking out that branch, just run npm run setup
after cloning and then you should be able to run the tests with npm test
.
I'm just using tsc --noEmit && TS_NODE_TRANSPILE_ONLY=true ts-node index.ts
in my project, and it's 3x faster than ts-node index.ts
( there are 500+ ts files in my project. before: 96 seconds, after: 32 seconds ).
also had to downgrade 8.0.3
to 7.0.1
to fix the performance issue, time to start tests was like 3 minutes vs few seconds difference, TS_NODE_FILES=true
didn't help anything
We had to downgrade too. This is what happens in our project 💥
I also created a project to reproduce the issue: https://github.com/lukaselmer/reproduce-slow-ts-node
git clone https://github.com/lukaselmer/reproduce-slow-ts-node reproduce-slow-ts-node-v7
cd reproduce-slow-ts-node-v7
npm i
npm start
npm start
git clone https://github.com/lukaselmer/reproduce-slow-ts-node reproduce-slow-ts-node-v8
cd reproduce-slow-ts-node-v8
git checkout v8
npm i
npm start
npm start
It seems that even with the additional options, 8.0.3
is still 2-7 times slower than 7.0.1
.
It doesn't seem fixed with version 8.1.0
😞
I also have the performance issues while running tests with tape and ts-node 8.1.0
. With the option TS_NODE_TRANSPILE_ONLY=true
the tests run 6 times faster, whereas TS_NODE_FILES=true
doesn't have any positive influence.
@lukaselmer I tried your branch but it doesn't actually test any slowness except for using a filesystem cache. If you do --no-cache
you end up with the same result. I'm not sure how many other people are requiring the same file contents over and over again - if they are we can do an in-memory cache for that but I suspect there's a different issue here not captured by your demo. If you do ts-node --no-cache
in v7, you'll see similar performance numbers.
@sheetalkamat FWIW, I tried the various options mentioned on that page but none of them fulfill the required use-case of ts-node
. Watch mode 100% didn't work because I need to 1. get the source file which was always empty to be able to emit the specific file and 2. it took over the console to emit TypeScript debugging statements. I also don't think file watching is needed in ts-node
, the issue is more that the TypeScript compiler will force the entire project to re-compute if the root files array changes (IIRC).
@weswigham @sheetalkamat One big difference to perf is outputting all file names instead of the root file name (https://github.com/TypeStrong/ts-node/commit/6dad5ad34d0cea80ff9be75d9a77e07f19d9175a) - any ideas why that would make such a huge difference in performance? Aside from that, I went ahead and adding caching to all FS operations (https://github.com/TypeStrong/ts-node/commit/1e838f76a3b3590b3ee37acfcb9d846a4e0bd08d) - this makes a bigger difference for people using non-SSDs, but I found in the simple example above it makes 1400+ queries to "directory exists", 700+ to "file exists", 170+ to "read file", etc. Any clues on why this is necessary for the compiler? It seems like a lot of lookups - when cached it goes down to barely 40 fs operations in total.
Please feel free to try 8.2.0, it has the two minor improvements above. It won't hit anywhere near as just re-using the cached emit multiple times yet since it shouldn't be overly common that people's files are exactly the same across their project.
Any clues on why this is necessary for the compiler? It seems like a lot of lookups - when cached it goes down to barely 40 fs operations in total.
AFAIK we expect the CompilerHost
provided implements appropriate caching (the builtin one based on sys
does not) - we actually have a set of caching compiler hosts we use for tsc -b
mode and the language service.
@blakeembrey as @weswigham suggested, we want CompilerHost
to maintain all the caches. The improvements you are seeing, I think because of module resolution since that is what requires us to query directory/file at multiple locations.
For watch API, you want to provide watchFile
and watchDirectory
implementations which notify the watch about changes. Eg. https://github.com/sheetalkamat/ts-loader/blob/master/src/servicesHost.ts#L458 where it creates the watcher and webpack's known state of modified files/directories is notified to watch at https://github.com/sheetalkamat/ts-loader/blob/master/src/watch-run.ts#L51
@lukaselmer I tried your branch but it doesn't actually test any slowness except for using a filesystem cache. If you do
--no-cache
you end up with the same result. I'm not sure how many other people are requiring the same file contents over and over again - if they are we can do an in-memory cache for that but I suspect there's a different issue here not captured by your demo. If you dots-node --no-cache
in v7, you'll see similar performance numbers.
I agree that with ts-node --no-cache
it takes longer to load the files with version 7. I also tried it in our project.
ts-node --no-cache
): Server startup: 24118.856ms--no-cache
command doesn't existThe file system cache is already implemented in 8.2.0
, right? Were there any other caches active in 7.0.1
? 🤔
@lukaselmer The cache 7.x is describing isn't to do with caching file system operations, but caching and checksumming the results from the TypeScript compiler. This is unstable since a change on a sub-sub-dependency could result in the current file failing, but using a cache this would go undetected (because the file didn't change). The right solution would be something like TypeScript's new incremental when it's exposed.
@blakeembrey Thank you for the explanation.
I also have the performance issues while running tests with tape and ts-node
8.1.0
. With the optionTS_NODE_TRANSPILE_ONLY=true
the tests run 6 times faster, whereasTS_NODE_FILES=true
doesn't have any positive influence.
@kiliw : That turns off type checking on the spec files. If you use any typescript types in your tests files, they're not going to be checked.
@vort3xxx thanks for the info
Same issue here, tsc takes 9 sec to compile an entire project, running only a subset of the tests takes 25 seconds to start them... latest ts, latest mocha, latest ts-node, latest everything
For anyone looking to run their mocha tests very fast until this issue is fixed you can:
tsc --watch
in a console windowThis way you can write your ts test, it gets compiled automatically to js, and when you run the test command it will execute it really quickly
I'm having an weird issue where
console.log(new Date())
import * as express from 'express'
console.log(new Date())
is taking 2 seconds on my project, but on a clean one it takes just a few ms
I also noticed that TS_NODE_TRANSPILE_ONLY=true
makes it slower and TS_NODE_FILES=true
doesn't seem to do anything.
Any ideas?
"ts-node": "^8.1.0",
"typescript": "^3.4.5",
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"noImplicitAny": false,
"sourceMap": true,
"noUnusedParameters": true,
"noUnusedLocals": true
}
}
ts-node index.ts
Update:
ts-node@7 seems to be many times faster
I have read over all the comments, the issue is still open and it's been close to 9 months. The latest version still has this problem and the only solution I can see here is to role back to 7.0.1
Are there any updates on this issue and when this will be resolved.
ts-node -r tsconfig-paths/register --files run-tests.ts | faucet
Using --files
helps run the tests a bit faster for my colleague but for myself they do not complete even after 11 minutes whilst I was making tea.
Hi, for some reason we have performance problems when using ts-node in our mocha tests. It takes about 500 ms to compile an empty ts file. I added some console.logs to measure the time to run this very line:
const output = service.getEmitOutput(fileName)
and this line takes 500 ms to run even though the content of the file is an empty string (thecode
variable on the line 314 is empty string). Actually, all our tests files take too long to process, while the production files take a few milliseconds to compile. So any*.spec.ts
takes about 500 ms to compile, a regular*.ts
file takes about 20 ms. Do you have any idea what could be the root cause or how should I debug it more?We use latest mocha, ts-node and typescript, but we tried some old versions too and the problem persists. The
tsconfig.json
: