mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-02-28 15:58:43 +00:00
Add dumpsys_period parameter to fps instrument
Add a new parameter to fps instrument to specify the time period between calls to ``dumpsys SurfaceFlinger --latency`` in seconds when collecting frame data. A lower value improves the granularity of timings when recording actions for UX Performance metrics.
This commit is contained in:
parent
aa2d187c4d
commit
625a3a39a5
@ -111,6 +111,18 @@ class FpsInstrument(Instrument):
|
|||||||
a content crash. E.g. a value of ``0.75`` means the number of actual frames counted is a
|
a content crash. E.g. a value of ``0.75`` means the number of actual frames counted is a
|
||||||
quarter lower than expected, it will treated as a content crash.
|
quarter lower than expected, it will treated as a content crash.
|
||||||
"""),
|
"""),
|
||||||
|
Parameter('dumpsys_period', kind=float, default=2, constraint=lambda x: x > 0,
|
||||||
|
description="""
|
||||||
|
Specifies the time period between calls to ``dumpsys SurfaceFlinger --latency`` in
|
||||||
|
seconds when collecting frame data. Using a lower value improves the granularity
|
||||||
|
of timings when recording actions that take a short time to complete. Note, this
|
||||||
|
will produce duplicate frame data in the raw dumpsys output, however, this is
|
||||||
|
filtered out in frames.csv. It may also affect the overall load on the system.
|
||||||
|
|
||||||
|
The default value of 2 seconds corresponds with the NUM_FRAME_RECORDS in
|
||||||
|
android/services/surfaceflinger/FrameTracker.h (as of the time of writing
|
||||||
|
currently 128) and a frame rate of 60 fps that is applicable to most devices.
|
||||||
|
"""),
|
||||||
]
|
]
|
||||||
|
|
||||||
clear_command = 'dumpsys SurfaceFlinger --latency-clear '
|
clear_command = 'dumpsys SurfaceFlinger --latency-clear '
|
||||||
@ -135,7 +147,8 @@ class FpsInstrument(Instrument):
|
|||||||
if hasattr(workload, 'view'):
|
if hasattr(workload, 'view'):
|
||||||
self.fps_outfile = os.path.join(context.output_directory, 'fps.csv')
|
self.fps_outfile = os.path.join(context.output_directory, 'fps.csv')
|
||||||
self.outfile = os.path.join(context.output_directory, 'frames.csv')
|
self.outfile = os.path.join(context.output_directory, 'frames.csv')
|
||||||
self.collector = LatencyCollector(self.outfile, self.device, workload.view or '', self.keep_raw, self.logger)
|
self.collector = LatencyCollector(self.outfile, self.device, workload.view or '',
|
||||||
|
self.keep_raw, self.logger, self.dumpsys_period)
|
||||||
self.device.execute(self.clear_command)
|
self.device.execute(self.clear_command)
|
||||||
else:
|
else:
|
||||||
self.logger.debug('Workload does not contain a view; disabling...')
|
self.logger.debug('Workload does not contain a view; disabling...')
|
||||||
@ -153,21 +166,22 @@ class FpsInstrument(Instrument):
|
|||||||
|
|
||||||
def update_result(self, context):
|
def update_result(self, context):
|
||||||
if self.is_enabled:
|
if self.is_enabled:
|
||||||
|
fps, frame_count, janks, not_at_vsync = float('nan'), 0, 0, 0
|
||||||
data = pd.read_csv(self.outfile)
|
data = pd.read_csv(self.outfile)
|
||||||
if not data.empty: # pylint: disable=maybe-no-member
|
if not data.empty: # pylint: disable=maybe-no-member
|
||||||
fp = FpsProcessor(data)
|
fp = FpsProcessor(data)
|
||||||
per_frame_fps, metrics = fp.process(self.collector.refresh_period, self.drop_threshold)
|
per_frame_fps, metrics = fp.process(self.collector.refresh_period, self.drop_threshold)
|
||||||
fps, frame_count, janks, not_at_vsync = metrics
|
fps, frame_count, janks, not_at_vsync = metrics
|
||||||
|
|
||||||
context.result.add_metric('FPS', fps)
|
|
||||||
context.result.add_metric('frame_count', frame_count)
|
|
||||||
context.result.add_metric('janks', janks)
|
|
||||||
context.result.add_metric('not_at_vsync', not_at_vsync)
|
|
||||||
|
|
||||||
if self.generate_csv:
|
if self.generate_csv:
|
||||||
per_frame_fps.to_csv(self.fps_outfile, index=False, header=True)
|
per_frame_fps.to_csv(self.fps_outfile, index=False, header=True)
|
||||||
context.add_artifact('fps', path='fps.csv', kind='data')
|
context.add_artifact('fps', path='fps.csv', kind='data')
|
||||||
|
|
||||||
|
context.result.add_metric('FPS', fps)
|
||||||
|
context.result.add_metric('frame_count', frame_count)
|
||||||
|
context.result.add_metric('janks', janks)
|
||||||
|
context.result.add_metric('not_at_vsync', not_at_vsync)
|
||||||
|
|
||||||
def slow_update_result(self, context):
|
def slow_update_result(self, context):
|
||||||
result = context.result
|
result = context.result
|
||||||
if self.crash_check and result.has_metric('execution_time'):
|
if self.crash_check and result.has_metric('execution_time'):
|
||||||
@ -192,16 +206,17 @@ class LatencyCollector(threading.Thread):
|
|||||||
# At the time of writing, this was hard-coded to 128. So at 60 fps
|
# At the time of writing, this was hard-coded to 128. So at 60 fps
|
||||||
# (and there is no reason to go above that, as it matches vsync rate
|
# (and there is no reason to go above that, as it matches vsync rate
|
||||||
# on pretty much all phones), there is just over 2 seconds' worth of
|
# on pretty much all phones), there is just over 2 seconds' worth of
|
||||||
# frames in there. Hence the sleep time of 2 seconds between dumps.
|
# frames in there. Hence the default sleep time of 2 seconds between dumps.
|
||||||
#command_template = 'while (true); do dumpsys SurfaceFlinger --latency {}; sleep 2; done'
|
#command_template = 'while (true); do dumpsys SurfaceFlinger --latency {}; sleep 2; done'
|
||||||
command_template = 'dumpsys SurfaceFlinger --latency {}'
|
command_template = 'dumpsys SurfaceFlinger --latency {}'
|
||||||
|
|
||||||
def __init__(self, outfile, device, activities, keep_raw, logger):
|
def __init__(self, outfile, device, activities, keep_raw, logger, dumpsys_period):
|
||||||
super(LatencyCollector, self).__init__()
|
super(LatencyCollector, self).__init__()
|
||||||
self.outfile = outfile
|
self.outfile = outfile
|
||||||
self.device = device
|
self.device = device
|
||||||
self.keep_raw = keep_raw
|
self.keep_raw = keep_raw
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
self.dumpsys_period = dumpsys_period
|
||||||
self.stop_signal = threading.Event()
|
self.stop_signal = threading.Event()
|
||||||
self.frames = []
|
self.frames = []
|
||||||
self.last_ready_time = 0
|
self.last_ready_time = 0
|
||||||
@ -226,7 +241,7 @@ class LatencyCollector(threading.Thread):
|
|||||||
for activity in self.activities:
|
for activity in self.activities:
|
||||||
if activity in view_list:
|
if activity in view_list:
|
||||||
wfh.write(self.device.execute(self.command_template.format(activity)))
|
wfh.write(self.device.execute(self.command_template.format(activity)))
|
||||||
time.sleep(2)
|
time.sleep(self.dumpsys_period)
|
||||||
finally:
|
finally:
|
||||||
wfh.close()
|
wfh.close()
|
||||||
# TODO: this can happen after the run during results processing
|
# TODO: this can happen after the run during results processing
|
||||||
|
Loading…
x
Reference in New Issue
Block a user