fake-timers: Lolex is very slow
- Lolex version : 3.0.0
- Environment : Ubuntu 16 inside VirtualBox
- Other libraries you are using: I’m running Node 10.5.0, and my test runner is Jest.
What actually happens
As in the title, Lolex is very slow, at least much slower than it could be. For a particular test in my project the current Lolex implementation takes 124s to complete on my machine. After some profiling and diagnosing the problem, I managed to trim that down to 22s.
The culprit is clock.timers: it’s an object, but it’s not an appropriate data structure for operations that Lolex performs on it. Profiling in Chrome showed that the slowest pieces of code in particular are the in loops with hasOwnProperty calls. Using Map for clock.timers resulted in a tremendous speed up.
How to reproduce
I have a piece of code that sets a new timeout a second after the function is called, something like this:
function doStuff() {
// some stuff
...
setTimeout(doStuff, 1000);
}
Then my test looks like so:
it('should count down', () => {
const start = process.hrtime();
server.respond();
expect(document.querySelector('#clock').textContent).toBe('0h 30m');
clock.tick(912000);
expect(document.querySelector('#clock').textContent).toBe('0h 14m');
clock.tick(828000);
expect(document.querySelector('#clock').textContent).toBe('0h 01m');
clock.tick(15000);
expect(document.querySelector('#clock').textContent).toBe('45s');
});
Basically there’s a clock component and I want to test what is displayed at different moments in time. While the test may not be perfect (it tests the whole integration instead of display and step separately), it highlighted the problem.
Next steps
I’ll gladly prepare a PR with the changes. From what I’ve managed to find out, the lowest supported version of Node for Lolex is 6, and it supports Map, so there should be no problem.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 15 (6 by maintainers)
I implemented a Map version and also an Array version to have more comparison between different data structures. Below are the results of stress testing and the code I used to perform the test. TL;DR both Map and Array versions perform comparably, and the current version (called “default” in the tests) stands out and is the slowest. All of the versions seem to reach a linear-ish growth in test duration at some point, and from that we can deduce that the current version can be even 13 times slower than the alternatives in some scenarios. In the most basic scenario of a recurring timeout the current version is ~3 times slower.
I’d like to note that I didn’t put much effort into implementing the Map and Array versions with the exception of implementing a “clever” fast removal of an item from an array, so this could possibly be further improved.
I’d like to also note that the slowdown from my original post was extremely highlighted because of a slower machine I’m using for work. Not everyone can use top-notch equiment and those are the cases where performance really matters.
The Results
I reran the tests several times and the results seem to be consistent on my machine.
The Code
So I’ve ended up preparing two new alternatives that perform comparably, with the map version being better in more extreme scenarios. OTOH the benefit of the array version is that old browsers will support it. What do you suggest I do at this point? Having three different versions is an overkill and I added more than one just for testing purposes in the first place. When you’ve made the decision I’ll prepare a PR.