Пример #1
0
 def _ComputeQueueingDuration(self, page, stats):
     """Returns a Value for the frame queueing durations."""
     queueing_durations = None
     none_value_reason = None
     if 'frame_queueing_durations' in stats.errors:
         none_value_reason = stats.errors['frame_queueing_durations']
     elif self._HasEnoughFrames(stats.frame_timestamps):
         queueing_durations = FlattenList(stats.frame_queueing_durations)
         if len(queueing_durations) == 0:
             queueing_durations = None
             none_value_reason = 'No frame queueing durations recorded.'
     else:
         none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
     return list_of_scalar_values.ListOfScalarValues(
         page,
         'queueing_durations',
         'ms',
         queueing_durations,
         description='The frame queueing duration quantifies how out of sync '
         'the compositor and renderer threads are. It is the amount '
         'of wall time that elapses between a '
         'ScheduledActionSendBeginMainFrame event in the compositor '
         'thread and the corresponding BeginMainFrame event in the '
         'main thread.',
         none_value_reason=none_value_reason)
Пример #2
0
    def _ComputeMeanPixelsCheckerboarded(self, page, stats):
        """Add the mean percentage of pixels checkerboarded.

    This looks at tiles which are only missing.
    It does not take into consideration tiles which are of low or
    non-ideal resolution.
    """
        mean_pixels_checkerboarded = None
        none_value_reason = None
        if self._HasEnoughFrames(stats.frame_timestamps):
            if rendering_stats.CHECKERBOARDED_PIXEL_ERROR in stats.errors:
                none_value_reason = stats.errors[
                    rendering_stats.CHECKERBOARDED_PIXEL_ERROR]
            else:
                mean_pixels_checkerboarded = round(
                    statistics.ArithmeticMean(
                        FlattenList(stats.checkerboarded_pixel_percentages)),
                    3)
        else:
            none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
        return scalar.ScalarValue(
            page,
            'mean_pixels_checkerboarded',
            'percent',
            mean_pixels_checkerboarded,
            description='Percentage of pixels that were checkerboarded.',
            none_value_reason=none_value_reason)
Пример #3
0
 def _ComputeLatencyMetric(self, page, stats, name, list_of_latency_lists):
     """Returns Values for the mean and discrepancy for given latency stats."""
     mean_latency = None
     latency_discrepancy = None
     none_value_reason = None
     if self._HasEnoughFrames(stats.frame_timestamps):
         latency_list = FlattenList(list_of_latency_lists)
         if len(latency_list) == 0:
             return ()
         mean_latency = round(statistics.ArithmeticMean(latency_list), 3)
         latency_discrepancy = (round(
             statistics.DurationsDiscrepancy(latency_list), 4))
     else:
         none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
     return (scalar.ScalarValue(
         page,
         'mean_%s' % name,
         'ms',
         mean_latency,
         description='Arithmetic mean of the raw %s values' % name,
         none_value_reason=none_value_reason),
             scalar.ScalarValue(
                 page,
                 '%s_discrepancy' % name,
                 'ms',
                 latency_discrepancy,
                 description='Discrepancy of the raw %s values' % name,
                 none_value_reason=none_value_reason))
Пример #4
0
    def AddResults(self, model, renderer_thread, interaction_records, results):
        self.VerifyNonOverlappedRecords(interaction_records)
        renderer_process = renderer_thread.parent
        stats = rendering_stats.RenderingStats(
            renderer_process, model.browser_process,
            [r.GetBounds() for r in interaction_records])

        input_event_latency = FlattenList(stats.input_event_latency)
        if input_event_latency:
            mean_input_event_latency = statistics.ArithmeticMean(
                input_event_latency)
            input_event_latency_discrepancy = statistics.DurationsDiscrepancy(
                input_event_latency)
            results.Add('mean_input_event_latency', 'ms',
                        round(mean_input_event_latency, 3))
            results.Add('input_event_latency_discrepancy', 'ms',
                        round(input_event_latency_discrepancy, 4))

        # List of raw frame times.
        frame_times = FlattenList(stats.frame_times)
        results.Add('frame_times', 'ms', frame_times)

        # Arithmetic mean of frame times.
        mean_frame_time = statistics.ArithmeticMean(frame_times)
        results.Add('mean_frame_time', 'ms', round(mean_frame_time, 3))

        # Absolute discrepancy of frame time stamps.
        frame_discrepancy = statistics.TimestampsDiscrepancy(
            stats.frame_timestamps)
        results.Add('jank', 'ms', round(frame_discrepancy, 4))

        # Are we hitting 60 fps for 95 percent of all frames?
        # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0.
        percentile_95 = statistics.Percentile(frame_times, 95.0)
        results.Add('mostly_smooth', 'score',
                    1.0 if percentile_95 < 19.0 else 0.0)

        # Mean percentage of pixels approximated (missing tiles, low resolution
        # tiles, non-ideal resolution tiles)
        results.Add(
            'mean_pixels_approximated', 'percent',
            round(
                statistics.ArithmeticMean(
                    FlattenList(stats.approximated_pixel_percentages)), 3))
Пример #5
0
    def _ComputeFrameTimeMetric(self, page, stats):
        """Returns Values for the frame time metrics.

    This includes the raw and mean frame times, as well as the percentage of
    frames that were hitting 60 fps.
    """
        frame_times = None
        mean_frame_time = None
        percentage_smooth = None
        none_value_reason = None
        if self._HasEnoughFrames(stats.frame_timestamps):
            frame_times = FlattenList(stats.frame_times)
            mean_frame_time = round(statistics.ArithmeticMean(frame_times), 3)
            # We use 17ms as a somewhat looser threshold, instead of 1000.0/60.0.
            smooth_threshold = 17.0
            smooth_count = sum(1 for t in frame_times if t < smooth_threshold)
            percentage_smooth = float(smooth_count) / len(frame_times) * 100.0
        else:
            none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
        return (list_of_scalar_values.ListOfScalarValues(
            page,
            'frame_times',
            'ms',
            frame_times,
            description='List of raw frame times, helpful to understand the '
            'other metrics.',
            none_value_reason=none_value_reason),
                scalar.ScalarValue(
                    page,
                    'mean_frame_time',
                    'ms',
                    mean_frame_time,
                    description='Arithmetic mean of frame times.',
                    none_value_reason=none_value_reason),
                scalar.ScalarValue(
                    page,
                    'percentage_smooth',
                    'score',
                    percentage_smooth,
                    description=
                    'Percentage of frames that were hitting 60 fps.',
                    none_value_reason=none_value_reason))
Пример #6
0
    def _ComputeFrameTimeMetric(self, page, stats):
        """Returns Values for the frame time metrics.

    This includes the raw and mean frame times, as well as the mostly_smooth
    metric which tracks whether we hit 60 fps for 95% of the frames.
    """
        frame_times = None
        mean_frame_time = None
        mostly_smooth = None
        none_value_reason = None
        if self._HasEnoughFrames(stats.frame_timestamps):
            frame_times = FlattenList(stats.frame_times)
            mean_frame_time = round(statistics.ArithmeticMean(frame_times), 3)
            # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0.
            percentile_95 = statistics.Percentile(frame_times, 95.0)
            mostly_smooth = 1.0 if percentile_95 < 19.0 else 0.0
        else:
            none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
        return (list_of_scalar_values.ListOfScalarValues(
            page,
            'frame_times',
            'ms',
            frame_times,
            description='List of raw frame times, helpful to understand the '
            'other metrics.',
            none_value_reason=none_value_reason),
                scalar.ScalarValue(
                    page,
                    'mean_frame_time',
                    'ms',
                    mean_frame_time,
                    description='Arithmetic mean of frame times.',
                    none_value_reason=none_value_reason),
                scalar.ScalarValue(
                    page,
                    'mostly_smooth',
                    'score',
                    mostly_smooth,
                    description='Were 95 percent of the frames hitting 60 fps?'
                    'boolean value (1/0).',
                    none_value_reason=none_value_reason))
Пример #7
0
    def _ComputeMeanPixelsApproximated(self, page, stats):
        """Add the mean percentage of pixels approximated.

    This looks at tiles which are missing or of low or non-ideal resolution.
    """
        mean_pixels_approximated = None
        none_value_reason = None
        if self._HasEnoughFrames(stats.frame_timestamps):
            mean_pixels_approximated = round(
                statistics.ArithmeticMean(
                    FlattenList(stats.approximated_pixel_percentages)), 3)
        else:
            none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
        return scalar.ScalarValue(
            page,
            'mean_pixels_approximated',
            'percent',
            mean_pixels_approximated,
            description='Percentage of pixels that were approximated '
            '(checkerboarding, low-resolution tiles, etc.).',
            none_value_reason=none_value_reason)
Пример #8
0
 def _ComputeFirstGestureScrollUpdateLatency(self, page, stats):
     """Returns a Value for the first gesture scroll update latency."""
     first_gesture_scroll_update_latency = None
     none_value_reason = None
     if self._HasEnoughFrames(stats.frame_timestamps):
         latency_list = FlattenList(stats.gesture_scroll_update_latency)
         if len(latency_list) == 0:
             return ()
         first_gesture_scroll_update_latency = round(latency_list[0], 4)
     else:
         none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE
     return (scalar.ScalarValue(
         page,
         'first_gesture_scroll_update_latency',
         'ms',
         first_gesture_scroll_update_latency,
         description=
         'First gesture scroll update latency measures the time it '
         'takes to process the very first gesture scroll update '
         'input event. The first scroll gesture can often get '
         'delayed by work related to page loading.',
         none_value_reason=none_value_reason), )
  def testInputLatencyFromTimeline(self):
    timeline = model.TimelineModel()

    # Create a browser process and a renderer process.
    browser = timeline.GetOrCreateProcess(pid = 1)
    browser_main = browser.GetOrCreateThread(tid = 11)
    renderer = timeline.GetOrCreateProcess(pid = 2)
    renderer_main = renderer.GetOrCreateThread(tid = 21)

    timer = MockTimer()
    ref_latency = ReferenceInputLatencyStats()

    # Create 10 input latency stats events for Action A.
    timer.Advance(2, 4)
    renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '')
    for _ in xrange(0, 10):
      AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
    timer.Advance(2, 4)
    renderer_main.EndSlice(timer.Get())

    # Create 5 input latency stats events not within any action.
    timer.Advance(2, 4)
    for _ in xrange(0, 5):
      AddInputLatencyStats(timer, browser_main, renderer_main, None)

    # Create 10 input latency stats events for Action B.
    timer.Advance(2, 4)
    renderer_main.BeginSlice('webkit.console', 'ActionB', timer.Get(), '')
    for _ in xrange(0, 10):
      AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
    timer.Advance(2, 4)
    renderer_main.EndSlice(timer.Get())

    # Create 10 input latency stats events for Action A.
    timer.Advance(2, 4)
    renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '')
    for _ in xrange(0, 10):
      AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency)
    timer.Advance(2, 4)
    renderer_main.EndSlice(timer.Get())

    browser.FinalizeImport()
    renderer.FinalizeImport()

    input_events = []

    timeline_markers = timeline.FindTimelineMarkers(
        ['ActionA', 'ActionB', 'ActionA'])
    timeline_ranges = [timeline_bounds.Bounds.CreateFromEvent(marker)
                       for marker in timeline_markers]
    for timeline_range in timeline_ranges:
      if timeline_range.is_empty:
        continue
      input_events.extend(GetInputLatencyEvents(browser, timeline_range))

    self.assertEquals(input_events, ref_latency.input_event)
    input_event_latency_result = ComputeInputEventLatencies(input_events)
    self.assertEquals(input_event_latency_result,
                      ref_latency.input_event_latency)

    stats = RenderingStats(renderer, browser, timeline_ranges)
    self.assertEquals(FlattenList(stats.input_event_latency), [
        latency for name, latency in ref_latency.input_event_latency
        if name != SCROLL_UPDATE_EVENT_NAME])
    self.assertEquals(FlattenList(stats.scroll_update_latency), [
        latency for name, latency in ref_latency.input_event_latency
        if name == SCROLL_UPDATE_EVENT_NAME])
    self.assertEquals(FlattenList(stats.gesture_scroll_update_latency), [
        latency for name, latency in ref_latency.input_event_latency
        if name == GESTURE_SCROLL_UPDATE_EVENT_NAME])
    def _ComputeSurfaceFlingerMetric(self, page, stats):
        jank_count = None
        avg_surface_fps = None
        max_frame_delay = None
        frame_lengths = None
        none_value_reason = None
        if self._HasEnoughFrames(stats.frame_timestamps):
            timestamps = FlattenList(stats.frame_timestamps)
            frame_count = len(timestamps)
            milliseconds = timestamps[-1] - timestamps[0]
            min_normalized_frame_length = 0.5

            frame_lengths, normalized_frame_lengths = \
                self._GetNormalizedDeltas(timestamps, stats.refresh_period,
                                          min_normalized_frame_length)
            if len(frame_lengths) < frame_count - 1:
                logging.warning('Skipping frame lengths that are too short.')
                frame_count = len(frame_lengths) + 1
            if len(frame_lengths) == 0:
                raise Exception('No valid frames lengths found.')
            _, normalized_changes = \
                self._GetNormalizedDeltas(frame_lengths, stats.refresh_period)
            jankiness = [
                max(0, round(change)) for change in normalized_changes
            ]
            pause_threshold = 20
            jank_count = sum(1 for change in jankiness
                             if change > 0 and change < pause_threshold)
            avg_surface_fps = int(
                round((frame_count - 1) * 1000.0 / milliseconds))
            max_frame_delay = round(max(normalized_frame_lengths))
            frame_lengths = normalized_frame_lengths
        else:
            none_value_reason = NOT_ENOUGH_FRAMES_MESSAGE

        return (
            scalar.ScalarValue(
                page,
                'avg_surface_fps',
                'fps',
                avg_surface_fps,
                description='Average frames per second as measured by the '
                'platform\'s SurfaceFlinger.',
                none_value_reason=none_value_reason),
            scalar.ScalarValue(
                page,
                'jank_count',
                'janks',
                jank_count,
                description='Number of changes in frame rate as measured by the '
                'platform\'s SurfaceFlinger.',
                none_value_reason=none_value_reason),
            scalar.ScalarValue(
                page,
                'max_frame_delay',
                'vsyncs',
                max_frame_delay,
                description='Largest frame time as measured by the platform\'s '
                'SurfaceFlinger.',
                none_value_reason=none_value_reason),
            list_of_scalar_values.ListOfScalarValues(
                page,
                'frame_lengths',
                'vsyncs',
                frame_lengths,
                description=
                'Frame time in vsyncs as measured by the platform\'s '
                'SurfaceFlinger.',
                none_value_reason=none_value_reason))
Пример #11
0
    def AddResults(self, model, renderer_thread, interaction_records, results):
        self.VerifyNonOverlappedRecords(interaction_records)
        renderer_process = renderer_thread.parent
        stats = rendering_stats.RenderingStats(
            renderer_process, model.browser_process,
            [r.GetBounds() for r in interaction_records])

        input_event_latency = FlattenList(stats.input_event_latency)
        if input_event_latency:
            mean_input_event_latency = statistics.ArithmeticMean(
                input_event_latency)
            input_event_latency_discrepancy = statistics.DurationsDiscrepancy(
                input_event_latency)
            results.AddValue(
                scalar.ScalarValue(results.current_page,
                                   'mean_input_event_latency', 'ms',
                                   round(mean_input_event_latency, 3)))
            results.AddValue(
                scalar.ScalarValue(results.current_page,
                                   'input_event_latency_discrepancy', 'ms',
                                   round(input_event_latency_discrepancy, 4)))
        scroll_update_latency = FlattenList(stats.scroll_update_latency)
        if scroll_update_latency:
            mean_scroll_update_latency = statistics.ArithmeticMean(
                scroll_update_latency)
            scroll_update_latency_discrepancy = statistics.DurationsDiscrepancy(
                scroll_update_latency)
            results.AddValue(
                scalar.ScalarValue(results.current_page,
                                   'mean_scroll_update_latency', 'ms',
                                   round(mean_scroll_update_latency, 3)))
            results.AddValue(
                scalar.ScalarValue(results.current_page,
                                   'scroll_update_latency_discrepancy', 'ms',
                                   round(scroll_update_latency_discrepancy,
                                         4)))
        gesture_scroll_update_latency = FlattenList(
            stats.gesture_scroll_update_latency)
        if gesture_scroll_update_latency:
            results.AddValue(
                scalar.ScalarValue(results.current_page,
                                   'first_gesture_scroll_update_latency', 'ms',
                                   round(gesture_scroll_update_latency[0], 4)))

        # List of queueing durations.
        frame_queueing_durations = FlattenList(stats.frame_queueing_durations)
        if frame_queueing_durations:
            results.AddValue(
                list_of_scalar_values.ListOfScalarValues(
                    results.current_page, 'queueing_durations', 'ms',
                    frame_queueing_durations))

        # List of raw frame times.
        frame_times = FlattenList(stats.frame_times)
        results.AddValue(
            list_of_scalar_values.ListOfScalarValues(
                results.current_page,
                'frame_times',
                'ms',
                frame_times,
                description=
                'List of raw frame times, helpful to understand the other '
                'metrics.'))

        # Arithmetic mean of frame times.
        mean_frame_time = statistics.ArithmeticMean(frame_times)
        results.AddValue(
            scalar.ScalarValue(results.current_page,
                               'mean_frame_time',
                               'ms',
                               round(mean_frame_time, 3),
                               description='Arithmetic mean of frame times.'))

        # Absolute discrepancy of frame time stamps.
        frame_discrepancy = statistics.TimestampsDiscrepancy(
            stats.frame_timestamps)
        results.AddValue(
            scalar.ScalarValue(
                results.current_page,
                'jank',
                'ms',
                round(frame_discrepancy, 4),
                description='Absolute discrepancy of frame time stamps, where '
                'discrepancy is a measure of irregularity. It quantifies '
                'the worst jank. For a single pause, discrepancy '
                'corresponds to the length of this pause in milliseconds. '
                'Consecutive pauses increase the discrepancy. This metric '
                'is important because even if the mean and 95th '
                'percentile are good, one long pause in the middle of an '
                'interaction is still bad.'))

        # Are we hitting 60 fps for 95 percent of all frames?
        # We use 19ms as a somewhat looser threshold, instead of 1000.0/60.0.
        percentile_95 = statistics.Percentile(frame_times, 95.0)
        results.AddValue(
            scalar.ScalarValue(
                results.current_page,
                'mostly_smooth',
                'score',
                1.0 if percentile_95 < 19.0 else 0.0,
                description='Were 95 percent of the frames hitting 60 fps?'
                'boolean value (1/0).'))

        # Mean percentage of pixels approximated (missing tiles, low resolution
        # tiles, non-ideal resolution tiles).
        results.AddValue(
            scalar.ScalarValue(
                results.current_page,
                'mean_pixels_approximated',
                'percent',
                round(
                    statistics.ArithmeticMean(
                        FlattenList(stats.approximated_pixel_percentages)), 3),
                description='Percentage of pixels that were approximated '
                '(checkerboarding, low-resolution tiles, etc.).'))