Changeset 142077 in webkit
- Timestamp:
- Feb 7, 2013 12:42:13 AM (11 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r142076 r142077 1 2013-02-04 Kentaro Hara <haraken@chromium.org> 2 3 [V8] Remove V8GCController::m_edenNodes and make minor DOM GC more precise 4 https://bugs.webkit.org/show_bug.cgi?id=108579 5 6 Reviewed by Adam Barth. 7 8 Currently V8GCController::m_edenNodes stores a list of nodes whose 9 wrappers have been created since the latest GC. The reason why we 10 needed m_edenNodes is that there was no way to know a list of wrappers 11 in the new space of V8. By using m_edenNodes, we had been approximating 12 'wrappers in the new space' by 'wrappers that have been created since 13 the latest GC'. 14 15 Now V8 provides VisitHandlesForPartialDependence(), with which WebKit 16 can know a list of wrappers in the new space. By using the API, we can 17 remove V8GCController::m_edenNodes. The benefit is that (1) we no longer 18 need to keep m_edenNodes and that (2) it enables more precise minor 19 DOM GC (Remember that m_edenNodes was just an approximation). 20 21 Performance benchmark: https://bugs.webkit.org/attachment.cgi?id=185940 22 The benchmark runs 300 iterations, each of which creates 100000 elements. 23 The benchmark measures average, min, median, max and stdev of execution times 24 of the 300 iterations. This will tell us the worst-case overhead of this change. 25 26 Before: 27 mean=59.91ms, min=39ms, median=42ms, max=258ms, stdev=47.48ms 28 29 After: 30 mean=58.75ms, min=35ms, median=41ms, max=250ms, stdev=47.32ms 31 32 As shown above, I couldn't observe any performance regression. 33 34 No tests. No change in behavior. 35 36 * bindings/v8/DOMDataStore.h: 37 (WebCore::DOMDataStore::setWrapperInObject): 38 * bindings/v8/DOMWrapperWorld.h: 39 (DOMWrapperWorld): 40 (WebCore::DOMWrapperWorld::getWorldWithoutContextCheck): 41 * bindings/v8/V8Binding.h: 42 (WebCore): 43 (WebCore::worldForEnteredContextIfIsolated): 44 (WebCore::worldForEnteredContextWithoutContextCheck): 45 * bindings/v8/V8DOMWindowShell.cpp: 46 (WebCore::V8DOMWindowShell::initializeIfNeeded): 47 * bindings/v8/V8GCController.cpp: 48 (WebCore::gcTree): 49 (WebCore): 50 (MinorGCWrapperVisitor): 51 (WebCore::MinorGCWrapperVisitor::MinorGCWrapperVisitor): 52 (WebCore::MinorGCWrapperVisitor::notifyFinished): 53 (WebCore::MajorGCWrapperVisitor::MajorGCWrapperVisitor): 54 (WebCore::V8GCController::gcPrologue): 55 (WebCore::V8GCController::minorGCPrologue): 56 (WebCore::V8GCController::majorGCPrologue): 57 * bindings/v8/V8GCController.h: 58 (V8GCController): 59 1 60 2013-02-06 Kent Tamura <tkent@chromium.org> 2 61 -
trunk/Source/WebCore/bindings/v8/DOMDataStore.h
r141999 r142077 165 165 wrapper.MakeWeak(isolate, object, weakCallback); 166 166 } 167 static void setWrapperInObject(Node* object, v8::Persistent<v8::Object> wrapper, v8::Isolate* isolate)168 {169 ASSERT(object->wrapper().IsEmpty());170 object->setWrapper(wrapper);171 V8GCController::didCreateWrapperForNode(object);172 wrapper.MakeWeak(isolate, static_cast<ScriptWrappable*>(object), weakCallback);173 }174 167 175 168 static void weakCallback(v8::Isolate*, v8::Persistent<v8::Value>, void* context); -
trunk/Source/WebCore/bindings/v8/DOMWrapperWorld.h
r141999 r142077 64 64 static void assertContextHasCorrectPrototype(v8::Handle<v8::Context>); 65 65 #endif 66 // FIXME: Rename this method to getWorld(). 66 67 static DOMWrapperWorld* isolated(v8::Handle<v8::Context> context) 67 68 { … … 69 70 assertContextHasCorrectPrototype(context); 70 71 #endif 72 return static_cast<DOMWrapperWorld*>(context->GetAlignedPointerFromEmbedderData(v8ContextIsolatedWorld)); 73 } 74 static DOMWrapperWorld* getWorldWithoutContextCheck(v8::Handle<v8::Context> context) 75 { 71 76 return static_cast<DOMWrapperWorld*>(context->GetAlignedPointerFromEmbedderData(v8ContextIsolatedWorld)); 72 77 } -
trunk/Source/WebCore/bindings/v8/V8Binding.h
r141999 r142077 438 438 Frame* toFrameIfNotDetached(v8::Handle<v8::Context>); 439 439 440 // FIXME: Rename this method to worldForEnteredContext(). 440 441 inline DOMWrapperWorld* worldForEnteredContextIfIsolated() 441 442 { 442 if (!v8::Context::InContext()) 443 v8::Handle<v8::Context> context = v8::Context::GetEntered(); 444 if (context.IsEmpty()) 443 445 return 0; 444 return DOMWrapperWorld::isolated(v8::Context::GetEntered()); 446 return DOMWrapperWorld::isolated(context); 447 } 448 449 // This is a slightly different version of worldForEnteredContext(). 450 // The difference is just that worldForEnteredContextWithoutContextCheck() 451 // does not call assertContextHasCorrectPrototype() (which is enabled on 452 // Debug builds only). Because assertContextHasCorrectPrototype() crashes 453 // if it is called when a current context is not completely initialized, 454 // you have to use worldForEnteredContextWithoutContextCheck() if you need 455 // to get a DOMWrapperWorld while a current context is being initialized. 456 // See https://bugs.webkit.org/show_bug.cgi?id=108579#c15 for more details. 457 inline DOMWrapperWorld* worldForEnteredContextWithoutContextCheck() 458 { 459 v8::Handle<v8::Context> context = v8::Context::GetEntered(); 460 if (context.IsEmpty()) 461 return 0; 462 return DOMWrapperWorld::getWorldWithoutContextCheck(context); 445 463 } 446 464 -
trunk/Source/WebCore/bindings/v8/V8DOMWindowShell.cpp
r141999 r142077 209 209 return false; 210 210 211 m_world->setIsolatedWorldField(m_context.get()); 212 211 213 bool isMainWorld = m_world->isMainWorld(); 212 214 213 215 v8::Local<v8::Context> context = v8::Local<v8::Context>::New(m_context.get()); 214 216 v8::Context::Scope contextScope(context); 215 216 m_world->setIsolatedWorldField(m_context.get());217 217 218 218 if (m_global.isEmpty()) { -
trunk/Source/WebCore/bindings/v8/V8GCController.cpp
r141999 r142077 179 179 } 180 180 181 class WrapperVisitor : public v8::PersistentHandleVisitor { 181 static void gcTree(Node* startNode) 182 { 183 Vector<v8::Persistent<v8::Value>, initialNodeVectorSize> newSpaceWrappers; 184 185 // We traverse a DOM tree in the DFS order starting from startNode. 186 // The traversal order does not matter for correctness but does matter for performance. 187 Node* node = startNode; 188 // To make each minor GC time bounded, we might need to give up 189 // traversing at some point for a large DOM tree. That being said, 190 // I could not observe the need even in pathological test cases. 191 do { 192 ASSERT(node); 193 if (!node->wrapper().IsEmpty()) { 194 // FIXME: Remove the special handling for image elements. 195 // The same special handling is in V8GCController::opaqueRootForGC(). 196 // Maybe should image elements be active DOM nodes? 197 // See https://code.google.com/p/chromium/issues/detail?id=164882 198 if (!node->isV8CollectableDuringMinorGC() || (node->hasTagName(HTMLNames::imgTag) && static_cast<HTMLImageElement*>(node)->hasPendingActivity())) { 199 // This node is not in the new space of V8. This indicates that 200 // the minor GC cannot anyway judge reachability of this DOM tree. 201 // Thus we give up traversing the DOM tree. 202 return; 203 } 204 node->setV8CollectableDuringMinorGC(false); 205 newSpaceWrappers.append(node->wrapper()); 206 } 207 if (node->firstChild()) { 208 node = node->firstChild(); 209 continue; 210 } 211 while (!node->nextSibling()) { 212 if (!node->parentNode()) 213 break; 214 node = node->parentNode(); 215 } 216 if (node->parentNode()) 217 node = node->nextSibling(); 218 } while (node != startNode); 219 220 // We completed the DOM tree traversal. All wrappers in the DOM tree are 221 // stored in newSpaceWrappers and are expected to exist in the new space of V8. 222 // We report those wrappers to V8 as an object group. 223 for (size_t i = 0; i < newSpaceWrappers.size(); i++) 224 newSpaceWrappers[i].MarkPartiallyDependent(); 225 if (newSpaceWrappers.size() > 0) 226 v8::V8::AddObjectGroup(&newSpaceWrappers[0], newSpaceWrappers.size()); 227 } 228 229 // Regarding a minor GC algorithm for DOM nodes, see this document: 230 // https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g49mWNMW2gaWvMN5NLk8/edit#slide=id.p 231 class MinorGCWrapperVisitor : public v8::PersistentHandleVisitor { 182 232 public: 183 explicit WrapperVisitor(v8::Isolate* isolate) 233 explicit MinorGCWrapperVisitor(v8::Isolate* isolate) 234 : m_isolate(isolate) 235 { 236 UNUSED_PARAM(m_isolate); 237 } 238 239 virtual void VisitPersistentHandle(v8::Persistent<v8::Value> value, uint16_t classId) OVERRIDE 240 { 241 // A minor DOM GC can collect only Nodes. 242 if (classId != v8DOMNodeClassId) 243 return; 244 245 // To make minor GC cycle time bounded, we limit the number of wrappers handled 246 // by each minor GC cycle to 10000. This value was selected so that the minor 247 // GC cycle time is bounded to 20 ms in a case where the new space size 248 // is 16 MB and it is full of wrappers (which is almost the worst case). 249 // Practically speaking, as far as I crawled real web applications, 250 // the number of wrappers handled by each minor GC cycle is at most 3000. 251 // So this limit is mainly for pathological micro benchmarks. 252 const unsigned wrappersHandledByEachMinorGC = 10000; 253 if (m_nodesInNewSpace.size() >= wrappersHandledByEachMinorGC) 254 return; 255 256 ASSERT(value->IsObject()); 257 v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>::Cast(value); 258 ASSERT(V8DOMWrapper::maybeDOMWrapper(value)); 259 ASSERT(V8Node::HasInstance(wrapper, m_isolate)); 260 Node* node = V8Node::toNative(wrapper); 261 m_nodesInNewSpace.append(node); 262 node->setV8CollectableDuringMinorGC(true); 263 } 264 265 void notifyFinished() 266 { 267 for (size_t i = 0; i < m_nodesInNewSpace.size(); i++) { 268 ASSERT(!m_nodesInNewSpace[i]->wrapper().IsEmpty()); 269 if (m_nodesInNewSpace[i]->isV8CollectableDuringMinorGC()) // This branch is just for performance. 270 gcTree(m_nodesInNewSpace[i]); 271 } 272 } 273 274 private: 275 Vector<Node*> m_nodesInNewSpace; 276 v8::Isolate* m_isolate; 277 }; 278 279 class MajorGCWrapperVisitor : public v8::PersistentHandleVisitor { 280 public: 281 explicit MajorGCWrapperVisitor(v8::Isolate* isolate) 184 282 : m_isolate(isolate) 185 283 { … … 249 347 }; 250 348 251 // Regarding a minor GC algorithm for DOM nodes, see this document:252 // https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g49mWNMW2gaWvMN5NLk8/edit#slide=id.p253 //254 // m_edenNodes stores nodes that have wrappers that have been created since the last minor/major GC.255 Vector<Node*>* V8GCController::m_edenNodes = 0;256 257 static void gcTree(Node* startNode)258 {259 Vector<v8::Persistent<v8::Value>, initialNodeVectorSize> newSpaceWrappers;260 261 // We traverse a DOM tree in the DFS order starting from startNode.262 // The traversal order does not matter for correctness but does matter for performance.263 Node* node = startNode;264 // To make each minor GC time bounded, we might need to give up265 // traversing at some point for a large DOM tree. That being said,266 // I could not observe the need even in pathological test cases.267 do {268 ASSERT(node);269 if (!node->wrapper().IsEmpty()) {270 // FIXME: Remove the special handling for image elements.271 // The same special handling is in V8GCController::opaqueRootForGC().272 // Maybe should image elements be active DOM nodes?273 // See https://code.google.com/p/chromium/issues/detail?id=164882274 if (!node->isV8CollectableDuringMinorGC() || (node->hasTagName(HTMLNames::imgTag) && static_cast<HTMLImageElement*>(node)->hasPendingActivity())) {275 // The fact that we encounter a node that is not in the Eden space276 // implies that its wrapper might be in the old space of V8.277 // This indicates that the minor GC cannot anyway judge reachability278 // of this DOM tree. Thus we give up traversing the DOM tree.279 return;280 }281 // A once traversed node is removed from the Eden space.282 node->setV8CollectableDuringMinorGC(false);283 newSpaceWrappers.append(node->wrapper());284 }285 if (node->firstChild()) {286 node = node->firstChild();287 continue;288 }289 while (!node->nextSibling()) {290 if (!node->parentNode())291 break;292 node = node->parentNode();293 }294 if (node->parentNode())295 node = node->nextSibling();296 } while (node != startNode);297 298 // We completed the DOM tree traversal. All wrappers in the DOM tree are299 // stored in newSpaceWrappers and are expected to exist in the new space of V8.300 // We report those wrappers to V8 as an object group.301 for (size_t i = 0; i < newSpaceWrappers.size(); i++)302 newSpaceWrappers[i].MarkPartiallyDependent();303 if (newSpaceWrappers.size() > 0)304 v8::V8::AddObjectGroup(&newSpaceWrappers[0], newSpaceWrappers.size());305 }306 307 void V8GCController::didCreateWrapperForNode(Node* node)308 {309 // To make minor GC cycle time bounded, we limit the number of wrappers handled310 // by each minor GC cycle to 10000. This value was selected so that the minor311 // GC cycle time is bounded to 20 ms in a case where the new space size312 // is 16 MB and it is full of wrappers (which is almost the worst case).313 // Practically speaking, as far as I crawled real web applications,314 // the number of wrappers handled by each minor GC cycle is at most 3000.315 // So this limit is mainly for pathological micro benchmarks.316 const unsigned wrappersHandledByEachMinorGC = 10000;317 ASSERT(!node->wrapper().IsEmpty());318 if (!m_edenNodes)319 m_edenNodes = adoptPtr(new Vector<Node*>).leakPtr();320 if (m_edenNodes->size() <= wrappersHandledByEachMinorGC) {321 // A node of a newly created wrapper is put into the Eden space.322 m_edenNodes->append(node);323 node->setV8CollectableDuringMinorGC(true);324 }325 }326 327 349 void V8GCController::gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) 328 350 { … … 331 353 else if (type == v8::kGCTypeMarkSweepCompact) 332 354 majorGCPrologue(); 333 334 if (isMainThreadOrGCThread() && m_edenNodes) {335 // The Eden space is cleared at every minor/major GC.336 m_edenNodes->clear();337 }338 355 } 339 356 … … 342 359 TRACE_EVENT_BEGIN0("v8", "GC"); 343 360 344 if (isMainThreadOrGCThread() && m_edenNodes) { 345 for (size_t i = 0; i < m_edenNodes->size(); i++) { 346 ASSERT(!m_edenNodes->at(i)->wrapper().IsEmpty()); 347 if (m_edenNodes->at(i)->isV8CollectableDuringMinorGC()) // This branch is just for performance. 348 gcTree(m_edenNodes->at(i)); 349 } 361 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 362 v8::HandleScope scope; 363 364 // A minor GC can handle the main world only. 365 DOMWrapperWorld* world = worldForEnteredContextWithoutContextCheck(); 366 if (world && world->isMainWorld()) { 367 MinorGCWrapperVisitor visitor(isolate); 368 v8::V8::VisitHandlesForPartialDependence(isolate, &visitor); 369 visitor.notifyFinished(); 350 370 } 351 371 } … … 359 379 v8::HandleScope scope; 360 380 361 WrapperVisitor visitor(isolate);381 MajorGCWrapperVisitor visitor(isolate); 362 382 v8::V8::VisitHandlesWithClassIds(&visitor); 363 383 visitor.notifyFinished(); -
trunk/Source/WebCore/bindings/v8/V8GCController.h
r141999 r142077 53 53 54 54 static Node* opaqueRootForGC(Node*, v8::Isolate*); 55 static void didCreateWrapperForNode(Node*);56 55 57 56 private:
Note: See TracChangeset
for help on using the changeset viewer.