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