Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active April 30, 2018 00:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mottosso/f16f86e65ac05f73ec23e020d06ace5a to your computer and use it in GitHub Desktop.
Save mottosso/f16f86e65ac05f73ec23e020d06ace5a to your computer and use it in GitHub Desktop.
Subprocess profiling, one-way

Alternative to: https://gist.github.com/mottosso/dd59f815025e4d76f453f3555e39558e

Writing to a subprocess, but not waiting for it to return, yields faster results, at the expense of interactivity.

$ c:\Python27\python.exe -u stdio_cli.py --bytes=10000 --iterations=10000
Created data 10,033 bytes (len 10000)
Read: 180.259705 MB/s
Write: 182.166 MB/s
Iterations: 10,000
Bytes/iteration: 20,066 B/i
Bytes/second: 189,395,539 B/s
Avarage roundtrip time: 0.000 ms
Min roundtrip time: 0.000 ms
Max roundtrip time: 0.000 ms
Delta roundtrip time: 0.000 ms
Total bytes: 100,330,000 B
Total time: 0.530 s
"""Subprocess IPC communication test
Determine the throughput and bandwidth of standard Python
subprocess interprocess communication.
"""
import os
import sys
import time
import argparse
import subprocess
KB = 1 << 10
MB = 1 << 20
BUFSIZE = 64 * KB
parser = argparse.ArgumentParser()
parser.add_argument("-o", "--output")
parser.add_argument("-i", "--iterations", default=100, type=int)
parser.add_argument("-b", "--bytes", default=1000, type=int)
parser.add_argument("-s", "--stats", action="store_true",
help="Print additional stats")
parser.add_argument("-p", "--plot", action="store_true")
parser.add_argument("-q", "--quiet", action="store_true")
opt = parser.parse_args()
data = "x" * opt.bytes
size = sys.getsizeof(data)
if not os.getenv("CHILD"):
popen = subprocess.Popen(
# Open yourself, but run the below block
[sys.executable, __file__],
env=dict(os.environ, **{"CHILD": "1"}),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
bufsize=-1,
universal_newlines=True,
)
# Wait for child to boot up..
response = popen.stdout.readline()
assert response == "Ready\n", "Was: '%s'" % response
print("Created data {:,} bytes (len {})".format(
size, len(data)
))
write_per_iteration = list()
rtrip_per_iteration = list() # roundtrip
child_wait_times = list()
totwrite = 0
writetime = 0
tot0 = time.clock()
for i in range(opt.iterations):
# Write
write0 = time.clock()
popen.stdin.write(data)
popen.stdin.write("\n")
popen.stdin.flush()
write1 = time.clock()
writetime += write1 - write0
totwrite += size
tot1 = time.clock()
totdur = tot1 - tot0 # s
popen.stdin.close()
popen.stdout.close()
popen.wait()
def plot():
import pygal
fname = os.path.join(os.getcwd(), "plot.svg")
print("Plotting to %s.." % fname)
plot = pygal.Line(width=2000, height=500)
plot.title = "Time per iteration"
plot.add("Roundtrip (ms)", rtrip_per_iteration, show_dots=False)
plot.add("Write to child (ms)", write_per_iteration, show_dots=False)
plot.add("Child wait (ms)", child_wait_times, show_dots=False)
plot.render_to_file(fname)
if opt.plot:
try:
plot()
except ImportError:
print("Plotting skipped, couldn't find pygal")
bpi = size
totb = opt.iterations * bpi
bps = totb / (totdur)
mbps = totwrite / MB / writetime
# avgtime = (sum(rtrip_per_iteration) / len(rtrip_per_iteration))
avgtime = 0
# mintime = min(rtrip_per_iteration)
mintime = 0
# maxtime = max(rtrip_per_iteration)
maxtime = 0
deltime = maxtime - mintime
results = (
("iterations", opt.iterations),
("bpi", size * 2),
("bps", bps),
("mbps", mbps),
("totwrite", totwrite / KB),
("totread", avgtime),
("avgtime", avgtime),
("mintime", mintime),
("maxtime", maxtime),
("deltime", deltime),
("totb", totb),
("totdur", totdur),
)
print("""\
Write: {mbps:,.3f} MB/s
Iterations: {iterations:,}
Bytes/iteration: {bpi:,} B/i
Bytes/second: {bps:,.0f} B/s
Avarage roundtrip time: {avgtime:.3f} ms
Min roundtrip time: {mintime:.3f} ms
Max roundtrip time: {maxtime:.3f} ms
Delta roundtrip time: {deltime:.3f} ms
Total bytes: {totb:,} B
Total time: {totdur:.3f} s
""".format(**dict(results)))
if opt.output:
import json
with open(opt.output, "w") as f:
print("Writing results to '%s'" % opt.output)
json.dump(dict(results), f, indent=2, sort_keys=True)
else:
totalbytes = 0
maxread = 32 * KB
sys.stdout.write("Ready\n")
sys.stdout.flush()
t0 = time.clock()
while True:
data = sys.stdin.readline()
if not data:
break
totalbytes += sys.getsizeof(data)
t1 = time.clock()
totaltime = t1 - t0
assert totaltime > 0, "Too fast"
rate = float(totalbytes) / MB / totaltime
sys.stderr.write("Read: %0.6f MB/s\n" % rate)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment