Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,814 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.pyc | ||
*.log | ||
.DS_Store | ||
*.zip |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,59 @@ | ||
# faas_measure | ||
A framework for measuring coldstart latency, io/network throughput, and more in AWS Lambda | ||
|
||
Please check our paper at [http://pages.cs.wisc.edu/~liangw/pub/atc18-final298.pdf] | ||
|
||
# Preparation | ||
|
||
1. Install boto3 using pip | ||
|
||
2. Update your AWS credentials in conf.py | ||
|
||
|
||
# How to use the measurement framework | ||
|
||
The measurement framework is only for conducting measurements in AWS Lambda. | ||
Only support Python and NodeJS as function runtime | ||
|
||
|
||
The code of measurement function is in code/python/. Please check the comments in the files. | ||
|
||
The code of the measurement framework is in utils.py. Very simple. Please check the comments in the files. | ||
|
||
Coldstatrt_test.py gives an example of how to use the framework to measure coldstart | ||
and warmstart latency. It has detailed comments to help users to understand how | ||
to use the framework. In the test we send two concurrent requests at a time. | ||
Consistency_test.py shows another way to use the framework, and how to append | ||
extra info to the output. | ||
|
||
*_test.py files are the other examples we developed. | ||
|
||
# Understand the output | ||
|
||
### The format of the output for non-performance related tests: | ||
|
||
round ID, sub-round ID, worker No., worker ID, | ||
function region, function runtime, function memory, function name, | ||
instance ID generated by the function, request ID, VM ID, instance ID from procfs, | ||
instance VM IP, public VM IP, private instance IP, VM uptime, vCPU No. and model, | ||
request received time (from function), response sent time (from function), | ||
response processing time (response sent time - request received time, ms), | ||
request sent time (from vantage point), response received time (from vantage point), | ||
request rtt (response received time - request sent time, ms) | ||
|
||
* Round ID: for a given test, one might perform it multiple rounds. | ||
Each round needs to have a unique "round ID". | ||
|
||
* Sub-round ID: in a given round, one might want to perform a task multiple times. | ||
Each time the framework will automatically generate a "sub-round ID" to record how | ||
many times the tasks was repeated. | ||
|
||
### The format of the output for performance related tests: | ||
|
||
The performance test result is after "vCPU No. and model". | ||
|
||
For performance related tests: only support Python as function runtime. For I/O | ||
and network throughput tests we used dd and iperf directly. | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
|
||
function run_cmd(cmd) { | ||
|
||
var exec = require('child_process').execSync; | ||
var res = exec(cmd); | ||
var out = res.toString().replace(/[\n\t\r]/g, ''); | ||
return out; | ||
} | ||
|
||
|
||
function get_ip() { | ||
var cmd = "curl -s 'http://checkip.dyndns.org'"; | ||
var tmp = run_cmd(cmd).toString().split(":"); | ||
tmp = tmp[1]; | ||
tmp = tmp.split("<"); | ||
res = tmp[0].replace(/\s/g, ''); | ||
return res; | ||
} | ||
|
||
|
||
|
||
function run_tests(req, context){ | ||
|
||
var out; | ||
var fs = require('fs'); | ||
var buf = fs.readFileSync('/proc/self/cgroup', 'utf8').toString() | ||
buf = buf.split("\n"); | ||
buf = buf[buf.length - 3]; | ||
buf = buf.split("/"); | ||
var r_id = buf[1]; | ||
var c_id = buf[2]; | ||
|
||
var tmp = run_cmd("cat /proc/uptime"); | ||
var uptime = tmp.toString().replace(/[\n\t\r]/g, ''); | ||
|
||
var vm_pub_ip = get_ip(); | ||
|
||
var vm_priv_ip = run_cmd("uname -n").toString().replace(/[\n\t\r]/g, ''); | ||
vm_priv_ip = vm_priv_ip.replace('ip-', ''); | ||
vm_priv_ip = vm_priv_ip.replace(/\-/g, '.'); | ||
|
||
|
||
var new_id = makeid(); | ||
|
||
var exist_id; | ||
|
||
var fname = "/tmp/test_access.log"; | ||
|
||
if (fs.existsSync(fname) != true){ | ||
fs.writeFileSync(fname, new_id); | ||
exist_id = new_id; | ||
} | ||
else{ | ||
exist_id = fs.readFileSync(fname, 'utf8').toString(); | ||
} | ||
|
||
out = r_id + "#" + c_id + "#" + exist_id + "#" + new_id; | ||
|
||
out = out + "#" + vm_pub_ip + "#" + vm_priv_ip + "#" + "unknown"; | ||
|
||
out = out + "#" + uptime; | ||
|
||
return out; | ||
} | ||
|
||
|
||
|
||
exports.handler = (event, context, callback) => { | ||
// Example input: {"eid":1, "memtest":0} | ||
var start = new Date().getTime(); | ||
var sleep_tm = event.cmds.sleep; | ||
|
||
var wait_util = sleep_tm; | ||
while (new Date().getTime() < wait_util){} | ||
|
||
|
||
var tmp = run_cmd("python stats.py stat_basic") | ||
|
||
var end = new Date().getTime(); | ||
var out = tmp + "#" + start + "#" + end + "#" + (end - start); | ||
callback(null, out); | ||
|
||
}; | ||
|
||
//////////////////////////////////// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
|
||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
from __future__ import print_function | ||
import json | ||
import time | ||
import os | ||
import socket | ||
import sys | ||
import uuid | ||
import subprocess | ||
|
||
try: | ||
import urllib2 | ||
from urllib2 import urlopen | ||
except: | ||
from urllib.request import urlopen | ||
|
||
import decimal | ||
|
||
|
||
|
||
# Set it to your own servers | ||
INST_PRIV_IP_DST = "8.8.8.8" | ||
VM_PUB_ID_DST = "http://ip.42.pl/raw" | ||
|
||
|
||
def fstr(f): | ||
ctx = decimal.Context() | ||
ctx.prec = 20 | ||
d1 = ctx.create_decimal(repr(f)) | ||
return format(d1, 'f') | ||
|
||
def get_meminfo(): | ||
buf = open('/proc/meminfo').read() | ||
buf = ','.join([v.replace(' ', '') for v in | ||
buf.split('\n') if v]) | ||
|
||
return buf | ||
|
||
def get_vmstat(): | ||
buf = open("/proc/vmstat").read() | ||
buf = [v.replace(' ', ":") for v in buf.split("\n")] | ||
buf = ";".join(buf) | ||
return buf | ||
|
||
|
||
def get_diskstat(): | ||
buf = open("/proc/diskstats").read() | ||
buf = [v for v in buf.split("\n") if v] | ||
buf = [v.replace(" ", ",").replace(",,,,,,,", ",").replace(",,,","").lstrip(",") for v in buf] | ||
buf = ";".join(buf) | ||
return buf | ||
|
||
|
||
def get_cpuinfo(): | ||
buf = "".join(open("/proc/cpuinfo").readlines()) | ||
cpuinfo = buf.replace("\n", ";").replace("\t", "") | ||
return cpuinfo | ||
|
||
|
||
def get_cpuinfo_short(): | ||
buf = "".join(open("/proc/cpuinfo").readlines()) | ||
cpuinfo = buf.replace("\n", ";").replace("\t", "") | ||
a1 = cpuinfo.count("processor") | ||
a2 = cpuinfo.split(";")[4].split(":")[1].strip() | ||
return "%s,%s" % (a1, a2) | ||
|
||
def get_inst_id(): | ||
log_file = '/tmp/inst_id.txt' | ||
new_id = str(uuid.uuid4()) | ||
try: | ||
exist_id = open(log_file).read().strip('\n') | ||
except: | ||
open(log_file, 'w').write(new_id) | ||
exist_id = new_id | ||
return exist_id, new_id | ||
|
||
def get_inst_priv_ip(): | ||
# Get inst private IP | ||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | ||
s.connect((INST_PRIV_IP_DST, 80)) | ||
ip = s.getsockname()[0] | ||
s.close() | ||
return ip | ||
|
||
def get_vm_priv_ip(): | ||
# Get private IP | ||
ip = socket.gethostbyname(socket.getfqdn()) | ||
return ip | ||
|
||
def get_vm_pub_ip(): | ||
ip = "None" | ||
try: | ||
ip = str(urlopen(VM_PUB_ID_DST).read()) | ||
except: | ||
pass | ||
return ip | ||
|
||
def get_vm_id(): | ||
# Get cgroup ids | ||
buf = open('/proc/self/cgroup').read().split('\n')[-3].split('/') | ||
vm_id, inst_id = buf[1], buf[2] | ||
return vm_id, inst_id | ||
|
||
def get_uptime(): | ||
# Get uptime | ||
uptime = ','.join(open('/proc/uptime').read().strip('\n').split(' ')) | ||
return uptime | ||
|
||
|
||
def stat_other(): | ||
hostname = os.popen('uname -n').read().strip('\n') | ||
kernel_ver = os.popen('uname -r').read().strip('\n') | ||
return [hostname, kernel_ver] | ||
|
||
def stat_basic(para=None): | ||
|
||
exist_id, new_id = get_inst_id() | ||
vm_id, inst_id = get_vm_id() | ||
uptime = get_uptime() | ||
vm_priv_ip = get_vm_priv_ip() | ||
vm_pub_ip = get_vm_pub_ip() | ||
inst_priv_ip = get_inst_priv_ip() | ||
cpu_info = get_cpuinfo_short() | ||
|
||
res = [exist_id, new_id, vm_id, inst_id, vm_priv_ip, vm_pub_ip, inst_priv_ip, uptime, cpu_info] | ||
res = "#".join([str(v) for v in res]) | ||
return res | ||
|
||
if __name__ == '__main__': | ||
func_name = sys.argv[1] | ||
res = eval(func_name)() | ||
print(res) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
from __future__ import print_function | ||
import json | ||
import time | ||
import os | ||
import sys | ||
import socket | ||
import random | ||
import uuid | ||
import subprocess | ||
|
||
try: | ||
import urllib2 | ||
from urllib2 import urlopen | ||
except: | ||
from urllib.request import urlopen | ||
|
||
import decimal | ||
|
||
|
||
def ioload(size, cnt): | ||
proc = subprocess.Popen(["dd", "if=/dev/urandom", "of=/tmp/ioload.log", "bs=%s" % size, "count=%s" % cnt, "conv=fdatasync", "oflag=dsync"], stderr=subprocess.PIPE) | ||
out, err = proc.communicate() | ||
buf = err.split("\n")[-2].split(",") | ||
t, s = buf[-2], buf[-1] | ||
t = t.split(" ")[1] | ||
s = s.split(" ")[1] | ||
return "%s,%s" % (t,s) | ||
|
||
def ioload_test(): | ||
bufs = [] | ||
for i in xrange(5): | ||
buf = ioload() | ||
bufs.append(buf) | ||
return ";".join(bufs) | ||
|
||
|
||
def network_test(port_offset, server_ip): | ||
port = 5000 + port_offset | ||
sp = subprocess.Popen(["./iperf3", "-c", server_ip, "-p", str(port), "-l", "-t", "1", "-Z", "-J"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
out, err = sp.communicate() | ||
_d = json.loads(out)["end"] | ||
sender = _d["streams"][0]["sender"] | ||
bps = str(sender["bits_per_second"]) | ||
maxr = str(sender["max_rtt"]) | ||
minr = str(sender["min_rtt"]) | ||
meanr = str(sender["mean_rtt"]) | ||
return ",".join([bps, meanr, minr, maxr]) | ||
|
||
def cpu_test(n): | ||
st = time.time() * 1000 | ||
r = 1 | ||
for i in range(1, n + 1): | ||
r *= i | ||
ed = time.time() * 1000 | ||
return fstr(float(ed) - float(st)) | ||
|
||
def cpu_rand_test(n): | ||
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | ||
st = time.time() * 1000 | ||
for i in range(1, n + 1): | ||
idx = [random.choice(s) for v in xrange(32)] | ||
ed = time.time() * 1000 | ||
return fstr(float(ed) - float(st)) | ||
|
||
def read_perf(): | ||
proc = subprocess.Popen(["dd", "if=/tmp/tmp", "of=/dev/null", "bs=100M", "count=1000"], stderr=subprocess.PIPE) | ||
out, err = proc.communicate() | ||
buf = err.split("\n")[-2].split(",") | ||
t, s = buf[-2], buf[-1] | ||
t = t.split(" ")[1] | ||
s = s.split(" ")[1] | ||
return "%s,%s" % (t,s) | ||
|
||
def read_test(): | ||
bufs = [] | ||
for i in xrange(10): | ||
buf = read_perf() | ||
bufs.append(buf) | ||
return ";".join(bufs) |
Oops, something went wrong.