diff --git a/analyze/memcached_qps_scan.py b/analyze/memcached_qps_scan.py
new file mode 100644
index 0000000000000000000000000000000000000000..330ccb24cb49421f5b5474fb0638738c67a805fb
--- /dev/null
+++ b/analyze/memcached_qps_scan.py
@@ -0,0 +1,84 @@
+import os
+import matplotlib.pyplot as plt
+
+experiments = [f for f in os.listdir('../data')
+    if f.startswith('memcached_qps_scan') and len(os.listdir(os.path.join('../data', f))) > 0]
+
+def parse_memcached_output(s):
+    lines = s.split('\n')
+    qps = int(float(lines[6].split(' ')[3]))
+    latency_99th = int(float(lines[1].split(' ')[-1]))
+    return (qps, latency_99th)
+
+def extract_qps_latency_exp(exp):
+    ret = ([], [])
+
+    for f in os.listdir(os.path.join('../data/', exp)):
+        if not f[0].isdigit():
+            continue
+
+        with open(os.path.join('../data/', exp, f), 'r') as f:
+            (qps, latency_99th) = parse_memcached_output(f.read())
+            if len(ret[0]) > 0 and qps < ret[0][-1]:
+                continue
+            ret[0].append(qps)
+            ret[1].append(latency_99th)
+
+    return ret
+
+def extract_qps_latency(threads, include_caladan = False):
+    ret = dict()
+
+    for exp in experiments:
+        split = exp.split('.')
+        if split[-2] != ('t' + str(threads)):
+            continue
+
+        if split[-3] == 'caladan' and not include_caladan:
+            continue
+
+        name = split[-3]
+        if name[0].isdigit():
+            name = 'default'
+
+        ret[name] = extract_qps_latency_exp(exp)
+
+    return ret
+
+def show_plot(threads, include_caladan = False):
+    plt.cla()
+    if include_caladan:
+        plt.title('Memcached QPS vs Latency (99th percentile), %d threads, with Caladan' % (threads,))
+    else:
+        plt.title('Memcached QPS vs Latency (99th percentile), %d threads' % (threads,))
+
+    data = extract_qps_latency(threads, include_caladan)
+
+    if include_caladan and threads == 1:
+        # Because we cannot run caladan on 1 threads, we use the t2 data
+        # but we half the QPS
+        name = 'caladan (per-thread QPS w/ 2 threads)'
+        data[name] = extract_qps_latency_exp('memcached_qps_scan.5.4.0-136-generic.caladan.t2.c160')
+        data[name] = ([qps / 2 for qps in data[name][0]], data[name][1])
+
+    for k in data:
+        plt.plot(data[k][0], data[k][1], label = k)
+
+    plt.xlabel('QPS')
+    plt.ylabel('Latency (99th percentile)')
+    plt.yscale('log')
+
+    plt.legend()
+    if os.getenv('SAVE_FIGURE') != 'true':
+        plt.rcParams.update({'font.size': 16})
+        plt.show()
+    else:
+        if include_caladan:
+            plt.savefig('../data/figs/memcached_qps_scan.t%d.caladan.png' % (threads,), dpi = 192)
+        else:
+            plt.savefig('../data/figs/memcached_qps_scan.t%d.png' % (threads,), dpi = 192)
+
+show_plot(1)
+show_plot(1, True)
+show_plot(8)
+show_plot(8, True)