Lazy evaluation

classic Classic list List threaded Threaded
54 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Naveen Chawla

On Tue, 12 Sep 2017 at 03:18 Andrea Giammarchi <[hidden email]> wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)
This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <[hidden email]> wrote:
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Steve Fink
In reply to this post by Andrea Giammarchi-2
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)
This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <[hidden email]> wrote:
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss




_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Matthew Robb
I think it would be nice to be able to define an own data property on an object that while defined delegated up the prototype chain. This would allow a getter in the proto to lazily assign to the own property without triggering the property setter. This is even more nice when combined with super as you could conceivably hit the setter which itself could assign to the own property of the current instance.

Is that all too complicated? It seems perfect if only a little complicated.

On Sep 12, 2017 5:39 PM, "Steve Fink" <[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)
This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <[hidden email]> wrote:
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss




_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: Lazy evaluation

Alex Kodat-2
I think to the degree that almost anyone would care, the public and private key proposal would address most of these issues. The private keys would not be affected by object shape so would have rip-roaring good performance. You'd simply need to provide getter functions in the prototype that check for undefined (or whatever) in the private variable, if not undefined return value, otherwise calculate value, set private variable, and return the value. Unless you're trying to calculate pi to a million decimal places this should be pretty much good enough for anything.

And it gives you the encapsulation you get in many other programming languages such as Java and C++. Public JS APIs would end up looking a lot like these for many applications -- a bunch of private variables and public functions including getters and setters.

Basically, most of our code already looks like this but we use closures rather than private variable to accomplish this. And yes, there is a dark side to closures, specifically the challenge in implementing closed function that don't gobble up a lot of memory. In V8 if I construct an object with say 20 function properties that's 20 JSFunction objects V8 has to create, each one being on the order of 72 bytes per function. It's not too horrible if we don't have a lot of these and they have relatively long lives but problematic for lots of instantiation of objects with lots of functions. The private key proposal would address this issue.

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Matthew Robb
Sent: Tuesday, September 12, 2017 4:49 PM
To: Steve Fink <[hidden email]>
Cc: [hidden email] >> es-discuss <[hidden email]>
Subject: Re: Lazy evaluation

I think it would be nice to be able to define an own data property on an object that while defined delegated up the prototype chain. This would allow a getter in the proto to lazily assign to the own property without triggering the property setter. This is even more nice when combined with super as you could conceivably hit the setter which itself could assign to the own property of the current instance.

Is that all too complicated? It seems perfect if only a little complicated.

On Sep 12, 2017 5:39 PM, "Steve Fink" <mailto:[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <mailto:[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
> I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <mailto:[hidden email]> wrote:

Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:[hidden email]> wrote:
Hi Andrea,
```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Andrea Giammarchi-2
In reply to this post by Steve Fink
The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```






On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)
This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <[hidden email]> wrote:
Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <[hidden email]> wrote:
Hi Andrea,

```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: Lazy evaluation

Alex Kodat-2
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"?

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```






On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <mailto:[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
> I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <mailto:[hidden email]> wrote:

Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:[hidden email]> wrote:
Hi Andrea,
```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss




_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: Lazy evaluation

Alex Kodat-2
In reply to this post by Andrea Giammarchi-2
Also, FWIW,  since we’re talking about nanoseconds of performance, here, I think slightly better than:

    return this.#random || (this.#random = Math.random());

Is

    return (this.#random === undefined) ? this.#random = Math.random() : this.#random;

This is because in the former, the compiled code has to determine whether this.#random is coercible into a Boolean. At the very least, it has to do an "Is this an object and, if so, not undefined ==> true" test. From first principles, the ternary expression is going to be more efficient. It simply checks if the value at this.#random is the same as the global undefined value and, if not, returns that value. No extra tests necessary.

And at least with V8 theory == practice in this case (at least in all my tests). And yes, we're talking nanosecond here.

While this looks a bit like the null propagation operator would be applicable, it's really not.

Also FWIW, if you get tired of seeing/typing undefined, we add _ to the global object which means the above can be written as:

    return (this.#random === _) ? this.#random = Math.random() : this.#random;

You might think that this would perform worse than the undefined but, in fact, for V8 it's identical -- undefined and _ are both just properties of the global object. I think this reads especially well in function calls where you want an undefined placeholder

  fooey(_, "whatever");

Visually, _ just says undefined to me.

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Isiah Meadows-2
Nit: `_` is a very valid identifier. (Consider Underscore/Lodash)

On Wed, Sep 13, 2017, 09:32 Alex Kodat <[hidden email]> wrote:
Also, FWIW,  since we’re talking about nanoseconds of performance, here, I think slightly better than:

    return this.#random || (this.#random = Math.random());

Is

    return (this.#random === undefined) ? this.#random = Math.random() : this.#random;

This is because in the former, the compiled code has to determine whether this.#random is coercible into a Boolean. At the very least, it has to do an "Is this an object and, if so, not undefined ==> true" test. From first principles, the ternary expression is going to be more efficient. It simply checks if the value at this.#random is the same as the global undefined value and, if not, returns that value. No extra tests necessary.

And at least with V8 theory == practice in this case (at least in all my tests). And yes, we're talking nanosecond here.

While this looks a bit like the null propagation operator would be applicable, it's really not.

Also FWIW, if you get tired of seeing/typing undefined, we add _ to the global object which means the above can be written as:

    return (this.#random === _) ? this.#random = Math.random() : this.#random;

You might think that this would perform worse than the undefined but, in fact, for V8 it's identical -- undefined and _ are both just properties of the global object. I think this reads especially well in function calls where you want an undefined placeholder

  fooey(_, "whatever");

Visually, _ just says undefined to me.

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Andrea Giammarchi-2
In reply to this post by Alex Kodat-2
Maybe you mean "will behave more or less as if (except more efficiently)"?

no, I meant: it will transpiled into something using private WeakMaps.

I don't have any interest in talk nanoseconds for something unrelated to the topic though.

Best Regards

On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <[hidden email]> wrote:
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"?

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```






On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <mailto:[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
> I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <mailto:[hidden email]> wrote:

Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:[hidden email]> wrote:
Hi Andrea,
```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss






_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

kai zhu
inline

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```

- overloaded getters/setters and Object.defineProperties are generally over-engineered anti-patterns in javascript-programming
* please use normal method-calls instead, which are more maintainable

- WeakMap is an over-engineered anti-pattern in javascript-programming
* they are inferior to using a plain-object cache-dictionary in pretty much every use-case.
* WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.

```javascript
/*jslint node: true */
'use strict';

function LazyClass() {
    this.cacheDict = {};
}
LazyClass.prototype.getRandomCached = function () {
    this.cacheDict.random = this.cacheDict.random || Math.random();
    return this.cacheDict.random;
};
LazyClass.prototype.clearCache = function () {
    this.cacheDict = {};
};

var lazyObject = new LazyClass();
console.log(lazyObject.getRandomCached());
console.log(lazyObject.getRandomCached());
lazyObject.clearCache();
console.log(lazyObject.getRandomCached());
```




On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi <[hidden email]> wrote:

Maybe you mean "will behave more or less as if (except more efficiently)"?

no, I meant: it will transpiled into something using private WeakMaps.

I don't have any interest in talk nanoseconds for something unrelated to the topic though.

Best Regards

On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <[hidden email]> wrote:
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"?

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```






On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <mailto:[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
> I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <mailto:[hidden email]> wrote:

Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:[hidden email]> wrote:
Hi Andrea,
```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Andrea Giammarchi-2
please use normal method-calls instead, which are more maintainable

no, thanks.


WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.

no, thanks.

We were discussing patterns here, nothing to "please don't" and IMO, dogma oriented rules are over-engineered anti-pattern too.

Let's not make this ML an awkward place for who wants to just learn or explore JS language features, thanks.



On Thu, Sep 14, 2017 at 9:55 AM, kai zhu <[hidden email]> wrote:
inline

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```

- overloaded getters/setters and Object.defineProperties are generally over-engineered anti-patterns in javascript-programming
* please use normal method-calls instead, which are more maintainable

- WeakMap is an over-engineered anti-pattern in javascript-programming
* they are inferior to using a plain-object cache-dictionary in pretty much every use-case.
* WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.

```javascript
/*jslint node: true */
'use strict';

function LazyClass() {
    this.cacheDict = {};
}
LazyClass.prototype.getRandomCached = function () {
    this.cacheDict.random = this.cacheDict.random || Math.random();
    return this.cacheDict.random;
};
LazyClass.prototype.clearCache = function () {
    this.cacheDict = {};
};

var lazyObject = new LazyClass();
console.log(lazyObject.getRandomCached());
console.log(lazyObject.getRandomCached());
lazyObject.clearCache();
console.log(lazyObject.getRandomCached());
```




On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi <[hidden email]> wrote:

Maybe you mean "will behave more or less as if (except more efficiently)"?

no, I meant: it will transpiled into something using private WeakMaps.

I don't have any interest in talk nanoseconds for something unrelated to the topic though.

Best Regards

On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <[hidden email]> wrote:
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"?

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```






On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <mailto:[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
> I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <mailto:[hidden email]> wrote:

Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:[hidden email]> wrote:
Hi Andrea,
```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: Lazy evaluation

Alex Kodat-2
I realize in going over the last few e-mails for this thread, that as usual, I’m to blame for a bit of poor communication. I had intended to include the link to the public-private field proposal in my comments about it but whiffed. So the link: https://tc39.github.io/proposal-class-fields/. And yes, this link has appeared on other e-mails (sorry?).

This brings me to a (noobie) meta-question. This proposal is very interesting and looks nice to me and it is indicated to be in Stage 3. This suggests that it's pretty close to going in as-is into the ECMAScript standard? In case I'm not the last person in the world to find the process: https://tc39.github.io/process-document/.

So is this the appropriate forum for questions or comments about this spec? I notice many of the very interesting discussions leading up to this proposal happened elsewhere (mostly GitHub). But there's no link in the spec proposal to a discussion page so maybe this is it? Sorry if this is a stupid question.

----
Alex Kodat

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

kai zhu
In reply to this post by Andrea Giammarchi-2
you can call it dogma.  i call it the the javascript-way of solving frontend-engineering problems, which is simpler and more practical for laymen than what i learned from cs-theory in school using c++/java.

its time we showed some pride in javascript (like the haskell/lisp/etc folks), and admit some of our 20+ year-old “hacks” are actually good design-patterns that rival and challenge traditional cs-teachings, instead of replacing them with bad-practices from other languages (*cough* operator-overloading *cough*).

On Sep 14, 2017, at 4:26 PM, Andrea Giammarchi <[hidden email]> wrote:

please use normal method-calls instead, which are more maintainable

no, thanks.


WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.

no, thanks.

We were discussing patterns here, nothing to "please don't" and IMO, dogma oriented rules are over-engineered anti-pattern too.

Let's not make this ML an awkward place for who wants to just learn or explore JS language features, thanks.





On Thu, Sep 14, 2017 at 9:55 AM, kai zhu <[hidden email]> wrote:
inline

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```

- overloaded getters/setters and Object.defineProperties are generally over-engineered anti-patterns in javascript-programming
* please use normal method-calls instead, which are more maintainable

- WeakMap is an over-engineered anti-pattern in javascript-programming
* they are inferior to using a plain-object cache-dictionary in pretty much every use-case.
* WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.

```javascript
/*jslint node: true */
'use strict';

function LazyClass() {
    this.cacheDict = {};
}
LazyClass.prototype.getRandomCached = function () {
    this.cacheDict.random = this.cacheDict.random || Math.random();
    return this.cacheDict.random;
};
LazyClass.prototype.clearCache = function () {
    this.cacheDict = {};
};

var lazyObject = new LazyClass();
console.log(lazyObject.getRandomCached());
console.log(lazyObject.getRandomCached());
lazyObject.clearCache();
console.log(lazyObject.getRandomCached());
```

<PastedGraphic-4.png>



On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi <[hidden email]> wrote:

Maybe you mean "will behave more or less as if (except more efficiently)"?

no, I meant: it will transpiled into something using private WeakMaps.

I don't have any interest in talk nanoseconds for something unrelated to the topic though.

Best Regards

On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <[hidden email]> wrote:
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"?

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```






On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <mailto:[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
> I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <mailto:[hidden email]> wrote:

Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:[hidden email]> wrote:
Hi Andrea,
```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss




_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Lazy evaluation

Andrea Giammarchi-2
there's nothing bad in using accessors (getters/setters) but you can do what you want, just don't tell everyone what you do is the only way 'cause it's not.

I'm off this thread, there's probably nothing else to add.

Best Regards

On Sat, Sep 16, 2017 at 9:15 AM, kai zhu <[hidden email]> wrote:
you can call it dogma.  i call it the the javascript-way of solving frontend-engineering problems, which is simpler and more practical for laymen than what i learned from cs-theory in school using c++/java.

its time we showed some pride in javascript (like the haskell/lisp/etc folks), and admit some of our 20+ year-old “hacks” are actually good design-patterns that rival and challenge traditional cs-teachings, instead of replacing them with bad-practices from other languages (*cough* operator-overloading *cough*).

On Sep 14, 2017, at 4:26 PM, Andrea Giammarchi <[hidden email]> wrote:

please use normal method-calls instead, which are more maintainable

no, thanks.


WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.

no, thanks.

We were discussing patterns here, nothing to "please don't" and IMO, dogma oriented rules are over-engineered anti-pattern too.

Let's not make this ML an awkward place for who wants to just learn or explore JS language features, thanks.





On Thu, Sep 14, 2017 at 9:55 AM, kai zhu <[hidden email]> wrote:
inline

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```

- overloaded getters/setters and Object.defineProperties are generally over-engineered anti-patterns in javascript-programming
* please use normal method-calls instead, which are more maintainable

- WeakMap is an over-engineered anti-pattern in javascript-programming
* they are inferior to using a plain-object cache-dictionary in pretty much every use-case.
* WeakMap's implicit-garbage-collecting feature is error-prone, hard to validate with tests, and generally becomes unmaintainable over time.

```javascript
/*jslint node: true */
'use strict';

function LazyClass() {
    this.cacheDict = {};
}
LazyClass.prototype.getRandomCached = function () {
    this.cacheDict.random = this.cacheDict.random || Math.random();
    return this.cacheDict.random;
};
LazyClass.prototype.clearCache = function () {
    this.cacheDict = {};
};

var lazyObject = new LazyClass();
console.log(lazyObject.getRandomCached());
console.log(lazyObject.getRandomCached());
lazyObject.clearCache();
console.log(lazyObject.getRandomCached());
```

<PastedGraphic-4.png>



On Sep 13, 2017, at 9:32 PM, Andrea Giammarchi <[hidden email]> wrote:

Maybe you mean "will behave more or less as if (except more efficiently)"?

no, I meant: it will transpiled into something using private WeakMaps.

I don't have any interest in talk nanoseconds for something unrelated to the topic though.

Best Regards

On Wed, Sep 13, 2017 at 1:54 PM, Alex Kodat <[hidden email]> wrote:
What do you mean by “will be transpiled through”? My understanding of the private property proposal is that private properties will be in fixed slots (inaccessible outside the class) in the object so there would be no WeakMap. Maybe you mean "will behave more or less as if (except more efficiently)"?

----
Alex Kodat

From: es-discuss [mailto:[hidden email]] On Behalf Of Andrea Giammarchi
Sent: Wednesday, September 13, 2017 2:31 AM
To: Steve Fink <[hidden email]>
Cc: [hidden email]
Subject: Re: Lazy evaluation

> The properties already existed, so defineProperty shouldn't modify the order IIUC

well, nope. the property existed in the prototype, not in the object.

anyway, I guess private properties, that are a possible solution, will be transpiled through a WeakMap so that most likely anything discussed in here won't make sense and the future code would look like the following

```js
class A {
  #random;
  get random() {
    return this.#random ||
          (this.#random = Math.random());
  }
}


// transpiled
var A = function (wm) {
  function A() {
    wm.set(this, {random: void 0});
  }
  Object.defineProperties(
    A.prototype,
    {
      random: {
        configurable: true,
        get: function () {
          return wm.get(this).random ||
                (wm.get(this).random = Math.random());
        }
      }
    }
  );
  return A;
}(new WeakMap);
```






On Tue, Sep 12, 2017 at 10:39 PM, Steve Fink <mailto:[hidden email]> wrote:
My intent was only to respond to the performance analysis, specifically the implication that the only performance cost is in building the new hidden class. That is not the case; everything that touches those objects is affected as well.

Whether or not it's still the right way to accomplish what you're after, I wasn't venturing an opinion. I could probably come up with a benchmark showing that your WeakMap approach can be faster -- eg by only accessing the property once, but feeding the old and new versions of the object into code that executes many many many times (doing something that never looks at that property, but is now slightly slower because it isn't monomorphic). But I suspect that for practical usage, redefining the property *is* faster than a WeakMap.

If I were to look beyond for other solutions for your problem, then I'm just speculating. Can decorators populate multiple properties once the expensive work is done?

I really want to tell the VM what's going on. I guess if it knew that accessing a getter property would convert it into a value property, and that it was doing something that would access the getter, then it could know to use the outgoing shape instead of the incoming shape. If only it knew that the getter was pure... but that way lies madness.

Given that most code that would slow down would also trigger the lazy defineProperty(), it's really not going to be that much of an issue. Any access after the first will see a single shape.

meh. Just take the perf hit, with awareness that you may be triggering slight slowdowns in all users of that object. Or you might not. I doubt it'll be that big, since you'll probably just end up with an inline cache for both shapes and there won't be all that much to optimize based on knowing a single shape.

Oh, and I think I was wrong about property enumeration order. The properties already existed, so defineProperty shouldn't modify the order IIUC. (I am awful with language semantics.)

On 9/11/17 2:48 PM, Andrea Giammarchi wrote:
Steve it's not solved in any other way. Even if you use a WeakMap with an object, you gonna lazy attach properties to that object.

I honestly would like to see alternatives, if any, 'cause so far there is a benchmark and it proves already lazy property assignment is around 4x faster.

So, it's easy to say "it's not the best approach" but apparently hard to prove that's the case?

Looking forward to see better alternatives.


On Mon, Sep 11, 2017 at 8:15 PM, Steve Fink <mailto:[hidden email]> wrote:
On 9/11/17 5:36 AM, Matthew Robb wrote:
> I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

This ^​ is very important for everyone to get on board with. Regardless the cost should be negligible as the shape is only changing at the point of delayed init. This will cause, for example V8, to deop the object and have to build a new hidden class but only the one time. I guess it would potentially be interesting to support an own property that when undefined would delegate up the proto chain.

(I don't know, but) I would expect it to be worse than this. The shape is changing at the point of delayed init, which means that if an engine is associating the possible set of shapes with the constructor (or some other form of allocation site + mandatory initialization), then that site will produce multiple shapes. All code using such objects, if they ever see both shapes, will have to handle them both. Even worse, if you have several of these delayed init properties and you end up lazily initializing them in different orders (which seems relatively easy to do), then the internal slot offsets will vary.

You don't need to bend over backwards to make things easy for the VMs, but you don't want to be mean to them either. :-)

Not to mention that the observable property iteration order will vary.

On Mon, Sep 11, 2017 at 7:09 AM, Andrea Giammarchi <mailto:[hidden email]> wrote:

Hi Peter.

Unless you have a faster way to do lazy property assignment, I think it's irrelevant if internally VMs are not too happy. VMs are there to solve our problems, not vice-versa ;-)

Regards



On Mon, Sep 11, 2017 at 11:54 AM, peter miller <mailto:[hidden email]> wrote:
Hi Andrea,
```
class CaseLazy {
  get bar() {
    var value = Math.random();
    Object.defineProperty(this, 'bar', {value});
    return value;
  }
}
```

Doesn't this count as redefining the shape of the object? Or are the compilers fine with it?


_______________________________________________
es-discuss mailing list
mailto:[hidden email]
https://mail.mozilla.org/listinfo/es-discuss





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
123