Create utils module for processing task results
Here we create a special rally.benchmark.processing.utils module for computing a couple of useful functions on the benchmark data. Change-Id: I9f3e6e50b9ba37946fbb56d7546b87b30beb0233
This commit is contained in:
parent
621e89b895
commit
019eccb3d3
@ -20,6 +20,7 @@ import os
|
||||
import mako.template
|
||||
|
||||
from rally.benchmark.processing.charts import histogram as histo
|
||||
from rally.benchmark.processing import utils
|
||||
|
||||
|
||||
def _process_main_duration(result):
|
||||
@ -72,7 +73,7 @@ def _process_atomic(result):
|
||||
|
||||
def avg(lst, key=None):
|
||||
lst = lst if not key else map(lambda x: x[key], lst)
|
||||
return sum(lst) / float(len(lst))
|
||||
return utils.mean(lst)
|
||||
|
||||
# NOTE(boris-42): In our result["result"] we have next structure:
|
||||
# {"error": NoneOrDict,
|
||||
|
67
rally/benchmark/processing/utils.py
Normal file
67
rally/benchmark/processing/utils.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import math
|
||||
|
||||
from rally import exceptions
|
||||
|
||||
|
||||
def mean(values):
|
||||
"""Find the simple average of a list of values.
|
||||
|
||||
:parameter values: non-empty list of numbers
|
||||
|
||||
:returns: float value
|
||||
"""
|
||||
if not values:
|
||||
raise exceptions.InvalidArgumentsException(
|
||||
message="the list should be non-empty")
|
||||
return math.fsum(values) / len(values)
|
||||
|
||||
|
||||
def percentile(values, percent):
|
||||
"""Find the percentile of a list of values.
|
||||
|
||||
:parameter values: list of numbers
|
||||
:parameter percent: float value from 0.0 to 1.0
|
||||
|
||||
:returns: the percentile of values
|
||||
"""
|
||||
if not values:
|
||||
return None
|
||||
values.sort()
|
||||
k = (len(values) - 1) * percent
|
||||
f = math.floor(k)
|
||||
c = math.ceil(k)
|
||||
if f == c:
|
||||
return values[int(k)]
|
||||
d0 = values[int(f)] * (c - k)
|
||||
d1 = values[int(c)] * (k - f)
|
||||
return (d0 + d1)
|
||||
|
||||
|
||||
def get_durations(raw_data, get_duration, is_successful):
|
||||
"""Retrieve the benchmark duration data from a list of records.
|
||||
|
||||
:parameter raw_data: list of records
|
||||
:parameter get_duration: function that retrieves the duration data from
|
||||
a given record
|
||||
:parameter is_successful: function that returns True if the record contains
|
||||
a successful benchmark result, False otherwise
|
||||
|
||||
:returns: list of float values corresponding to benchmark durations
|
||||
"""
|
||||
data = [get_duration(run) for run in raw_data if is_successful(run)]
|
||||
return data
|
@ -19,7 +19,6 @@ from __future__ import print_function
|
||||
|
||||
import collections
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import pprint
|
||||
import prettytable
|
||||
@ -30,6 +29,7 @@ import yaml
|
||||
from oslo.config import cfg
|
||||
|
||||
from rally.benchmark.processing import plot
|
||||
from rally.benchmark.processing import utils
|
||||
from rally.cmd import cliutils
|
||||
from rally.cmd.commands import use
|
||||
from rally.cmd import envutils
|
||||
@ -156,10 +156,10 @@ class TaskCommands(object):
|
||||
atomic_action_table.add_row([k,
|
||||
len(v),
|
||||
max(v),
|
||||
sum(v) / len(v),
|
||||
utils.mean(v),
|
||||
min(v),
|
||||
percentile(v, 0.90),
|
||||
percentile(v, 0.95)])
|
||||
utils.percentile(v, 0.90),
|
||||
utils.percentile(v, 0.95)])
|
||||
print(atomic_action_table)
|
||||
print()
|
||||
|
||||
@ -210,8 +210,8 @@ class TaskCommands(object):
|
||||
_print_atomic_actions(result["data"]["raw"])
|
||||
|
||||
raw = result["data"]["raw"]
|
||||
durations = map(lambda x: x['duration'],
|
||||
filter(lambda r: not r['error'], raw))
|
||||
durations = utils.get_durations(raw, lambda x: x['duration'],
|
||||
lambda r: not r['error'])
|
||||
table = prettytable.PrettyTable(["max (sec)",
|
||||
"avg (sec)",
|
||||
"min (sec)",
|
||||
@ -221,10 +221,10 @@ class TaskCommands(object):
|
||||
"total times"])
|
||||
if durations:
|
||||
table.add_row([max(durations),
|
||||
sum(durations) / len(durations),
|
||||
utils.mean(durations),
|
||||
min(durations),
|
||||
percentile(durations, 0.90),
|
||||
percentile(durations, 0.95),
|
||||
utils.percentile(durations, 0.90),
|
||||
utils.percentile(durations, 0.95),
|
||||
float(len(durations)) / len(raw),
|
||||
len(raw)])
|
||||
else:
|
||||
@ -256,10 +256,10 @@ class TaskCommands(object):
|
||||
if values:
|
||||
row = [str(key),
|
||||
max(values),
|
||||
sum(values) / len(values),
|
||||
utils.mean(values),
|
||||
min(values),
|
||||
percentile(values, 0.90),
|
||||
percentile(values, 0.95)]
|
||||
utils.percentile(values, 0.90),
|
||||
utils.percentile(values, 0.95)]
|
||||
else:
|
||||
row = [str(key)] + ['n/a'] * 5
|
||||
ssr_table.add_row(row)
|
||||
@ -352,24 +352,3 @@ class TaskCommands(object):
|
||||
api.delete_task(tid, force=force)
|
||||
else:
|
||||
api.delete_task(task_id, force=force)
|
||||
|
||||
|
||||
def percentile(N, percent):
|
||||
"""Find the percentile of a list of values.
|
||||
|
||||
@parameter N - is a list of values.
|
||||
@parameter percent - a float value from 0.0 to 1.0.
|
||||
|
||||
@return - the percentile of the values
|
||||
"""
|
||||
if not N:
|
||||
return None
|
||||
N.sort()
|
||||
k = (len(N) - 1) * percent
|
||||
f = math.floor(k)
|
||||
c = math.ceil(k)
|
||||
if f == c:
|
||||
return N[int(k)]
|
||||
d0 = N[int(f)] * (c - k)
|
||||
d1 = N[int(c)] * (k - f)
|
||||
return (d0 + d1)
|
||||
|
36
tests/benchmark/processing/test_utils.py
Normal file
36
tests/benchmark/processing/test_utils.py
Normal file
@ -0,0 +1,36 @@
|
||||
# Copyright 2014: Mirantis Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from rally.benchmark.processing import utils
|
||||
from rally import exceptions
|
||||
from tests import test
|
||||
|
||||
|
||||
class ProcessingUtilsTestCase(test.TestCase):
|
||||
|
||||
def test_percentile(self):
|
||||
lst = range(1, 101)
|
||||
result = utils.percentile(lst, 0.1)
|
||||
self.assertTrue(result == 10.9)
|
||||
|
||||
def test_mean(self):
|
||||
lst = range(1, 100)
|
||||
result = utils.mean(lst)
|
||||
self.assertTrue(result == 50.0)
|
||||
|
||||
def test_mean_empty_list(self):
|
||||
lst = []
|
||||
self.assertRaises(exceptions.InvalidArgumentsException,
|
||||
utils.mean, lst)
|
@ -151,8 +151,3 @@ class TaskCommandsTestCase(test.TestCase):
|
||||
expected_calls = [mock.call(task_uuid, force=force) for task_uuid
|
||||
in task_uuids]
|
||||
self.assertTrue(mock_api.delete_task.mock_calls == expected_calls)
|
||||
|
||||
def test_percentile(self):
|
||||
l = range(1, 101)
|
||||
result = task.percentile(l, 0.1)
|
||||
self.assertTrue(result == 10.9)
|
||||
|
Loading…
x
Reference in New Issue
Block a user