| OLD | NEW |
| (Empty) |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 from collections import defaultdict | |
| 6 from itertools import starmap | |
| 7 from telemetry.core import util | |
| 8 from telemetry.page import legacy_page_test | |
| 9 from telemetry.value import scalar | |
| 10 | |
| 11 from measurements import timeline_controller | |
| 12 import py_utils | |
| 13 | |
| 14 | |
| 15 class BlinkStyle(legacy_page_test.LegacyPageTest): | |
| 16 | |
| 17 def __init__(self): | |
| 18 super(BlinkStyle, self).__init__() | |
| 19 self._controller = None | |
| 20 | |
| 21 def WillNavigateToPage(self, page, tab): | |
| 22 self._controller = timeline_controller.TimelineController() | |
| 23 self._controller.trace_categories = 'blink_style,blink.console' | |
| 24 self._controller.SetUp(page, tab) | |
| 25 self._controller.Start(tab) | |
| 26 | |
| 27 def DidRunPage(self, platform): | |
| 28 if self._controller: | |
| 29 self._controller.CleanUp(platform) | |
| 30 | |
| 31 def ValidateAndMeasurePage(self, page, tab, results): | |
| 32 with tab.action_runner.CreateInteraction('wait-for-quiescence'): | |
| 33 tab.ExecuteJavaScript('console.time("");') | |
| 34 try: | |
| 35 util.WaitFor(tab.HasReachedQuiescence, 15) | |
| 36 except py_utils.TimeoutException: | |
| 37 # Some sites never reach quiesence. As this benchmark normalizes/ | |
| 38 # categories results, it shouldn't be necessary to reach the same | |
| 39 # state on every run. | |
| 40 pass | |
| 41 | |
| 42 tab.ExecuteJavaScript(''' | |
| 43 for (var i = 0; i < 11; i++) { | |
| 44 var cold = i % 2 == 0; | |
| 45 var name = "update_style"; | |
| 46 if (cold) name += "_cold"; | |
| 47 console.time(name); | |
| 48 // Occasionally documents will break the APIs we need | |
| 49 try { | |
| 50 // On cold runs, force a new StyleResolver | |
| 51 if (cold) { | |
| 52 var style = document.createElement("style"); | |
| 53 document.head.appendChild(style); | |
| 54 style.remove(); | |
| 55 } | |
| 56 // Invalidate style for the whole document | |
| 57 document.documentElement.lang += "z"; | |
| 58 // Force a style update (but not layout) | |
| 59 getComputedStyle(document.documentElement).color; | |
| 60 } catch (e) {} | |
| 61 console.timeEnd(name); | |
| 62 }''') | |
| 63 | |
| 64 self._controller.Stop(tab, results) | |
| 65 renderer = self._controller.model.GetRendererThreadFromTabId(tab.id) | |
| 66 markers = [event for event in renderer.async_slices | |
| 67 if event.name.startswith('update_style') | |
| 68 and event.category == 'blink.console'] | |
| 69 # Drop the first run. | |
| 70 markers = markers[1:] | |
| 71 assert len(markers) == 10 | |
| 72 | |
| 73 def duration(event): | |
| 74 if event.has_thread_timestamps: | |
| 75 return event.thread_duration | |
| 76 else: | |
| 77 return event.duration | |
| 78 | |
| 79 for marker in markers: | |
| 80 for event in renderer.all_slices: | |
| 81 if (event.name == 'Document::updateStyle' | |
| 82 and event.start >= marker.start | |
| 83 and event.end <= marker.end): | |
| 84 access_count = event.args.get('resolverAccessCount') | |
| 85 if access_count is None: | |
| 86 # absent in earlier versions | |
| 87 continue | |
| 88 min_access_count = 50 | |
| 89 | |
| 90 if access_count >= min_access_count: | |
| 91 result = 1000 * (duration(event) / access_count) | |
| 92 results.AddValue(scalar.ScalarValue( | |
| 93 page, marker.name, 'ms/1000 elements', result)) | |
| 94 | |
| 95 class ParserEvent(object): | |
| 96 | |
| 97 def __init__(self, summary_event, tokenize_event, parse_event): | |
| 98 min_sheet_length = 1000 | |
| 99 ua_sheet_mode = 5 | |
| 100 enormous_token_threshold = 100 | |
| 101 large_token_threshold = 5 | |
| 102 | |
| 103 self.mode = summary_event.args.get('mode') | |
| 104 self.length = summary_event.args.get('length') | |
| 105 self.tokens = summary_event.args.get('tokenCount') | |
| 106 self.tokenize_duration = duration(tokenize_event) | |
| 107 self.parse_duration = duration(parse_event) | |
| 108 self.chars_per_token = 0 | |
| 109 if self.tokens: | |
| 110 self.chars_per_token = self.length / float(self.tokens) | |
| 111 if self.mode == ua_sheet_mode or self.length < min_sheet_length: | |
| 112 self.category = 'ignored' | |
| 113 elif self.chars_per_token > enormous_token_threshold: | |
| 114 self.category = 'enormous_tokens' | |
| 115 elif self.chars_per_token > large_token_threshold: | |
| 116 self.category = 'large_tokens' | |
| 117 else: | |
| 118 self.category = 'regular' | |
| 119 | |
| 120 parser_events = [event for event in renderer.all_slices | |
| 121 if event.name == 'CSSParserImpl::parseStyleSheet' | |
| 122 or event.name == 'CSSParserImpl::parseStyleSheet.tokenize' | |
| 123 or event.name == 'CSSParserImpl::parseStyleSheet.parse'] | |
| 124 | |
| 125 merged_events = starmap(ParserEvent, zip(*[iter(parser_events)] * 3)) | |
| 126 | |
| 127 events_by_category = defaultdict(list) | |
| 128 for event in merged_events: | |
| 129 if event.category != 'ignored': | |
| 130 events_by_category[event.category].append(event) | |
| 131 | |
| 132 for category, events in events_by_category.items(): | |
| 133 parse_duration = sum(event.parse_duration for event in events) | |
| 134 tokenize_duration = sum(event.tokenize_duration for event in events) | |
| 135 tokens = sum(event.tokens for event in events) | |
| 136 length = sum(event.length for event in events) | |
| 137 | |
| 138 results.AddValue( | |
| 139 scalar.ScalarValue(page, ('parse_css_%s' % category), | |
| 140 'tokens/s', 1000 / (parse_duration / tokens))) | |
| 141 | |
| 142 results.AddValue( | |
| 143 scalar.ScalarValue(page, ('tokenize_css_%s' % category), | |
| 144 'char/s', 1000 / (tokenize_duration / length))) | |
| OLD | NEW |