Skip to content

Commit

Permalink
up
Browse files Browse the repository at this point in the history
  • Loading branch information
liangw89 committed Jul 8, 2018
1 parent 22a63d2 commit de20832
Show file tree
Hide file tree
Showing 18 changed files with 1,814 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
*.pyc
*.log
.DS_Store
*.zip
57 changes: 57 additions & 0 deletions README.md
@@ -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.



85 changes: 85 additions & 0 deletions code/nodejs/index.js
@@ -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);

};

////////////////////////////////////
134 changes: 134 additions & 0 deletions code/nodejs/stats.py
@@ -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)
81 changes: 81 additions & 0 deletions code/nodejs/tests.py
@@ -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)

0 comments on commit de20832

Please sign in to comment.