-
Notifications
You must be signed in to change notification settings - Fork 87
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
fixes #309: call stack exceeded in Control.Bind.Bind instance for Array #311
base: master
Are you sure you want to change the base?
Conversation
There's likely not any tests for However, what is the underlying issue here and how does this fix the underlying issue? |
The underlying issue is that JS implementations have a limit to the number of arguments that can be passed to a function (even through |
Where is that documented? |
Found this on MDN docs:
|
Like all OOM errors, it is a violation of the JS spec. The JS spec requires infinite memory for compliance, which obviously no implementation can provide. The best I can do is this article on MDN which recommends a strategy like the one I've used here. |
Since this is already a bug, I think fixing the underlying issue with a limit of 65535 is desirable because that's a known hard limit. If someone has proof that this number is still too large for a given JS engine, we could decrease the number for the other JS engine. But I think a limit of 10k is too small given that it loops potentially 6 times unnecessarily. |
Personally, I would prefer to stay well below the known upper bound. Implementations may reduce this limit in the future, and lesser-known implementations (such as those for embedded systems) may already have a lower limit today. I don't think the overhead of 6 loop iterations matters much for such heavy workloads. We could prove it out with benchmarking if we really wanted to know. edit: The example from mdn uses |
I still stand by what I said. When we find a new limit, we can decrease it. But until I have hard evidence of that, there's nothing to say that my arbitrary choice is better/worse than your arbitrary choice. |
Current change is var v = f(arr[i]);
for (var j = 0; j < v.length;)
Array.prototype.push.apply(result, v.slice(j, j += CHUNK)); But this always slice at least once if even not necessary, should we consider it? |
I agree that
So it should work fine for now. |
This does seem like a better implementation. Thanks for the suggestion. |
This implementation only slices if needed at the cost of an var APPLY_CHUNK_SIZE = 10e3;
var push = Function.prototype.apply.bind(Array.prototype.push);
export const arrayBind = function (arr) {
return function (f) {
var result = [];
for (var i = 0, l = arr.length; i < l; i++) {
var subArr = f(arr[i]);
if (subArr.length <= APPLY_CHUNK_SIZE) {
push(result, subArr);
} else {
for (var j = 0; j < v.length;) {
Array.prototype.push.apply(result, v.slice(j, j += APPLY_CHUNK_SIZE));
}
}
}
return result;
};
}; Testing would need to check for off-by-one errors here. |
Description of the change
Fixes #309 by batching the arguments passed to
Array.prototype.push
byFunction.prototype.apply
. I chose 10,000 for the batch size because it's definitely in the safe range for major implementations while still being large enough to not have much effect on performance.I couldn't find where the tests for Control.Bind live so I didn't add a test.
Checklist: