|
|
Created:
3 years, 6 months ago by kris.selden Modified:
3 years, 6 months ago CC:
v8-reviews_googlegroups.com Target Ref:
refs/heads/master Project:
v8 Visibility:
Public. |
Description[runtime] PreventExtensionsWithTransition: before adding the new
transition, check to see if we have already done this transition.
BUG=v8:6450
Review-Url: https://codereview.chromium.org/2915863004
Cr-Commit-Position: refs/heads/master@{#46129}
Committed: https://chromium.googlesource.com/v8/v8/+/6681949808bb89675ab64c95a237ae76955e191b
Patch Set 1 #Patch Set 2 : [runtime] fix memory leak in PreventExtensionsWithTransition #
Total comments: 7
Patch Set 3 : [runtime] fix memory leak in PreventExtensionsWithTransition #
Total comments: 7
Patch Set 4 : [runtime] fix memory leak in PreventExtensionsWithTransition #Patch Set 5 : [runtime] fix memory leak in PreventExtensionsWithTransition #Patch Set 6 : [runtime] fix memory leak in PreventExtensionsWithTransition #
Total comments: 2
Patch Set 7 : [runtime] fix memory leak in PreventExtensionsWithTransition #Patch Set 8 : [runtime] fix memory leak in PreventExtensionsWithTransition #
Messages
Total messages: 48 (28 generated)
bmeurer@chromium.org changed reviewers: + bmeurer@chromium.org, ishell@chromium.org, verwaest@chromium.org
Igor, Toon, please take a look.
Thanks for the CL Kris! The change makes sense to me, but I'm wondering whether we shouldn't just check whether we're already in the correct state. This is obviously faster but won't catch all cases...
On 2017/06/01 18:50:52, Toon Verwaest wrote: > Thanks for the CL Kris! The change makes sense to me, but I'm wondering whether > we shouldn't just check whether we're already in the correct state. This is > obviously faster but won't catch all cases... For sealed, I could check the kExtensible bit before searching but there is no frozen bit, and if there are any properties, is there a quick check for frozen? One thought I had was CopyForPreventExtensions could be a copy on write, only return a new_map if the descriptors needed to change. Another simple fix would just be to call TestIntegrityLevel, but it is only re-adding transitions that is leaking memory.
On 2017/06/01 21:27:07, kris.selden wrote: > On 2017/06/01 18:50:52, Toon Verwaest wrote: > > Thanks for the CL Kris! The change makes sense to me, but I'm wondering > whether > > we shouldn't just check whether we're already in the correct state. This is > > obviously faster but won't catch all cases... > > For sealed, I could check the kExtensible bit before searching but there is no > frozen bit, and if there are any properties, is there a quick check for frozen? > One thought I had was CopyForPreventExtensions could be a copy on write, only > return a new_map if the descriptors needed to change. > > Another simple fix would just be to call TestIntegrityLevel, but it is only > re-adding transitions that is leaking memory. could I add a bit for frozen for when a map transitions via PreventExtensionsWithTransition?
There is indeed no quick way to check it, but the complexity if making it frozen equals the complexity of checking it. You just save the memory overhead by not retransitioning. I think it's worth just doing the expensive check; until we have a good reason to optimize beyond that... Note that the freeze-transition isn't guaranteed to be at the end. While the user can't add new properties to the object, V8 still can and would in certain cases. Private symbols can always be added, and will be if e.g., the object is used as a key in a Map (to store the identity hash).
> You just save the memory overhead by not retransitioning It is quite surprising that `Object.freeze` is not idempotent. This confusion lead us to introduce a development aide, which very unexpectedly resulted in a poor development experience. More context here -> https://github.com/emberjs/ember.js/issues/15290 > I think it's worth just doing the expensive check; until we have a good reason to optimize beyond that... Would the above provided link be a good reason?
I think I didn't formulate my review very clearly, since I think you misunderstood my comment as something along the lines of "don't do this, it just saves memory". What I meant was: do save memory by not retransitioning, but don't locally optimize the performance of figuring out whether we are retransitioning by walking 1 step back. The sentence "you just save the memory overhead" was meant to affirm that you should do this; not that it isn't really relevant :) So back to my suggestion: I think you should replace the hack that checks the incoming transition as a quick-check with a full-blown TestIntegrityLevel, which avoids doing the transition if we're already at the right level. For performance reasons we probably want to split out a JSObject version that's less general than the current JSReceiver::TestIntegrityLevel; and optimized for both dictionary and fast-mode objects. As a final optimization it can also peak the transition. By separating these two concerns you avoid the memory overhead in general, which you currently don't (the transition might not be at the end and might be interleaved with other similar transitions). And the regular TestIntegrityLevel (e.g., Object.isFrozen) will also benefit from your performance optimizations.
On 2017/06/07 09:19:23, Toon Verwaest wrote: > I think I didn't formulate my review very clearly, since I think you > misunderstood my comment as something along the lines of "don't do this, it just > saves memory". > > What I meant was: do save memory by not retransitioning, but don't locally > optimize the performance of figuring out whether we are retransitioning by > walking 1 step back. The sentence "you just save the memory overhead" was meant > to affirm that you should do this; not that it isn't really relevant :) > > So back to my suggestion: I think you should replace the hack that checks the > incoming transition as a quick-check with a full-blown TestIntegrityLevel, which > avoids doing the transition if we're already at the right level. For performance > reasons we probably want to split out a JSObject version that's less general > than the current JSReceiver::TestIntegrityLevel; and optimized for both > dictionary and fast-mode objects. As a final optimization it can also peak the > transition. > > By separating these two concerns you avoid the memory overhead in general, which > you currently don't (the transition might not be at the end and might be > interleaved with other similar transitions). And the regular TestIntegrityLevel > (e.g., Object.isFrozen) will also benefit from your performance optimizations. I think I've addressed the concerns above, is there anything more I can do to move this patch along? There are a few common places integrity is checked like array shift that would benefit from a fast path in the test.
Looks good. Some final minor comments. https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc File src/objects.cc (right): https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7426 src/objects.cc:7426: // prevent memory leaks by adding unnecessary transitions Prevent ... *not* adding ... transitions. :) https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7530 src/objects.cc:7530: ElementsAccessor* accessor = object->GetElementsAccessor(); If we only support fast elements, finding any element makes it false. https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7553 src/objects.cc:7553: Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); We definitely want to share this slow path with the code for JSReceiver above.
https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc File src/objects.cc (right): https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7530 src/objects.cc:7530: ElementsAccessor* accessor = object->GetElementsAccessor(); On 2017/06/13 09:57:06, Toon Verwaest wrote: > If we only support fast elements, finding any element makes it false. Come to think of it; if we ever followed the fast transition, we always end up with slow elements. See CopyForPreventExtensions; it always sets the elements kind to something slow (except typed arrays).
On 2017/06/16 10:03:02, Toon Verwaest wrote: > https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc > File src/objects.cc (right): > > https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7530 > src/objects.cc:7530: ElementsAccessor* accessor = object->GetElementsAccessor(); > On 2017/06/13 09:57:06, Toon Verwaest wrote: > > If we only support fast elements, finding any element makes it false. > > Come to think of it; if we ever followed the fast transition, we always end up > with slow elements. See CopyForPreventExtensions; it always sets the elements > kind to something slow (except typed arrays). Yes, dictionary elements, I noticed this last night, I have a cleaned up patch coming shortly
I've addressed the comments, this is ready for review. https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc File src/objects.cc (right): https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7426 src/objects.cc:7426: // prevent memory leaks by adding unnecessary transitions On 2017/06/13 09:57:05, Toon Verwaest wrote: > Prevent ... *not* adding ... transitions. :) Done. https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7530 src/objects.cc:7530: ElementsAccessor* accessor = object->GetElementsAccessor(); On 2017/06/16 10:03:02, Toon Verwaest wrote: > On 2017/06/13 09:57:06, Toon Verwaest wrote: > > If we only support fast elements, finding any element makes it false. > > Come to think of it; if we ever followed the fast transition, we always end up > with slow elements. See CopyForPreventExtensions; it always sets the elements > kind to something slow (except typed arrays). I think what I did addresses this but I need your review. https://codereview.chromium.org/2915863004/diff/20001/src/objects.cc#newcode7553 src/objects.cc:7553: Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); On 2017/06/13 09:57:05, Toon Verwaest wrote: > We definitely want to share this slow path with the code for JSReceiver above. Done.
I like this version a lot! Added some (final, I suppose) comments. https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc File src/objects.cc (right): https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc#newcode7518 src/objects.cc:7518: FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, j = 0, j, j < capacity, j++, { Since you don't create handles here you don't need a FOR_WITH_HANDLE_SCOPE. Just use a regular for instead. https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc#newcode7520 src/objects.cc:7520: if (!dict->IsKey(isolate, k)) continue; You also need to check whether the key is "deleted". In the case of global properties we actually create "deleted" entries so we can cache property lookups that find properties behind the global object. Below I describe that we probably shouldn't support the global object on this fast path, but even so I think we should add the IsDeleted check here to avoid forgetting to add it later if we ever use this for other purposes. https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc#newcode7578 src/objects.cc:7578: if (object->IsJSObject()) { object->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER This makes sure that objects with indexed and named interceptors are dealt with generically (e.g., localStorage); and we only take the fast path in case we only need to deal with regular-type properties are you're doing below. https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc#newcode7596 src/objects.cc:7596: if (object->IsAccessCheckNeeded() && If you only support > LAST_CUSTOM_ELEMENTS_RECEIVER as described above, this shouldn't be necessary. I presume the performance of Object.freeze(window) isn't of vital importance ... :) https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc#newcode7601 src/objects.cc:7601: if (object->IsJSGlobalProxy()) { And in that case you can also drop this. https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc#newcode7616 src/objects.cc:7616: if (!TestElementsIntegrityLevel(*object, level)) { return Just(!object->map()->is_extensible() && TestElementsIntegrityLevel(*object, level) && TestPropertiesIntegrityLevel(*object, level));
https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc File src/objects.cc (right): https://codereview.chromium.org/2915863004/diff/40001/src/objects.cc#newcode7518 src/objects.cc:7518: FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, j = 0, j, j < capacity, j++, { On 2017/06/20 08:43:23, Toon Verwaest wrote: > Since you don't create handles here you don't need a FOR_WITH_HANDLE_SCOPE. Just > use a regular for instead. sorry, that was a bit of copy & paste from some other 'Fast' method.
lgtm
The CQ bit was checked by kris.selden@gmail.com to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: v8_linux64_rel_ng on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux64_rel_ng/builds/27831) v8_linux64_rel_ng_triggered on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux64_rel_ng_triggered...) v8_linux_arm_rel_ng on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux_arm_rel_ng/builds/...) v8_linux_arm_rel_ng_triggered on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux_arm_rel_ng_trigger...)
The CQ bit was checked by kris.selden@gmail.com to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: v8_linux64_rel_ng on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux64_rel_ng/builds/27834) v8_linux_arm64_rel_ng on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux_arm64_rel_ng/build...)
The CQ bit was checked by kris.selden@gmail.com to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: v8_linux_nodcheck_rel_ng on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux_nodcheck_rel_ng/bu...) v8_linux_nodcheck_rel_ng_triggered on master.tryserver.v8 (JOB_FAILED, http://build.chromium.org/p/tryserver.v8/builders/v8_linux_nodcheck_rel_ng_tr...)
The CQ bit was checked by kris.selden@gmail.com to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
On 2017/06/20 19:25:37, Toon Verwaest wrote: > lgtm it's green now, last issue was filtering private symbols.
https://codereview.chromium.org/2915863004/diff/100001/src/objects.cc File src/objects.cc (right): https://codereview.chromium.org/2915863004/diff/100001/src/objects.cc#newcode... src/objects.cc:7495: if (!dict->IsKey(isolate, dict->KeyAt(i)) || dict->IsDeleted(i)) continue; Private symbols can also occur as keys in NameDictionaries. I presume this also doesn't work in that case :s. https://codereview.chromium.org/2915863004/diff/100001/src/objects.cc#newcode... src/objects.cc:7507: DCHECK(map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER); DCHECK_LT(LAST_CUSTOM_ELEMENTS_RECEIVER, map->instance_type()); same below
The CQ bit was checked by kris.selden@gmail.com to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: v8_linux_verify_csa_rel_ng on master.tryserver.v8 (JOB_TIMED_OUT, http://build.chromium.org/p/tryserver.v8/builders/v8_linux_verify_csa_rel_ng/...)
The CQ bit was checked by kris.selden@gmail.com to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
lgtm, thanks!
The CQ bit was checked by verwaest@chromium.org
CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
CQ is committing da patch. Bot data: {"patchset_id": 140001, "attempt_start_ts": 1498133815000830, "parent_rev": "2b730f63356051b59c268fd932878800cf2cc3ad", "commit_rev": "6681949808bb89675ab64c95a237ae76955e191b"}
Message was sent while issue was closed.
Description was changed from ========== [runtime] PreventExtensionsWithTransition: before adding the new transition, check to see if we have already done this transition. BUG=v8:6450 ========== to ========== [runtime] PreventExtensionsWithTransition: before adding the new transition, check to see if we have already done this transition. BUG=v8:6450 Review-Url: https://codereview.chromium.org/2915863004 Cr-Commit-Position: refs/heads/master@{#46129} Committed: https://chromium.googlesource.com/v8/v8/+/6681949808bb89675ab64c95a237ae76955... ==========
Message was sent while issue was closed.
Committed patchset #8 (id:140001) as https://chromium.googlesource.com/v8/v8/+/6681949808bb89675ab64c95a237ae76955... |