17db96d56Sopenharmony_ci# This file should be kept compatible with both Python 2.6 and Python >= 3.0.
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_cifrom __future__ import division
47db96d56Sopenharmony_cifrom __future__ import print_function
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ci"""
77db96d56Sopenharmony_ciccbench, a Python concurrency benchmark.
87db96d56Sopenharmony_ci"""
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ciimport time
117db96d56Sopenharmony_ciimport os
127db96d56Sopenharmony_ciimport sys
137db96d56Sopenharmony_ciimport itertools
147db96d56Sopenharmony_ciimport threading
157db96d56Sopenharmony_ciimport subprocess
167db96d56Sopenharmony_ciimport socket
177db96d56Sopenharmony_cifrom optparse import OptionParser, SUPPRESS_HELP
187db96d56Sopenharmony_ciimport platform
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_ci# Compatibility
217db96d56Sopenharmony_citry:
227db96d56Sopenharmony_ci    xrange
237db96d56Sopenharmony_ciexcept NameError:
247db96d56Sopenharmony_ci    xrange = range
257db96d56Sopenharmony_ci
267db96d56Sopenharmony_citry:
277db96d56Sopenharmony_ci    map = itertools.imap
287db96d56Sopenharmony_ciexcept AttributeError:
297db96d56Sopenharmony_ci    pass
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ciTHROUGHPUT_DURATION = 2.0
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ciLATENCY_PING_INTERVAL = 0.1
357db96d56Sopenharmony_ciLATENCY_DURATION = 2.0
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ciBANDWIDTH_PACKET_SIZE = 1024
387db96d56Sopenharmony_ciBANDWIDTH_DURATION = 2.0
397db96d56Sopenharmony_ci
407db96d56Sopenharmony_ci
417db96d56Sopenharmony_cidef task_pidigits():
427db96d56Sopenharmony_ci    """Pi calculation (Python)"""
437db96d56Sopenharmony_ci    _map = map
447db96d56Sopenharmony_ci    _count = itertools.count
457db96d56Sopenharmony_ci    _islice = itertools.islice
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ci    def calc_ndigits(n):
487db96d56Sopenharmony_ci        # From http://shootout.alioth.debian.org/
497db96d56Sopenharmony_ci        def gen_x():
507db96d56Sopenharmony_ci            return _map(lambda k: (k, 4*k + 2, 0, 2*k + 1), _count(1))
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci        def compose(a, b):
537db96d56Sopenharmony_ci            aq, ar, as_, at = a
547db96d56Sopenharmony_ci            bq, br, bs, bt = b
557db96d56Sopenharmony_ci            return (aq * bq,
567db96d56Sopenharmony_ci                    aq * br + ar * bt,
577db96d56Sopenharmony_ci                    as_ * bq + at * bs,
587db96d56Sopenharmony_ci                    as_ * br + at * bt)
597db96d56Sopenharmony_ci
607db96d56Sopenharmony_ci        def extract(z, j):
617db96d56Sopenharmony_ci            q, r, s, t = z
627db96d56Sopenharmony_ci            return (q*j + r) // (s*j + t)
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_ci        def pi_digits():
657db96d56Sopenharmony_ci            z = (1, 0, 0, 1)
667db96d56Sopenharmony_ci            x = gen_x()
677db96d56Sopenharmony_ci            while 1:
687db96d56Sopenharmony_ci                y = extract(z, 3)
697db96d56Sopenharmony_ci                while y != extract(z, 4):
707db96d56Sopenharmony_ci                    z = compose(z, next(x))
717db96d56Sopenharmony_ci                    y = extract(z, 3)
727db96d56Sopenharmony_ci                z = compose((10, -10*y, 0, 1), z)
737db96d56Sopenharmony_ci                yield y
747db96d56Sopenharmony_ci
757db96d56Sopenharmony_ci        return list(_islice(pi_digits(), n))
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_ci    return calc_ndigits, (50, )
787db96d56Sopenharmony_ci
797db96d56Sopenharmony_cidef task_regex():
807db96d56Sopenharmony_ci    """regular expression (C)"""
817db96d56Sopenharmony_ci    # XXX this task gives horrendous latency results.
827db96d56Sopenharmony_ci    import re
837db96d56Sopenharmony_ci    # Taken from the `inspect` module
847db96d56Sopenharmony_ci    pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)', re.MULTILINE)
857db96d56Sopenharmony_ci    with open(__file__, "r") as f:
867db96d56Sopenharmony_ci        arg = f.read(2000)
877db96d56Sopenharmony_ci    return pat.findall, (arg, )
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_cidef task_sort():
907db96d56Sopenharmony_ci    """list sorting (C)"""
917db96d56Sopenharmony_ci    def list_sort(l):
927db96d56Sopenharmony_ci        l = l[::-1]
937db96d56Sopenharmony_ci        l.sort()
947db96d56Sopenharmony_ci
957db96d56Sopenharmony_ci    return list_sort, (list(range(1000)), )
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_cidef task_compress_zlib():
987db96d56Sopenharmony_ci    """zlib compression (C)"""
997db96d56Sopenharmony_ci    import zlib
1007db96d56Sopenharmony_ci    with open(__file__, "rb") as f:
1017db96d56Sopenharmony_ci        arg = f.read(5000) * 3
1027db96d56Sopenharmony_ci
1037db96d56Sopenharmony_ci    def compress(s):
1047db96d56Sopenharmony_ci        zlib.decompress(zlib.compress(s, 5))
1057db96d56Sopenharmony_ci    return compress, (arg, )
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_cidef task_compress_bz2():
1087db96d56Sopenharmony_ci    """bz2 compression (C)"""
1097db96d56Sopenharmony_ci    import bz2
1107db96d56Sopenharmony_ci    with open(__file__, "rb") as f:
1117db96d56Sopenharmony_ci        arg = f.read(3000) * 2
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_ci    def compress(s):
1147db96d56Sopenharmony_ci        bz2.compress(s)
1157db96d56Sopenharmony_ci    return compress, (arg, )
1167db96d56Sopenharmony_ci
1177db96d56Sopenharmony_cidef task_hashing():
1187db96d56Sopenharmony_ci    """SHA1 hashing (C)"""
1197db96d56Sopenharmony_ci    import hashlib
1207db96d56Sopenharmony_ci    with open(__file__, "rb") as f:
1217db96d56Sopenharmony_ci        arg = f.read(5000) * 30
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci    def compute(s):
1247db96d56Sopenharmony_ci        hashlib.sha1(s).digest()
1257db96d56Sopenharmony_ci    return compute, (arg, )
1267db96d56Sopenharmony_ci
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_cithroughput_tasks = [task_pidigits, task_regex]
1297db96d56Sopenharmony_cifor mod in 'bz2', 'hashlib':
1307db96d56Sopenharmony_ci    try:
1317db96d56Sopenharmony_ci        globals()[mod] = __import__(mod)
1327db96d56Sopenharmony_ci    except ImportError:
1337db96d56Sopenharmony_ci        globals()[mod] = None
1347db96d56Sopenharmony_ci
1357db96d56Sopenharmony_ci# For whatever reasons, zlib gives irregular results, so we prefer bz2 or
1367db96d56Sopenharmony_ci# hashlib if available.
1377db96d56Sopenharmony_ci# (NOTE: hashlib releases the GIL from 2.7 and 3.1 onwards)
1387db96d56Sopenharmony_ciif bz2 is not None:
1397db96d56Sopenharmony_ci    throughput_tasks.append(task_compress_bz2)
1407db96d56Sopenharmony_cielif hashlib is not None:
1417db96d56Sopenharmony_ci    throughput_tasks.append(task_hashing)
1427db96d56Sopenharmony_cielse:
1437db96d56Sopenharmony_ci    throughput_tasks.append(task_compress_zlib)
1447db96d56Sopenharmony_ci
1457db96d56Sopenharmony_cilatency_tasks = throughput_tasks
1467db96d56Sopenharmony_cibandwidth_tasks = [task_pidigits]
1477db96d56Sopenharmony_ci
1487db96d56Sopenharmony_ci
1497db96d56Sopenharmony_ciclass TimedLoop:
1507db96d56Sopenharmony_ci    def __init__(self, func, args):
1517db96d56Sopenharmony_ci        self.func = func
1527db96d56Sopenharmony_ci        self.args = args
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci    def __call__(self, start_time, min_duration, end_event, do_yield=False):
1557db96d56Sopenharmony_ci        step = 20
1567db96d56Sopenharmony_ci        niters = 0
1577db96d56Sopenharmony_ci        duration = 0.0
1587db96d56Sopenharmony_ci        _time = time.time
1597db96d56Sopenharmony_ci        _sleep = time.sleep
1607db96d56Sopenharmony_ci        _func = self.func
1617db96d56Sopenharmony_ci        _args = self.args
1627db96d56Sopenharmony_ci        t1 = start_time
1637db96d56Sopenharmony_ci        while True:
1647db96d56Sopenharmony_ci            for i in range(step):
1657db96d56Sopenharmony_ci                _func(*_args)
1667db96d56Sopenharmony_ci            t2 = _time()
1677db96d56Sopenharmony_ci            # If another thread terminated, the current measurement is invalid
1687db96d56Sopenharmony_ci            # => return the previous one.
1697db96d56Sopenharmony_ci            if end_event:
1707db96d56Sopenharmony_ci                return niters, duration
1717db96d56Sopenharmony_ci            niters += step
1727db96d56Sopenharmony_ci            duration = t2 - start_time
1737db96d56Sopenharmony_ci            if duration >= min_duration:
1747db96d56Sopenharmony_ci                end_event.append(None)
1757db96d56Sopenharmony_ci                return niters, duration
1767db96d56Sopenharmony_ci            if t2 - t1 < 0.01:
1777db96d56Sopenharmony_ci                # Minimize interference of measurement on overall runtime
1787db96d56Sopenharmony_ci                step = step * 3 // 2
1797db96d56Sopenharmony_ci            elif do_yield:
1807db96d56Sopenharmony_ci                # OS scheduling of Python threads is sometimes so bad that we
1817db96d56Sopenharmony_ci                # have to force thread switching ourselves, otherwise we get
1827db96d56Sopenharmony_ci                # completely useless results.
1837db96d56Sopenharmony_ci                _sleep(0.0001)
1847db96d56Sopenharmony_ci            t1 = t2
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_cidef run_throughput_test(func, args, nthreads):
1887db96d56Sopenharmony_ci    assert nthreads >= 1
1897db96d56Sopenharmony_ci
1907db96d56Sopenharmony_ci    # Warm up
1917db96d56Sopenharmony_ci    func(*args)
1927db96d56Sopenharmony_ci
1937db96d56Sopenharmony_ci    results = []
1947db96d56Sopenharmony_ci    loop = TimedLoop(func, args)
1957db96d56Sopenharmony_ci    end_event = []
1967db96d56Sopenharmony_ci
1977db96d56Sopenharmony_ci    if nthreads == 1:
1987db96d56Sopenharmony_ci        # Pure single-threaded performance, without any switching or
1997db96d56Sopenharmony_ci        # synchronization overhead.
2007db96d56Sopenharmony_ci        start_time = time.time()
2017db96d56Sopenharmony_ci        results.append(loop(start_time, THROUGHPUT_DURATION,
2027db96d56Sopenharmony_ci                            end_event, do_yield=False))
2037db96d56Sopenharmony_ci        return results
2047db96d56Sopenharmony_ci
2057db96d56Sopenharmony_ci    started = False
2067db96d56Sopenharmony_ci    ready_cond = threading.Condition()
2077db96d56Sopenharmony_ci    start_cond = threading.Condition()
2087db96d56Sopenharmony_ci    ready = []
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_ci    def run():
2117db96d56Sopenharmony_ci        with ready_cond:
2127db96d56Sopenharmony_ci            ready.append(None)
2137db96d56Sopenharmony_ci            ready_cond.notify()
2147db96d56Sopenharmony_ci        with start_cond:
2157db96d56Sopenharmony_ci            while not started:
2167db96d56Sopenharmony_ci                start_cond.wait()
2177db96d56Sopenharmony_ci        results.append(loop(start_time, THROUGHPUT_DURATION,
2187db96d56Sopenharmony_ci                            end_event, do_yield=True))
2197db96d56Sopenharmony_ci
2207db96d56Sopenharmony_ci    threads = []
2217db96d56Sopenharmony_ci    for i in range(nthreads):
2227db96d56Sopenharmony_ci        threads.append(threading.Thread(target=run))
2237db96d56Sopenharmony_ci    for t in threads:
2247db96d56Sopenharmony_ci        t.daemon = True
2257db96d56Sopenharmony_ci        t.start()
2267db96d56Sopenharmony_ci    # We don't want measurements to include thread startup overhead,
2277db96d56Sopenharmony_ci    # so we arrange for timing to start after all threads are ready.
2287db96d56Sopenharmony_ci    with ready_cond:
2297db96d56Sopenharmony_ci        while len(ready) < nthreads:
2307db96d56Sopenharmony_ci            ready_cond.wait()
2317db96d56Sopenharmony_ci    with start_cond:
2327db96d56Sopenharmony_ci        start_time = time.time()
2337db96d56Sopenharmony_ci        started = True
2347db96d56Sopenharmony_ci        start_cond.notify(nthreads)
2357db96d56Sopenharmony_ci    for t in threads:
2367db96d56Sopenharmony_ci        t.join()
2377db96d56Sopenharmony_ci
2387db96d56Sopenharmony_ci    return results
2397db96d56Sopenharmony_ci
2407db96d56Sopenharmony_cidef run_throughput_tests(max_threads):
2417db96d56Sopenharmony_ci    for task in throughput_tasks:
2427db96d56Sopenharmony_ci        print(task.__doc__)
2437db96d56Sopenharmony_ci        print()
2447db96d56Sopenharmony_ci        func, args = task()
2457db96d56Sopenharmony_ci        nthreads = 1
2467db96d56Sopenharmony_ci        baseline_speed = None
2477db96d56Sopenharmony_ci        while nthreads <= max_threads:
2487db96d56Sopenharmony_ci            results = run_throughput_test(func, args, nthreads)
2497db96d56Sopenharmony_ci            # Taking the max duration rather than average gives pessimistic
2507db96d56Sopenharmony_ci            # results rather than optimistic.
2517db96d56Sopenharmony_ci            speed = sum(r[0] for r in results) / max(r[1] for r in results)
2527db96d56Sopenharmony_ci            print("threads=%d: %d" % (nthreads, speed), end="")
2537db96d56Sopenharmony_ci            if baseline_speed is None:
2547db96d56Sopenharmony_ci                print(" iterations/s.")
2557db96d56Sopenharmony_ci                baseline_speed = speed
2567db96d56Sopenharmony_ci            else:
2577db96d56Sopenharmony_ci                print(" ( %d %%)" % (speed / baseline_speed * 100))
2587db96d56Sopenharmony_ci            nthreads += 1
2597db96d56Sopenharmony_ci        print()
2607db96d56Sopenharmony_ci
2617db96d56Sopenharmony_ci
2627db96d56Sopenharmony_ciLAT_END = "END"
2637db96d56Sopenharmony_ci
2647db96d56Sopenharmony_cidef _sendto(sock, s, addr):
2657db96d56Sopenharmony_ci    sock.sendto(s.encode('ascii'), addr)
2667db96d56Sopenharmony_ci
2677db96d56Sopenharmony_cidef _recv(sock, n):
2687db96d56Sopenharmony_ci    return sock.recv(n).decode('ascii')
2697db96d56Sopenharmony_ci
2707db96d56Sopenharmony_cidef latency_client(addr, nb_pings, interval):
2717db96d56Sopenharmony_ci    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2727db96d56Sopenharmony_ci    try:
2737db96d56Sopenharmony_ci        _time = time.time
2747db96d56Sopenharmony_ci        _sleep = time.sleep
2757db96d56Sopenharmony_ci        def _ping():
2767db96d56Sopenharmony_ci            _sendto(sock, "%r\n" % _time(), addr)
2777db96d56Sopenharmony_ci        # The first ping signals the parent process that we are ready.
2787db96d56Sopenharmony_ci        _ping()
2797db96d56Sopenharmony_ci        # We give the parent a bit of time to notice.
2807db96d56Sopenharmony_ci        _sleep(1.0)
2817db96d56Sopenharmony_ci        for i in range(nb_pings):
2827db96d56Sopenharmony_ci            _sleep(interval)
2837db96d56Sopenharmony_ci            _ping()
2847db96d56Sopenharmony_ci        _sendto(sock, LAT_END + "\n", addr)
2857db96d56Sopenharmony_ci    finally:
2867db96d56Sopenharmony_ci        sock.close()
2877db96d56Sopenharmony_ci
2887db96d56Sopenharmony_cidef run_latency_client(**kwargs):
2897db96d56Sopenharmony_ci    cmd_line = [sys.executable, '-E', os.path.abspath(__file__)]
2907db96d56Sopenharmony_ci    cmd_line.extend(['--latclient', repr(kwargs)])
2917db96d56Sopenharmony_ci    return subprocess.Popen(cmd_line) #, stdin=subprocess.PIPE,
2927db96d56Sopenharmony_ci                            #stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
2937db96d56Sopenharmony_ci
2947db96d56Sopenharmony_cidef run_latency_test(func, args, nthreads):
2957db96d56Sopenharmony_ci    # Create a listening socket to receive the pings. We use UDP which should
2967db96d56Sopenharmony_ci    # be painlessly cross-platform.
2977db96d56Sopenharmony_ci    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2987db96d56Sopenharmony_ci    sock.bind(("127.0.0.1", 0))
2997db96d56Sopenharmony_ci    addr = sock.getsockname()
3007db96d56Sopenharmony_ci
3017db96d56Sopenharmony_ci    interval = LATENCY_PING_INTERVAL
3027db96d56Sopenharmony_ci    duration = LATENCY_DURATION
3037db96d56Sopenharmony_ci    nb_pings = int(duration / interval)
3047db96d56Sopenharmony_ci
3057db96d56Sopenharmony_ci    results = []
3067db96d56Sopenharmony_ci    threads = []
3077db96d56Sopenharmony_ci    end_event = []
3087db96d56Sopenharmony_ci    start_cond = threading.Condition()
3097db96d56Sopenharmony_ci    started = False
3107db96d56Sopenharmony_ci    if nthreads > 0:
3117db96d56Sopenharmony_ci        # Warm up
3127db96d56Sopenharmony_ci        func(*args)
3137db96d56Sopenharmony_ci
3147db96d56Sopenharmony_ci        results = []
3157db96d56Sopenharmony_ci        loop = TimedLoop(func, args)
3167db96d56Sopenharmony_ci        ready = []
3177db96d56Sopenharmony_ci        ready_cond = threading.Condition()
3187db96d56Sopenharmony_ci
3197db96d56Sopenharmony_ci        def run():
3207db96d56Sopenharmony_ci            with ready_cond:
3217db96d56Sopenharmony_ci                ready.append(None)
3227db96d56Sopenharmony_ci                ready_cond.notify()
3237db96d56Sopenharmony_ci            with start_cond:
3247db96d56Sopenharmony_ci                while not started:
3257db96d56Sopenharmony_ci                    start_cond.wait()
3267db96d56Sopenharmony_ci            loop(start_time, duration * 1.5, end_event, do_yield=False)
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ci        for i in range(nthreads):
3297db96d56Sopenharmony_ci            threads.append(threading.Thread(target=run))
3307db96d56Sopenharmony_ci        for t in threads:
3317db96d56Sopenharmony_ci            t.daemon = True
3327db96d56Sopenharmony_ci            t.start()
3337db96d56Sopenharmony_ci        # Wait for threads to be ready
3347db96d56Sopenharmony_ci        with ready_cond:
3357db96d56Sopenharmony_ci            while len(ready) < nthreads:
3367db96d56Sopenharmony_ci                ready_cond.wait()
3377db96d56Sopenharmony_ci
3387db96d56Sopenharmony_ci    # Run the client and wait for the first ping(s) to arrive before
3397db96d56Sopenharmony_ci    # unblocking the background threads.
3407db96d56Sopenharmony_ci    chunks = []
3417db96d56Sopenharmony_ci    process = run_latency_client(addr=sock.getsockname(),
3427db96d56Sopenharmony_ci                                 nb_pings=nb_pings, interval=interval)
3437db96d56Sopenharmony_ci    s = _recv(sock, 4096)
3447db96d56Sopenharmony_ci    _time = time.time
3457db96d56Sopenharmony_ci
3467db96d56Sopenharmony_ci    with start_cond:
3477db96d56Sopenharmony_ci        start_time = _time()
3487db96d56Sopenharmony_ci        started = True
3497db96d56Sopenharmony_ci        start_cond.notify(nthreads)
3507db96d56Sopenharmony_ci
3517db96d56Sopenharmony_ci    while LAT_END not in s:
3527db96d56Sopenharmony_ci        s = _recv(sock, 4096)
3537db96d56Sopenharmony_ci        t = _time()
3547db96d56Sopenharmony_ci        chunks.append((t, s))
3557db96d56Sopenharmony_ci
3567db96d56Sopenharmony_ci    # Tell the background threads to stop.
3577db96d56Sopenharmony_ci    end_event.append(None)
3587db96d56Sopenharmony_ci    for t in threads:
3597db96d56Sopenharmony_ci        t.join()
3607db96d56Sopenharmony_ci    process.wait()
3617db96d56Sopenharmony_ci    sock.close()
3627db96d56Sopenharmony_ci
3637db96d56Sopenharmony_ci    for recv_time, chunk in chunks:
3647db96d56Sopenharmony_ci        # NOTE: it is assumed that a line sent by a client wasn't received
3657db96d56Sopenharmony_ci        # in two chunks because the lines are very small.
3667db96d56Sopenharmony_ci        for line in chunk.splitlines():
3677db96d56Sopenharmony_ci            line = line.strip()
3687db96d56Sopenharmony_ci            if line and line != LAT_END:
3697db96d56Sopenharmony_ci                send_time = eval(line)
3707db96d56Sopenharmony_ci                assert isinstance(send_time, float)
3717db96d56Sopenharmony_ci                results.append((send_time, recv_time))
3727db96d56Sopenharmony_ci
3737db96d56Sopenharmony_ci    return results
3747db96d56Sopenharmony_ci
3757db96d56Sopenharmony_cidef run_latency_tests(max_threads):
3767db96d56Sopenharmony_ci    for task in latency_tasks:
3777db96d56Sopenharmony_ci        print("Background CPU task:", task.__doc__)
3787db96d56Sopenharmony_ci        print()
3797db96d56Sopenharmony_ci        func, args = task()
3807db96d56Sopenharmony_ci        nthreads = 0
3817db96d56Sopenharmony_ci        while nthreads <= max_threads:
3827db96d56Sopenharmony_ci            results = run_latency_test(func, args, nthreads)
3837db96d56Sopenharmony_ci            n = len(results)
3847db96d56Sopenharmony_ci            # We print out milliseconds
3857db96d56Sopenharmony_ci            lats = [1000 * (t2 - t1) for (t1, t2) in results]
3867db96d56Sopenharmony_ci            #print(list(map(int, lats)))
3877db96d56Sopenharmony_ci            avg = sum(lats) / n
3887db96d56Sopenharmony_ci            dev = (sum((x - avg) ** 2 for x in lats) / n) ** 0.5
3897db96d56Sopenharmony_ci            print("CPU threads=%d: %d ms. (std dev: %d ms.)" % (nthreads, avg, dev), end="")
3907db96d56Sopenharmony_ci            print()
3917db96d56Sopenharmony_ci            #print("    [... from %d samples]" % n)
3927db96d56Sopenharmony_ci            nthreads += 1
3937db96d56Sopenharmony_ci        print()
3947db96d56Sopenharmony_ci
3957db96d56Sopenharmony_ci
3967db96d56Sopenharmony_ciBW_END = "END"
3977db96d56Sopenharmony_ci
3987db96d56Sopenharmony_cidef bandwidth_client(addr, packet_size, duration):
3997db96d56Sopenharmony_ci    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
4007db96d56Sopenharmony_ci    sock.bind(("127.0.0.1", 0))
4017db96d56Sopenharmony_ci    local_addr = sock.getsockname()
4027db96d56Sopenharmony_ci    _time = time.time
4037db96d56Sopenharmony_ci    _sleep = time.sleep
4047db96d56Sopenharmony_ci    def _send_chunk(msg):
4057db96d56Sopenharmony_ci        _sendto(sock, ("%r#%s\n" % (local_addr, msg)).rjust(packet_size), addr)
4067db96d56Sopenharmony_ci    # We give the parent some time to be ready.
4077db96d56Sopenharmony_ci    _sleep(1.0)
4087db96d56Sopenharmony_ci    try:
4097db96d56Sopenharmony_ci        start_time = _time()
4107db96d56Sopenharmony_ci        end_time = start_time + duration * 2.0
4117db96d56Sopenharmony_ci        i = 0
4127db96d56Sopenharmony_ci        while _time() < end_time:
4137db96d56Sopenharmony_ci            _send_chunk(str(i))
4147db96d56Sopenharmony_ci            s = _recv(sock, packet_size)
4157db96d56Sopenharmony_ci            assert len(s) == packet_size
4167db96d56Sopenharmony_ci            i += 1
4177db96d56Sopenharmony_ci        _send_chunk(BW_END)
4187db96d56Sopenharmony_ci    finally:
4197db96d56Sopenharmony_ci        sock.close()
4207db96d56Sopenharmony_ci
4217db96d56Sopenharmony_cidef run_bandwidth_client(**kwargs):
4227db96d56Sopenharmony_ci    cmd_line = [sys.executable, '-E', os.path.abspath(__file__)]
4237db96d56Sopenharmony_ci    cmd_line.extend(['--bwclient', repr(kwargs)])
4247db96d56Sopenharmony_ci    return subprocess.Popen(cmd_line) #, stdin=subprocess.PIPE,
4257db96d56Sopenharmony_ci                            #stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
4267db96d56Sopenharmony_ci
4277db96d56Sopenharmony_cidef run_bandwidth_test(func, args, nthreads):
4287db96d56Sopenharmony_ci    # Create a listening socket to receive the packets. We use UDP which should
4297db96d56Sopenharmony_ci    # be painlessly cross-platform.
4307db96d56Sopenharmony_ci    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
4317db96d56Sopenharmony_ci        sock.bind(("127.0.0.1", 0))
4327db96d56Sopenharmony_ci        addr = sock.getsockname()
4337db96d56Sopenharmony_ci
4347db96d56Sopenharmony_ci        duration = BANDWIDTH_DURATION
4357db96d56Sopenharmony_ci        packet_size = BANDWIDTH_PACKET_SIZE
4367db96d56Sopenharmony_ci
4377db96d56Sopenharmony_ci        results = []
4387db96d56Sopenharmony_ci        threads = []
4397db96d56Sopenharmony_ci        end_event = []
4407db96d56Sopenharmony_ci        start_cond = threading.Condition()
4417db96d56Sopenharmony_ci        started = False
4427db96d56Sopenharmony_ci        if nthreads > 0:
4437db96d56Sopenharmony_ci            # Warm up
4447db96d56Sopenharmony_ci            func(*args)
4457db96d56Sopenharmony_ci
4467db96d56Sopenharmony_ci            results = []
4477db96d56Sopenharmony_ci            loop = TimedLoop(func, args)
4487db96d56Sopenharmony_ci            ready = []
4497db96d56Sopenharmony_ci            ready_cond = threading.Condition()
4507db96d56Sopenharmony_ci
4517db96d56Sopenharmony_ci            def run():
4527db96d56Sopenharmony_ci                with ready_cond:
4537db96d56Sopenharmony_ci                    ready.append(None)
4547db96d56Sopenharmony_ci                    ready_cond.notify()
4557db96d56Sopenharmony_ci                with start_cond:
4567db96d56Sopenharmony_ci                    while not started:
4577db96d56Sopenharmony_ci                        start_cond.wait()
4587db96d56Sopenharmony_ci                loop(start_time, duration * 1.5, end_event, do_yield=False)
4597db96d56Sopenharmony_ci
4607db96d56Sopenharmony_ci            for i in range(nthreads):
4617db96d56Sopenharmony_ci                threads.append(threading.Thread(target=run))
4627db96d56Sopenharmony_ci            for t in threads:
4637db96d56Sopenharmony_ci                t.daemon = True
4647db96d56Sopenharmony_ci                t.start()
4657db96d56Sopenharmony_ci            # Wait for threads to be ready
4667db96d56Sopenharmony_ci            with ready_cond:
4677db96d56Sopenharmony_ci                while len(ready) < nthreads:
4687db96d56Sopenharmony_ci                    ready_cond.wait()
4697db96d56Sopenharmony_ci
4707db96d56Sopenharmony_ci        # Run the client and wait for the first packet to arrive before
4717db96d56Sopenharmony_ci        # unblocking the background threads.
4727db96d56Sopenharmony_ci        process = run_bandwidth_client(addr=addr,
4737db96d56Sopenharmony_ci                                       packet_size=packet_size,
4747db96d56Sopenharmony_ci                                       duration=duration)
4757db96d56Sopenharmony_ci        _time = time.time
4767db96d56Sopenharmony_ci        # This will also wait for the parent to be ready
4777db96d56Sopenharmony_ci        s = _recv(sock, packet_size)
4787db96d56Sopenharmony_ci        remote_addr = eval(s.partition('#')[0])
4797db96d56Sopenharmony_ci
4807db96d56Sopenharmony_ci        with start_cond:
4817db96d56Sopenharmony_ci            start_time = _time()
4827db96d56Sopenharmony_ci            started = True
4837db96d56Sopenharmony_ci            start_cond.notify(nthreads)
4847db96d56Sopenharmony_ci
4857db96d56Sopenharmony_ci        n = 0
4867db96d56Sopenharmony_ci        first_time = None
4877db96d56Sopenharmony_ci        while not end_event and BW_END not in s:
4887db96d56Sopenharmony_ci            _sendto(sock, s, remote_addr)
4897db96d56Sopenharmony_ci            s = _recv(sock, packet_size)
4907db96d56Sopenharmony_ci            if first_time is None:
4917db96d56Sopenharmony_ci                first_time = _time()
4927db96d56Sopenharmony_ci            n += 1
4937db96d56Sopenharmony_ci        end_time = _time()
4947db96d56Sopenharmony_ci
4957db96d56Sopenharmony_ci    end_event.append(None)
4967db96d56Sopenharmony_ci    for t in threads:
4977db96d56Sopenharmony_ci        t.join()
4987db96d56Sopenharmony_ci    process.kill()
4997db96d56Sopenharmony_ci
5007db96d56Sopenharmony_ci    return (n - 1) / (end_time - first_time)
5017db96d56Sopenharmony_ci
5027db96d56Sopenharmony_cidef run_bandwidth_tests(max_threads):
5037db96d56Sopenharmony_ci    for task in bandwidth_tasks:
5047db96d56Sopenharmony_ci        print("Background CPU task:", task.__doc__)
5057db96d56Sopenharmony_ci        print()
5067db96d56Sopenharmony_ci        func, args = task()
5077db96d56Sopenharmony_ci        nthreads = 0
5087db96d56Sopenharmony_ci        baseline_speed = None
5097db96d56Sopenharmony_ci        while nthreads <= max_threads:
5107db96d56Sopenharmony_ci            results = run_bandwidth_test(func, args, nthreads)
5117db96d56Sopenharmony_ci            speed = results
5127db96d56Sopenharmony_ci            #speed = len(results) * 1.0 / results[-1][0]
5137db96d56Sopenharmony_ci            print("CPU threads=%d: %.1f" % (nthreads, speed), end="")
5147db96d56Sopenharmony_ci            if baseline_speed is None:
5157db96d56Sopenharmony_ci                print(" packets/s.")
5167db96d56Sopenharmony_ci                baseline_speed = speed
5177db96d56Sopenharmony_ci            else:
5187db96d56Sopenharmony_ci                print(" ( %d %%)" % (speed / baseline_speed * 100))
5197db96d56Sopenharmony_ci            nthreads += 1
5207db96d56Sopenharmony_ci        print()
5217db96d56Sopenharmony_ci
5227db96d56Sopenharmony_ci
5237db96d56Sopenharmony_cidef main():
5247db96d56Sopenharmony_ci    usage = "usage: %prog [-h|--help] [options]"
5257db96d56Sopenharmony_ci    parser = OptionParser(usage=usage)
5267db96d56Sopenharmony_ci    parser.add_option("-t", "--throughput",
5277db96d56Sopenharmony_ci                      action="store_true", dest="throughput", default=False,
5287db96d56Sopenharmony_ci                      help="run throughput tests")
5297db96d56Sopenharmony_ci    parser.add_option("-l", "--latency",
5307db96d56Sopenharmony_ci                      action="store_true", dest="latency", default=False,
5317db96d56Sopenharmony_ci                      help="run latency tests")
5327db96d56Sopenharmony_ci    parser.add_option("-b", "--bandwidth",
5337db96d56Sopenharmony_ci                      action="store_true", dest="bandwidth", default=False,
5347db96d56Sopenharmony_ci                      help="run I/O bandwidth tests")
5357db96d56Sopenharmony_ci    parser.add_option("-i", "--interval",
5367db96d56Sopenharmony_ci                      action="store", type="int", dest="check_interval", default=None,
5377db96d56Sopenharmony_ci                      help="sys.setcheckinterval() value "
5387db96d56Sopenharmony_ci                           "(Python 3.8 and older)")
5397db96d56Sopenharmony_ci    parser.add_option("-I", "--switch-interval",
5407db96d56Sopenharmony_ci                      action="store", type="float", dest="switch_interval", default=None,
5417db96d56Sopenharmony_ci                      help="sys.setswitchinterval() value "
5427db96d56Sopenharmony_ci                           "(Python 3.2 and newer)")
5437db96d56Sopenharmony_ci    parser.add_option("-n", "--num-threads",
5447db96d56Sopenharmony_ci                      action="store", type="int", dest="nthreads", default=4,
5457db96d56Sopenharmony_ci                      help="max number of threads in tests")
5467db96d56Sopenharmony_ci
5477db96d56Sopenharmony_ci    # Hidden option to run the pinging and bandwidth clients
5487db96d56Sopenharmony_ci    parser.add_option("", "--latclient",
5497db96d56Sopenharmony_ci                      action="store", dest="latclient", default=None,
5507db96d56Sopenharmony_ci                      help=SUPPRESS_HELP)
5517db96d56Sopenharmony_ci    parser.add_option("", "--bwclient",
5527db96d56Sopenharmony_ci                      action="store", dest="bwclient", default=None,
5537db96d56Sopenharmony_ci                      help=SUPPRESS_HELP)
5547db96d56Sopenharmony_ci
5557db96d56Sopenharmony_ci    options, args = parser.parse_args()
5567db96d56Sopenharmony_ci    if args:
5577db96d56Sopenharmony_ci        parser.error("unexpected arguments")
5587db96d56Sopenharmony_ci
5597db96d56Sopenharmony_ci    if options.latclient:
5607db96d56Sopenharmony_ci        kwargs = eval(options.latclient)
5617db96d56Sopenharmony_ci        latency_client(**kwargs)
5627db96d56Sopenharmony_ci        return
5637db96d56Sopenharmony_ci
5647db96d56Sopenharmony_ci    if options.bwclient:
5657db96d56Sopenharmony_ci        kwargs = eval(options.bwclient)
5667db96d56Sopenharmony_ci        bandwidth_client(**kwargs)
5677db96d56Sopenharmony_ci        return
5687db96d56Sopenharmony_ci
5697db96d56Sopenharmony_ci    if not options.throughput and not options.latency and not options.bandwidth:
5707db96d56Sopenharmony_ci        options.throughput = options.latency = options.bandwidth = True
5717db96d56Sopenharmony_ci    if options.check_interval:
5727db96d56Sopenharmony_ci        sys.setcheckinterval(options.check_interval)
5737db96d56Sopenharmony_ci    if options.switch_interval:
5747db96d56Sopenharmony_ci        sys.setswitchinterval(options.switch_interval)
5757db96d56Sopenharmony_ci
5767db96d56Sopenharmony_ci    print("== %s %s (%s) ==" % (
5777db96d56Sopenharmony_ci        platform.python_implementation(),
5787db96d56Sopenharmony_ci        platform.python_version(),
5797db96d56Sopenharmony_ci        platform.python_build()[0],
5807db96d56Sopenharmony_ci    ))
5817db96d56Sopenharmony_ci    # Processor identification often has repeated spaces
5827db96d56Sopenharmony_ci    cpu = ' '.join(platform.processor().split())
5837db96d56Sopenharmony_ci    print("== %s %s on '%s' ==" % (
5847db96d56Sopenharmony_ci        platform.machine(),
5857db96d56Sopenharmony_ci        platform.system(),
5867db96d56Sopenharmony_ci        cpu,
5877db96d56Sopenharmony_ci    ))
5887db96d56Sopenharmony_ci    print()
5897db96d56Sopenharmony_ci
5907db96d56Sopenharmony_ci    if options.throughput:
5917db96d56Sopenharmony_ci        print("--- Throughput ---")
5927db96d56Sopenharmony_ci        print()
5937db96d56Sopenharmony_ci        run_throughput_tests(options.nthreads)
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci    if options.latency:
5967db96d56Sopenharmony_ci        print("--- Latency ---")
5977db96d56Sopenharmony_ci        print()
5987db96d56Sopenharmony_ci        run_latency_tests(options.nthreads)
5997db96d56Sopenharmony_ci
6007db96d56Sopenharmony_ci    if options.bandwidth:
6017db96d56Sopenharmony_ci        print("--- I/O bandwidth ---")
6027db96d56Sopenharmony_ci        print()
6037db96d56Sopenharmony_ci        run_bandwidth_tests(options.nthreads)
6047db96d56Sopenharmony_ci
6057db96d56Sopenharmony_ciif __name__ == "__main__":
6067db96d56Sopenharmony_ci    main()
607