Skip to content

Commit 1f29fa9

Browse files
committed
perf: refactor various timestamp caches into ES6 Maps
This change satisfies webpack#6234 which desires to change the timestamp caches from plain objects to the ES6 Map type to avoid deopts, since v8 expects things to be added to Maps but objects are only fast if properties aren't added dynamically after initial assignment.
1 parent e8c2596 commit 1f29fa9

9 files changed

+56
-44
lines changed

lib/CachePlugin.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class CachePlugin {
4242
compiler.hooks.run.tapAsync("CachePlugin", (compiler, callback) => {
4343
if(!compiler._lastCompilationFileDependencies) return callback();
4444
const fs = compiler.inputFileSystem;
45-
const fileTs = compiler.fileTimestamps = {};
45+
const fileTs = compiler.fileTimestamps = new Map();
4646
asyncLib.forEach(compiler._lastCompilationFileDependencies, (file, callback) => {
4747
fs.stat(file, (err, stat) => {
4848
if(err) {
@@ -53,13 +53,14 @@ class CachePlugin {
5353
if(stat.mtime)
5454
this.applyMtime(+stat.mtime);
5555

56-
fileTs[file] = +stat.mtime || Infinity;
56+
fileTs.set(file, +stat.mtime || Infinity);
57+
5758
callback();
5859
});
5960
}, err => {
6061
if(err) return callback(err);
61-
Object.keys(fileTs).forEach(key => {
62-
fileTs[key] += this.FS_ACCURENCY;
62+
Array.from(fileTs.keys()).forEach(key => {
63+
fileTs.set(key, fileTs.get(key) + this.FS_ACCURENCY);
6364
});
6465
callback();
6566
});

lib/Compiler.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ class Compiler extends Tapable {
7878
this.recordsOutputPath = null;
7979
this.records = {};
8080

81-
this.fileTimestamps = {};
82-
this.contextTimestamps = {};
81+
this.fileTimestamps = new Map();
82+
this.contextTimestamps = new Map();
8383

8484
this.resolverFactory = new ResolverFactory();
8585
this.resolvers = {
@@ -177,8 +177,8 @@ class Compiler extends Tapable {
177177
}
178178

179179
watch(watchOptions, handler) {
180-
this.fileTimestamps = {};
181-
this.contextTimestamps = {};
180+
this.fileTimestamps = new Map();
181+
this.contextTimestamps = new Map();
182182
return new Watching(this, watchOptions, handler);
183183
}
184184

lib/ContextModule.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class ContextModule extends Module {
129129
}
130130

131131
needRebuild(fileTimestamps, contextTimestamps) {
132-
const ts = contextTimestamps[this.context];
132+
const ts = contextTimestamps.get(this.context);
133133
if(!ts) {
134134
return true;
135135
}

lib/NormalModule.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,12 +543,12 @@ class NormalModule extends Module {
543543
// Missing timestamp -> need rebuild
544544
// Timestamp bigger than buildTimestamp -> need rebuild
545545
for(const file of this.buildInfo.fileDependencies) {
546-
const timestamp = fileTimestamps[file];
546+
const timestamp = fileTimestamps.get(file);
547547
if(!timestamp) return true;
548548
if(timestamp >= this.buildTimestamp) return true;
549549
}
550550
for(const file of this.buildInfo.contextDependencies) {
551-
const timestamp = contextTimestamps[file];
551+
const timestamp = contextTimestamps.get(file);
552552
if(!timestamp) return true;
553553
if(timestamp >= this.buildTimestamp) return true;
554554
}

lib/WatchIgnorePlugin.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ class IgnoringWatchFileSystem {
4040
if(err) return callback(err);
4141

4242
ignoredFiles.forEach(path => {
43-
fileTimestamps[path] = 1;
43+
fileTimestamps.set(path, 1);
4444
});
4545

4646
ignoredDirs.forEach(path => {
47-
dirTimestamps[path] = 1;
47+
dirTimestamps.set(path, 1);
4848
});
4949

5050
callback(err, filesModified, dirsModified, missingModified, fileTimestamps, dirTimestamps);
@@ -56,14 +56,14 @@ class IgnoringWatchFileSystem {
5656
getContextTimestamps: () => {
5757
const dirTimestamps = watcher.getContextTimestamps();
5858
ignoredDirs.forEach(path => {
59-
dirTimestamps[path] = 1;
59+
dirTimestamps.set(path, 1);
6060
});
6161
return dirTimestamps;
6262
},
6363
getFileTimestamps: () => {
6464
const fileTimestamps = watcher.getFileTimestamps();
6565
ignoredFiles.forEach(path => {
66-
fileTimestamps[path] = 1;
66+
fileTimestamps.set(path, 1);
6767
});
6868
return fileTimestamps;
6969
}

lib/node/NodeWatchFileSystem.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"use strict";
66

77
const Watchpack = require("watchpack");
8+
const objectToMap = require("../util/objectToMap");
89

910
class NodeWatchFileSystem {
1011
constructor(inputFileSystem) {
@@ -41,7 +42,7 @@ class NodeWatchFileSystem {
4142
if(this.inputFileSystem && this.inputFileSystem.purge) {
4243
this.inputFileSystem.purge(changes);
4344
}
44-
const times = this.watcher.getTimes();
45+
const times = objectToMap(this.watcher.getTimes());
4546
callback(null,
4647
changes.filter(file => files.includes(file)).sort(),
4748
changes.filter(file => dirs.includes(file)).sort(),
@@ -67,15 +68,15 @@ class NodeWatchFileSystem {
6768
},
6869
getFileTimestamps: () => {
6970
if(this.watcher)
70-
return this.watcher.getTimes();
71+
return objectToMap(this.watcher.getTimes());
7172
else
72-
return {};
73+
return new Map();
7374
},
7475
getContextTimestamps: () => {
7576
if(this.watcher)
76-
return this.watcher.getTimes();
77+
return objectToMap(this.watcher.getTimes());
7778
else
78-
return {};
79+
return new Map();
7980
}
8081
};
8182
}

lib/util/objectToMap.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* convert an object into its 2D array equivalent to be turned
3+
* into an ES6 map
4+
*
5+
* @param {object} obj - any object type that works with Object.keys()
6+
* @returns {Map} an ES6 Map of KV pairs
7+
*/
8+
module.exports = function objectToMap(obj) {
9+
return new Map(Object.keys(obj).map(key => [key, obj[key]]));
10+
};

test/NodeWatchFileSystem.unittest.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ describe("NodeWatchFileSystem", function() {
6868
if(err) throw err;
6969
filesModified.should.be.eql([fileDirect]);
7070
dirsModified.should.be.eql([]);
71-
Object.assign({}, fileTimestamps).should.have.property(fileDirect).have.type("number");
71+
fileTimestamps.get(fileDirect).should.have.type("number");
7272
watcher.close();
7373
done();
7474
});
@@ -87,7 +87,7 @@ describe("NodeWatchFileSystem", function() {
8787
if(err) throw err;
8888
filesModified.should.be.eql([fileDirect]);
8989
dirsModified.should.be.eql([]);
90-
Object.assign({}, fileTimestamps).should.have.property(fileDirect).have.type("number");
90+
fileTimestamps.get(fileDirect).should.have.type("number");
9191
watcher.close();
9292
done();
9393
});
@@ -104,7 +104,7 @@ describe("NodeWatchFileSystem", function() {
104104
if(err) throw err;
105105
filesModified.should.be.eql([]);
106106
dirsModified.should.be.eql([fixtures]);
107-
Object.assign({}, dirTimestamps).should.have.property(fixtures).have.type("number");
107+
dirTimestamps.get(fixtures).should.have.type("number");
108108
watcher.close();
109109
done();
110110
});
@@ -123,7 +123,7 @@ describe("NodeWatchFileSystem", function() {
123123
if(err) throw err;
124124
filesModified.should.be.eql([]);
125125
dirsModified.should.be.eql([fixtures]);
126-
Object.assign({}, dirTimestamps).should.have.property(fixtures).have.type("number");
126+
dirTimestamps.get(fixtures).should.have.type("number");
127127
watcher.close();
128128
done();
129129
});
@@ -140,7 +140,7 @@ describe("NodeWatchFileSystem", function() {
140140
if(err) throw err;
141141
filesModified.should.be.eql([]);
142142
dirsModified.should.be.eql([fixtures]);
143-
Object.assign({}, dirTimestamps).should.have.property(fixtures).have.type("number");
143+
dirTimestamps.get(fixtures).should.have.type("number");
144144
watcher.close();
145145
done();
146146
});
@@ -159,7 +159,7 @@ describe("NodeWatchFileSystem", function() {
159159
if(err) throw err;
160160
filesModified.should.be.eql([]);
161161
dirsModified.should.be.eql([fixtures]);
162-
Object.assign({}, dirTimestamps).should.have.property(fixtures).have.type("number");
162+
dirTimestamps.get(fixtures).should.have.type("number");
163163
watcher.close();
164164
done();
165165
});
@@ -177,9 +177,9 @@ describe("NodeWatchFileSystem", function() {
177177
if(err) throw err;
178178
filesModified.should.be.eql([fileSubdir, fileDirect]);
179179
dirsModified.should.be.eql([fixtures]);
180-
Object.assign({}, fileTimestamps).should.have.property(fileDirect).have.type("number");
181-
Object.assign({}, fileTimestamps).should.have.property(fileSubdir).have.type("number");
182-
Object.assign({}, dirTimestamps).should.have.property(fixtures).have.type("number");
180+
fileTimestamps.get(fileDirect).should.have.type("number");
181+
fileTimestamps.get(fileSubdir).should.have.type("number");
182+
dirTimestamps.get(fixtures).should.have.type("number");
183183
watcher.close();
184184
done();
185185
});
@@ -197,9 +197,9 @@ describe("NodeWatchFileSystem", function() {
197197
if(err) throw err;
198198
filesModified.should.be.eql([fileSubdir, fileDirect]);
199199
dirsModified.should.be.eql([fixtures]);
200-
Object.assign({}, fileTimestamps).should.have.property(fileDirect).have.type("number");
201-
Object.assign({}, fileTimestamps).should.have.property(fileSubdir).have.type("number");
202-
Object.assign({}, dirTimestamps).should.have.property(fixtures).have.type("number");
200+
fileTimestamps.get(fileDirect).should.have.type("number");
201+
fileTimestamps.get(fileSubdir).should.have.type("number");
202+
dirTimestamps.get(fixtures).should.have.type("number");
203203
watcher.close();
204204
done();
205205
});

test/NormalModule.unittest.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,14 @@ describe("NormalModule", function() {
241241
fileB = "fileB";
242242
fileDependencies = [fileA, fileB];
243243
contextDependencies = [fileA, fileB];
244-
fileTimestamps = {
245-
[fileA]: 1,
246-
[fileB]: 1,
247-
};
248-
contextTimestamps = {
249-
[fileA]: 1,
250-
[fileB]: 1,
251-
};
244+
fileTimestamps = new Map([
245+
[fileA, 1],
246+
[fileB, 1]
247+
]);
248+
contextTimestamps = new Map([
249+
[fileA, 1],
250+
[fileB, 1],
251+
]);
252252
normalModule.buildTimestamp = 2;
253253
setDeps(fileDependencies, contextDependencies);
254254
});
@@ -259,31 +259,31 @@ describe("NormalModule", function() {
259259
});
260260
describe("given a file timestamp is newer than the buildTimestamp", function() {
261261
beforeEach(function() {
262-
fileTimestamps[fileA] = 3;
262+
fileTimestamps.set(fileA, 3);
263263
});
264264
it("returns true", function() {
265265
normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true);
266266
});
267267
});
268268
describe("given a no file timestamp exists", function() {
269269
beforeEach(function() {
270-
fileTimestamps = {};
270+
fileTimestamps = new Map();
271271
});
272272
it("returns true", function() {
273273
normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true);
274274
});
275275
});
276276
describe("given a context timestamp is newer than the buildTimestamp", function() {
277277
beforeEach(function() {
278-
contextTimestamps[fileA] = 3;
278+
contextTimestamps.set(fileA, 3);
279279
});
280280
it("returns true", function() {
281281
normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true);
282282
});
283283
});
284284
describe("given a no context timestamp exists", function() {
285285
beforeEach(function() {
286-
contextTimestamps = {};
286+
contextTimestamps = new Map();
287287
});
288288
it("returns true", function() {
289289
normalModule.needRebuild(fileTimestamps, contextTimestamps).should.eql(true);

0 commit comments

Comments
 (0)