• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2013 The Chromium OS 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
5import logging
6
7from autotest_lib.server.cros.network import netperf_runner
8
9class NetperfSession(object):
10    """Abstracts a network performance measurement to reduce variance."""
11
12    MAX_DEVIATION_FRACTION = 0.03
13    MEASUREMENT_MAX_SAMPLES = 10
14    MEASUREMENT_MAX_FAILURES = 2
15    MEASUREMENT_MIN_SAMPLES = 3
16    WARMUP_SAMPLE_TIME_SECONDS = 2
17    WARMUP_WINDOW_SIZE = 2
18    WARMUP_MAX_SAMPLES = 10
19
20
21    @staticmethod
22    def _from_samples(samples):
23        """Construct a NetperfResult that averages previous results.
24
25        This makes typing this considerably easier.
26
27        @param samples: list of NetperfResult
28        @return NetperfResult average of samples.
29
30        """
31        return netperf_runner.NetperfResult.from_samples(samples)
32
33
34    def __init__(self, client_proxy, server_proxy, ignore_failures=False):
35        """Construct a NetperfSession.
36
37        @param client_proxy: WiFiClient object.
38        @param server_proxy: LinuxSystem object.
39
40        """
41        self._client_proxy = client_proxy
42        self._server_proxy = server_proxy
43        self._ignore_failures = ignore_failures
44
45
46    def warmup_wifi_part(self, warmup_client=True):
47        """Warm up a rate controller on the client or server.
48
49        WiFi "warms up" in that rate controllers dynamically adjust to
50        environmental conditions by increasing symbol rates until loss is
51        observed.  This manifests as initially slow data transfer rates that
52        get better over time.
53
54        We'll say that a rate controller is warmed up if a small sample of
55        WARMUP_WINDOW_SIZE throughput measurements has an average throughput
56        within a standard deviation of the previous WARMUP_WINDOW_SIZE samples.
57
58        @param warmup_client: bool True iff we should warmup the client rate
59                controller.  Otherwise we warm up the server rate controller.
60
61        """
62        if warmup_client:
63            # We say a station is warm if the TX throughput is maximized.
64            # Each station only controls its own transmission TX rate.
65            logging.info('Warming up the client WiFi rate controller.')
66            test_type = netperf_runner.NetperfConfig.TEST_TYPE_TCP_STREAM
67        else:
68            logging.info('Warming up the server WiFi rate controller.')
69            test_type = netperf_runner.NetperfConfig.TEST_TYPE_TCP_MAERTS
70        config = netperf_runner.NetperfConfig(
71                test_type, test_time=self.WARMUP_SAMPLE_TIME_SECONDS)
72        warmup_history = []
73        with netperf_runner.NetperfRunner(
74                self._client_proxy, self._server_proxy, config) as runner:
75            while len(warmup_history) < self.WARMUP_MAX_SAMPLES:
76                warmup_history.append(runner.run())
77                if len(warmup_history) > 2 * self.WARMUP_WINDOW_SIZE:
78                    # Grab 2 * WARMUP_WINDOW_SIZE samples, divided into the most
79                    # recent chunk and the chunk before that.
80                    start = -2 * self.WARMUP_WINDOW_SIZE
81                    middle = -self.WARMUP_WINDOW_SIZE
82                    past_result = self._from_samples(
83                            warmup_history[start:middle])
84                    recent_result = self._from_samples(warmup_history[middle:])
85                    if recent_result.throughput < (past_result.throughput +
86                                                   past_result.throughput_dev):
87                        logging.info('Rate controller is warmed.')
88                        return
89            else:
90                logging.warning('Did not completely warmup the WiFi part.')
91
92
93    def warmup_stations(self):
94        """Warms up both the client and server stations."""
95        self.warmup_wifi_part(warmup_client=True)
96        self.warmup_wifi_part(warmup_client=False)
97
98
99    def run(self, config):
100        """Measure the average and standard deviation of a netperf test.
101
102        @param config: NetperfConfig object.
103
104        """
105        logging.info('Performing %s measurements in netperf session.',
106                     config.human_readable_tag)
107        history = []
108        none_count = 0
109        final_result = None
110        with netperf_runner.NetperfRunner(
111                self._client_proxy, self._server_proxy, config) as runner:
112            while len(history) + none_count < self.MEASUREMENT_MAX_SAMPLES:
113                result = runner.run(ignore_failures=self._ignore_failures)
114                if result is None:
115                    none_count += 1
116                    # Might occur when, e.g., signal strength is too low.
117                    if none_count > self.MEASUREMENT_MAX_FAILURES:
118                        logging.error('Too many failures (%d), aborting',
119                                      none_count)
120                        break
121                    continue
122
123                history.append(result)
124                if len(history) < self.MEASUREMENT_MIN_SAMPLES:
125                    continue
126
127                final_result = self._from_samples(history)
128                if final_result.all_deviations_less_than_fraction(
129                        self.MAX_DEVIATION_FRACTION):
130                    break
131
132        if final_result is None:
133            final_result = self._from_samples(history)
134        logging.info('Took averaged measurement %r.', final_result)
135        return history or None
136