Question: would a call site optimization featuring pipelines work? Several
Array prototype methods always return arrays, so that's why I'm thinking a
pipeline would be feasible. It's probably not trivial to implement,
especially considering lazy evaluation of a specific internal reference is
necessary to make it work (no extra *JS closures* necessary, though), but I
think it could really speed up a lot of cases, simply by virtue of reducing
GC.
// Unoptimized.
let B = A
.map(Math.abs)
.map(x => x * 10)
.map(function (x) { return new Foo(this, x) }, true)
.filter(foo => foo.value > 10)
let C = B
.map(foo => foo.get())
.forEach(x => console.log(x))
assert(C === undefined)
// Optimized, see below for code stubs
{
@initPipeline(A)
@runMap(Math.abs, false)
@runMap(x => x * 10, false)
@runMap(x => new Foo(x), true, true)
@runFilter(foo => foo.value > 10, false)
@assignResult(B)
}
{
@initPipeline(B)
@runMap(foo => foo.get(), false)
@runForEach(x => console.log(x))
@assignValue(C)
}
assert(C === undefined)
This would significantly reduce garbage collection for such chains.
Here's a set of code stubs for that idea, in pseudo-JS:
// Legend:
// $variable - internal variable
// %Func - runtime function
// @value - compile-time macro/value
// `output` - output code, literal code
// Note that the stubs implicitly create blocks, and their arguments are
// implicitly evaluated.
// This is just a helper.
function %CloneArray(array) {
const ret = []
const len = TO_LENGTH(array.length)
for (let i = 0; i < len; i++) {
if (HAS_INDEX(array, i, true)) {
DefineIndexedProperty(ret, i, array[i])
}
}
return ret
}
stub @initPipeline(@init) {
// The initial copy should be considered the master copy.
`let $ret = {got: true, value: @init}`
// This is a lazy value that, when initialized, does the equivalent of the
// following code:
// if (!$ret.got) {
// $ret.got = true
// $ret.value = %CloneArray($ret.value)
// }
`let $ref = %Ref($ret)`
`let $len = @init.length`
}
// Return the actual result
stub @assignResult(@dest) {
`if ($ret.got) {`
`@dest = $ret.value`
`} else {`
`@dest = %CloneArray($ret.value)`
`}`
}
// Assign the result of the last part
stub @assignValue(@dest) {
`@dest = $ret.value`
}
// This is just a helper.
stub @iterate(@passed, @receiver, stub @missing, stub @exists) {
if (@passed) `if (IS_UNDEFINED(@receiver)) {`
`for (let $i = 0; $i < $len; $i++) {`
`if (HAS_INDEX($ret.value, $i, true)) {`
@missing(`$i`, `$ret.value[$i]`)
`}`
`}`
if (@passed) {
`} else {`
`for (let $i = 0; $i < $len; $i++) {`
`if (HAS_INDEX($ret.value, $i, true)) {`
@exists(`$i`, `$ret.value[$i]`, `$receiver`)
`}`
`}`
`}`
}
}
stub @runMap(@f, @passed, @receiver) {
`let $f = @f`
if (@passed) `let $receiver = @receiver`
@iterate(@passed, @receiver, (@i, @entry) => {
`@entry = $f(@entry, @i, $ref)`
}, (@i, @entry, $receiver) => {
`@entry = %Call($f, $receiver, @entry, @i, $ref)`
})
}
stub @runFilter(@f, @passed, @receiver) {
`let $new = []`
`let $newLen = 0`
`let $f = @f`
if (@passed) `let $receiver = @receiver`
@iterate(@passed, @receiver, (@i, @entry) => {
`let $entry = @entry`
`if ($f($entry, @i, $ref)) {`
`$new[$newLen++] = $entry`
`}`
}, (@i, @entry, $receiver) => {
`let $entry = @entry`
`if (%Call(@f, $receiver, $entry, $i, $ref)) {`
`$new[$newLen++] = $entry`
`}`
})
`$ret.value = $new`
`$ret.got = false`
`$len = $newLen`
}
stub @runForEach(@f, @passed, @receiver) {
`let $f = @f`
if (@passed) `let $receiver = @receiver`
@iterate(@passed, @receiver, (@i, @entry) => {
`@f(@entry, @i, $ref)`
}, (@i, @entry, $receiver) => {
`%Call(@f, @receiver, @entry, @i, $ref)`
})
// Terminate the chain
`$ret.value = undefined`
`$ret.got = true`
}
On Friday, March 4, 2016 at 9:55:36 AM UTC-5, Jakob Kummerow wrote:
>
> Also ES6 is mostly a burden for JS implementations, not much of an
> opportunity. We have to invest some serious effort just to *maintain*
> previous
> performance as we're implementing ES6, and in some cases not even that is
> possible.
>
> On Fri, Mar 4, 2016 at 3:49 PM, 'Andreas Rossberg' via v8-users <
> [email protected] <javascript:>> wrote:
>
>> There is no refcount in JS implementation, and no affordable way to
>> discover aliasing dynamically. And given the gazillion intersession and
>> mutation points in JavaScript's semantics, it is also practically
>> impossible to analyse aliasing at compile time, in all but the most trivial
>> cases. Note also that the callback itself may have or acquire references to
>> the same object as A and look at them during the execution of map itself.
>>
>> On 4 March 2016 at 15:34, 'Robert Eisele' via v8-users <
>> [email protected] <javascript:>> wrote:
>>
>>> Sounds reasonable, but wouldn't it make sense to make this pattern
>>> matching based on internal references (plus refcount) instead of
>>> variable-name matching? That would solve the issue probably.
>>>
>>> Above that, I agree that the limiting factors are based on function
>>> calls if it's implemented in a naive way. However, I think ES6 gives a huge
>>> opportunity introducing a lot of high-level semantic information, which can
>>> be used to infer what the user wants and execute it much faster. So even if
>>> the user writes down a function call, it can be a simple loop inside of v8.
>>> I thought it's already done this way, when I heard ES6 became much faster
>>> recently.
>>>
>>> On Friday, March 4, 2016 at 11:34:51 AM UTC+1, Jakob Kummerow wrote:
>>>>
>>>> On Thu, Mar 3, 2016 at 7:48 PM, 'Robert Eisele' via v8-users <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> I wonder if v8 is able to optimize the pattern
>>>>>
>>>>> A = A.map(x => ~x)
>>>>>
>>>>> In this case v8 can work on the array instead of creating a new object
>>>>> and replacing it with the original array.
>>>>>
>>>>
>>>> Counter-example:
>>>> var A = B;
>>>> A = A.map(...);
>>>> // B must not have been modified
>>>>
>>>> You can make these as complicated as you want:
>>>> var B = [1, 2, 3];
>>>> (function(A) {
>>>> A = A.map(...); // How can this .map() call know that B exists?
>>>> })(B);
>>>>
>>>> In general, it's pretty much impossible to tell whether there are any
>>>> additional references to an object somewhere; the only exception is when
>>>> the object has just been allocated in the current function.
>>>>
>>>> Also, on the scale of machine instructions, the overhead of a function
>>>> call is pretty large, which affects many of the Array builtins like .map()
>>>> and .forEach(). In many real usage scenarios, that might not matter,
>>>> because the arrays in question are small enough and/or the callbacks
>>>> themselves expensive enough that call overhead doesn't make much of a
>>>> difference. But when you micro-benchmark simple cases like "A.map(x =>
>>>> ~x)" vs. "for (var i = 0; i < A.length; i++) { A[i] = ~A[i]; }" for a
>>>> couple million elements, you'll see a pretty big difference, and the
>>>> majority of that will be caused by the function calls.
>>>>
>>>>
>>>>> Is such a behavior already implemented?
>>>>>
>>>>
>>>> No, because it's complicated, and the benefit is somewhat unclear. We
>>>> might be able to optimize some patterns in the (distant) future.
>>>>
>>>>
>>>>> Thanks!
>>>>>
>>>>> --
>>>>> --
>>>>> v8-users mailing list
>>>>> [email protected]
>>>>> http://groups.google.com/group/v8-users
>>>>> ---
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "v8-users" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>>> an email to [email protected].
>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>
>>>>
>>>> --
>>> --
>>> v8-users mailing list
>>> [email protected] <javascript:>
>>> http://groups.google.com/group/v8-users
>>> ---
>>> You received this message because you are subscribed to the Google
>>> Groups "v8-users" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected] <javascript:>.
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>> --
>> --
>> v8-users mailing list
>> [email protected] <javascript:>
>> http://groups.google.com/group/v8-users
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "v8-users" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected] <javascript:>.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
--
--
v8-users mailing list
[email protected]
http://groups.google.com/group/v8-users
---
You received this message because you are subscribed to the Google Groups
"v8-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.