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

array-set has terrible memory usage #259

Closed
MagicDuck opened this issue Apr 17, 2017 · 10 comments
Closed

array-set has terrible memory usage #259

MagicDuck opened this issue Apr 17, 2017 · 10 comments

Comments

@MagicDuck
Copy link

We have a large project that is build using webpack, which uses source-map to generate the sourcemaps.
There are issues with performance of the phase that generates the sourcemaps so we did some profiling. One thing we noticed right off the bat is the sharp sawtooth shape of the memory usage graph of the node process. This basically means that a lot of the time is spent garbage collecting. Digging deeper, we profiled using the google chrome dev tools and noticed that one of the things allocating a lot of memory is array-set. It was using gobs of memory by making large source strings keys into an object which causes string copies. We monkey patched it on our side, but it probably makes sense to change this class to use some modern javascript Set/Map data structure so everybody gets the benefit.
Here's the monkey patch we use:

var ArraySet = require("webpack-core/node_modules/source-map/lib/source-map/array-set").ArraySet;

/**
 * the backing map
 * use a map to check uniqueness instead of an object's keys
 * @return {Map}
 */
ArraySet.prototype._map = function ArraySet__map() {
    if (!this._theMap) {
        this._theMap = new Map();
    }

    return this._theMap;
};

/**
 * Return how many unique items are in this ArraySet. If duplicates have been
 * added, than those do not count towards the size.
 *
 * @returns Number
 */
ArraySet.prototype.size = function ArraySet_size() {
    return this._map().size;
};

/**
 * Add the given string to this set.
 *
 * @param String aStr
 */
ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
    var isDuplicate = this.has(aStr);
    var idx = this._array.length;
    if (!isDuplicate || aAllowDuplicates) {
        this._array.push(aStr);
    }
    if (!isDuplicate) {
        this._map().set(aStr, idx);
    }
};

/**
 * Is the given string a member of this set?
 *
 * @param String aStr
 */
ArraySet.prototype.has = function ArraySet_has(aStr) {
    return this._map().has(aStr);
};

/**
 * What is the index of the given string in the array?
 *
 * @param String aStr
 */
ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
    var idx = this._map().get(aStr);
    if (idx >= 0) {
        return idx;
    }
    throw new Error('"' + aStr + '" is not in the set.');
};
@tromey
Copy link
Contributor

tromey commented Apr 19, 2017

We had a brief discussion of this on irc. I believe we're still reluctant to require anything after ES5; but on the other hand, feature detection would be ok. So if you wanted to submit this as a PR that used Map when it was available, and the existing code when it was not, then that would be alright.

@MagicDuck
Copy link
Author

Sure, but curious why the reluctance given nodejs 4 is the minimum supported?

@tromey
Copy link
Contributor

tromey commented Apr 19, 2017

Sure, but curious why the reluctance given nodejs 4 is the minimum supported?

I don't know the answer, sorry. I'm just learning about this project. Is 4 really the minimum? All I saw was "node": ">=0.10.0" in package.json.

@MagicDuck
Copy link
Author

4 is the lowest version on the nodejs website...

@MagicDuck
Copy link
Author

MagicDuck commented Apr 20, 2017

Maybe you need to up your package.json node version. projects like webpack are already operating on the assumption of es6. Webpack is upgrading its entire codebase to use classes, etc.

@MagicDuck
Copy link
Author

PR is here: #260
(It would still be good to understand why we can't just assume Map as it would keep the code cleaner. Even IE11 supports Map...)

@tromey
Copy link
Contributor

tromey commented Apr 25, 2017

It would still be good to understand why we can't just assume Map as it would keep the code cleaner.

Yeah, that would be good. I will try again to find out.

@tromey
Copy link
Contributor

tromey commented May 26, 2017

I will try again to find out.

Given the recent test failures with the old version of node, I think it is fine to just move on to the future; that is, assume Map and do whatever other cleanups seem reasonable.

hildjj added a commit to hildjj/source-map that referenced this issue May 8, 2018
… the API the same. That file needs careful review. Everything else is pretty straightforward. Fixes mozilla#334.  Fixes mozilla#259.
@edmorley
Copy link

Hi! This was fixed on master as part of #336, however many projects can't update to source-maps 0.7.x due to node 6 not being supported and no sync API. As such it would be great if we could backport the fix to 0.6.x to help everyone using webpack etc. Would such a PR be accepted?

@edmorley
Copy link

Ah ignore that - the PR that auto-closed this wasn't the one that originally fixed the issue. This was fixed originally in #260, which made it into 0.5.7. Sorry for the noise!

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

No branches or pull requests

3 participants