Bug 56168

Summary: Avoid slow-path for put() in Array.slice()
Product: WebKit Reporter: Andreas Kling <kling>
Component: JavaScriptCoreAssignee: Andreas Kling <kling>
Status: RESOLVED INVALID    
Severity: Normal CC: barraclough, darin, ggaren, luiz, oliver
Priority: P2 Keywords: Performance
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
Proposed patch
none
Proposed patch v2 darin: review-

Andreas Kling
Reported 2011-03-10 19:46:00 PST
Array.slice() currently creates the returned array early on. We can defer it until we know the number of elements to include, and use the CreateCompact mechanism to avoid the put() slow-path when populating the array.
Attachments
Proposed patch (2.47 KB, patch)
2011-03-10 19:46 PST, Andreas Kling
no flags
Proposed patch v2 (2.46 KB, patch)
2011-03-11 03:35 PST, Andreas Kling
darin: review-
Andreas Kling
Comment 1 2011-03-10 19:46:51 PST
Created attachment 85429 [details] Proposed patch
Andreas Kling
Comment 2 2011-03-11 03:35:04 PST
Created attachment 85455 [details] Proposed patch v2 Do the constructEmptyArray() short-circuit for the (begin == end) case as well.
Darin Adler
Comment 3 2011-03-12 19:55:00 PST
Comment on attachment 85455 [details] Proposed patch v2 View in context: https://bugs.webkit.org/attachment.cgi?id=85455&action=review I’m pretty sure this is wrong. This doesn’t handle the case where there are holes in the object we are slicing. Both arrays and non-array objects can have empty slots with undefined values. The old code handled this. Are there any test cases that cover it? > Source/JavaScriptCore/runtime/ArrayPrototype.cpp:484 > + JSArray* resObj = new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), sliceCount, CreateCompact); Lets go nuts with this patch and call this result. There’s no need for a separate local variable named result. JSValue::encode should work fine on a JSArray*. > Source/JavaScriptCore/runtime/ArrayPrototype.cpp:489 > + for (unsigned k = 0; k < sliceCount; k++) > + resObj->uncheckedSetIndex(globalData, k, getProperty(exec, thisObj, k + begin)); Shouldn’t we also stop on the first exception we see?
Darin Adler
Comment 4 2011-03-12 19:55:31 PST
There is probably a great way to do this, but I think we’ll have to have different code for the rare case where there is a hole in the passed-in array. Or perhaps I missed something.
Oliver Hunt
Comment 5 2011-03-12 21:29:24 PST
> > Source/JavaScriptCore/runtime/ArrayPrototype.cpp:489 > > + for (unsigned k = 0; k < sliceCount; k++) > > + resObj->uncheckedSetIndex(globalData, k, getProperty(exec, thisObj, k + begin)); > > Shouldn’t we also stop on the first exception we see? Yes. And apparently a test for this as well. var proto = { get 0() { throw "argh" }, get 1() { fail("called second getter") }, length: 2} Array.prototype.slice.call(proto, ...) Array.prototype.slice.call(Object.create(proto), ...) var array = []; array.__proto__ = proto; array.length = 2; Array.prototype.slice.call(array, ...) (In reply to comment #4) > There is probably a great way to do this, but I think we’ll have to have different code for the rare case where there is a hole in the passed-in array. Or perhaps I missed something. Then general implementation for all of the array functions is sadly unpleasant. You query the length and then iterate through as long as you can do canGet(i) (or whatever the function is called, i can't remember off the top of my head). And then you continue with a generic get() until you reach the length. We do need a nicer way of doing this, but as yet we haven't come up with one.
Oliver Hunt
Comment 6 2011-03-12 21:32:54 PST
(In reply to comment #5) > > > Source/JavaScriptCore/runtime/ArrayPrototype.cpp:489 > > > + for (unsigned k = 0; k < sliceCount; k++) > > > + resObj->uncheckedSetIndex(globalData, k, getProperty(exec, thisObj, k + begin)); > > > > Shouldn’t we also stop on the first exception we see? > > Yes. And apparently a test for this as well. > var proto = { get 0() { throw "argh" }, get 1() { fail("called second getter") }, length: 2} > Array.prototype.slice.call(proto, ...) > Array.prototype.slice.call(Object.create(proto), ...) > var array = []; > array.__proto__ = proto; > array.length = 2; > Array.prototype.slice.call(array, ...) > > (In reply to comment #4) > > There is probably a great way to do this, but I think we’ll have to have different code for the rare case where there is a hole in the passed-in array. Or perhaps I missed something. > > Then general implementation for all of the array functions is sadly unpleasant. You query the length and then iterate through as long as you can do canGet(i) (or whatever the function is called, i can't remember off the top of my head). And then you continue with a generic get() until you reach the length. > > We do need a nicer way of doing this, but as yet we haven't come up with one. And I just see that this code does a completely generic get() anyway (there's no optimisation for this being an array so that comment is unhelpful. Unless we want to optimise for the array case)
Andreas Kling
Comment 7 2015-05-22 01:10:14 PDT
Oh god I had no idea what I was doing in 2011. I hope I feel this way about 2015, come 2019.
Note You need to log in before you can comment on or make changes to this bug.