schangxiang@126.com
2025-09-18 49a51c068d62084bc4c3e77c4be94a20de556c4a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# cache-require-paths
 
> Caches resolved paths in module require to avoid Node hunting for right module. Speeds up app load.
 
[![NPM][cache-require-paths-icon] ][cache-require-paths-url]
 
[![Build status][cache-require-paths-ci-image] ][cache-require-paths-ci-url]
[![semantic-release][semantic-image] ][semantic-url]
 
[cache-require-paths-icon]: https://nodei.co/npm/cache-require-paths.png?downloads=true
[cache-require-paths-url]: https://npmjs.org/package/cache-require-paths
[cache-require-paths-ci-image]: https://travis-ci.org/bahmutov/cache-require-paths.png?branch=master
[cache-require-paths-ci-url]: https://travis-ci.org/bahmutov/cache-require-paths
[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
[semantic-url]: https://github.com/semantic-release/semantic-release
 
This is a partial solution to Node "hunting" for right file to load when you require a 3rd party
dependency. See [Node’s `require` is dog slow](https://kev.inburke.com/kevin/node-require-is-dog-slow/) 
and [Faster Node app require](http://glebbahmutov.com/blog/faster-node-app-require/) for details.
 
## Use
 
    npm install --save cache-require-paths
 
Load the module first in your application file
 
```js
// index.js
require('cache-require-paths');
...
```
 
The first time the app loads, a cache of resolved file paths will be saved to `.cache-require-paths.json`
in the current directory.  Every application startup after that will reuse this filename cache to avoid
"hunting" for the right filename.
 
To save cached paths to a different file, set the environmental variable `CACHE_REQUIRE_PATHS_FILE`.
 
## Results
 
Here are results for loading common packages without and with caching resolved require paths.
You can run any of this experiments inside the `test` folder. `node index.js` loads
using the standard resolve. `node index.js --cache` uses a cache of the resolves paths.
 
Using node 0.10.37
 
    require('X')    |  standard (ms)  |  with cache (ms)  |  speedup (%)
    ------------------------------------------------------------------
    express@4.12.3  |        72       |       46          |     36
    karma@0.12.31   |       230       |      170          |     26
    grunt@0.4.5     |       120       |       95          |     20
    sails@0.11.0    |       170       |      120          |     29
 
Using node 0.12.2 - all startup times became slower.
 
    require('X')    |  standard (ms)  |  with cache (ms)  |  speedup (%)
    ------------------------------------------------------------------
    express@4.12.3  |        90       |       55          |     38
    karma@0.12.31   |       250       |      200          |     20
    grunt@0.4.5     |       150       |      120          |     20
    sails@0.11.0    |       200       |      145          |     27
 
## TODO
 
- [ ] Cache only the absolute paths (relative paths resolve quickly)
- [ ] Invalidate cache if dependencies in the package.json change
 
## Discussion
 
You can see Node on Mac OS X searchig for a file to load when loading an absolute path
like `require(express)` by using the following command to make a log of all system level
calls from Node (start this from another terminal before running node program)
 
    sudo dtruss -d -n 'node' > /tmp/require.log 2>&1
 
Then run the test program, for example in the `test` folder run
 
    $ node index.js
 
Kill the `dtruss` process and open the generated `/tmp/require.log`. It shows every system call
with the following 4 columns: process id (should be single node process), relative time (microseconds),
system call with arguments, and after the equality sign the numerical result of the call.
 
When loading `express` dependency from the test program using `require('express');` we see
the following search (I abbreviated paths for clarity):
 
    # microseconds call
    664730 stat64(".../test/node_modules/express\0", 0x7FFF5FBFECF8, 0x204)        = 0 0
    664784 stat64(".../test/node_modules/express.js\0", 0x7FFF5FBFED28, 0x204)         = -1 Err#2
    664834 stat64(".../test/node_modules/express.json\0", 0x7FFF5FBFED28, 0x204)       = -1 Err#2
    664859 stat64(".../test/node_modules/express.node\0", 0x7FFF5FBFED28, 0x204)       = -1 Err#2
    664969 open(".../test/node_modules/express/package.json\0", 0x0, 0x1B6)        = 11 0
    664976 fstat64(0xB, 0x7FFF5FBFEC38, 0x1B6)         = 0 0
    665022 read(0xB, "{\n  \"name\": \"express\", ...}", 0x103D)        = 4157 0
    665030 close(0xB)      = 0 0
 
By default, Node checks if the local `node_modules/express` folder exists first (first `stat64` call),
Then it tries to check the status of the `node_modules/express.js` file and fails. 
Then `node_modules/express.json` file. Then `node_modules/express.node` file. Finally it opens
the `node_modules/express/package.json` file and reads the contents. 
 
Note that this is not the end of the story. Node loader only loads `express/package.json` to fetch
`main` filename or use the default `index.js`! Each wasted file system call takes only 100 microseconds,
but the tiny delays add up to hundreds of milliseconds and finally seconds for larger frameworks.
 
Profile the same program with `--cache` option added to the command line arguments
 
    $ node index.js --cache
 
This option loads the `cache-require-paths` module as the first require of the application
 
```js
var useCache = process.argv.some(function (str) {
  return str === '--cache';
});
if (useCache) {
  console.log('using filename cache');
  require('cache-require-paths');
}
```
 
The trace now shows *no calls to find `express` package*, just straight load of the `express/index.js` file.
 
    643466 stat64(".../node_modules/express/index.js\0", 0x7FFF5FBFED28, 0x3)         = 0 0
    643501 lstat64(".../node_modules\0", 0x7FFF5FBFED08, 0x3)         = 0 0
    643513 lstat64(".../node_modules/express\0", 0x7FFF5FBFED08, 0x3)         = 0 0
    643523 lstat64(".../node_modules/express/index.js\0", 0x7FFF5FBFED08, 0x3)        = 0 0
    643598 open(".../node_modules/express/index.js\0", 0x0, 0x1B6)        = 12 0
    643600 fstat64(0xC, 0x7FFF5FBFED58, 0x1B6)         = 0 0
 
Mission achieved. Note that the speedup only happens after the first application run finishes successfully.
The resolution cache needs to be saved to a local file, and this happens only on process exit.
 
## Small print
 
Author: Gleb Bahmutov © 2015
 
* [@bahmutov](https://twitter.com/bahmutov)
* [glebbahmutov.com](http://glebbahmutov.com)
* [blog](http://glebbahmutov.com/blog)
 
License: MIT - do anything with the code, but don't blame me if it does not work.
 
Spread the word: tweet, star on github, etc.
 
Support: if you find any problems with this module, email / tweet /
[open issue](https://github.com/bahmutov/cache-require-paths/issues) on Github