You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
13 KiB
306 lines
13 KiB
# Copyright 2016 The Chromium OS Authors. All rights reserved. |
|
# Use of this source code is governed by a BSD-style license that can be |
|
# found in the LICENSE file. |
|
"""Computes the metrics for functions, Chrome OS components and benchmarks.""" |
|
|
|
from collections import defaultdict |
|
|
|
|
|
def ComputeDistanceForFunction(child_functions_statistics_sample, |
|
child_functions_statistics_reference): |
|
"""Computes the distance metric for a function. |
|
|
|
Args: |
|
child_functions_statistics_sample: A dict that has as a key the name of a |
|
function and as a value the inclusive count fraction. The keys are |
|
the child functions of a sample parent function. |
|
child_functions_statistics_reference: A dict that has as a key the name of |
|
a function and as a value the inclusive count fraction. The keys are |
|
the child functions of a reference parent function. |
|
|
|
Returns: |
|
A float value representing the sum of inclusive count fraction |
|
differences of pairs of common child functions. If a child function is |
|
present in a single data set, then we consider the missing inclusive |
|
count fraction as 0. This value describes the difference in behaviour |
|
between a sample and the reference parent function. |
|
""" |
|
# We initialize the distance with a small value to avoid the further |
|
# division by zero. |
|
distance = 1.0 |
|
|
|
for child_function, inclusive_count_fraction_reference in \ |
|
child_functions_statistics_reference.iteritems(): |
|
inclusive_count_fraction_sample = 0.0 |
|
|
|
if child_function in child_functions_statistics_sample: |
|
inclusive_count_fraction_sample = \ |
|
child_functions_statistics_sample[child_function] |
|
distance += \ |
|
abs(inclusive_count_fraction_sample - |
|
inclusive_count_fraction_reference) |
|
|
|
for child_function, inclusive_count_fraction_sample in \ |
|
child_functions_statistics_sample.iteritems(): |
|
if child_function not in child_functions_statistics_reference: |
|
distance += inclusive_count_fraction_sample |
|
|
|
return distance |
|
|
|
|
|
def ComputeScoreForFunction(distance, reference_fraction, sample_fraction): |
|
"""Computes the score for a function. |
|
|
|
Args: |
|
distance: A float value representing the difference in behaviour between |
|
the sample and the reference function. |
|
reference_fraction: A float value representing the inclusive count |
|
fraction of the reference function. |
|
sample_fraction: A float value representing the inclusive count |
|
fraction of the sample function. |
|
|
|
Returns: |
|
A float value representing the score of the function. |
|
""" |
|
return reference_fraction * sample_fraction / distance |
|
|
|
|
|
def ComputeMetricsForComponents(cwp_function_groups, function_metrics): |
|
"""Computes the metrics for a set of Chrome OS components. |
|
|
|
For every Chrome OS group, we compute the number of functions matching the |
|
group, the cumulative and average score, the cumulative and average distance |
|
of all those functions. A function matches a group if the path of the file |
|
containing its definition contains the common path describing the group. |
|
|
|
Args: |
|
cwp_function_groups: A dict having as a key the name of the group and as a |
|
value a common path describing the group. |
|
function_metrics: A dict having as a key the name of the function and the |
|
name of the file where it is declared concatenated by a ',', and as a |
|
value a tuple containing the distance and the score metrics. |
|
|
|
Returns: |
|
A dict containing as a key the name of the group and as a value a tuple |
|
with the group file path, the number of functions matching the group, |
|
the cumulative and average score, cumulative and average distance of all |
|
those functions. |
|
""" |
|
function_groups_metrics = defaultdict(lambda: (0, 0.0, 0.0, 0.0, 0.0)) |
|
|
|
for function_key, metric in function_metrics.iteritems(): |
|
_, function_file = function_key.split(',') |
|
|
|
for group, common_path in cwp_function_groups: |
|
if common_path not in function_file: |
|
continue |
|
|
|
function_distance = metric[0] |
|
function_score = metric[1] |
|
group_statistic = function_groups_metrics[group] |
|
|
|
function_count = group_statistic[1] + 1 |
|
function_distance_cum = function_distance + group_statistic[2] |
|
function_distance_avg = function_distance_cum / float(function_count) |
|
function_score_cum = function_score + group_statistic[4] |
|
function_score_avg = function_score_cum / float(function_count) |
|
|
|
function_groups_metrics[group] = \ |
|
(common_path, |
|
function_count, |
|
function_distance_cum, |
|
function_distance_avg, |
|
function_score_cum, |
|
function_score_avg) |
|
break |
|
|
|
return function_groups_metrics |
|
|
|
|
|
def ComputeMetricsForBenchmark(function_metrics): |
|
function_count = len(function_metrics.keys()) |
|
distance_cum = 0.0 |
|
distance_avg = 0.0 |
|
score_cum = 0.0 |
|
score_avg = 0.0 |
|
|
|
for distance, score in function_metrics.values(): |
|
distance_cum += distance |
|
score_cum += score |
|
|
|
distance_avg = distance_cum / float(function_count) |
|
score_avg = score_cum / float(function_count) |
|
return function_count, distance_cum, distance_avg, score_cum, score_avg |
|
|
|
|
|
def ComputeFunctionCountForBenchmarkSet(set_function_metrics, cwp_functions, |
|
metric_string): |
|
"""Computes the function count metric pair for the benchmark set. |
|
|
|
For the function count metric, we count the unique functions covered by the |
|
set of benchmarks. We compute the fraction of unique functions out |
|
of the amount of CWP functions given. |
|
|
|
We compute also the same metric pair for every group from the keys of the |
|
set_function_metrics dict. |
|
|
|
Args: |
|
set_function_metrics: A list of dicts having as a key the name of a group |
|
and as value a list of functions that match the given group. |
|
cwp_functions: A dict having as a key the name of the groups and as a value |
|
the list of CWP functions that match an individual group. |
|
metric_string: A tuple of strings that will be mapped to the tuple of metric |
|
values in the returned function group dict. This is done for convenience |
|
for the JSON output. |
|
|
|
Returns: |
|
A tuple with the metric pair and a dict with the group names and values |
|
of the metric pair. The first value of the metric pair represents the |
|
function count and the second value the function count fraction. |
|
The dict has as a key the name of the group and as a value a dict that |
|
maps the metric_string to the values of the metric pair of the group. |
|
""" |
|
cwp_functions_count = sum(len(functions) |
|
for functions in cwp_functions.itervalues()) |
|
set_groups_functions = defaultdict(set) |
|
for benchmark_function_metrics in set_function_metrics: |
|
for group_name in benchmark_function_metrics: |
|
set_groups_functions[group_name] |= \ |
|
set(benchmark_function_metrics[group_name]) |
|
|
|
set_groups_functions_count = {} |
|
set_functions_count = 0 |
|
for group_name, functions \ |
|
in set_groups_functions.iteritems(): |
|
set_group_functions_count = len(functions) |
|
if group_name in cwp_functions: |
|
set_groups_functions_count[group_name] = { |
|
metric_string[0]: set_group_functions_count, |
|
metric_string[1]: |
|
set_group_functions_count / float(len(cwp_functions[group_name]))} |
|
else: |
|
set_groups_functions_count[group_name] = \ |
|
{metric_string[0]: set_group_functions_count, metric_string[1]: 0.0} |
|
set_functions_count += set_group_functions_count |
|
|
|
set_functions_count_fraction = \ |
|
set_functions_count / float(cwp_functions_count) |
|
return (set_functions_count, set_functions_count_fraction), \ |
|
set_groups_functions_count |
|
|
|
|
|
def ComputeDistanceForBenchmarkSet(set_function_metrics, cwp_functions, |
|
metric_string): |
|
"""Computes the distance variation metric pair for the benchmark set. |
|
|
|
For the distance variation metric, we compute the sum of the distance |
|
variations of the functions covered by a set of benchmarks. |
|
We define the distance variation as the difference between the distance |
|
value of a functions and the ideal distance value (1.0). |
|
If a function appears in multiple common functions files, we consider |
|
only the minimum value. We compute also the distance variation per |
|
function. |
|
|
|
In addition, we compute also the same metric pair for every group from |
|
the keys of the set_function_metrics dict. |
|
|
|
Args: |
|
set_function_metrics: A list of dicts having as a key the name of a group |
|
and as value a list of functions that match the given group. |
|
cwp_functions: A dict having as a key the name of the groups and as a value |
|
the list of CWP functions that match an individual group. |
|
metric_string: A tuple of strings that will be mapped to the tuple of metric |
|
values in the returned function group dict. This is done for convenience |
|
for the JSON output. |
|
|
|
Returns: |
|
A tuple with the metric pair and a dict with the group names and values |
|
of the metric pair. The first value of the metric pair represents the |
|
distance variation per function and the second value the distance variation. |
|
The dict has as a key the name of the group and as a value a dict that |
|
maps the metric_string to the values of the metric pair of the group. |
|
""" |
|
set_unique_functions = defaultdict(lambda: defaultdict(lambda: float('inf'))) |
|
set_function_count = 0 |
|
total_distance_variation = 0.0 |
|
for benchmark_function_metrics in set_function_metrics: |
|
for group_name in benchmark_function_metrics: |
|
for function_key, metrics in \ |
|
benchmark_function_metrics[group_name].iteritems(): |
|
previous_distance = \ |
|
set_unique_functions[group_name][function_key] |
|
min_distance = min(metrics[0], previous_distance) |
|
set_unique_functions[group_name][function_key] = min_distance |
|
groups_distance_variations = defaultdict(lambda: (0.0, 0.0)) |
|
for group_name, functions_distances in set_unique_functions.iteritems(): |
|
group_function_count = len(functions_distances) |
|
group_distance_variation = \ |
|
sum(functions_distances.itervalues()) - float(group_function_count) |
|
total_distance_variation += group_distance_variation |
|
set_function_count += group_function_count |
|
groups_distance_variations[group_name] = \ |
|
{metric_string[0]: |
|
group_distance_variation / float(group_function_count), |
|
metric_string[1]: group_distance_variation} |
|
|
|
return (total_distance_variation / set_function_count, |
|
total_distance_variation), groups_distance_variations |
|
|
|
|
|
def ComputeScoreForBenchmarkSet(set_function_metrics, cwp_functions, |
|
metric_string): |
|
"""Computes the function count metric pair for the benchmark set. |
|
|
|
For the score metric, we compute the sum of the scores of the functions |
|
from a set of benchmarks. If a function appears in multiple common |
|
functions files, we consider only the maximum value. We compute also the |
|
fraction of this sum from the sum of all the scores of the functions from |
|
the CWP data covering the given groups, in the ideal case (the ideal |
|
score of a function is 1.0). |
|
|
|
In addition, we compute the same metric pair for every group from the |
|
keys of the set_function_metrics dict. |
|
|
|
Args: |
|
set_function_metrics: A list of dicts having as a key the name of a group |
|
and as value a list of functions that match the given group. |
|
cwp_functions: A dict having as a key the name of the groups and as a value |
|
the list of CWP functions that match an individual group. |
|
metric_string: A tuple of strings that will be mapped to the tuple of metric |
|
values in the returned function group dict. This is done for convenience |
|
for the JSON output. |
|
|
|
Returns: |
|
A tuple with the metric pair and a dict with the group names and values |
|
of the metric pair. The first value of the pair is the fraction of the sum |
|
of the scores from the ideal case and the second value represents the |
|
sum of scores of the functions. The dict has as a key the name of the group |
|
and as a value a dict that maps the metric_string to the values of the |
|
metric pair of the group. |
|
""" |
|
cwp_functions_count = sum(len(functions) |
|
for functions in cwp_functions.itervalues()) |
|
set_unique_functions = defaultdict(lambda: defaultdict(lambda: 0.0)) |
|
total_score = 0.0 |
|
|
|
for benchmark_function_metrics in set_function_metrics: |
|
for group_name in benchmark_function_metrics: |
|
for function_key, metrics in \ |
|
benchmark_function_metrics[group_name].iteritems(): |
|
previous_score = \ |
|
set_unique_functions[group_name][function_key] |
|
max_score = max(metrics[1], previous_score) |
|
set_unique_functions[group_name][function_key] = max_score |
|
|
|
groups_scores = defaultdict(lambda: (0.0, 0.0)) |
|
|
|
for group_name, function_scores in set_unique_functions.iteritems(): |
|
group_function_count = float(len(cwp_functions[group_name])) |
|
group_score = sum(function_scores.itervalues()) |
|
total_score += group_score |
|
groups_scores[group_name] = { |
|
metric_string[0]: group_score / group_function_count, |
|
metric_string[1]: group_score |
|
} |
|
|
|
return (total_score / cwp_functions_count, total_score), groups_scores
|
|
|