├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── bin ├── README.md ├── arp_clear.sh ├── arp_poison.sh ├── click_startup.sh ├── gen_switch.py ├── install.sh ├── kernel_install.sh ├── log_cwnd.py ├── node_install.sh ├── nsdi2020.sh ├── profile_etalon_ccanel.py ├── profile_etalon_ccanel_bootstrap.py ├── retcp_install.sh ├── s ├── switch_install.sh ├── sxp.py ├── tune.sh └── utils.sh ├── etc ├── README.md ├── c_config.h ├── handles ├── netplan │ └── 99-etalon.yaml ├── python_config.py └── script_config.sh ├── experiments ├── README.md ├── adu │ ├── README.md │ ├── adu_common.py │ ├── adu_graphs.py │ ├── graphs-one-ring │ │ ├── elephant_fct_cdf.pdf │ │ ├── elephant_tp_cdf.pdf │ │ ├── mice_fct_cdf.pdf │ │ └── mice_tp_cdf.pdf │ ├── graphs-two-ring │ │ └── utilization.pdf │ ├── one_ring.py │ └── two_rings.py ├── buffers │ ├── README.md │ ├── buffer_common.py │ ├── buffers.py │ ├── buffers_cc.py │ ├── buffers_graphs.py │ ├── delay_graphs.py │ ├── delay_sensitivity.py │ ├── graphs │ │ ├── delay_sensitivity_rtt.pdf │ │ ├── nsdi2020 │ │ │ ├── 1581462196-nsdi2020.txt │ │ │ ├── 1_seq-old-cubic.pdf │ │ │ ├── 2_seq-current-cubic.pdf │ │ │ ├── 3_seq-future-cubic.pdf │ │ │ ├── 4-1_seq-current-all.pdf │ │ │ ├── 4-2_util-lat-current-all_util.pdf │ │ │ ├── 5-1_seq-static-cubic.pdf │ │ │ ├── 5-2_util-lat-static-cubic_util.pdf │ │ │ ├── 5-3_util-lat-static-cubic_lat50.pdf │ │ │ ├── 5-4_util-lat-static-cubic_lat99.pdf │ │ │ ├── 6-1-1_seq-dyn-cubic.pdf │ │ │ ├── 6-2_util-lat-dyn-cubic_util.pdf │ │ │ ├── 6-3_util-lat-dyn-cubic_lat50.pdf │ │ │ ├── 6-4_util-lat-dyn-cubic_lat99.pdf │ │ │ ├── 7-1-2_seq-dyn-bbr.pdf │ │ │ ├── 7-1-2_seq-dyn-bic.pdf │ │ │ ├── 7-1-2_seq-dyn-cdg.pdf │ │ │ ├── 7-1-2_seq-dyn-cubic.pdf │ │ │ ├── 7-1-2_seq-dyn-dctcp.pdf │ │ │ ├── 7-1-2_seq-dyn-highspeed.pdf │ │ │ ├── 7-1-2_seq-dyn-htcp.pdf │ │ │ ├── 7-1-2_seq-dyn-hybla.pdf │ │ │ ├── 7-1-2_seq-dyn-illinois.pdf │ │ │ ├── 7-1-2_seq-dyn-lp.pdf │ │ │ ├── 7-1-2_seq-dyn-nv.pdf │ │ │ ├── 7-1-2_seq-dyn-reno.pdf │ │ │ ├── 7-1-2_seq-dyn-retcp.pdf │ │ │ ├── 7-1-2_seq-dyn-scalable.pdf │ │ │ ├── 7-1-2_seq-dyn-vegas.pdf │ │ │ ├── 7-1-2_seq-dyn-veno.pdf │ │ │ ├── 7-1-2_seq-dyn-westwood.pdf │ │ │ ├── 7-1-2_seq-dyn-yeah.pdf │ │ │ ├── 7-2_util-lat-dyn-all-1200us_util.pdf │ │ │ ├── 8-1_seq-static-retcp.pdf │ │ │ ├── 8-2_util-lat-static-retcp_util.pdf │ │ │ ├── 8-3_util-lat-static-retcp_lat50.pdf │ │ │ ├── 8-4_util-lat-static-retcp_lat99.pdf │ │ │ ├── 9-1_seq-dyn-retcp.pdf │ │ │ ├── 9-2_util-lat-dyn-retcp_util.pdf │ │ │ ├── 9-3_util-lat-dyn-retcp_lat50.pdf │ │ │ ├── 9-4_util-lat-dyn-retcp_lat99.pdf │ │ │ ├── calcs.txt │ │ │ ├── sim-1-flows.pdf │ │ │ ├── sim-10-flows.pdf │ │ │ ├── sim-15-flows.pdf │ │ │ ├── sim-5-flows.pdf │ │ │ └── sim_data.txt │ │ ├── optsys │ │ │ ├── seq_new-long-cubic.pdf │ │ │ ├── seq_new-long-reno.pdf │ │ │ ├── seq_old-cubic.pdf │ │ │ ├── seq_old-reno.pdf │ │ │ ├── seq_resize-cubic-retcp-3000.pdf │ │ │ ├── seq_resize-cubic-retcp-3500.pdf │ │ │ ├── seq_resize-cubic.pdf │ │ │ ├── seq_resize-cubic_inset.pdf │ │ │ ├── seq_resize-reno-retcp-3000.pdf │ │ │ ├── seq_resize-reno-retcp-3500.pdf │ │ │ ├── seq_resize-reno.pdf │ │ │ ├── seq_static-all.pdf │ │ │ ├── seq_static-cubic-retcp.pdf │ │ │ ├── seq_static-cubic.pdf │ │ │ ├── seq_static-reno-retcp.pdf │ │ │ └── seq_static-reno.pdf │ │ ├── resize-99_vs_latency.pdf │ │ ├── resize-median_vs_latency.pdf │ │ ├── resize_vs_circuit_util.pdf │ │ ├── seq_resize-bbr.pdf │ │ ├── seq_resize-bic.pdf │ │ ├── seq_resize-cdg.pdf │ │ ├── seq_resize-cubic.pdf │ │ ├── seq_resize-dctcp.pdf │ │ ├── seq_resize-highspeed.pdf │ │ ├── seq_resize-htcp.pdf │ │ ├── seq_resize-hybla.pdf │ │ ├── seq_resize-illinois.pdf │ │ ├── seq_resize-lp.pdf │ │ ├── seq_resize-nv.pdf │ │ ├── seq_resize-reno.pdf │ │ ├── seq_resize-retcp.pdf │ │ ├── seq_resize-scalable.pdf │ │ ├── seq_resize-vegas.pdf │ │ ├── seq_resize-veno.pdf │ │ ├── seq_resize-westwood.pdf │ │ ├── seq_resize-yeah.pdf │ │ ├── seq_resize.pdf │ │ ├── seq_static.pdf │ │ ├── static_vs_circuit_util.pdf │ │ ├── static_vs_latency.pdf │ │ ├── throughput_vs_latency.pdf │ │ └── throughput_vs_latency99.pdf │ ├── nsdi2020.py │ ├── nsdi2020_graphs.py │ ├── optsys.py │ ├── sandbox.py │ ├── sequence_graphs │ │ ├── sg.py │ │ └── sg_cc.py │ ├── sim.py │ ├── validation.py │ └── validation_results.py ├── click_common.py ├── common.py ├── hdfs │ ├── README.md │ ├── graphs │ │ ├── hdfs_99th.pdf │ │ ├── hdfs_throughput.pdf │ │ ├── hdfs_utilization.pdf │ │ └── hdfs_writes_cdf.pdf │ ├── hdfs_dfsioe.py │ └── hdfs_graphs.py └── parse_logs.py ├── libADU ├── Makefile ├── README.md └── libADU.c ├── reHDFS ├── BlockPlacementPolicyRDCN.java └── README.md ├── reTCP ├── .gitignore ├── Makefile ├── README.md ├── kernel-patch.patch └── retcp.c ├── rpycd ├── README.md ├── rpycd.py └── rpycd.service └── vhost ├── .gitignore ├── README.md ├── config ├── hadoop_config │ ├── .gitignore │ ├── README.md │ ├── core-site.xml │ ├── hdfs-site.xml │ ├── mapred-site.xml │ ├── masters │ ├── topology.script.sh │ └── yarn-site.xml ├── hibench │ ├── README.md │ ├── dfsioe.conf │ ├── hadoop.conf │ └── hibench.conf └── ssh │ ├── README.md │ ├── authorized_keys │ ├── config │ ├── id_rsa │ └── id_rsa.pub └── etalon.dockerfile /.gitignore: -------------------------------------------------------------------------------- 1 | /data 2 | *~ 3 | .DS_Store 4 | *.pyc 5 | *.aux 6 | *.bbl 7 | *.blg 8 | paper.log 9 | paper.pdf 10 | handles.ec2 11 | server 12 | record_load.log 13 | record_tdf.log 14 | 15 | # Object files 16 | *.o 17 | *.ko 18 | *.obj 19 | *.elf 20 | 21 | # Precompiled Headers 22 | *.gch 23 | *.pch 24 | 25 | # Libraries 26 | *.lib 27 | *.a 28 | *.la 29 | *.lo 30 | 31 | # Shared objects (inc. Windows DLLs) 32 | *.dll 33 | *.so 34 | *.so.* 35 | *.dylib 36 | 37 | # Executables 38 | *.exe 39 | *.out 40 | *.app 41 | *.i*86 42 | *.x86_64 43 | *.hex 44 | 45 | # Debug files 46 | *.dSYM/ 47 | *.su 48 | 49 | nfqueue_test 50 | 51 | # make cache 52 | .cache.mk 53 | 54 | # VIM swap files 55 | *.swp 56 | *.swo 57 | 58 | # Emacs temporary files 59 | *~ 60 | \#*\# 61 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "click-etalon"] 2 | path = click-etalon 3 | url = https://github.com/mukerjee/click-etalon.git 4 | [submodule "flowgrind-etalon"] 5 | path = flowgrind-etalon 6 | url = https://github.com/mukerjee/flowgrind-etalon.git 7 | [submodule "libVT"] 8 | path = libVT 9 | url = https://github.com/mukerjee/libVT 10 | [submodule "ns3-etalon"] 11 | path = ns3-etalon 12 | url = https://github.com/Flasew/ns3-etalon.git 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2018 Matt Mukerjee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Etalon 2 | 3 | Etalon is a reconfigurable datacenter network (RDCN) emulator designed to run on 4 | public testbeds. We define an RDCN to be a datacenter network designed around 5 | adding and removing circuits in realtime to add bandwidth to portions of the 6 | network on demand. This has been seen in many research papers with a variety of 7 | technologies: e.g., 60GHz wireless 8 | ([Flyways](https://dl.acm.org/citation.cfm?id=2018442)), optical circuit 9 | switching ([c-Through](https://dl.acm.org/citation.cfm?id=1851222), 10 | [Helios](https://dl.acm.org/citation.cfm?id=1851223), 11 | [Mordia](https://dl.acm.org/citation.cfm?id=2486007), etc.), or free-space 12 | optics ([FireFly](https://dl.acm.org/citation.cfm?id=2626328), 13 | [ProjecToR](https://dl.acm.org/citation.cfm?id=2934911)). Some of these 14 | proposals incorporate a packet switch, which we model as well. With Etalon, 15 | we wish to provide a platform where repeatable end-to-end 16 | experiments on RDCN can be emulated at scale. 17 | 18 | 19 | ## Overview 20 | 21 | Etalon emulates a datacenter by running multiple virtual hosts ("vhosts"; Docker 22 | containers) on different physical machines. Physical machines represent 23 | datacenter "racks" in Etalon. vhosts on the same physical machine connect to the 24 | physical NIC using a virtualized switch (macvlan via docker), using ```tc``` to 25 | limit vhost link bandwidth (similar to a host to ToR link in a real datacenter). 26 | 27 | A separate physical machine emulates the reconfigurable switch in Etalon. It 28 | does this using a software switch (Click). Within the software switch, we 29 | emulate ToR VOQs, a circuit switch, and a packet switch. All parameters for 30 | these elements are adjustable (e.g., VOQ length, circuit switch reconfiguration 31 | penalty, link bandwidths/delays, etc.). 32 | 33 | Each "rack" is connected to the software switch via a physical switch in the 34 | testbed (e.g., a 40Gbps switch in CloudLab's Apt cluster). We also assume a 35 | second (control) network connects each "rack" and the software switch for 36 | convenience. 37 | 38 | Finally, to allow our system to scale beyond a single 40Gbps link, we use time 39 | dilation (```libVT```) to emulate many 40Gbps links simultaneously. 40 | 41 | See our paper for more information. 42 | 43 | 44 | ## Instructions 45 | 46 | Cluster Setup 47 | ------------- 48 | 49 | Coming soon... 50 | 51 | 52 | Running An Experiment 53 | --------------------- 54 | 55 | After finishing the initial setup, creating an experiment, running it, and 56 | collecting the results are all handled by our experiment framework, run on the 57 | software switch machine. 58 | 59 | The basic workflow for running an experiment on Etalon is: 60 | 61 | 1. On the software switch machine, launch ```/etalon/bin/click_startup.sh```. 62 | 63 | 2. In another window on the software switch machine, launch an experiment file 64 | script (e.g., ```cd /etalon/experiments/buffers; ./buffers.py```). 65 | 66 | The experiment file script will build a docker image (on the software switch 67 | machine), push the image to all of the "rack" machines, launch a "rack" of 68 | vhosts, run the experiment (e.g., running 16 flows for 40 seconds), and then 69 | collect the results (e.g., logs), and tar them. These logs can be processed by 70 | graphing scripts which we provide (see [Graphing the 71 | Results](#graphing-the-results) below). 72 | 73 | See ```experiments/buffers/buffers.py``` and 74 | ```experiments/buffers/buffer_common.py``` for an example of an experiment file 75 | script. Generally, an experiment file script will call 76 | ```initializeExperiment(image)```, loop through some switch configurations 77 | (e.g., changing ToR VOQ buffer sizes) while running some benchmark on each 78 | configuration (e.g., flowgrind or dfsio), and then call 79 | ```finishExperiment()```. The resulting tarball will always be in the directory 80 | where the script was launched from, and be named based on the timestamp at 81 | launch. ```finishExperiment()``` prints out this timestamp to make it easy to 82 | distinguish multiple resulting tar files. 83 | 84 | 85 | Graphing the Results 86 | -------------------- 87 | 88 | After running an experiment, there is a tar file with the results on the 89 | software switch machine. We assume that most users will want the option to 90 | archive the raw result data on their local machine, so we assume graphing 91 | scripts will be run on the local machine, not on the software switch machine. 92 | 93 | To graph results: 94 | 95 | 1. Copy the results tar file to your local machine. The file ```sxp.py``` in 96 | ```etalon/bin``` can simplify this. Assuming your ```etalon/etc/handles``` file 97 | is setup correctly, ```sxp.py``` will try to ```scp``` a results file from the 98 | folders ```experiments/buffers/```, ```experiments/adu```, or 99 | ```experiment/hdfs``` on the software switch machine to the local machine, given 100 | some timestamp. e.g., ```./sxp.py 1519494787``` will look in those directories 101 | for a tar file with 1519494787 in its name. 102 | 103 | 2. Untar the results file, into a new results folder (macOS' built in unarchiver 104 | will do this automatically, on Linux you'll need to create a folder first and 105 | put the tar in that folder and unarchive). 106 | 107 | 3. Call a graphing script with the results folder as an argument , e.g., ```cd 108 | experiments/buffers/ ; ./buffer_graphs.py ../../bin/1519494787-buffers```. This 109 | expects a folder named ```graphs``` to exist in the current folder. 110 | 111 | After running this you should have some graphs in a subfolder called 112 | ```graphs```. We include all graphing scripts used to create the graphs in our 113 | paper. If you wish to create other graphs, ```experiments/parse_logs.py``` will 114 | likely be useful. 115 | 116 | 117 | ## Directory Overview 118 | 119 | Read the README.md in each subfolder for more information. 120 | 121 | - ```bin```: various runables (e.g., performance tuning scripts, click script 122 | generation, installation scripts). 123 | 124 | - ```click-etalon```: our software switch emulation; a modified version of Click. 125 | 126 | - ```etc```: various configurations scripts. 127 | 128 | - ```experiments```: experiment framework and various example experiment file 129 | scripts. 130 | 131 | - ```flowgrind-etalon```: our flow generator; a modified version of flowgrind. 132 | 133 | - ```libADU```: an interposition library that tells the software switch how much 134 | data is waiting in the endhost stack. 135 | 136 | - ```libVT```: a virtual time interposition library ("time dilation") that we 137 | use to scale the number of links. 138 | 139 | - ```reHDFS```: our modified HDFS write replica placement algorithm for 140 | reconfigurable datacenter networks. 141 | 142 | - ```reTCP```: our TCP congestion control variant for reconfigurable datacenter 143 | networks. 144 | 145 | - ```rpycd```: a daemon that runs on each "rack" that allows the software switch 146 | machine to setup experiments on each "rack". 147 | 148 | - ```vhost```: code and supporting files for building the vhost image (docker 149 | image). 150 | 151 | 152 | ## Questions, Comments, Feedback 153 | 154 | We'd be happy to hear if you're using our emulator in your research. Let us know 155 | if you have any questions, comments, or feedback by sending an email to 156 | ```mukerjee at cs cmu edu```. 157 | -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | # bin 2 | 3 | Various runables: 4 | 5 | - `arp_clear.sh`: Clears the ARP table. 6 | 7 | - `arp_poison.sh`: Sets all physical host ARP entries to the switch host's MAC 8 | address, causing packets destined for the other physical hosts to be sent to 9 | the switch host instead. 10 | 11 | - `click_startup.sh`: Launches the hybrid switch. 12 | 13 | - `gen_switch.py`: Generates the hybrid switch specification. Executed by 14 | `click_startup.sh`. 15 | 16 | - `install.sh`: The top-level script for configuring an Etalon physical 17 | host. Call this on every physical host in the testbed cluster. 18 | 19 | - `kernel_install.sh`: A helper script that installs the updated kernel needed 20 | by reTCP. Executed by `install.sh`. Should not be executed directly. 21 | 22 | - `node_install.sh`: A helper script that configures a regular Etalon 23 | host. Executed by `install.sh`. Should not be executed directly. 24 | 25 | - `profile_etalon_ccanel.py`: A CloudLab profile that creates an Etalon cluster. 26 | 27 | - `profile_etalon_ccanel_bootstrap.py`: A simple CloudLab profile for creating a 28 | disk image for use with the `profile_etalon_ccanel.py` profile. 29 | 30 | - `retcp_install.sh`: A helper script that installs the reTCP kernel 31 | module. Configured by `install.sh` to run automatically on boot. Should not be 32 | executed directly. 33 | 34 | - `s`: Logs into a physical host via SSH using its handle (e.g., `./s host1` 35 | will log into `host1`). Assumes that `../etc/handles` is configured properly. 36 | 37 | - `switch_install.sh`: A helper script that configures an Etalon switch host. 38 | Executed by `install.sh`. Should not be executed directly. 39 | 40 | - `sxp.py`: Copies an experiment results file from the switch host to the local 41 | host using `scp`. E.g., `./sxp.py 1519494787` looks on the switch host for an 42 | experiment (in `/etalon/experiments/\[buffers, adu, hdfs\]`) result file with 43 | the timestamp `1519494787` and copies it to the local host. Assumes that 44 | `../etc/handles` is configured properly. 45 | 46 | - `tune.sh`: A helper script that sets IP addresses, does some tuning, and 47 | generates a proper `/etc/hosts` file. Assumes that hosts have hostnames 48 | "host1", "host2", etc. and that the switch has hostname "switch". Configured 49 | by `install.sh` to run automatically on boot. 50 | 51 | - `utils.sh`: Utility functions used by the other scripts. 52 | 53 | ## Instructions for creating a new Etalon disk image on CloudLab: 54 | 55 | ***Note: These instructions are untested.*** 56 | 57 | 1. Launch a dummy CloudLab cluster using the 58 | `profile_etalon_ccanel_bootstrap.py` profile. This will launch a simple 59 | one-node cluster. We will use this node as the basis for a new disk image. 60 | 61 | 2. Log in to the node, clone the Etalon repository into the home directory, and 62 | run `kernel_install.sh`. This will install the reTCP kernel patch and reboot 63 | the host. 64 | 65 | 3. Use the CloudLab web interface to create a new disk image based on this node. 66 | -------------------------------------------------------------------------------- /bin/arp_clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source /etalon/etc/script_config.sh 4 | 5 | # clear arp 6 | sudo ip link set arp off dev "$DATA_IF" 7 | sudo ip link set arp on dev "$DATA_IF" 8 | sudo ip link set arp off dev "$CONTROL_IF" 9 | sudo ip link set arp on dev "$CONTROL_IF" 10 | -------------------------------------------------------------------------------- /bin/arp_poison.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source /etalon/etc/script_config.sh 4 | 5 | /etalon/bin/arp_clear.sh 6 | 7 | sudo ping switch -c1 -W1 8 | 9 | for i in $(seq 1 "$NUM_RACKS"); do 10 | sudo arp -s "host$i" "$(arp | grep switch | tr -s " " | cut -d" " -f3)" 11 | done 12 | -------------------------------------------------------------------------------- /bin/click_startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | # Generate hybrid switch definition. 6 | /etalon/bin/gen_switch.py after > /tmp/hybrid-switch.click 7 | # Clear allocated hugepages, in case the previous run crashed and did not 8 | # deallocate them. (Not sure if this is actually necessary.) 9 | sudo rm -rfv /dev/hugepages/* /mnt/huge/* 10 | # Run the hybrid switch. 11 | # 12 | # Setting the coremask: On the Intel E5-2680's in the MAAS cluster, the 13 | # even-numbered cores are on socket 0 and the odd cores are on socket 1. The NIC 14 | # is attached to socket 1. So we need to only use the odd cores. 15 | # 16 | # Setting the memory channels: n = 4? n = 3? 17 | # http://dpdk-guide.gitlab.io/dpdk-guide/tuning/memory.html 18 | # http://dpdk.org/doc/guides/nics/mlx4.html 19 | sudo nice -n -20 \ 20 | click --dpdk -l 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39 \ 21 | -n 4 -- /tmp/hybrid-switch.click 22 | -------------------------------------------------------------------------------- /bin/gen_switch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os import path 4 | import sys 5 | # Directory containing this program. 6 | PROGDIR = path.dirname(path.realpath(__file__)) 7 | # For python_config. 8 | sys.path.insert(0, path.join(PROGDIR, '..', 'etc')) 9 | 10 | from python_config import DATA_EXT_IF, NUM_RACKS, HOSTS_PER_RACK, \ 11 | CIRCUIT_BW_Gbps_TDF, PACKET_BW_Gbps_TDF, CIRCUIT_LATENCY_s_TDF, \ 12 | PACKET_LATENCY_s_TDF, RECONFIG_DELAY_us, TDF, CLICK_PORT, \ 13 | get_data_ip_from_host, get_phost_from_id, get_host_from_rack_and_id 14 | 15 | log_poss = ["before", "after"] 16 | assert len(sys.argv) == 2, \ 17 | "Expected one argument: ".format(log_poss) 18 | log_pos = sys.argv[1] 19 | assert log_pos in log_poss, \ 20 | "Log position must be one of {}, but is: {}".format(log_poss, log_pos) 21 | 22 | def add_log(): 23 | """ Insert an HSLog element. This function must be called exactly once. """ 24 | # The name is required so that RunSchedule can call its handlers. 25 | print ' -> hsl :: HSLog($NUM_RACKS)' 26 | 27 | print 28 | print '// For more information, see etalon/bin/gen-switch.py.' 29 | print 30 | print 'define($DEVNAME %s)' % DATA_EXT_IF 31 | print 'define($NUM_RACKS %s)' % NUM_RACKS 32 | print 33 | 34 | # defining all host and vhost (data_net) IPs 35 | k = 0 36 | ip_def = 'define(' 37 | for i in xrange(1, NUM_RACKS+1): 38 | ip_def += '$IP%d %s, ' % (i, get_data_ip_from_host(get_phost_from_id(i))) 39 | for j in xrange(1, HOSTS_PER_RACK+1): 40 | ip_str = '$IP%d%d' % (i, j) 41 | ip = get_data_ip_from_host(get_host_from_rack_and_id(i, j)) 42 | ip_def += '%s %s, ' % (ip_str, ip) 43 | k += 1 44 | if k == 4: 45 | ip_def = ip_def[:-1] 46 | ip_def += '\n ' 47 | k = 0 48 | ip_def = ip_def.strip()[:-1] + ')' 49 | 50 | print ip_def 51 | print 52 | 53 | # all other params (set in ../etc/python_config.py) 54 | print 'define ($CIRCUIT_BW_Gbps_TDF %.1fGbps, $PACKET_BW_Gbps_TDF %.1fGbps)' % ( 55 | CIRCUIT_BW_Gbps_TDF, PACKET_BW_Gbps_TDF) 56 | print 57 | 58 | print 'define ($PACKET_LATENCY_s_TDF %s)' % PACKET_LATENCY_s_TDF 59 | print 'define ($CIRCUIT_LATENCY_s_TDF %s)' % CIRCUIT_LATENCY_s_TDF 60 | print 'define ($BIG_BUFFER_SIZE %s, $SMALL_BUFFER_SIZE %s)' % ( 61 | 128, 16) 62 | print 63 | 64 | print 'define ($RECONFIG_DELAY_us %d)' % RECONFIG_DELAY_us 65 | print 'define ($TDF %d)' % (int(TDF)) 66 | print 67 | 68 | # mappings of elements to cores 69 | print 'StaticThreadSched(in 0,' 70 | print ' traffic_matrix 1,' 71 | print ' sol 2,' 72 | print ' runner 3,' 73 | j = 4 74 | k = 0 75 | for i in xrange(1, NUM_RACKS+1): 76 | print ' hybrid_switch/circuit_link%d %d,' % (i, j) 77 | k += 1 78 | j += 1 79 | k = 0 80 | for i in xrange(1, NUM_RACKS+1): 81 | print ' hybrid_switch/packet_up_link%d %d,' % (i, j) 82 | k += 1 83 | if k == 4: 84 | j += 1 85 | k = 0 86 | k = 0 87 | for i in xrange(1, NUM_RACKS+1): 88 | print ' hybrid_switch/ps/packet_link%d %d,' % (i, j) 89 | k += 1 90 | if k == 4: 91 | j += 1 92 | k = 0 93 | print ')' 94 | print 95 | 96 | print 'ControlSocket("TCP", %d)' % (CLICK_PORT) 97 | print 98 | 99 | # the three control elements (see the paper) 100 | print 'traffic_matrix :: EstimateTraffic($NUM_RACKS, SOURCE QUEUE)' 101 | print 'sol :: Solstice($NUM_RACKS, $CIRCUIT_BW_Gbps_TDF, $PACKET_BW_Gbps_TDF, ' \ 102 | '$RECONFIG_DELAY_us, $TDF)' 103 | print 'runner :: RunSchedule($NUM_RACKS, RESIZE false)' 104 | print 105 | 106 | # entry and exit points 107 | print 'in :: FromDPDKDevice(0, MTU 9000)' 108 | print 'out :: ToDPDKDevice(0)' 109 | print 110 | 111 | # arp. Pattern 0 (port 0) is IP packets. Pattern 1 (port 1) is ARP replies. 112 | # Pattern 2 (port 2) is ARP requests. 113 | print 'arp_c :: Classifier(12/0800, 12/0806 20/0002, 12/0806 20/0001)' 114 | print 'arp_q :: ARPQuerier($DEVNAME:ip, $DEVNAME:eth)' 115 | print 'arp_r :: ARPResponder($DEVNAME)' 116 | print 117 | 118 | # defining input classifiers (i.e., packets from vhost h13 (rack 1, host 3) go 119 | # to switch input port 1, packets from h24 go to switch input port 2, etc.) 120 | print 'elementclass in_classify {' 121 | print ' input[0] -> IPClassifier(' 122 | for i in xrange(1, NUM_RACKS+1): 123 | rack_str = ' src host $IP%d or ' % (i) 124 | k = 1 125 | for j in xrange(1, HOSTS_PER_RACK+1): 126 | rack_str += 'src host $IP%d%d or ' % (i, j) 127 | k += 1 128 | if k == 4: 129 | rack_str = rack_str[:-1] 130 | rack_str += '\n ' 131 | if i < NUM_RACKS: 132 | print rack_str[:-4] + ',' 133 | else: 134 | print rack_str[:-4] 135 | print ' )' 136 | print ' => %soutput' % (str(list(xrange(NUM_RACKS)))) 137 | print '}' 138 | print 139 | 140 | # defining out classifiers (i.e., packets to vhost h13 (rack 1, host 3) go 141 | # to switch output port 1, packets to h24 go to switch output port 2, etc.) 142 | print 'elementclass out_classify {' 143 | print ' input[0] -> IPClassifier(' 144 | for i in xrange(1, NUM_RACKS+1): 145 | rack_str = ' dst host $IP%d or ' % (i) 146 | k = 1 147 | for j in xrange(1, HOSTS_PER_RACK+1): 148 | rack_str += 'dst host $IP%d%d or ' % (i, j) 149 | k += 1 150 | if k == 4: 151 | rack_str = rack_str[:-1] 152 | rack_str += '\n ' 153 | if i < NUM_RACKS: 154 | print rack_str[:-4] + ',' 155 | else: 156 | print rack_str[:-4] 157 | print ' )' 158 | print ' => %soutput' % (str(list(xrange(NUM_RACKS)))) 159 | print '}' 160 | print 161 | 162 | # defining a packet link 163 | print 'elementclass packet_link {' 164 | print ' input%s' % (str(list(xrange(NUM_RACKS)))) 165 | print ' => RoundRobinSched' 166 | print ' -> lu :: LinkUnqueue($PACKET_LATENCY_s_TDF, $PACKET_BW_Gbps_TDF)' 167 | print ' -> output' 168 | print '}' 169 | print 170 | 171 | # defining a circuit link 172 | print 'elementclass circuit_link {' 173 | print ' input%s' % (str(list(xrange(NUM_RACKS)))) 174 | print ' => ps :: SimplePullSwitch(-1)' 175 | print ' -> lu :: LinkUnqueue($CIRCUIT_LATENCY_s_TDF, $CIRCUIT_BW_Gbps_TDF)' 176 | print ' -> StoreData(1, 1) -> SetIPChecksum' # did packet go over circuit? 177 | print ' -> output' 178 | print '}' 179 | print 180 | 181 | 182 | ########################## 183 | # Packet Switch Definition 184 | ########################## 185 | print 'elementclass packet_switch {' 186 | 187 | # out classifier definition (used to put packets into right VOQ) 188 | out_classify = ' ' 189 | for i in xrange(1, NUM_RACKS+1): 190 | out_classify += 'c%d, ' % i 191 | print out_classify[:-2] + ' :: out_classify' 192 | print 193 | 194 | # VOQ definitions 195 | for i in xrange(1, NUM_RACKS+1): 196 | queues = ' ' 197 | for j in xrange(1, NUM_RACKS+1): 198 | queues += 'q%d%d, ' % (i, j) 199 | queues = queues[:-1] 200 | if i < NUM_RACKS: 201 | print queues 202 | else: 203 | print queues[:-1] 204 | print ' :: Queue(CAPACITY 3)' 205 | print 206 | 207 | # packet down links definitions 208 | pl = '' 209 | k = 0 210 | for i in xrange(1, NUM_RACKS+1): 211 | pl += 'packet_link%d, ' % i 212 | k += 1 213 | if k == 4: 214 | pl = pl[:-1] 215 | pl += '\n ' 216 | k = 0 217 | pl = pl.strip()[:-1] 218 | pl = ' ' + pl + ' :: packet_link' 219 | print pl 220 | print 221 | 222 | # wiring input to VOQs 223 | for i in xrange(1, NUM_RACKS+1): 224 | input = ' input[%d] -> c%d => ' % (i-1, i) 225 | for j in xrange(1, NUM_RACKS+1): 226 | input += 'q%d%d, ' % (i, j) 227 | input = input[:-2] 228 | print input 229 | print 230 | 231 | # wiring VOQs to packet down links to outputs 232 | for i in xrange(1, NUM_RACKS+1): 233 | output = ' ' 234 | for j in xrange(1, NUM_RACKS+1): 235 | output += 'q%d%d, ' % (j, i) 236 | output = output[:-2] 237 | output += ' => packet_link%d -> [%d]output' % (i, i-1) 238 | print output 239 | print 240 | 241 | # if packets would be dropped at queue, send them out 242 | # a special output for reuse (see hybrid switch) 243 | for i in xrange(1, NUM_RACKS+1): 244 | output = ' ' 245 | for j in xrange(1, NUM_RACKS+1): 246 | output += 'q%d%d[1], ' % (i, j) 247 | output = output[:-2] 248 | output += ' -> [%d]output' % (i-1 + NUM_RACKS) 249 | print output 250 | 251 | print '}' 252 | print 253 | ############################## 254 | # End Packet Switch Definition 255 | ############################## 256 | 257 | 258 | ############### 259 | # Hybrid Switch 260 | ############### 261 | print 'hybrid_switch :: {' 262 | 263 | # out classifier definition (used to put packets into right VOQ) 264 | out_classify = ' ' 265 | for i in xrange(1, NUM_RACKS+1): 266 | out_classify += 'c%d, ' % i 267 | print out_classify[:-2] + ' :: out_classify' 268 | print 269 | 270 | # VOQ definitions 271 | for i in xrange(1, NUM_RACKS+1): 272 | queues = ' ' 273 | for j in xrange(1, NUM_RACKS+1): 274 | queues += 'q%d%d, ' % (i, j) 275 | queues = queues[:-1] 276 | if i < NUM_RACKS: 277 | print queues 278 | else: 279 | print queues[:-1] 280 | print ' :: {' 281 | # We need to name the LockQueue "q" so that EstimateTraffic can find it. If 282 | # threshold-based ECN marking is enabled (e.g., for DCTCP), then LockQueue 283 | # annotates a packet with whether it should be marked. 284 | print ' input[0] -> q :: LockQueue(CAPACITY $SMALL_BUFFER_SIZE)' 285 | # lq is the loss queue. It fills up with packets that were dropped in the packet 286 | # switch. lq packets will have priority over q packets (see PrioSched, below). 287 | print ' input[1] -> lq :: Queue(CAPACITY 5)' 288 | # lq and q connect to input ports 0 and 1, respectively. When trying to pull a 289 | # packet, PrioSched checks its input ports in order, starting from port 0, 290 | # meaning that packets from port 0 (lq) will always be pulled before packets 291 | # from port 1 (q). 292 | print ' lq, q => PrioSched -> output' 293 | print ' }' 294 | print 295 | 296 | # circuit link definition 297 | cl = '' 298 | k = 0 299 | for i in xrange(1, NUM_RACKS+1): 300 | cl += 'circuit_link%d, ' % i 301 | k += 1 302 | if k == 4: 303 | cl = cl[:-1] 304 | cl += '\n ' 305 | k = 0 306 | cl = cl.strip()[:-1] 307 | cl = ' ' + cl + ' :: circuit_link' 308 | print cl 309 | print 310 | 311 | # packet up link definition 312 | pl = '' 313 | k = 0 314 | for i in xrange(1, NUM_RACKS+1): 315 | pl += 'packet_up_link%d, ' % i 316 | k += 1 317 | if k == 4: 318 | pl = pl[:-1] 319 | pl += '\n ' 320 | k = 0 321 | pl = pl.strip()[:-1] 322 | pl = ' ' + pl + ' :: packet_link' 323 | print pl 324 | print 325 | 326 | # packet switch definition 327 | print ' ps :: packet_switch' 328 | print 329 | 330 | # wiring inputs to VOQs 331 | for i in xrange(1, NUM_RACKS+1): 332 | # Input port paint 333 | input = ' input[%d] -> Paint(%d, 20) -> c%d => ' % (i-1, i, i) 334 | for j in xrange(1, NUM_RACKS+1): 335 | input += 'q%d%d, ' % (i, j) 336 | input = input[:-2] 337 | print input 338 | print 339 | 340 | # wiring VOQs to circuit links to outputs 341 | for i in xrange(1, NUM_RACKS+1): 342 | output = ' ' 343 | for j in xrange(1, NUM_RACKS+1): 344 | output += 'q%d%d, ' % (j, i) 345 | output = output[:-2] 346 | # dest paint 347 | output += ' => circuit_link%d -> Paint(%d, 21) -> ' \ 348 | '[%d]output' % (i, i, i-1) 349 | print output 350 | print 351 | 352 | # wiring VOQs to an on/off element to allow packet switch shutoff 353 | # for individual VOQs (i.e., when the circuit link is on) 354 | for i in xrange(1, NUM_RACKS+1): 355 | for j in xrange(1, NUM_RACKS+1): 356 | print ' q%d%d -> pps%d%d :: SimplePullSwitch(0)' % (i, j, i, j) 357 | 358 | # wiring on/off elements to packet up link to packet switch to output 359 | for i in xrange(1, NUM_RACKS+1): 360 | output = ' ' 361 | for j in xrange(1, NUM_RACKS+1): 362 | output += 'pps%d%d, ' % (i, j) 363 | output = output[:-2] 364 | # dest paint 365 | output += ' => packet_up_link%d -> [%d]ps[%d] -> ' \ 366 | 'Paint(%d, 21) -> [%d]output' % (i, i-1, i-1, i, i-1) 367 | print output 368 | print 369 | 370 | # Wiring dropped packet switch packets to loss queues in respective VOQ. 371 | for i in xrange(1, NUM_RACKS+1): 372 | output = ' ps[%d] -> out_classify => ' % (i-1 + NUM_RACKS) 373 | for j in xrange(1, NUM_RACKS+1): 374 | output += '[1]q%d%d, ' % (i, j) 375 | output = output[:-2] 376 | print output 377 | 378 | print '}' 379 | print 380 | ################### 381 | # End Hybrid Switch 382 | ################### 383 | 384 | ################## 385 | # Main Connections 386 | ################## 387 | print 'in -> arp_c' 388 | print ' -> MarkIPHeader(14)' 389 | print ' -> StripToNetworkHeader ' 390 | print ' -> GetIPAddress(IP dst)' 391 | # The second pattern matches all packets, i.e., any packets that do not match 392 | # the first pattern. Packets that match the second pattern are forwarded out 393 | # port 1, which is connected to the downstream elements. 394 | print ' -> pc :: IPClassifier(dst host $DEVNAME:ip icmp echo, -)[1] ' 395 | # Only diverts ACKs if set to 1 (meaning that packets should be sent our port 396 | # 1). Used in validation. Normally, acks are passed through to port 0 (set by 397 | # the "0" parameter). When connecting elements together, since we do not specify 398 | # a port to connect to the next element, by default port 0 is used. 399 | print ' -> divert_acks :: Switch(0)' 400 | # Set the time at which the packet hits this element. 401 | print ' -> st :: SetTimestamp(FIRST true) ' 402 | if log_pos == "before": 403 | add_log() 404 | # Split the packet stream based on rack. 405 | print ' -> in_classify%s' % (str(list(xrange(NUM_RACKS)))) 406 | # The hybrid switch itself. 407 | print ' => hybrid_switch%s' % (str(list(xrange(NUM_RACKS)))) 408 | if log_pos == "after": 409 | add_log() 410 | # ECE marking (for reTCP). The name is required so that RunSchedule can call 411 | # its handler. 412 | print ' -> ecem :: ECEMark($NUM_RACKS)' 413 | # Packets then pass through MarkIPCE, which looks at the THRESH_EXCEEDED_ANNO 414 | # user annotation and, if it equals 1, sets the packet's ECN bits to "Congestion 415 | # Experienced". This element must be named so that scripts can call its write 416 | # handlers. 417 | print ' -> ecn :: MarkIPCE(FORCE true)' 418 | print ' -> arp_q ' 419 | print ' -> out' 420 | print 421 | # Used in validation. Port 1 is connected to a different path. Packets normally 422 | # go out port 0. Setting the element's "switch" handler to 1 forwards packets 423 | # out port 1 instead. 424 | print 'divert_acks[1] ' \ 425 | '-> acks :: IPClassifier(tcp ack and len < 100, -)[1] -> st' 426 | print 'acks -> arp_q' 427 | print 428 | # Connect ARP replies to the ARPQuerier. 429 | print 'arp_c[1] -> [1]arp_q' 430 | # Connect ARP requests to the ARPResponder. 431 | print 'arp_c[2] -> arp_r -> out' 432 | print 433 | # ping responder. pc[0] is ICMP echo packets. 434 | print 'pc -> ICMPPingResponder -> arp_q' 435 | 436 | ###################### 437 | # End Main Connections 438 | ###################### 439 | -------------------------------------------------------------------------------- /bin/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Set up an Etalon testbed machine. 4 | # 5 | # The first argument is the machine name (e.g., "host1", "switch", etc.). The 6 | # second argument is whether to reboot automatically ("yes" or "no"). 7 | 8 | set -o errexit 9 | 10 | NEW_HOSTNAME=$1 11 | REBOOT=$2 12 | UBUNTU_VERSION_SUPPORTED=18.04 13 | OFED_VERSION=4.6-1.0.1.1 14 | 15 | # Validate. 16 | if [ ! -d "$HOME/etalon" ]; then 17 | echo "Error: Etalon repo not located at \"$HOME/etalon\"!" 18 | exit 1 19 | fi 20 | if [[ ! $REBOOT =~ ^(yes|no)$ ]]; then 21 | echo "The second parameter must be either \"yes\" or \"no\", but is: " \ 22 | "$REBOOT" 23 | exit 1 24 | fi 25 | source "$HOME/etalon/bin/utils.sh" 26 | hostname_validate "$NEW_HOSTNAME" 27 | ubuntu_validate_version "$UBUNTU_VERSION_SUPPORTED" 28 | 29 | # Put etalon in a global location. 30 | sudo rm -rfv /etalon 31 | sudo ln -sfv "$HOME/etalon" / 32 | 33 | # On boot, run tuning and install reTCP. Preserve preexisting crontab entries. 34 | (if crontab -l; then 35 | crontab -l 36 | fi 37 | echo "@reboot sleep 60 && /etalon/bin/tune.sh && /etalon/bin/retcp_install.sh" 38 | ) | crontab - 39 | if ! crontab -l | grep tune.sh; then 40 | echo "Error adding entry to crontab!" 41 | exit 1 42 | fi 43 | 44 | # Change the hostname. 45 | OLD_HOSTNAME=$(hostname) 46 | sudo hostname "$NEW_HOSTNAME" 47 | sudo sed -i "s/$OLD_HOSTNAME/$NEW_HOSTNAME/g" /etc/hostname 48 | sudo sed -i "s/$OLD_HOSTNAME/$NEW_HOSTNAME/g" /etc/hosts 49 | if [ "$(hostname)" != "$NEW_HOSTNAME" ]; then 50 | echo "Error: Problem setting hostname to $NEW_HOSTNAME" 51 | exit 1 52 | fi 53 | 54 | # Set IPs. 55 | source /etalon/etc/script_config.sh 56 | if echo "$NEW_HOSTNAME" | grep -q switch; then 57 | NEW_CONTROL_IP=$SWITCH_CONTROL_IP 58 | NEW_DATA_IP=$SWITCH_DATA_IP 59 | else 60 | HOST_NUM="${NEW_HOSTNAME:4}" 61 | NEW_CONTROL_IP=10.$CONTROL_NET.100.$HOST_NUM 62 | NEW_DATA_IP=10.$DATA_NET.100.$HOST_NUM 63 | fi 64 | sudo cp -fv /etalon/etc/netplan/99-etalon.yaml /etc/netplan/ 65 | sudo sed -i "s/CONTROL_IP/$NEW_CONTROL_IP/g" /etc/netplan/99-etalon.yaml 66 | sudo sed -i "s/DATA_IP/$NEW_DATA_IP/g" /etc/netplan/99-etalon.yaml 67 | sudo netplan apply 68 | 69 | # Make our own /etc/hosts. 70 | printf "%s\\t%s\\n" "127.0.0.1" "localhost" | sudo tee /etc/hosts 71 | printf "%s\\t%s\\n" "$SWITCH_DATA_IP" "switch" | sudo tee -a /etc/hosts 72 | # Add all the physical hosts to /etc/hosts. 73 | for i in $(seq 1 "$NUM_RACKS"); do 74 | printf "%s\\t%s\\n" "10.$DATA_NET.100.$i" "host$i" | sudo tee -a /etc/hosts 75 | done 76 | # Add all the emulated hosts to /etc/hosts. 77 | for i in $(seq 1 "$NUM_RACKS"); do 78 | for j in $(seq 1 "$HOSTS_PER_RACK"); do 79 | printf "%s\\t%s\\n" "10.$DATA_NET.$i.$j" "h$i$j.$FQDN" | \ 80 | sudo tee -a /etc/hosts 81 | done 82 | done 83 | if echo "$NEW_HOSTNAME" | grep -q switch; then 84 | # If this is the switch, then communicate with everyone over the control 85 | # network instead of the data network. 86 | sudo sed -i "s/10\\.$DATA_NET\\./10\\.$CONTROL_NET\\./g" /etc/hosts 87 | fi 88 | 89 | # Disable unattended upgrades. 90 | sudo sed -i "s/1/0/g" /etc/apt/apt.conf.d/20auto-upgrades 91 | 92 | # Load all available TCP variants. Loop over the list of IPv4 kernel modules. 93 | for VAR in /lib/modules/"$(uname -r)"/kernel/net/ipv4/*.ko; do 94 | # Find the TCP modules, but remove "tcp_probe" because it does not work and 95 | # "tcp_diag" because it is for monitoring only. 96 | if (grep "tcp" <<< "$VAR") && ! (grep "tcp_probe" <<< "$VAR") && \ 97 | ! (grep "tcp_diag" <<< "$VAR"); then 98 | # Drop the absolute path and the ".ko" extension. 99 | VAR=$(basename "$VAR" ".ko") 100 | # Load the module. 101 | sudo modprobe "$VAR" 102 | # Mark the module to be loaded automatically on boot. 103 | echo "$VAR" | sudo tee -a /etc/modules 104 | fi 105 | done 106 | 107 | # Install dependencies. 108 | sudo apt update 109 | sudo apt install -y git linuxptp python-pip 110 | sudo -H pip install numpy rpyc 111 | 112 | # Install development/debugging tools. 113 | sudo apt install -y emacs gdb iperf3 iptables mstflint tcpdump tmux pylint tree 114 | 115 | # Install Mellanox OFED. 116 | # https://docs.mellanox.com/display/MLNXOFEDv461000/Introduction 117 | echo "Installing MLNX OFED..." 118 | # Download and mount the ISO. 119 | cd 120 | wget http://www.mellanox.com/downloads/ofed/MLNX_OFED-$OFED_VERSION/MLNX_OFED_LINUX-$OFED_VERSION-ubuntu$UBUNTU_VERSION_SUPPORTED-x86_64.iso 121 | if mount | grep "/mnt "; then 122 | sudo umount /mnt 123 | fi 124 | sudo mount -o ro,loop \ 125 | MLNX_OFED_LINUX-$OFED_VERSION-ubuntu$UBUNTU_VERSION_SUPPORTED-x86_64.iso \ 126 | /mnt 127 | # Install. Do "--kernel-only" to avoid conflicts with DPDK's dependencies. 128 | sudo /mnt/mlnxofedinstall --kernel-only --force 129 | sudo connectx_port_config -c eth,eth 130 | echo "options mlx4_core log_num_mgm_entry_size=-7" | \ 131 | sudo tee -a /etc/modprobe.d/mlx4.conf 132 | sudo /etc/init.d/openibd restart 133 | # Clean up, but keep the ISO as a record. 134 | sudo umount /mnt 135 | 136 | # Install Docker. 137 | echo "Installing Docker..." 138 | GET_DOCKER="$HOME/get-docker.sh" 139 | curl -fsSL https://get.docker.com -o "$GET_DOCKER" 140 | sudo sh "$GET_DOCKER" 141 | rm -fv "$GET_DOCKER" 142 | 143 | # Install PTP. 144 | echo "Setting up PTP..." 145 | printf "[enp68s0]\\n" | sudo tee -a /etc/linuxptp/ptp4l.conf 146 | sudo sed -i "/(PTP) service/a Requires=network.target\\nAfter=network.target" \ 147 | /lib/systemd/system/ptp4l.service 148 | sudo sed -i "s/ -i eth0//" /lib/systemd/system/ptp4l.service 149 | sudo sed -i "s/-w -s eth0/-c enp68s0 -s CLOCK_REALTIME -w/" \ 150 | /lib/systemd/system/phc2sys.service 151 | sudo systemctl daemon-reload 152 | sudo systemctl enable phc2sys.service 153 | if systemctl list-unit-files | grep ntp.service; then 154 | sudo systemctl disable ntp.service 155 | fi 156 | 157 | if [ "$NEW_HOSTNAME" = "switch" ]; then 158 | source /etalon/bin/switch_install.sh 159 | else 160 | source /etalon/bin/node_install.sh "$NEW_HOSTNAME" 161 | fi 162 | 163 | # Do this last because afterwards apt complains and prevents packages from being 164 | # installed. 165 | source /etalon/bin/kernel_install.sh 166 | 167 | # Fix permissions of ~/.config. Do this last because something else is setting 168 | # the owner to "root". 169 | if [ -d "$HOME/.config" ]; then 170 | sudo chown -R "$(whoami)":"$(whoami)" "$HOME/.config" 171 | fi 172 | 173 | echo "Done" 174 | if [ "$REBOOT" = "yes" ]; then 175 | sudo reboot 176 | fi 177 | -------------------------------------------------------------------------------- /bin/kernel_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Install the Etalon reTCP kernel patch. 4 | 5 | set -o errexit 6 | 7 | UBUNTU_VERSION_SUPPORTED="bionic" 8 | BUILD_DIR=$HOME/build 9 | SRC_DIR=$BUILD_DIR/ubuntu-$UBUNTU_VERSION_SUPPORTED 10 | 11 | # Validate. 12 | if [ ! -d /etalon ]; then 13 | echo "Error: Etalon repo not located at \"/etalon\"!" 14 | exit 1 15 | fi 16 | source /etalon/bin/utils.sh 17 | ubuntu_validate_codename $UBUNTU_VERSION_SUPPORTED 18 | 19 | # Prepare the build directory. 20 | rm -rf "$BUILD_DIR" 21 | mkdir -pv "$BUILD_DIR" 22 | 23 | # Install dependencies and download sources. 24 | sudo sed -i "/^# deb-src /s/^# //" /etc/apt/sources.list 25 | sudo apt update 26 | sudo apt -y build-dep "linux-image-$(uname -r)" 27 | sudo apt install -y \ 28 | git fakeroot libssl-dev libelf-dev libudev-dev libpci-dev flex bison \ 29 | python libiberty-dev libdw-dev elfutils systemtap-sdt-dev libunwind-dev \ 30 | libaudit-dev liblzma-dev libnuma-dev linux-cloud-tools-common \ 31 | linux-tools-common kernel-wedge 32 | git clone \ 33 | "git://kernel.ubuntu.com/ubuntu/ubuntu-$UBUNTU_VERSION_SUPPORTED.git" \ 34 | "$SRC_DIR" 35 | 36 | # Apply the patch, compile and install. 37 | cd "$SRC_DIR" 38 | git checkout Ubuntu-4.15.0-50.54 39 | git apply /etalon/reTCP/kernel-patch.patch 40 | fakeroot debian/rules clean 41 | # Perf needs $PYTHON to be set. 42 | MAKEFLAGS="-j $(nproc)" PYTHON=$(command -v python) fakeroot debian/rules \ 43 | binary-headers binary-generic binary-perarch 44 | sudo dpkg --force-all -i "$BUILD_DIR"/*.deb 45 | printf "\\nGRUB_DEFAULT=\"Advanced options for Ubuntu>Ubuntu, with Linux 4.15.0-50-generic\"\\n" \ 46 | | sudo tee -a /etc/default/grub 47 | sudo update-grub 48 | -------------------------------------------------------------------------------- /bin/log_cwnd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import mmap 4 | import os 5 | from os import path 6 | import sys 7 | import time 8 | 9 | 10 | def parse_adr(adr): 11 | """ 12 | Converts an IP address + port in the form aaaaaaaa:pppp to the form 13 | a.b.c.d:p. 14 | """ 15 | ip_raw, port_raw = adr.split(":") 16 | # Split into a list of the octets. 17 | ip_hexs = [ip_raw[idx: idx + 2] for idx in xrange(0, len(ip_raw), 2)] 18 | # Convert each octet to an int. 19 | ip_ints = [int(hex_str, 16) for hex_str in ip_hexs] 20 | ip_ints.reverse() 21 | return("{}:{}".format(".".join([str(x) for x in ip_ints]), int(port_raw, 16))) 22 | 23 | 24 | def main(): 25 | assert len(sys.argv) == 6, \ 26 | ("Expected five arguments: logging interval (seconds), logging " 27 | "duration (seconds), local IP address + port (a.b.c.d:p1), remote IP " 28 | "address + port (e.f.g.h:p2), output file") 29 | intv_s, dur_s, lcl_adr_tgt, rem_adr_tgt, out = sys.argv[1:] 30 | dur_s = float(dur_s) 31 | intv_s = float(intv_s) 32 | # Make sure that the output file does not already exist. 33 | if path.exists(out): 34 | print("Output file already exists: {}".format(out)) 35 | sys.exit(-1) 36 | else: 37 | # Create the output directory if it does not already exist. 38 | odr = path.dirname(out) 39 | if odr and not path.isdir(odr): 40 | os.makedirs(odr) 41 | 42 | # While polling the file, only record the lines to maximize sample rate. 43 | start_s = time.time() 44 | cur_s = start_s 45 | delt_s = 0 46 | tstamp_lines = [] 47 | with open("/proc/net/tcp", "r+") as f: 48 | while (delt_s < dur_s): 49 | delt_s = time.time() - start_s 50 | tstamp_lines.append((delt_s, [line for line in f])) 51 | f.seek(0) 52 | time.sleep(intv_s) 53 | 54 | # Do all the data parsing once we are done.. 55 | cwnds = [] 56 | for tstamp_s, lines in tstamp_lines: 57 | # Find the lines corresponding to outgoing connections, and extract 58 | # their cwnds. Skip the first line, which is the column headings. 59 | for line in lines[1:]: 60 | splits = line.strip().split() 61 | lcl_adr = parse_adr(splits[1]) 62 | rem_adr = parse_adr(splits[2]) 63 | if lcl_adr == lcl_adr_tgt and rem_adr == rem_adr_tgt: 64 | cwnds.append((tstamp_s, int(splits[15]))) 65 | 66 | with open(out, "w") as f: 67 | for tstamp_s, cwnd in cwnds: 68 | f.write("{},{}\n".format(tstamp_s, cwnd)) 69 | 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /bin/node_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Set up an Etalon node machine. 4 | # 5 | # The first argument is the new hostname. 6 | 7 | set -o errexit 8 | 9 | NEW_HOSTNAME=$1 10 | 11 | # Validate. 12 | if [ ! -d /etalon ]; then 13 | echo "Error: Etalon repo not located at \"/etalon\"!" 14 | exit 1 15 | fi 16 | source /etalon/bin/utils.sh 17 | hostname_validate "$NEW_HOSTNAME" 18 | 19 | sudo apt update 20 | sudo apt install -y iputils-arping 21 | 22 | # Start the rpyc daemon. 23 | echo "Starting rpyc daemon..." 24 | sudo systemctl enable /etalon/rpycd/rpycd.service 25 | 26 | # Pipework. 27 | echo "Installing Pipework..." 28 | cd 29 | sudo bash -c "curl https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework > /usr/local/bin/pipework" 30 | sudo chmod +x /usr/local/bin/pipework 31 | 32 | # Give SSH access to the switch. 33 | cat /etalon/vhost/config/ssh/id_rsa.pub >> "$HOME/.ssh/authorized_keys" 34 | -------------------------------------------------------------------------------- /bin/nsdi2020.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run NSDI 2020 experiments. 4 | 5 | set -o errexit 6 | 7 | function cleanup { 8 | # Remove old results files that require root priviledges. 9 | sudo rm -fv /tmp/*click.txt 10 | # Remove results from aborted experiments. 11 | rm -fv "$HOME"/1tb/*txt 12 | } 13 | 14 | rm -fv /tmp/docker_built 15 | 16 | cleanup 17 | # Flush the OS buffer cache. 18 | sudo sync; 19 | echo "1" | sudo tee /proc/sys/vm/drop_caches 20 | # Run experiments. 21 | "$HOME"/etalon/experiments/buffers/nsdi2020.py 22 | cleanup 23 | 24 | ssh -t host1 'sudo sysctl -w net.ipv4.tcp_congestion_control=cubic' 25 | ssh -t host2 'sudo sysctl -w net.ipv4.tcp_congestion_control=cubic' 26 | ssh -t host3 'sudo sysctl -w net.ipv4.tcp_congestion_control=cubic' 27 | -------------------------------------------------------------------------------- /bin/profile_etalon_ccanel.py: -------------------------------------------------------------------------------- 1 | """ A CloudLab profile that creates an Etalon cluster. """ 2 | 3 | import geni.portal as portal 4 | import geni.rspec.pg as pg 5 | import geni.rspec.igext as igext 6 | 7 | SWITCH_DISK_IMAGE = "urn:publicid:IDN+utah.cloudlab.us+image+emulab-ops//UBUNTU16-64-STD" 8 | NODE_DISK_IMAGE = "urn:publicid:IDN+apt.cloudlab.us+image+dna-PG0:etalon-ccanel" 9 | 10 | pc = portal.Context() 11 | rspec = pg.Request() 12 | 13 | pc.defineParameter( 14 | name="node_type", 15 | description=("Hardware spec of nodes.
Refer to manuals at " 16 | "APT " 17 | "for more details."), 18 | typ=portal.ParameterType.NODETYPE, 19 | defaultValue="r320", 20 | legalValues=[("r320", "APT r320"), ("c6220", "APT c6220")], 21 | advanced=False, 22 | groupId=None) 23 | pc.defineParameter( 24 | name="num_nodes", 25 | description=("Number of hosts (not including OCS) to use.
Check cluster availability " 26 | "here."), 27 | typ=portal.ParameterType.INTEGER, 28 | defaultValue=8, 29 | legalValues=[], 30 | advanced=False, 31 | groupId=None) 32 | pc.defineParameter( 33 | name="switch", 34 | description="Preferred Mellanox switch", 35 | typ=portal.ParameterType.STRING, 36 | defaultValue="mellanox3", 37 | legalValues=[("mellanox1", "Switch 1"), 38 | ("mellanox2", "Switch 2"), ("mellanox3", "Switch 3"), 39 | ("mellanox4", "Switch 4"), ("mellanox5", "Switch 5"), 40 | ("mellanox6", "Switch 6"), ("mellanox7", "Switch 7"), 41 | ("mellanox8", "Switch 8"), ("mellanox9", "Switch 9")], 42 | advanced=False, 43 | groupId=None) 44 | 45 | params = pc.bindParameters() 46 | if params.num_nodes < 1: 47 | pc.reportError(portal.ParameterError("You must choose a minimum of 1 node ")) 48 | pc.verifyParameters() 49 | 50 | lan = pg.LAN("lan") 51 | nodes = [] 52 | 53 | switch_node = pg.RawPC("switch") 54 | iface = switch_node.addInterface("if-switch", pg.IPv4Address("10.2.100.100", "255.255.255.0")) 55 | lan.addInterface(iface) 56 | 57 | switch_node.hardware_type = params.node_type 58 | switch_node.disk_image = SWITCH_DISK_IMAGE 59 | switch_node.Desire(params.switch, 1.0) 60 | switch_node.addService( 61 | pg.Install("https://github.com/ccanel/etalon/archive/master.tar.gz", "/local/")) 62 | switch_node.addService(pg.Execute("/bin/bash", "/local/etalon-master/bin/switch_install.sh")) 63 | nodes.append(switch_node) 64 | rspec.addResource(switch_node) 65 | 66 | for i in range(1, params.num_nodes + 1): 67 | node = pg.RawPC("host%s" % i) 68 | iface = node.addInterface("if-host%s" % i, pg.IPv4Address("10.2.100.%d" % i, "255.255.255.0")) 69 | lan.addInterface(iface) 70 | 71 | node.hardware_type = params.node_type 72 | node.disk_image = NODE_DISK_IMAGE 73 | node.Desire(params.switch, 1.0) 74 | node.addService( 75 | pg.Install("https://github.com/ccanel/etalon/archive/master.tar.gz", "/local/")) 76 | node.addService(pg.Execute("/bin/bash", "/local/etalon-master/bin/node_install.sh")) 77 | nodes.append(node) 78 | rspec.addResource(node) 79 | rspec.addResource(lan) 80 | 81 | instructions = ("Use this profile to instantiate a set of nodes on the APT cluster, all bound to " 82 | "the same mellanox switch, for use with Etalon.") 83 | desc = "Etalon Reconfigurable Datacenter Emulator." 84 | tour = igext.Tour() 85 | tour.Description(type=igext.Tour.TEXT, desc=desc) 86 | tour.Instructions(type=igext.Tour.MARKDOWN, inst=instructions) 87 | rspec.addTour(tour) 88 | pc.printRequestRSpec(rspec) 89 | -------------------------------------------------------------------------------- /bin/profile_etalon_ccanel_bootstrap.py: -------------------------------------------------------------------------------- 1 | """ 2 | A simple CloudLab profile for creating a disk image for use with the 3 | "profile_etalon-ccanel.py" profile. 4 | """ 5 | 6 | import geni.portal as portal 7 | import geni.rspec.pg as pg 8 | import geni.rspec.igext as igext 9 | 10 | DISK_IMAGE = "urn:publicid:IDN+utah.cloudlab.us+image+emulab-ops//UBUNTU16-64-STD" 11 | 12 | pc = portal.Context() 13 | rspec = pg.Request() 14 | 15 | pc.defineParameter( 16 | name="node_type", 17 | description=("Hardware spec of node.
Refer to manuals at " 18 | "APT " 19 | "for more details."), 20 | typ=portal.ParameterType.NODETYPE, 21 | defaultValue="r320", 22 | legalValues=[("r320", "APT r320"), ("c6220", "APT c6220")], 23 | advanced=False, 24 | groupId=None) 25 | params = pc.bindParameters() 26 | 27 | node = pg.RawPC("host") 28 | node.hardware_type = params.node_type 29 | node.disk_image = DISK_IMAGE 30 | rspec.addResource(node) 31 | 32 | instructions = "Use this profile to create a new Etalon disk image with the reTCP kernel patch." 33 | desc = "A very basic profile with a single clean node." 34 | tour = igext.Tour() 35 | tour.Description(type=igext.Tour.TEXT, desc=desc) 36 | tour.Instructions(type=igext.Tour.MARKDOWN, inst=instructions) 37 | rspec.addTour(tour) 38 | pc.printRequestRSpec(rspec) 39 | -------------------------------------------------------------------------------- /bin/retcp_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Install reTCP. 4 | # 5 | # This is done in a cron script on boot because, as part of the install process, 6 | # we install a new kernel and reboot. We want to make sure that the new kernel's 7 | # /lib/modules/$(uname -r) directory has been created before trying to copy the 8 | # reTCP kernel module there. 9 | 10 | set -o errexit 11 | 12 | cd /etalon/reTCP/ 13 | make -j "$(nproc)" 14 | sudo cp -fv retcp.ko "/lib/modules/$(uname -r)" 15 | sudo depmod 16 | sudo modprobe retcp 17 | 18 | # Always load the reTCP kernel module on boot. 19 | if ! grep "retcp" /etc/modules; then 20 | echo "retcp" | sudo tee -a /etc/modules 21 | fi 22 | -------------------------------------------------------------------------------- /bin/s: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | sys.path.insert(0, '../etc') 5 | import os 6 | 7 | from python_config import * 8 | 9 | if __name__ == '__main__': 10 | os.system('ssh -A -o StrictHostKeyChecking=no %s' % ( 11 | handle_to_machine(sys.argv[1]))) 12 | -------------------------------------------------------------------------------- /bin/switch_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Set up an Etalon switch machine. 4 | 5 | set -o errexit 6 | 7 | # Validate. 8 | if [ ! -d "/etalon" ]; then 9 | echo "Error: Etalon repo not located at \"/etalon\"!" 10 | exit 1 11 | fi 12 | 13 | # Add entries to the FORWARD iptable to enable Linux IP forwarding for the 14 | # emulated hosts. This is not required. but is useful for debugging. This allows 15 | # us to disable the Etalon hybrid switch without breaking connectivity between 16 | # the emulated hosts. Otherwise, to send traffic between the emulated hosts 17 | # without using the Etalon hybrid switch, we would need to modify the emulated 18 | # hosts' ARP tables to remove the poison records that divert traffic destined 19 | # for other emulated hosts to the switch machine. 20 | for i in $(seq 1 "$NUM_RACKS"); do 21 | for j in $(seq 1 "$HOSTS_PER_RACK"); do 22 | sudo iptables -I FORWARD -s "10.$DATA_NET.$i.$j" -j ACCEPT 23 | done 24 | done 25 | sudo rm -fv /etc/iptables/rules.v4 26 | sudo mkdir -pv /etc/iptables 27 | sudo iptables-save | while read -r x; do 28 | echo "$x" | sudo tee -a /etc/iptables/rules.v4 29 | done 30 | echo iptables-persistent iptables-persistent/autosave_v4 boolean true | \ 31 | sudo debconf-set-selections 32 | echo iptables-persistent iptables-persistent/autosave_v6 boolean true | \ 33 | sudo debconf-set-selections 34 | 35 | # Install dependencies. 36 | sudo apt update 37 | sudo apt install -y \ 38 | autoconf \ 39 | cmake \ 40 | iptables-persistent \ 41 | lib32z1-dev \ 42 | libcurl4-gnutls-dev \ 43 | libnuma-dev \ 44 | libxmlrpc-core-c3-dev \ 45 | maven \ 46 | openjdk-8-jdk \ 47 | uuid-dev 48 | 49 | cd /etalon 50 | git submodule update --init 51 | 52 | # Mount volumes. 53 | # 54 | # NOTE: These two volumes only apply to the machine "node5.maas" in the CMU CMCL 55 | # machine room (GHC 8126). 56 | # 57 | # Mount a 100 GB tmpfs on /tmp. 58 | echo "tmpfs /tmp tmpfs " \ 59 | "defaults,noatime,nosuid,nodev,noexec,mode=1777,size=100G 0 0" | \ 60 | sudo tee -a /etc/fstab 61 | # Mount a 1 TB scratch disk on $HOME/1tb 62 | sudo mkfs.ext4 /dev/sdc 63 | UUID=$(sudo blkid | grep sdc | cut -d" " -f2 | sed "s/\"//g") 64 | mkdir -pv "$HOME/1tb" 65 | echo "$UUID $HOME/1tb ext4 defaults 0 2" | sudo tee -a /etc/fstab 66 | sudo mount -a 67 | sudo chown -R "$(whoami)":"$(whoami)" "$HOME/1tb" 68 | 69 | # Mellanox DPDK. 70 | # http://www.mellanox.com/related-docs/prod_software/MLNX_DPDK_Quick_Start_Guide_v16.11_2.3.pdf 71 | echo "Installing Mellanox DPDK..." 72 | sudo apt install -y dpdk dpdk-dev 73 | 74 | # Huge pages. http://dpdk.org/doc/guides/linux_gsg/sys_reqs.html 75 | echo "Setting up huge pages..." 76 | if mount | grep "/mnt/huge_1GB "; then 77 | sudo umount /mnt/huge_1GB 78 | fi 79 | # Configure huge pages to be allocated on boot. 80 | sudo sed -i -r "s/GRUB_CMDLINE_LINUX=\"(.*)\"/GRUB_CMDLINE_LINUX=\"\\1 default_hugepagesz=1G hugepagesz=1G hugepages=4\"/" /etc/default/grub 81 | sudo update-grub 82 | sudo rm -rfv /mnt/huge_1GB 83 | sudo mkdir -v /mnt/huge_1GB 84 | echo "nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0" | sudo tee -a /etc/fstab 85 | 86 | # RTE_SDK location. 87 | echo "Setting RTE_SDK location..." 88 | { 89 | echo "" 90 | echo "export RTE_SDK=/usr/share/dpdk" 91 | echo "export RTE_TARGET=x86_64-default-linuxapp-gcc" 92 | } >> "$HOME/.bashrc" 93 | export RTE_SDK=/usr/share/dpdk 94 | export RTE_TARGET=x86_64-default-linuxapp-gcc 95 | 96 | # Click. 97 | echo "Installing Click..." 98 | cd /etalon/click-etalon 99 | ./configure --enable-user-multithread --disable-linuxmodule --enable-intel-cpu \ 100 | --enable-nanotimestamp --enable-dpdk 101 | make -j "$(nproc)" 102 | # "make install" needs gzcat. 103 | WHICH_ZCAT="$(command -v zcat)" 104 | sudo ln -sfv "$WHICH_ZCAT" "$(dirname "$WHICH_ZCAT")/gzcat" 105 | sudo make -j "$(nproc)" install 106 | 107 | # Flowgrind. 108 | echo "Installing Flowgrind..." 109 | cd /etalon/flowgrind-etalon 110 | autoreconf -i 111 | ./configure 112 | make -j "$(nproc)" 113 | sudo make -j "$(nproc)" install 114 | # Copy to the dir in which the docker build will run. 115 | cp -fv "$(command -v flowgrindd)" /etalon/vhost/ 116 | 117 | # libVT. 118 | echo "Installing libVT..." 119 | cd /etalon/libVT 120 | sudo make -j "$(nproc)" install 121 | # Copy to the dir in which the docker build will run. 122 | cp -fv libVT.so /etalon/vhost/ 123 | 124 | # Set up SSH keys. 125 | echo "Setting up SSH..." 126 | cp -fv /etalon/vhost/config/ssh/id_rsa "$HOME/.ssh" 127 | cp -fv /etalon/vhost/config/ssh/id_rsa.pub "$HOME/.ssh" 128 | chmod 600 "$HOME/.ssh/id_rsa" 129 | chmod 600 "$HOME/.ssh/id_rsa.pub" 130 | -------------------------------------------------------------------------------- /bin/sxp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env PYTHONPATH=../etc/ python 2 | 3 | import sys 4 | import os 5 | 6 | from python_config import handle_to_machine 7 | 8 | if __name__ == '__main__': 9 | scp = 'scp -o StrictHostKeyChecking=no ' \ 10 | '%s:/etalon/experiments/%s/%s-*.tar.gz ./' 11 | switch = handle_to_machine('switch') 12 | os.system(scp % (switch, 'buffers', sys.argv[1])) 13 | os.system(scp % (switch, 'adu', sys.argv[1])) 14 | os.system(scp % (switch, 'hdfs', sys.argv[1])) 15 | -------------------------------------------------------------------------------- /bin/tune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Perform network tuning. 4 | 5 | set -o errexit 6 | 7 | source /etalon/etc/script_config.sh 8 | 9 | # NIC tuning. Data interface. 10 | sudo ethtool -C "$DATA_IF" tx-usecs 0 11 | sudo ethtool -L "$DATA_IF" rx 1 12 | sudo service irqbalance stop 13 | sudo /usr/sbin/set_irq_affinity.sh "$DATA_IF" 14 | # Control interface. 15 | sudo ethtool -C "$CONTROL_IF" tx-usecs 0 rx-usecs 0 adaptive-rx off 16 | sudo service irqbalance stop 17 | sudo /usr/sbin/set_irq_affinity.sh "$CONTROL_IF" 18 | 19 | # Kernel tuning. 20 | sudo sysctl -w net.ipv4.neigh.default.gc_thresh3=8192 21 | sudo sysctl -w net.ipv4.tcp_congestion_control=reno 22 | 23 | ulimit -n 4096 24 | -------------------------------------------------------------------------------- /bin/utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Common utility functions for the testbed setup scripts. 4 | 5 | set -o errexit 6 | 7 | function hostname_validate { 8 | NEW_HOSTNAME="$1" 9 | if [ -z "$NEW_HOSTNAME" ]; then 10 | echo "Error: Must provide a hostname!" 11 | exit 1 12 | fi 13 | } 14 | 15 | function ubuntu_validate_version { 16 | UBUNTU_VERSION_SUPPORTED="$1" 17 | # Check that the Ubuntu version is correct. 18 | UBUNTU_VERSION_CURRENT="$(lsb_release -ds | grep -oP "Ubuntu \\K([0-9]+\\.[0-9]+)")" 19 | if [ "$UBUNTU_VERSION_CURRENT" != "$UBUNTU_VERSION_SUPPORTED" ]; then 20 | echo "Ubuntu version must be $UBUNTU_VERSION_SUPPORTED, but is:" \ 21 | "$UBUNTU_VERSION_CURRENT" 22 | exit 23 | fi 24 | } 25 | 26 | function ubuntu_validate_codename { 27 | UBUNTU_VERSION_SUPPORTED="$1" 28 | # Check that the Ubuntu version is correct. 29 | UBUNTU_VERSION_CURRENT="$(lsb_release -cs)" 30 | if [ "$UBUNTU_VERSION_CURRENT" != "$UBUNTU_VERSION_SUPPORTED" ]; then 31 | echo "Ubuntu version must be $UBUNTU_VERSION_SUPPORTED, but is:" \ 32 | "$UBUNTU_VERSION_CURRENT" 33 | exit 34 | fi 35 | } 36 | -------------------------------------------------------------------------------- /etc/README.md: -------------------------------------------------------------------------------- 1 | # etc 2 | 3 | Configurations files for Etalon: 4 | 5 | - ```handles```: a list of mappings of "handles" (e.g., host1, host2, switch, 6 | etc.) to public DNS names. These handles can then be used in other scripts 7 | (e.g., ```../bin/s``` or ```../bin/sxp.sh```). 8 | 9 | - ```python_config.py```: Etalon settings used by python scripts. 10 | 11 | - ```script_config.sh```: Etalon settings used by shell scripts. -------------------------------------------------------------------------------- /etc/c_config.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _C_CONFIG_H_ 3 | #define _C_CONFIG_H_ 4 | 5 | #define LOCAL_CTRL_DEVNAME "enp68s0" 6 | #define SWITCH_CTRL_IP "10.2.100.100" 7 | #define SWITCH_CTRL_PORT "8123" 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /etc/handles: -------------------------------------------------------------------------------- 1 | host1 node2.maas 2 | host2 node3.maas 3 | host3 node4.maas 4 | -------------------------------------------------------------------------------- /etc/netplan/99-etalon.yaml: -------------------------------------------------------------------------------- 1 | network: 2 | version: 2 3 | ethernets: 4 | eno4: 5 | addresses: 6 | - CONTROL_IP/16 7 | enp68s0: 8 | addresses: 9 | - DATA_IP/16 10 | mtu: 9000 11 | -------------------------------------------------------------------------------- /etc/script_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | export NUM_RACKS=3 6 | export HOSTS_PER_RACK=16 7 | 8 | # networks 9 | export DATA_IF=enp68s0 10 | export DATA_NET=1 11 | export CONTROL_IF=eno4 12 | export CONTROL_NET=2 13 | 14 | # switch 15 | export SWITCH_DATA_IP=10.$DATA_NET.100.100 16 | export SWITCH_CONTROL_IP=10.$CONTROL_NET.100.100 17 | 18 | # fqdn e.g., h37.etalon.local 19 | export FQDN=etalon.local 20 | -------------------------------------------------------------------------------- /experiments/README.md: -------------------------------------------------------------------------------- 1 | # experiments 2 | 3 | Framework for running experiments using Etalon, as well as some example 4 | experiments and graphing scripts from our paper: 5 | 6 | Framework 7 | --------- 8 | 9 | - ```click_common.py```: Functions for modifying / retrieving info to /from the 10 | software switch. 11 | 12 | - ```common.py```: Experiment framework. Provides functions for starting 13 | experiments (e.g., launching vhosts on each physical machines), running a 14 | benchmark app (e.g., flowgrind, dfsioe), and collecting the results (e.g., 15 | logs). 16 | 17 | - ```parse_logs.py```: Various log processing functions used in our graphing 18 | scripts. 19 | 20 | Example experiments 21 | ------------------- 22 | 23 | - ```buffers```: Buffer experiments (section 4 of our paper). 24 | 25 | - ```adu```: ADU experiments (section 5 of our paper). 26 | 27 | - ```hdfs```: HDFS experiments (section 6 of our paper). 28 | 29 | 30 | See subfolder README.md for more details. 31 | -------------------------------------------------------------------------------- /experiments/adu/README.md: -------------------------------------------------------------------------------- 1 | # experiments/adus 2 | 3 | Runs ADU experiments and graphs them (section 5 of our paper). 4 | 5 | - ```adu_common.py```: Common configuration information across ADU experiments. 6 | 7 | - ```adu_graphs.py```: Generates graphs for an ADU experiment. Expects a 8 | directory of output data from ```one_ring.py``` or ```two_rings.py``` as an 9 | argument. Expects that the folder "graphs" exists in the directory it is run 10 | in. 11 | 12 | - ```one_ring.py```: Experiment file for single ring ADU experiments (section 13 | 5.3 in our paper). 14 | 15 | - ```two_rings.py```: Experiment file for two ring ADU experiments (section 5.4 16 | in our paper). 17 | -------------------------------------------------------------------------------- /experiments/adu/adu_common.py: -------------------------------------------------------------------------------- 1 | CONFIGS = [ 2 | {'type': 'normal', 'traffic_source': 'QUEUE'}, 3 | {'type': 'resize', 'traffic_source': 'QUEUE', 'in_advance': 20000}, 4 | {'type': 'resize', 'traffic_source': 'QUEUE', 'in_advance': 20000, 'cc': 'retcp'}, 5 | {'type': 'normal', 'traffic_source': 'ADU'}, 6 | {'type': 'resize', 'traffic_source': 'ADU', 'in_advance': 20000}, 7 | {'type': 'resize', 'traffic_source': 'ADU', 'in_advance': 20000, 'cc': 'retcp'}, 8 | {'type': 'no_circuit', 'traffic_source': 'QUEUE', 'buffer_size': 128, 'packet_link_bandwidth': 0.5}, 9 | {'type': 'no_circuit', 'traffic_source': 'QUEUE', 'buffer_size': 128, 'packet_link_bandwidth': 1.0}, 10 | {'type': 'no_circuit', 'traffic_source': 'QUEUE', 'buffer_size': 128, 'packet_link_bandwidth': 2.0}, 11 | {'type': 'no_circuit', 'traffic_source': 'QUEUE', 'buffer_size': 128, 'packet_link_bandwidth': 4.0}, 12 | ] 13 | 14 | for c in CONFIGS: 15 | c['packet_log'] = False 16 | if c['type'] == 'resize': 17 | c['type'] = 'normal' 18 | c['queue_resize'] = True 19 | -------------------------------------------------------------------------------- /experiments/adu/adu_graphs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | sys.path.insert(0, '..') 5 | import glob 6 | import numpy as np 7 | 8 | from collections import defaultdict 9 | from dotmap import DotMap 10 | from tabulate import tabulate 11 | from simpleplotlib import plot 12 | from parse_logs import parse_flowgrind_config 13 | 14 | SMALL_FLOW_MAX_SIZE = 1000000 15 | dur_units = 1e-3 16 | bytes_units = 2.0**-30 17 | 18 | types = ['static', 'resize', 'reTCP', 'adu', 'adu+resize', 'adu+reTCP', 19 | 'fixed', 'packet10', 'packet20', 'packet40', 'packet80'] 20 | 21 | fn_keys = { 22 | 'normal-16-QUEUE-False': 'static', 23 | 'normal-16-QUEUE-True-20000-reno': 'resize', 24 | 'normal-16-QUEUE-True-20000-retcp': 'reTCP', 25 | 'normal-16-ADU-False': 'adu', 26 | 'normal-16-ADU-True-20000-reno': 'adu+resize', 27 | 'normal-16-ADU-True-20000-retcp': 'adu+reTCP', 28 | 'fixed-16-': 'fixed', 29 | 'fixed-128-': 'fixed_big', 30 | 'no_circuit-128-QUEUE-False-12000-reno-0.0006-0.5-': 'packet10', 31 | '-1.0-': 'packet20', 32 | '-2.0-': 'packet40', 33 | '-4.0-': 'packet80', 34 | '-4.5-': 'packet90', 35 | } 36 | 37 | files = { 38 | 'static': '/*-normal-*-QUEUE-False-*-reno-*flowgrind.txt', 39 | 'resize': '/*-normal-*-QUEUE-True-*-reno-*flowgrind.txt', 40 | 'reTCP': '/*-normal-*-QUEUE-True-*-retcp-*flowgrind.txt', 41 | 'adu': '/*-normal-*-ADU-False-*-reno-*flowgrind.txt', 42 | 'adu+resize': '/*-normal-*-ADU-True-*-reno-*flowgrind.txt', 43 | 'adu+reTCP': '/*-normal-*-ADU-True-*-retcp-*flowgrind.txt', 44 | 'fixed': '/*-fixed-16-*flowgrind.txt', 45 | 'packet10': '/*-no_circuit-*-0.5-*flowgrind.txt', 46 | 'packet20': '/*-no_circuit-*-1.0-*flowgrind.txt', 47 | 'packet40': '/*-no_circuit-*-2.0-*flowgrind.txt', 48 | 'packet80': '/*-no_circuit-*-4.0-*flowgrind.txt', 49 | } 50 | 51 | all_file_data = defaultdict(list) 52 | 53 | 54 | def get_default_plot_options(x, y): 55 | options = DotMap() 56 | options.plot_type = 'LINE' 57 | options.rcParams['figure.figsize'] = [6.0, 4.0] 58 | options.legend.options.fontsize = 19 59 | options.legend.options.labels = ['ToR VOQ', 'ADU', 'Fixed Sched.'] 60 | options.legend.styles = [DotMap(zorder=i, linewidth=5) 61 | for i in range(len(x))] 62 | options.series_options = [DotMap(zorder=i, linewidth=5) 63 | for i in range(len(x))] 64 | options.series_options[2].linewidth = 10 65 | options.series_options[1].zorder = 5 66 | options.y.label.ylabel = 'CDF (%)' 67 | return options 68 | 69 | 70 | def get_data(type): 71 | data = [(0, 0, 0), (0, 0, SMALL_FLOW_MAX_SIZE+1)] 72 | if glob.glob(sys.argv[1] + files[type]): 73 | fn = glob.glob(sys.argv[1] + files[type])[0] 74 | tputs, durs, bytes = parse_flowgrind_config(fn) 75 | data = zip(tputs, durs, bytes) 76 | 77 | all_file_data['big_tp'].append([d[0] for d in data 78 | if d[2] > SMALL_FLOW_MAX_SIZE]) 79 | all_file_data['big_dur'].append([d[1] for d in data 80 | if d[2] > SMALL_FLOW_MAX_SIZE]) 81 | all_file_data['small_tp'].append([d[0] for d in data 82 | if d[2] <= SMALL_FLOW_MAX_SIZE]) 83 | all_file_data['small_dur'].append([d[1] for d in data 84 | if d[2] <= SMALL_FLOW_MAX_SIZE]) 85 | return data 86 | 87 | 88 | def graph_small_durs(data): 89 | x = [sorted([p[1] * dur_units for p in d if p[2] <= 90 | SMALL_FLOW_MAX_SIZE]) for d in data] 91 | y = [[float(j) / (len(x[i])-1) * 100 for j in xrange(len(x[i]))] 92 | for i in xrange(len(x))] 93 | 94 | options = get_default_plot_options(x, y) 95 | options.x.limits = [0, 100] 96 | options.output_fn = 'graphs/mice_fct_cdf.pdf' 97 | options.x.label.xlabel = 'Flow-completion time (ms)' 98 | plot(x, y, options) 99 | 100 | 101 | def graph_big_durs(data): 102 | x = [sorted([p[1] * dur_units for p in d if p[2] > 103 | SMALL_FLOW_MAX_SIZE]) for d in data] 104 | y = [[float(j) / (len(x[i])-1) * 100 for j in xrange(len(x[i]))] 105 | for i in xrange(len(x))] 106 | 107 | options = get_default_plot_options(x, y) 108 | options.output_fn = 'graphs/elephant_fct_cdf.pdf' 109 | options.x.label.xlabel = 'Flow-completion time (ms)' 110 | plot(x, y, options) 111 | 112 | 113 | def graph_small_tp(data): 114 | x = [sorted([p[0] for p in d if p[2] <= SMALL_FLOW_MAX_SIZE]) 115 | for d in data] 116 | y = [[float(j) / (len(x[i])-1) * 100 for j in xrange(len(x[i]))] 117 | for i in xrange(len(x))] 118 | 119 | options = get_default_plot_options(x, y) 120 | options.x.limits = [0, 10] 121 | options.output_fn = 'graphs/mice_tp_cdf.pdf' 122 | options.x.label.xlabel = 'Throughput (Gbps)' 123 | plot(x, y, options) 124 | 125 | 126 | def graph_big_tp(data): 127 | x = [sorted([p[0] for p in d if p[2] > SMALL_FLOW_MAX_SIZE]) 128 | for d in data] 129 | y = [[float(j) / (len(x[i])-1) * 100 for j in xrange(len(x[i]))] 130 | for i in xrange(len(x))] 131 | 132 | options = get_default_plot_options(x, y) 133 | options.x.limits = [0, 10] 134 | options.output_fn = 'graphs/elephant_tp_cdf.pdf' 135 | options.x.label.xlabel = 'Throughput (Gbps)' 136 | plot(x, y, options) 137 | 138 | 139 | def bytes_graph(): 140 | data = {} 141 | for fn in glob.glob(sys.argv[1] + '/*.counters.txt'): 142 | key = [key for n, key in fn_keys.items() if n in fn][0] 143 | c, p, _ = eval(open(fn).read()) 144 | c = sum([int(b.split('\n')[-1]) * bytes_units for b in c]) 145 | p = sum([int(b.split('\n')[-1]) * bytes_units for b in p]) 146 | data[key] = p, c 147 | 148 | y = [data[t] for t in types] 149 | x = np.array([[0, 1] for i in xrange(len(y))]) 150 | 151 | options = DotMap() 152 | options.plot_type = 'BAR' 153 | options.bar_labels.show = False 154 | options.legend.options.labels = ['ToR VOQ', 'VOQ + Resize', 155 | 'VOQ + reTCP', 'ADU', 'ADU + Resize', 156 | 'ADU + reTCP', 'Fixed Sched.', 157 | 'Packet (10G)', 'Packet (20G)', 158 | 'Packet (40G)', 'Packet (80G)'] 159 | options.legend.options.fontsize = 12 160 | options.legend.options.ncol = 4 161 | options.series.color_groups = [0, 0, 0, 1, 1, 1, 2, 3, 3, 3, 3] 162 | options.y.limits = [0, 85] 163 | options.y.label_offset = [-.07, -.11] 164 | options.x.ticks.major.show = False 165 | options.x.ticks.major.labels = DotMap(text=['Packet', 'Circuit']) 166 | options.y.ticks.major.labels = DotMap(locations=[0, 15, 30, 45, 60]) 167 | options.output_fn = 'graphs/utilization.pdf' 168 | options.x.label.xlabel = 'Which switch' 169 | options.y.label.ylabel = 'Bytes sent (GB)' 170 | plot(x, y, options) 171 | 172 | if __name__ == '__main__': 173 | data = [get_data(t) for t in types] 174 | 175 | afd = all_file_data 176 | rows = [] 177 | rows.append(['Median small FCT (ms)'] + 178 | [np.percentile(p, 50) * dur_units for p in afd['small_dur']]) 179 | rows.append(['99th small FCT (ms)'] + 180 | [np.percentile(p, 99) * dur_units for p in afd['small_dur']]) 181 | rows.append(['Median big FCT (ms)'] + 182 | [np.percentile(p, 50) * dur_units for p in afd['big_dur']]) 183 | rows.append(['99th big FCT (ms)'] + 184 | [np.percentile(p, 99) * dur_units for p in afd['big_dur']]) 185 | header_strings = ['', 'ToR VOQ', 'VOQ + Resize', 'VOQ + reTCP', 186 | 'ADU', 'ADU + Resize', 'ADU + reTCP', 187 | 'Fixed Sched.', 'Packet (10)', 'Packet (20)', 188 | 'Packet (40)', 'Packet (80)'] 189 | print tabulate(rows, headers=header_strings, numalign="right", 190 | floatfmt=".2f") 191 | print tabulate(rows, headers=header_strings, numalign="right", 192 | floatfmt=".2f", tablefmt="latex") 193 | 194 | data = [data[types.index('static')], data[types.index('adu')], 195 | data[types.index('fixed')]] 196 | graph_small_durs(data) 197 | graph_big_durs(data) 198 | graph_small_tp(data) 199 | graph_big_tp(data) 200 | try: 201 | bytes_graph() 202 | except: 203 | pass 204 | -------------------------------------------------------------------------------- /experiments/adu/graphs-one-ring/elephant_fct_cdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/adu/graphs-one-ring/elephant_fct_cdf.pdf -------------------------------------------------------------------------------- /experiments/adu/graphs-one-ring/elephant_tp_cdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/adu/graphs-one-ring/elephant_tp_cdf.pdf -------------------------------------------------------------------------------- /experiments/adu/graphs-one-ring/mice_fct_cdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/adu/graphs-one-ring/mice_fct_cdf.pdf -------------------------------------------------------------------------------- /experiments/adu/graphs-one-ring/mice_tp_cdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/adu/graphs-one-ring/mice_tp_cdf.pdf -------------------------------------------------------------------------------- /experiments/adu/graphs-two-ring/utilization.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/adu/graphs-two-ring/utilization.pdf -------------------------------------------------------------------------------- /experiments/adu/one_ring.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import adu_common 4 | 5 | import sys 6 | sys.path.insert(0, '/etalon/experiments') 7 | from common import initializeExperiment, finishExperiment, flowgrind 8 | from click_common import setConfig 9 | 10 | adu_common.CONFIGS.append({'packet_log': False, 11 | 'type': 'fixed', 12 | 'traffic_source': 'ADU', 13 | 'fixed_schedule': '2 39600 7/0/1/2/3/4/5/6 400 -1/-1/-1/-1/-1/-1/-1/-1'}) 14 | 15 | for config in adu_common.CONFIGS: 16 | if config['traffic_source'] == 'ADU': 17 | initializeExperiment('flowgrindd_adu') 18 | else: 19 | initializeExperiment('flowgrindd') 20 | 21 | print '--- running test type %s...' % config 22 | setConfig(config) 23 | print '--- done...' 24 | 25 | settings = {'big_and_small': True} 26 | flowgrind(settings) 27 | 28 | finishExperiment() 29 | -------------------------------------------------------------------------------- /experiments/adu/two_rings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import adu_common 4 | 5 | import sys 6 | sys.path.insert(0, '/etalon/experiments') 7 | from common import initializeExperiment, finishExperiment, flowgrind 8 | from click_common import setConfig 9 | 10 | adu_common.CONFIGS.append({'packet_log': False, 11 | 'type': 'fixed', 12 | 'traffic_source': 'ADU', 13 | 'fixed_schedule': '4 19600 7/0/1/2/3/4/5/6 400 -1/-1/-1/-1/-1/-1/-1/-1 19600 6/7/0/1/2/3/4/5 400 -1/-1/-1/-1/-1/-1/-1/-1'}) 14 | 15 | for config in adu_common.CONFIGS: 16 | if config['traffic_source'] == 'ADU': 17 | initializeExperiment('flowgrindd_adu') 18 | else: 19 | initializeExperiment('flowgrindd') 20 | 21 | print '--- running test type %s...' % config 22 | setConfig(config) 23 | print '--- done...' 24 | 25 | settings = {'big_and_small_two_rings': True} 26 | flowgrind(settings) 27 | 28 | finishExperiment() 29 | -------------------------------------------------------------------------------- /experiments/buffers/README.md: -------------------------------------------------------------------------------- 1 | # experiments/buffers 2 | 3 | Runs buffer experiments and graphs them (section 4 of our paper). 4 | 5 | - ```buffer_common.py```: Common configuration information across buffer 6 | experiments. 7 | 8 | - ```buffer_graphs.py```: Generates graphs for buffer experiments, specifically 9 | figures 6, 7, 8, and 9 in our paper. Expects a directory of output data from 10 | ```buffers.py``` as an argument. 11 | 12 | - ```buffers.py```: Experiment file for buffer experiments. 13 | 14 | - ```delay_graphs.py```: Generates delay graphs for buffer experiments, 15 | specifically figure 10 in our paper. Expects a directory of output data from 16 | ```delay_sensitivity.py``` as an argument. 17 | 18 | - ```delay_sensitivity.py```: Experiment file for delay sensitivity analysis for 19 | buffer experiments. 20 | 21 | - ```sequence_graphs.py```: Generates sequence graphs for buffer experiments, 22 | specifically figure 5 in our paper. Expects a directory of output data from 23 | ```buffers.py``` as an argument. 24 | 25 | - ```validation.py```: Experiment file for validating Elaton. 26 | 27 | - ```validation_results.py```: Generates validation table data for buffer 28 | experiments, seen in Table 1 in our paper. Expects a directory of output data 29 | from ```validation.py``` as an argument. 30 | -------------------------------------------------------------------------------- /experiments/buffers/buffer_common.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def gen_static_sweep(mn, mx): 4 | # Generate a sweep of configuration with different static buffer sizes. 5 | # 6 | # mn: minimum buffer size 7 | # mx: maximum buffer size 8 | # 9 | # mn and mx are in terms of powers of 2 (e.g., mn = 7 -> 2^7 = 128 packets). 10 | return [{ 11 | # Enable the hybrid switch's packet log. This should already be enabled 12 | # by default. 13 | 'packet_log': True, 14 | 'type': 'strobe', 15 | 'buffer_size': 2**exp 16 | } for exp in xrange(mn, mx + 1)] 17 | 18 | 19 | def gen_resize_sweep(mn, mx, dl): 20 | # Generate a sweep of configurations with different amounts of dynamic 21 | # buffer resizing. 22 | # 23 | # mn: minimum resizing duration 24 | # mx: maximum resizing duration 25 | # dl: delta by which to increase the resizing duration. 26 | # 27 | # mn, mx, and dl are in terms of microseconds. 28 | return [{ 29 | # Enable the hybrid switch's packet log. This should already be enabled 30 | # by default. 31 | 'packet_log': True, 32 | 'type': 'strobe', 33 | 'queue_resize': True, 34 | 'buffer_size': 16, 35 | 'in_advance': us 36 | } for us in xrange(mn, mx + 1, dl)] 37 | -------------------------------------------------------------------------------- /experiments/buffers/buffers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | sys.path.insert(0, '/etalon/experiments') 5 | 6 | import buffer_common 7 | import click_common 8 | import common 9 | 10 | 11 | def main(): 12 | cnfs = buffer_common.gen_static_sweep(2, 7) + buffer_common.gen_resize_sweep(0, 8000, 500) 13 | # Use the first experiment's CC mode, or "reno" if no CC mode is specified. 14 | # This avoid unnecessarily restarting the cluster. 15 | common.initializeExperiment('flowgrindd', cnfs[0].get("cc", "reno")) 16 | 17 | # For every configuration, add a copy that uses reTCP as the CC mode. Put 18 | # the new configurations at the end so that the CC mode needs to be changed 19 | # only once. 20 | cnfs += [dict(cnf, {'cc': "retcp"}) for cnf in cnfs] 21 | tot = len(cnfs) 22 | for cnt, cnf in enumerate(cnfs, 1): 23 | print('--- running test type {}...'.format(cnf['type'])) 24 | print('--- setting switch buffer size to {}...'.format( 25 | cnf['buffer_size'])) 26 | click_common.setConfig(cnf) 27 | print('--- done...') 28 | print("--- experiment {} of {}".format(cnt, tot)) 29 | common.flowgrind(settings={"flows": [{"src": "r1", "dst": "r2"}]}) 30 | 31 | common.finishExperiment() 32 | 33 | 34 | if __name__ == "__main__": 35 | main() 36 | -------------------------------------------------------------------------------- /experiments/buffers/buffers_cc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os import path 4 | import sys 5 | # Directory containing this program. 6 | PROGDIR = path.dirname(path.realpath(__file__)) 7 | # For click_common and common. 8 | sys.path.insert(0, path.join(PROGDIR, "..")) 9 | # For buffer_common. 10 | sys.path.insert(0, path.join(PROGDIR, "..", "..")) 11 | # For python_config. 12 | sys.path.insert(0, path.join(PROGDIR, "..", "..", "etc")) 13 | 14 | import buffer_common 15 | import click_common 16 | import common 17 | import python_config 18 | 19 | 20 | # If True, then do not run experiments and instead only print configurations. 21 | DRY_RUN = False 22 | # Flowgrind flow duration, in seconds. 23 | # (week length (in us) x 3 + 100 us (for good measure)) x (1 s / 1e6 us) * 1e3 24 | DUR_S = 1.3 25 | # This schedule mimics an eight-rack cluster where each rack gets a circuit to 26 | # every other rack for 180us. Values are the srcs, indices are the dsts. E.g., 27 | # 1/2/0 means that 1->0, 2->1, and 0->2. Day/night pairs 1-6 are the "other" 28 | # circuits. 7 is the circuit that our test flow will traverse. There are only 29 | # seven configurations total for an eight-rack cluster because we do not need a 30 | # configuration where each of the racks connects to itself. 31 | # FIXED = ("14 " 32 | # "3600 1/2/0 400 -1/-1/-1 " # 1 33 | # "3600 1/2/0 400 -1/-1/-1 " # 2 34 | # "3600 1/2/0 400 -1/-1/-1 " # 3 35 | # "3600 1/2/0 400 -1/-1/-1 " # 4 36 | # "3600 1/2/0 400 -1/-1/-1 " # 5 37 | # "3600 1/2/0 400 -1/-1/-1 " # 6 38 | # "3600 2/0/1 400 -1/-1/-1") # 7 39 | FIXED = ("4 " 40 | "3600 1/2/0 400 -1/-1/-1 " # 1 41 | "3600 2/0/1 400 -1/-1/-1") # 2 42 | # FIXED = None 43 | # Control which experimnets are run. 44 | RESIZE_US_MIN = 0 45 | RESIZE_US_MAX = 225 46 | RESIZE_US_DELTA = 25 47 | # RESIZE_US_MIN = 0 48 | # RESIZE_US_MAX = 1200 49 | # RESIZE_US_DELTA = 100 50 | 51 | 52 | def maybe(fnc, do=not DRY_RUN): 53 | if do: 54 | fnc() 55 | 56 | 57 | def main(): 58 | # Generate the list of configurations first so that we know the total number 59 | # of experiments. CC modes are the outside loop to minimize how frequently 60 | # we change the CC mode, since doing so requires restarting the cluster. 61 | # ccs = python_config.CCS 62 | ccs = ["cubic"] 63 | cnfs = [] 64 | for cc in ccs: 65 | # sweep = buffer_common.gen_static_sweep(4, 12) 66 | sweep = buffer_common.gen_resize_sweep( 67 | int(round(RESIZE_US_MIN * python_config.TDF)), 68 | int(round(RESIZE_US_MAX * python_config.TDF)), 69 | int(round(RESIZE_US_DELTA * python_config.TDF))) 70 | 71 | for cnf in sweep: 72 | cnf["cc"] = cc 73 | if cc == "dctcp": 74 | # For DCTCP, enable threshold-based ECN marking. 75 | cnf['ecn'] = python_config.DCTCP_THRESH 76 | if FIXED is not None: 77 | cnf["type"] = "fixed" 78 | cnf["fixed_schedule"] = FIXED 79 | cnfs += sweep 80 | # Total number of experiments. 81 | tot = len(cnfs) 82 | 83 | # Use the first CC mode when starting the cluster to avoid needing to 84 | # restart the cluster for the first CC mode's experiments. 85 | maybe(lambda: common.initializeExperiment("flowgrindd", ccs[0])) 86 | for cnt, cnf in enumerate(cnfs, 1): 87 | maybe(lambda: click_common.setConfig(cnf)) 88 | print("--- experiment {} of {}, config:\n{}".format(cnt, tot, cnf)) 89 | maybe(lambda: common.flowgrind( 90 | settings={"flows": [{"src": "r1", "dst": "r2"}], "dur": DUR_S})) 91 | maybe(common.finishExperiment) 92 | 93 | 94 | if __name__ == "__main__": 95 | main() 96 | -------------------------------------------------------------------------------- /experiments/buffers/buffers_graphs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import collections 4 | import glob 5 | import multiprocessing 6 | from os import path 7 | import shelve 8 | import sys 9 | # Directory containing this program. 10 | PROGDIR = path.dirname(path.realpath(__file__)) 11 | # For parse_logs. 12 | sys.path.insert(0, path.join(PROGDIR, "..")) 13 | # For python_config. 14 | sys.path.insert(0, path.join(PROGDIR, "..", "..", "etc")) 15 | 16 | import dotmap 17 | from matplotlib import pyplot 18 | import numpy as np 19 | import simpleplotlib 20 | simpleplotlib.default_options.rcParams["font.family"] = "Tahoma" 21 | 22 | import parse_logs 23 | import python_config 24 | 25 | # SR_RACKS = (1, 2) 26 | SR_RACKS = (2, 3) 27 | 28 | 29 | class FileReaderArgs(object): 30 | def __init__(self, fln, msg_len): 31 | self.fln = fln 32 | self.msg_len = msg_len 33 | 34 | 35 | class FileReader(object): 36 | def __call__(self, args): 37 | """ 38 | Parses a single results file. "args" is a FileReaderArgs object. Returns 39 | tuples of the form (filename, results). 40 | """ 41 | return args.fln, parse_logs.parse_packet_log(args.fln, args.msg_len) 42 | 43 | 44 | def get_data(rdb_filepath, edr, key, files, key_fnc, msg_len=112, sync=False): 45 | # Open results database file. 46 | rdb = shelve.open(rdb_filepath, protocol=2, writeback=True) 47 | data = rdb.get(key) 48 | 49 | if data is None: 50 | ptn = path.join(edr, files[key]) 51 | flns = glob.glob(ptn) 52 | assert flns, "Found no files for pattern: {}".format(ptn) 53 | print("Found files for pattern: {}".format(ptn)) 54 | for fln in flns: 55 | print(" {}".format(fln)) 56 | 57 | args = [FileReaderArgs(fln, msg_len) for fln in flns] 58 | if sync: 59 | # Single-threaded mode. 60 | raw_data = [FileReader()(arg) for arg in args] 61 | else: 62 | # Multithreaded mode. 63 | pool = multiprocessing.Pool() 64 | raw_data = pool.map(FileReader(), args) 65 | # Clean up pool. 66 | pool.close() 67 | pool.join() 68 | 69 | data = collections.defaultdict(lambda: collections.defaultdict(dict)) 70 | for fln, results in raw_data: 71 | lbl = key_fnc[key](path.basename(fln)) 72 | lats, (_, tpt_c, _), _, _ = results 73 | data["tpt_c"][lbl] = tpt_c[SR_RACKS] 74 | # First, convert from grouping based on combined, circuit, and 75 | # packet latency to grouping backed on percentile. Then, extract the 76 | # values for a certain percentile. Finally, drop the percentile. 77 | data["lat"][50][lbl] = [ 78 | lt for _, lt in zip(*lats)[parse_logs.PERCENTILES.index(50)]] 79 | data["lat"][99][lbl] = [ 80 | lt for _, lt in zip(*lats)[parse_logs.PERCENTILES.index(99)]] 81 | 82 | # Convert from dictionary to key-value pairs, then sort by the key, then 83 | # extract the keys only. 84 | data["keys"] = list(zip(*sorted(data["tpt_c"].items()))[0]) 85 | # Convert from dictionary to key-value pairs, then sort by the key, then 86 | # extract the values only. 87 | data["lat"][50] = list(zip(*sorted(data["lat"][50].items()))[1]) 88 | data["lat"][99] = list(zip(*sorted(data["lat"][99].items()))[1]) 89 | data["tpt_c"] = list(zip(*sorted(data["tpt_c"].items()))[1]) 90 | # Store the new data in the database. 91 | rdb[key] = dict(data) 92 | 93 | rdb.close() 94 | return data 95 | 96 | 97 | def plot_lat(keys, latencies, fln, ylb, ylm=None, xlr=0, 98 | odr=path.join(PROGDIR, "graphs"), flt=lambda key: True): 99 | # Sort the data based on the x-values (keys). 100 | keys, latencies = zip( 101 | *sorted(zip(keys, latencies), key=lambda p: int(p[0]))) 102 | # Filter. 103 | keys, latencies = zip( 104 | *[(k, l) for k, l in zip(keys, latencies) if flt(k)]) 105 | x = [keys for _ in xrange(len(latencies[0]))] 106 | y = zip(*latencies) 107 | 108 | print("") 109 | print("raw latency data for: {}".format(fln)) 110 | print("{}:".format(ylb.strip("\n"))) 111 | print(" all: {}".format( 112 | ", ".join(["({}: {})".format(a, b) for a, b in zip(x[0], y[0])]))) 113 | print(" circuit: {}".format( 114 | ". ".join(["({}: {})".format(a, b) for a, b in zip(x[1], y[1])]))) 115 | print(" packet: {}".format( 116 | ", ".join(["({}: {})".format(a, b) for a, b in zip(x[2], y[2])]))) 117 | print("") 118 | 119 | options = dotmap.DotMap() 120 | options.legend.options.labels = ["all traffic", "only circuit", 121 | "only packet"] 122 | options.legend.options.fontsize = 20 123 | options.output_fn = path.join(odr, "{}.pdf".format(fln)) 124 | options.plot_type = "LINE" 125 | options.series_options = [ 126 | dotmap.DotMap(marker="o", markersize=10, linewidth=5) 127 | for _ in xrange(len(x))] 128 | options.x.label.fontsize = options.y.label.fontsize = 20 129 | options.x.label.xlabel = "Buffer size (packets)" if "static" in fln \ 130 | else "Early buffer resizing ($\mu$s)" 131 | options.x.ticks.major.options.labelsize = \ 132 | options.y.ticks.major.options.labelsize = 20 133 | options.x.ticks.major.labels = \ 134 | dotmap.DotMap(locations=[4, 8, 16, 32, 64, 128]) \ 135 | if "static" in fln else dotmap.DotMap(locations=keys) 136 | options.x.ticks.major.labels.options.rotation = xlr 137 | options.x.ticks.major.labels.options.rotation_mode = "anchor" 138 | options.x.ticks.major.labels.options.horizontalalignment = \ 139 | "center" if xlr == 0 else "right" 140 | options.y.label.ylabel = "{} latency ($\mu$s)".format(ylb) 141 | ylm = ylm if ylm is not None else 1.2 * max([max(line) for line in y]) 142 | options.y.limits = [0, ylm] 143 | simpleplotlib.plot(x, y, options) 144 | 145 | 146 | def plot_circuit_util(keys, tpts_Gbps_c, fln, xlb, num_racks, 147 | odr=path.join(PROGDIR, "graphs"), srt=True, xlr=0, lbs=23, 148 | flt=lambda key: True, order=None): 149 | """ srt: sort, xlr: x label rotation (degrees), lbs: bar label size """ 150 | if srt: 151 | # Sort the data based on the x-values (keys). 152 | keys, tpts_Gbps_c = zip(*sorted(zip(keys, tpts_Gbps_c))) 153 | # Filter. 154 | keys, tpts_Gbps_c = zip( 155 | *[(key, tpt_Gbps_c) 156 | for key, tpt_Gbps_c in zip(keys, tpts_Gbps_c) if flt(key)]) 157 | # Convert circuit throughput into circuit utilization. The throughput values 158 | # are averages over the entire experiment, including when the circuit 159 | # network was not active: they were computed by dividing the number of bytes 160 | # transported by the circuit network over the course the entire experiment 161 | # by the length of the experiment. To calculate the average circuit 162 | # utilization, we divide the achieved average throughput by the maximum 163 | # possible average throughput. The maximum possible average throughput is 164 | # equal to the circuit bandwidth multiplied by the fraction of the 165 | # experiment during which the circuit network was in use. This is equal to 166 | # the fraction of the schedule during which a circuit was assigned between 167 | # the racks in question (1 / (num_racks - 1)) times the fraction of that 168 | # time during which the circuit was up (reconfiguration time / day length = 169 | # 0.9). Therefore: 170 | # 171 | # util = throughput = throughput * (num_racks - 1) 172 | # ------------------------------- ---------------------------- 173 | # bandwidth * 1 * 0.9 0.9 * bandwidth 174 | # ------------- 175 | # num_racks - 1 176 | # 177 | # Finally, we multiply by 100 to convert to a percent. 178 | utls = [tpt_Gbps_c * (num_racks - 1) / 179 | (0.9 * python_config.CIRCUIT_BW_Gbps) * 100 180 | for tpt_Gbps_c in tpts_Gbps_c] 181 | 182 | if order is not None: 183 | # Reorder the lines. 184 | real_keys = [] 185 | real_utls = [] 186 | for item in order: 187 | for idx, possibility in enumerate(keys): 188 | if possibility == item: 189 | real_keys.append(possibility) 190 | real_utls.append(utls[idx]) 191 | break 192 | keys = real_keys 193 | utls = real_utls 194 | 195 | print("") 196 | print("raw util data for: {}".format(fln)) 197 | print("{}:".format(xlb)) 198 | print(" {}".format(", ".join( 199 | ["({}: {})".format(key, utl) for key, utl in zip(keys, utls)]))) 200 | print("") 201 | 202 | options = dotmap.DotMap() 203 | options.plot_type = "BAR" 204 | options.legend.options.fontsize = 20 205 | options.bar_labels.format_string = "%1.0f" 206 | options.bar_labels.options.fontsize = lbs 207 | options.output_fn = path.join(odr, "{}.pdf".format(fln)) 208 | options.y.limits = (0, 100) 209 | options.x.label.fontsize = options.y.label.fontsize = 20 210 | options.x.label.xlabel = xlb 211 | options.y.label.ylabel = "Average circuit\nutilization (%)" 212 | options.x.ticks.major.options.labelsize = \ 213 | options.y.ticks.major.options.labelsize = 20 214 | options.x.ticks.major.labels = dotmap.DotMap(text=keys) 215 | options.x.ticks.major.labels.options.rotation = xlr 216 | options.x.ticks.major.labels.options.rotation_mode = "anchor" 217 | options.x.ticks.major.labels.options.horizontalalignment = \ 218 | "center" if xlr == 0 else "right" 219 | options.y.ticks.major.show = True 220 | options.x.ticks.major.show = False 221 | simpleplotlib.plot([np.arange(len(utls))], [utls], options) 222 | 223 | 224 | def plot_util_vs_latency(tpts, latencies, fln): 225 | x = [[min( 226 | j / (0.9 * 1. / (python_config.NUM_RACKS - 1) 227 | * python_config.CIRCUIT_BW_Gbps) * 100, 228 | 100.0) for j in t] 229 | for t in tpts] 230 | y = [zip(*l)[0] for l in latencies] 231 | 232 | options = dotmap.DotMap() 233 | options.plot_type = "LINE" 234 | options.legend.options.labels = ["Static buffers (vary size)", 235 | "Dynamic buffers (vary $\\tau$)", 236 | "reTCP", 237 | "reTCP + dynamic buffers (vary $\\tau$)"] 238 | options.legend.options.fontsize = 19 239 | options.series_options = [ 240 | dotmap.DotMap(marker="o", markersize=10, linewidth=5) 241 | for _ in xrange(len(x))] 242 | options.series_options[2].marker = "x" 243 | options.series_options[2].s = 100 244 | del options.series_options[2].markersize 245 | options.series_options[2].zorder = 10 246 | options.output_fn = \ 247 | path.join(PROGDIR, "graphs", "throughput_vs_latency99.pdf") \ 248 | if "99" in fln \ 249 | else path.join(PROGDIR, "graphs", "throughput_vs_latency.pdf") 250 | options.x.label.xlabel = "Circuit utilization (%)" 251 | options.y.label.ylabel = "99th percent. latency ($\mu$s)" if "99" in fln \ 252 | else "Median latency ($\mu$s)" 253 | options.y.limits = [0, 1000] if "99" in fln else [0, 600] 254 | options.y.ticks.major.labels = \ 255 | dotmap.DotMap(locations=[0, 200, 400, 600, 800, 1000]) \ 256 | if "99" in fln else \ 257 | dotmap.DotMap(locations=[0, 100, 200, 300, 400, 500, 600]) 258 | simpleplotlib.plot(x, y, options) 259 | 260 | 261 | def lat(name, edr, odr, ptn, key_fnc, prc, ylb, ylm=None, xlr=0, 262 | flt=lambda key: True, msg_len=112, sync=False): 263 | print("Plotting: {}".format(name)) 264 | # Names are of the form "_
_". Experiments 265 | # where
are the same should be based on the same data. Therefore, 266 | # use
as the database key. 267 | basename = name.split("_")[1] if "_" in name else name 268 | data = get_data(path.join(edr, "{}.db".format(basename)), edr, basename, 269 | files={basename: ptn}, key_fnc={basename: key_fnc}, 270 | msg_len=msg_len, sync=sync) 271 | plot_lat(data["keys"], data["lat"][prc], name, ylb, ylm, xlr, odr, flt) 272 | pyplot.close() 273 | 274 | 275 | def util(name, edr, odr, ptn, key_fnc, xlb, num_racks, srt=True, xlr=0, lbs=23, 276 | flt=lambda key: True, order=None, msg_len=112, sync=False): 277 | """ 278 | srt: sort 279 | xlr: x label rotation (degrees) 280 | lbs: bar label fontsize 281 | flt: filter function that takes in a key 282 | """ 283 | print("Plotting: {}".format(name)) 284 | # Names are of the form "_
_". Experiments 285 | # where
are the same should be based on the same data. Therefore, 286 | # use
as the database key. 287 | basename = name.split("_")[1] if "_" in name else name 288 | data = get_data(path.join(edr, "{}.db".format(basename)), edr, basename, 289 | files={basename: ptn}, key_fnc={basename: key_fnc}, 290 | msg_len=msg_len, sync=sync) 291 | plot_circuit_util(data["keys"], data["tpt_c"], name, xlb, num_racks, odr, 292 | srt, xlr, lbs, flt, order) 293 | pyplot.close() 294 | -------------------------------------------------------------------------------- /experiments/buffers/delay_graphs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | sys.path.insert(0, '..') 5 | import os 6 | import shelve 7 | import glob 8 | import numpy as np 9 | 10 | from multiprocessing import Pool 11 | from dotmap import DotMap 12 | from simpleplotlib import plot 13 | from parse_logs import parse_packet_log 14 | 15 | DAY_LEN = 180 16 | TDF = 20 17 | SR = (1, 2) 18 | link_delays = [1e-4, 2e-4, 3e-4, 4e-4, 6e-4, 8e-4, 12e-4, 16e-4, 24e-4] 19 | 20 | types = ['static', 'resize'] 21 | 22 | files = { 23 | 'static': '/tmp/*-strobe-*-False-*-%.4f-*click.txt', 24 | 'resize': '/tmp/*-QUEUE-True-*-%.4f-*click.txt', 25 | } 26 | 27 | key_fn = { 28 | 'static': lambda fn: int(fn.split('strobe-')[1].split('-')[0]), 29 | 'resize': lambda fn: int(fn.split('True-')[1].split('-')[0]) / 20.0, 30 | } 31 | 32 | 33 | def get_data_from_file(fn): 34 | key = key_fn['static'](fn.split('/')[-1]) 35 | print fn, key 36 | _, lat, _, circ_util, _, _, _ = parse_packet_log(fn) 37 | lat50 = [x[1] for x in zip(*lat)[1]][0] 38 | circ_util = circ_util[SR] 39 | return key, (circ_util, lat50) 40 | 41 | 42 | def get_data(): 43 | if 'data' in db: 44 | return db['data'] 45 | data = [] 46 | for d in link_delays: 47 | print d 48 | p = Pool() 49 | buffer_data = dict(p.map(get_data_from_file, 50 | glob.glob(sys.argv[1] + files['static'] % d))) 51 | p.close() 52 | buffer_data = zip(*sorted(buffer_data.items()))[1] 53 | fn = glob.glob(sys.argv[1] + files['resize'] % d)[-1] 54 | print fn 55 | _, lat, _, circ_util, _, _, _ = parse_packet_log(fn) 56 | lat50 = [x[1] for x in zip(*lat)[1]][0] 57 | circ_util = circ_util[SR] 58 | interp_latency = np.interp( 59 | [circ_util], zip(*buffer_data)[0], zip(*buffer_data)[1])[0] 60 | reduction = (1 - (float(lat50) / interp_latency)) * 100 61 | num_of_rtts = DAY_LEN / (d * 1e6 / TDF * 2) 62 | data.append((num_of_rtts, reduction)) 63 | print 'reduction', reduction 64 | return sorted(data) 65 | 66 | if __name__ == '__main__': 67 | if not os.path.isdir(sys.argv[1]): 68 | print 'first arg must be dir' 69 | sys.exit(-1) 70 | db = shelve.open(sys.argv[1] + '/delay_shelve.db') 71 | db['data'] = get_data() 72 | 73 | x = [zip(*db['data'])[0]] 74 | y = [zip(*db['data'])[1]] 75 | 76 | options = DotMap() 77 | options.plot_type = 'LINE' 78 | options.legend.options.fontsize = 19 79 | options.series_options = [DotMap(marker='o', markersize=10, linewidth=5) 80 | for i in range(len(x))] 81 | options.output_fn = 'graphs/delay_sensitivity_rtt.pdf' 82 | options.x.label.xlabel = '# of RTTs in a day' 83 | options.y.label.ylabel = 'Median lat. improve. (%)' 84 | options.x.limits = [0, 19] 85 | options.y.limits = [0, 55] 86 | options.x.ticks.major.labels = DotMap( 87 | locations=[0, 1, 2, 3, 4, 5, 6, 9, 18]) 88 | options.y.ticks.major.labels = DotMap( 89 | locations=[0, 10, 20, 30, 40, 50]) 90 | 91 | plot(x, y, options) 92 | db.close() 93 | -------------------------------------------------------------------------------- /experiments/buffers/delay_sensitivity.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | sys.path.insert(0, '/etalon/experiments') 5 | from common import initializeExperiment, finishExperiment, flowgrind 6 | from click_common import setConfig 7 | 8 | CONFIGS = [ 9 | {'buffer_size': 4}, 10 | {'buffer_size': 8}, 11 | {'buffer_size': 16}, 12 | {'buffer_size': 32}, 13 | {'buffer_size': 64}, 14 | {'buffer_size': 128}, 15 | {'buffer_size': 16, 'in_advance': 20000, 'queue_resize': True}, 16 | ] 17 | 18 | # 0.5 1 1.5 2 3 4 6 8 12 19 | link_delays = [1e-4, 2e-4, 3e-4, 4e-4, 6e-4, 8e-4, 12e-4, 16e-4, 24e-4] 20 | sched = ['strobe'] 21 | 22 | initializeExperiment('flowgrindd') 23 | 24 | for t in sched: 25 | for d in link_delays: 26 | for config in CONFIGS: 27 | config['circuit_link_delay'] = d 28 | config['type'] = t 29 | if t == 'short_reconfig' and 'in_advance' in config: 30 | config['in_advance'] = 10000 31 | print '--- running test type %s...' % config['type'] 32 | print '--- setting switch buffer size to %d...' % \ 33 | config['buffer_size'] 34 | setConfig(config) 35 | print '--- done...' 36 | 37 | settings = {'flows': []} 38 | settings['flows'].append({'src': 'r1', 'dst': 'r2'}) 39 | flowgrind(settings) 40 | 41 | finishExperiment() 42 | -------------------------------------------------------------------------------- /experiments/buffers/graphs/delay_sensitivity_rtt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/delay_sensitivity_rtt.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/1581462196-nsdi2020.txt: -------------------------------------------------------------------------------- 1 | Plotting: 4-1_seq-current-all 2 | Computing optimal... 3 | circuit bounds: [4620, 4800, 9420, 9600, 14221, 14401] 4 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/4-1_seq-current-all.pdf 5 | Plotting: 4-2_util-lat-current-all_util 6 | 7 | raw util data for: 4-2_util-lat-current-all_util 8 | TCP variant: 9 | (bbr: 26.4347509141), (bic: 49.407773728), (cdg: 53.6401513086), (cubic: 48.6681323576), (dctcp: 45.5280979983), (highspeed: 49.9226449221), (htcp: 49.3985011882), (hybla: 50.9580828542), (illinois: 54.5887053702), (lp: 53.1175769898), (nv: 42.0977934584), (reno: 53.2015919923), (scalable: 54.8209029109), (vegas: 51.6268840753), (veno: 52.1194872959), (westwood: 53.3471442274), (yeah: 50.7505959449) 10 | 11 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/4-2_util-lat-current-all_util.pdf 12 | Plotting: 5-1_seq-static-cubic 13 | Computing optimal... 14 | circuit bounds: [4620, 4800, 9420, 9600, 14220, 14400] 15 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/5-1_seq-static-cubic.pdf 16 | Plotting: 5-2_util-lat-static-cubic_util 17 | 18 | raw util data for: 5-2_util-lat-static-cubic_util 19 | Static buffer size (packets): 20 | (4: 21.2514478607), (8: 31.0930893694), (16: 48.6681323576), (32: 76.7432404654), (64: 99.8915083872), (128: 99.9084851223) 21 | 22 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/5-2_util-lat-static-cubic_util.pdf 23 | Plotting: 5-3_util-lat-static-cubic_lat50 24 | 25 | raw latency data for: 5-3_util-lat-static-cubic_lat50 26 | Median: 27 | all: (4: 42.0), (8: 69.0), (16: 123.0), (32: 236.0), (64: 453.0), (128: 884.0) 28 | circuit: (4: 31.0). (8: 32.0). (16: 33.0). (32: 34.0). (64: 48.0). (128: 286.0) 29 | packet: (4: 42.0), (8: 71.0), (16: 127.0), (32: 238.0), (64: 461.0), (128: 906.0) 30 | 31 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/5-3_util-lat-static-cubic_lat50.pdf 32 | Plotting: 5-4_util-lat-static-cubic_lat99 33 | 34 | raw latency data for: 5-4_util-lat-static-cubic_lat99 35 | 99th percentile: 36 | all: (4: 52.0), (8: 81.0), (16: 139.0), (32: 254.0), (64: 484.0), (128: 943.0) 37 | circuit: (4: 68.0). (8: 95.0). (16: 149.0). (32: 259.0). (64: 484.0). (128: 927.0) 38 | packet: (4: 52.0), (8: 81.0), (16: 139.0), (32: 254.0), (64: 484.0), (128: 943.0) 39 | 40 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/5-4_util-lat-static-cubic_lat99.pdf 41 | Plotting: 6-1-1_seq-dyn-cubic 42 | Computing optimal... 43 | circuit bounds: [4620, 4800, 9420, 9600, 14220, 14400] 44 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/6-1-1_seq-dyn-cubic.pdf 45 | Plotting: 6-2_util-lat-dyn-cubic_util 46 | 47 | raw util data for: 6-2_util-lat-dyn-cubic_util 48 | Resize time ($\mu$s): 49 | (0: 49.2391874981), (300: 60.3302181346), (600: 64.888886183), (900: 71.5695464216), (1200: 78.5569784536), (1500: 85.8680460173), (1800: 91.1010705866), (2100: 95.8895269929), (2400: 97.7459589752), (2700: 97.3984437601), (3000: 96.7200247499) 50 | 51 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/6-2_util-lat-dyn-cubic_util.pdf 52 | Plotting: 6-3_util-lat-dyn-cubic_lat50 53 | 54 | raw latency data for: 6-3_util-lat-dyn-cubic_lat50 55 | Median: 56 | all: (0: 123.0), (300: 124.0), (600: 124.0), (900: 127.0), (1200: 129.0), (1500: 129.0), (1800: 130.0), (2100: 131.0), (2400: 136.0), (2700: 136.0), (3000: 138.0) 57 | circuit: (0: 33.0). (300: 33.0). (600: 33.0). (900: 33.0). (1200: 34.0). (1500: 35.0). (1800: 37.0). (2100: 40.0). (2400: 41.0). (2700: 38.0). (3000: 37.0) 58 | packet: (0: 127.0), (300: 128.0), (600: 129.0), (900: 129.0), (1200: 130.0), (1500: 131.0), (1800: 135.0), (2100: 136.0), (2400: 138.0), (2700: 150.0), (3000: 179.0) 59 | 60 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/6-3_util-lat-dyn-cubic_lat50.pdf 61 | Plotting: 6-4_util-lat-dyn-cubic_lat99 62 | 63 | raw latency data for: 6-4_util-lat-dyn-cubic_lat99 64 | 99th percentile: 65 | all: (0: 139.0), (300: 149.0), (600: 180.0), (900: 208.0), (1200: 239.0), (1500: 265.0), (1800: 286.0), (2100: 318.0), (2400: 358.0), (2700: 379.0), (3000: 381.0) 66 | circuit: (0: 149.0). (300: 171.0). (600: 194.0). (900: 227.0). (1200: 251.0). (1500: 274.0). (1800: 294.0). (2100: 324.0). (2400: 360.0). (2700: 382.0). (3000: 387.0) 67 | packet: (0: 139.0), (300: 143.0), (600: 175.0), (900: 208.0), (1200: 237.0), (1500: 261.0), (1800: 282.0), (2100: 316.0), (2400: 357.0), (2700: 377.0), (3000: 381.0) 68 | 69 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/6-4_util-lat-dyn-cubic_lat99.pdf 70 | Plotting: 7-2_util-lat-dyn-all-1200us_util 71 | 72 | raw util data for: 7-2_util-lat-dyn-all-1200us_util 73 | TCP variant: 74 | (bbr: 42.8126499696), (bic: 87.4415809262), (cdg: 93.3769610719), (cubic: 78.5569784536), (dctcp: 87.9366411877), (highspeed: 88.5761847546), (htcp: 89.467430686), (hybla: 89.0986702667), (illinois: 90.9289695676), (lp: 95.7929472994), (nv: 54.1179210414), (reno: 95.4782426426), (scalable: 94.6768300704), (vegas: 91.932767946), (veno: 89.0944300118), (westwood: 96.5143976198), (yeah: 88.4168777913) 75 | 76 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/7-2_util-lat-dyn-all-1200us_util.pdf 77 | Plotting: 8-1_seq-static-retcp 78 | Computing optimal... 79 | circuit bounds: [4620, 4800, 9420, 9600, 14220, 14400] 80 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/8-1_seq-static-retcp.pdf 81 | Plotting: 8-2_util-lat-static-retcp_util 82 | 83 | raw util data for: 8-2_util-lat-static-retcp_util 84 | Static buffer size (packets): 85 | (4: 23.9663535723), (8: 33.8794884513), (16: 52.6648582692), (32: 83.2223428763), (64: 99.822322856), (128: 99.8987458054) 86 | 87 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/8-2_util-lat-static-retcp_util.pdf 88 | Plotting: 8-3_util-lat-static-retcp_lat50 89 | 90 | raw latency data for: 8-3_util-lat-static-retcp_lat50 91 | Median: 92 | all: (4: 40.0), (8: 66.0), (16: 122.0), (32: 230.0), (64: 437.0), (128: 834.0) 93 | circuit: (4: 31.0). (8: 32.0). (16: 33.0). (32: 36.0). (64: 50.0). (128: 332.0) 94 | packet: (4: 41.0), (8: 69.0), (16: 124.0), (32: 237.0), (64: 453.0), (128: 869.0) 95 | 96 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/8-3_util-lat-static-retcp_lat50.pdf 97 | Plotting: 8-4_util-lat-static-retcp_lat99 98 | 99 | raw latency data for: 8-4_util-lat-static-retcp_lat99 100 | 99th percentile: 101 | all: (4: 52.0), (8: 81.0), (16: 139.0), (32: 254.0), (64: 484.0), (128: 945.0) 102 | circuit: (4: 68.0). (8: 97.0). (16: 151.0). (32: 259.0). (64: 495.0). (128: 933.0) 103 | packet: (4: 52.0), (8: 81.0), (16: 139.0), (32: 254.0), (64: 484.0), (128: 945.0) 104 | 105 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/8-4_util-lat-static-retcp_lat99.pdf 106 | Plotting: 9-1_seq-dyn-retcp 107 | Computing optimal... 108 | circuit bounds: [4620, 4800, 9420, 9600, 14220, 14400] 109 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/9-1_seq-dyn-retcp.pdf 110 | Plotting: 9-2_util-lat-dyn-retcp_util 111 | 112 | raw util data for: 9-2_util-lat-dyn-retcp_util 113 | Resize time ($\mu$s): 114 | (0: 79.698570467), (50: 87.2699516406), (100: 91.0865532859), (150: 92.7175336353), (200: 93.8808467615), (250: 92.7726855962), (300: 93.2573713105) 115 | 116 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/9-2_util-lat-dyn-retcp_util.pdf 117 | Plotting: 9-3_util-lat-dyn-retcp_lat50 118 | 119 | raw latency data for: 9-3_util-lat-dyn-retcp_lat50 120 | Median: 121 | all: (0: 122.0), (50: 114.0), (100: 114.0), (150: 121.0), (200: 122.0), (250: 116.0), (300: 116.0) 122 | circuit: (0: 38.0). (50: 40.0). (100: 39.0). (150: 40.0). (200: 39.0). (250: 38.0). (300: 38.0) 123 | packet: (0: 126.0), (50: 121.0), (100: 121.0), (150: 124.0), (200: 126.0), (250: 121.0), (300: 121.0) 124 | 125 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/9-3_util-lat-dyn-retcp_lat50.pdf 126 | Plotting: 9-4_util-lat-dyn-retcp_lat99 127 | 128 | raw latency data for: 9-4_util-lat-dyn-retcp_lat99 129 | 99th percentile: 130 | all: (0: 139.0), (50: 139.0), (100: 139.0), (150: 147.0), (200: 177.0), (250: 199.0), (300: 237.0) 131 | circuit: (0: 144.0). (50: 141.0). (100: 141.0). (150: 162.0). (200: 191.0). (250: 228.0). (300: 260.0) 132 | packet: (0: 139.0), (50: 139.0), (100: 139.0), (150: 139.0), (200: 140.0), (250: 162.0), (300: 195.0) 133 | 134 | /home/ccanel/src/etalon/experiments/buffers/graphs/nsdi2020/9-4_util-lat-dyn-retcp_lat99.pdf 135 | -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/1_seq-old-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/1_seq-old-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/2_seq-current-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/2_seq-current-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/3_seq-future-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/3_seq-future-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/4-1_seq-current-all.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/4-1_seq-current-all.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/4-2_util-lat-current-all_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/4-2_util-lat-current-all_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/5-1_seq-static-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/5-1_seq-static-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/5-2_util-lat-static-cubic_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/5-2_util-lat-static-cubic_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/5-3_util-lat-static-cubic_lat50.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/5-3_util-lat-static-cubic_lat50.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/5-4_util-lat-static-cubic_lat99.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/5-4_util-lat-static-cubic_lat99.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/6-1-1_seq-dyn-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/6-1-1_seq-dyn-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/6-2_util-lat-dyn-cubic_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/6-2_util-lat-dyn-cubic_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/6-3_util-lat-dyn-cubic_lat50.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/6-3_util-lat-dyn-cubic_lat50.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/6-4_util-lat-dyn-cubic_lat99.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/6-4_util-lat-dyn-cubic_lat99.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-bbr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-bbr.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-bic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-bic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-cdg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-cdg.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-dctcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-dctcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-highspeed.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-highspeed.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-htcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-htcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-hybla.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-hybla.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-illinois.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-illinois.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-lp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-lp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-nv.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-nv.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-reno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-reno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-retcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-retcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-scalable.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-scalable.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-vegas.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-vegas.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-veno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-veno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-westwood.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-westwood.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-yeah.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-1-2_seq-dyn-yeah.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/7-2_util-lat-dyn-all-1200us_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/7-2_util-lat-dyn-all-1200us_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/8-1_seq-static-retcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/8-1_seq-static-retcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/8-2_util-lat-static-retcp_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/8-2_util-lat-static-retcp_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/8-3_util-lat-static-retcp_lat50.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/8-3_util-lat-static-retcp_lat50.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/8-4_util-lat-static-retcp_lat99.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/8-4_util-lat-static-retcp_lat99.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/9-1_seq-dyn-retcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/9-1_seq-dyn-retcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/9-2_util-lat-dyn-retcp_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/9-2_util-lat-dyn-retcp_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/9-3_util-lat-dyn-retcp_lat50.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/9-3_util-lat-dyn-retcp_lat50.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/9-4_util-lat-dyn-retcp_lat99.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/9-4_util-lat-dyn-retcp_lat99.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/calcs.txt: -------------------------------------------------------------------------------- 1 | 2 | Calculations for Etalon NSDI 2020 paper camera ready version. 3 | 4 | Main results: 1581462196-nsdi2020 5 | Figures 2a (old technologies) and 2b (new technologies): 1581228040-nsdi2020 (I think) 6 | 7 | 8 | All variants util static: 9 | (26+49+54+49+46+50+49+51+55+53+42+53+55+52+52+53+51) / 17 = 49.41 10 | 11 | 12 | All variants util improvement: 13 | ((43-26) + (87-49) + (93-54) + (79-49) + (88-46) + (89-50) + (89-49) + (89-51) + (91-55) + (96-53) + (54-42) + (95-53) + (95-55) + (92-52) + (89-52) + (97-53) + (88-51)) / 17 = 36.12 14 | 15 | 16 | reTCP util improvement vs. static: 17 | static: (4: 21.2514478607), (8: 31.0930893694), (16: 48.6681323576), (32: 76.7432404654), (64: 99.8915083872), (128: 99.9084851223) 18 | reTCP: (4: 23.9663535723), (8: 33.8794884513), (16: 52.6648582692), (32: 83.2223428763), (64: 99.822322856), (128: 99.8987458054) 19 | average: ((23.9663535723-21.2514478607) + (33.8794884513-31.0930893694) + (52.6648582692-48.6681323576) + (83.2223428763-76.7432404654) + (99.822322856-99.8915083872) + (99.8987458054-99.9084851223)) / 6 = 2.64% 20 | 21 | 22 | static 16 -> dynamic 1800us 23 | 48.67% -> 91.10% : 1.87x 24 | 123us -> 286us : 2.33x 25 | 26 | 27 | static 32 -> dynamic 1800us: 28 | 76.74% -> 91.10% : 1.19x 29 | 254us -> 286us : 1.13x 30 | 31 | 32 | static 64 -> dynamic 1800us: 33 | 99.89% -> 91.10% : 0.91x 34 | 484us -> 286us : 0.59x 35 | 36 | 37 | static 64 -> dynamic 150us + reTCP 38 | 99.89% -> 92.72% : 0.93x 39 | 484us -> 147us : 0.30x 40 | 41 | 42 | static 16 -> dynamic 150us + reTCP 43 | 48.67% -> 92.72% : 1.91x 44 | 123us -> 147us : 1.20x 45 | 46 | 47 | dynamic 1800us -> dynamic 150us + reTCP 48 | 91.10% -> 92.72% : 1.02x 49 | 286us -> 147us : 0.51x 50 | -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/sim-1-flows.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/sim-1-flows.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/sim-10-flows.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/sim-10-flows.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/sim-15-flows.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/sim-15-flows.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/nsdi2020/sim-5-flows.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/nsdi2020/sim-5-flows.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_new-long-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_new-long-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_new-long-reno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_new-long-reno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_old-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_old-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_old-reno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_old-reno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_resize-cubic-retcp-3000.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_resize-cubic-retcp-3000.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_resize-cubic-retcp-3500.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_resize-cubic-retcp-3500.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_resize-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_resize-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_resize-cubic_inset.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_resize-cubic_inset.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_resize-reno-retcp-3000.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_resize-reno-retcp-3000.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_resize-reno-retcp-3500.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_resize-reno-retcp-3500.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_resize-reno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_resize-reno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_static-all.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_static-all.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_static-cubic-retcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_static-cubic-retcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_static-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_static-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_static-reno-retcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_static-reno-retcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/optsys/seq_static-reno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/optsys/seq_static-reno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/resize-99_vs_latency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/resize-99_vs_latency.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/resize-median_vs_latency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/resize-median_vs_latency.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/resize_vs_circuit_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/resize_vs_circuit_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-bbr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-bbr.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-bic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-bic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-cdg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-cdg.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-cubic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-cubic.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-dctcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-dctcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-highspeed.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-highspeed.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-htcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-htcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-hybla.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-hybla.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-illinois.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-illinois.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-lp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-lp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-nv.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-nv.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-reno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-reno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-retcp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-retcp.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-scalable.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-scalable.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-vegas.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-vegas.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-veno.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-veno.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-westwood.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-westwood.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize-yeah.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize-yeah.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_resize.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_resize.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/seq_static.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/seq_static.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/static_vs_circuit_util.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/static_vs_circuit_util.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/static_vs_latency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/static_vs_latency.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/throughput_vs_latency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/throughput_vs_latency.pdf -------------------------------------------------------------------------------- /experiments/buffers/graphs/throughput_vs_latency99.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/buffers/graphs/throughput_vs_latency99.pdf -------------------------------------------------------------------------------- /experiments/buffers/nsdi2020.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os import path 4 | import sys 5 | # Directory containing this program. 6 | PROGDIR = path.dirname(path.realpath(__file__)) 7 | # For click_common and common. 8 | sys.path.insert(0, path.join(PROGDIR, "..")) 9 | # For python_config. 10 | sys.path.insert(0, path.join(PROGDIR, "..", "..", "etc")) 11 | 12 | import click_common 13 | import common 14 | import python_config 15 | 16 | # If True, then do not run experiments and instead only print configurations. 17 | DRY_RUN = False 18 | # If True, then collect tcpdump traces for every experiment. 19 | TCPDUMP = False 20 | # If True, then racks will be launched in serial. 21 | SYNC = False 22 | # The number of racks to mimic when creating the strobe schedule. 23 | NUM_RACKS_FAKE = 8 24 | # Run static buffer experiments up to buffer size 2**STATIC_POW_MAX. 25 | STATIC_POW_MAX = 7 26 | # Long prebuffering sweep bounds. 27 | RESIZE_LONG_MIN_us = 0 28 | RESIZE_LONG_MAX_us = 3000 29 | RESIZE_LONG_DELTA_us = 300 30 | ALL_VARIANTS_uss = [1200] 31 | # Short prebuffering sweep bounds. 32 | RESIZE_SHORT_MIN_us = 0 33 | RESIZE_SHORT_MAX_us = 300 34 | RESIZE_SHORT_DELTA_us = 50 35 | # VOQ capacities. 36 | SMALL_QUEUE_CAP = 16 37 | BIG_QUEUE_CAP = 50 38 | # Vary night len. 39 | NIGHT_LEN_POW_MIN = 2 40 | NIGHT_LEN_POW_MAX = 11 41 | # Duration of prebuffering required for reTCP to achieve high utilization. 42 | RETCP_RESIZE_us = 150 43 | 44 | 45 | def maybe(fnc, do=not DRY_RUN): 46 | """ Executes "fnc" if "do" is True, otherwise does nothing. """ 47 | if do: 48 | fnc() 49 | 50 | 51 | def main(): 52 | # Experiments: 53 | # (1) Long nights/days, static buffers. CUBIC. Old optical switches. 54 | # (2) Very short nights/days, static buffers, CUBIC. Future optical 55 | # switches. 56 | # (3) Short nights/days, static buffers, all TCP variants. 57 | # (4) Short nights/days, dynamic buffers, all TCP variants. 58 | # (5) Vary night/day length, static buffers + CUBIC and dynamic buffers + reTCP. 59 | 60 | # Assemble configurations. Generate the list of configurations first so that 61 | # we know the total number of experiments. 62 | cnfs = [] 63 | # CC modes are the outside loop to minimize how frequently we change the CC 64 | # mode, since doing so requires restarting the cluster. 65 | for cc in python_config.CCS: 66 | # if cc in ["cubic"]: 67 | # # (1) Old switches. 68 | # cnfs += [{"type": "fake_strobe", 69 | # "num_racks_fake": NUM_RACKS_FAKE, 70 | # "night_len_us": 1000 * python_config.TDF, 71 | # "day_len_us": 9000 * python_config.TDF, 72 | # "cc": cc}] 73 | # # (2) Future switches. 74 | # cnfs += [{"type": "fake_strobe", 75 | # "num_racks_fake": NUM_RACKS_FAKE, 76 | # "night_len_us": 1 * python_config.TDF, 77 | # "day_len_us": 9 * python_config.TDF, "cc": cc}] 78 | # # (3) Static buffers. 79 | # for exp in xrange(2, STATIC_POW_MAX + 1): 80 | # # Only do full sweeps for CUBIC and reTCP, but capture 16 packets 81 | # # for all variants. 82 | # if cc in ["cubic", "retcp"] or exp == 4: 83 | # cnfs += [{"type": "fake_strobe", 84 | # "num_racks_fake": NUM_RACKS_FAKE, 85 | # "small_queue_cap": 2**exp, 86 | # "big_queue_cap": 2**exp, 87 | # "cc": cc}] 88 | # # (4) Long prebuffering. 89 | # for us in xrange(RESIZE_LONG_MIN_us, RESIZE_LONG_MAX_us + 1, 90 | # RESIZE_LONG_DELTA_us): 91 | # # Only do a full sweep for CUBIC, but capture a few key us's for all 92 | # # variants. 93 | # if cc == "cubic" or us in ALL_VARIANTS_uss: 94 | # cnfs += [{"type": "fake_strobe", 95 | # "num_racks_fake": NUM_RACKS_FAKE, 96 | # "queue_resize": True, 97 | # "in_advance": int(round(us * python_config.TDF)), 98 | # "cc": cc}] 99 | # # (4) Short prebuffering, only for reTCP. 100 | # if cc == "retcp": 101 | # for us in xrange(RESIZE_SHORT_MIN_us, RESIZE_SHORT_MAX_us + 1, 102 | # RESIZE_SHORT_DELTA_us): 103 | # cnfs += [{"type": "fake_strobe", 104 | # "num_racks_fake": NUM_RACKS_FAKE, 105 | # "queue_resize": True, 106 | # "in_advance": int(round(us * python_config.TDF)), 107 | # "cc": cc}] 108 | # (5) Vary night len. 109 | for exp in xrange(NIGHT_LEN_POW_MIN, NIGHT_LEN_POW_MAX + 1): 110 | night_len_us = 2**exp 111 | day_len_us = 9 * night_len_us 112 | night_len_us_tdf = int(round(night_len_us * python_config.TDF)) 113 | day_len_us_tdf = int(round(day_len_us * python_config.TDF)) 114 | # CUBIC with static buffers. 115 | if cc == "cubic": 116 | cnfs += [{"type": "fake_strobe", 117 | "num_racks_fake": NUM_RACKS_FAKE, 118 | "small_queue_cap": SMALL_QUEUE_CAP, 119 | "big_queue_cap": SMALL_QUEUE_CAP, 120 | "night_len_us": night_len_us_tdf, 121 | "day_len_us": day_len_us_tdf, 122 | "cc": cc}] 123 | # reTCP with dynamic buffers. 124 | if cc == "retcp": 125 | cnfs += [{"type": "fake_strobe", 126 | "num_racks_fake": NUM_RACKS_FAKE, 127 | "small_queue_cap": SMALL_QUEUE_CAP, 128 | "big_queue_cap": BIG_QUEUE_CAP, 129 | "night_len_us": night_len_us_tdf, 130 | "day_len_us": day_len_us_tdf, 131 | "queue_resize": True, 132 | # Use 150us or 75% of the circuit downtime length, 133 | # whichever is less. 134 | "in_advance": int(round(min( 135 | 0.75 * ((NUM_RACKS_FAKE - 1) * 136 | (night_len_us + day_len_us) - day_len_us), 137 | RETCP_RESIZE_us) * python_config.TDF)), 138 | "cc": cc}] 139 | 140 | # Set paramters that apply to all configurations. 141 | for cnf in cnfs: 142 | # Enable the hybrid switch's packet log. This should already be enabled 143 | # by default. 144 | cnf["packet_log"] = True 145 | # If the night and day lengths have not been set already, then do so 146 | # here. Explicitly set the night and day lengths instead of relying on 147 | # their defaults so that we can automatically calculate the experiment 148 | # duration, below. 149 | if "night_len_us" not in cnf: 150 | cnf["night_len_us"] = int(round(20 * python_config.TDF)) 151 | cnf["day_len_us"] = int(round(180 * python_config.TDF)) 152 | if "small_queue_cap" not in cnf: 153 | cnf["small_queue_cap"] = SMALL_QUEUE_CAP 154 | cnf["big_queue_cap"] = BIG_QUEUE_CAP 155 | if cnf["cc"] == "dctcp": 156 | # If the configuration uses DCTCP, then enable threshold-based ECN 157 | # marking. 158 | cnf["ecn"] = ( 159 | python_config.DCTCP_THRESH, 160 | int(round(float(cnf["big_queue_cap"]) / cnf["small_queue_cap"] * 161 | python_config.DCTCP_THRESH))) 162 | 163 | 164 | # Assemble settings. Generate the list of settings first so that we can 165 | # the estimated total duration. 166 | cnfs = [ 167 | (cnf, { 168 | # Generate a flow from each machine on rack 1 to its corresponding 169 | # partner machine on rack 2. 170 | "flows": [{"src": "r2", "dst": "r3"}], 171 | # Run the flow for three thousand weeks plus 100 ms for good 172 | # measure, converted to seconds. The resulting duration is not under 173 | # TDF, but the flow will be under TDF when it is executed (i.e., the 174 | # flow will actually take TDF times longer than the value computed 175 | # here). 176 | "dur": (((cnf["night_len_us"] + cnf["day_len_us"]) # One night and day under TDF. 177 | / python_config.TDF # Convert from TDF to real time. 178 | * (cnf["num_racks_fake"] - 1) # Convert to a full week. 179 | / 1e3 # Convert from us to ms. 180 | * 3000 # 3000 weeks. 181 | + 100) # Extra 100 ms, for good measure. 182 | / 1e3), # Convert to seconds. 183 | "tcpdump": TCPDUMP 184 | }) for cnf in cnfs] 185 | 186 | # Total number of experiments. 187 | tot = len(cnfs) 188 | # Estimated total duration. 189 | dur = sum([stg["dur"] for cnf, stg in cnfs]) * python_config.TDF 190 | print("Estimated total duration: > {:.2f} seconds, > {:.2f} hours".format( 191 | dur, dur / 3600.)) 192 | # Run experiments. Use the first experiment's CC mode to avoid unnecessarily 193 | # restarting the cluster. 194 | maybe(lambda: common.initializeExperiment( 195 | "flowgrindd", cc=cnfs[0][0]["cc"], sync=SYNC)) 196 | for cnt, (cnf, stg) in enumerate(cnfs, start=1): 197 | maybe(lambda: click_common.setConfig(cnf)) 198 | print("--- experiment {} of {}, config:\n{}".format(cnt, tot, cnf)) 199 | maybe(lambda: common.flowgrind(settings=stg)) 200 | maybe(common.finishExperiment) 201 | 202 | 203 | if __name__ == "__main__": 204 | main() 205 | -------------------------------------------------------------------------------- /experiments/buffers/optsys.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os import path 4 | import sys 5 | # Directory containing this program. 6 | PROGDIR = path.dirname(path.realpath(__file__)) 7 | # For click_common and common. 8 | sys.path.insert(0, path.join(PROGDIR, "..")) 9 | # For python_config. 10 | sys.path.insert(0, path.join(PROGDIR, "..", "..", "etc")) 11 | 12 | import click_common 13 | import common 14 | import python_config 15 | 16 | # If True, then do not run experiments and instead only print configurations. 17 | DRY_RUN = False 18 | MAX_RESIZE = 4500 19 | 20 | 21 | def maybe(fnc, do=not DRY_RUN): 22 | if do: 23 | fnc() 24 | 25 | 26 | def main(): 27 | cnfs = [] 28 | 29 | # CC modes are the outside loop to minimize how frequently we change the CC 30 | # mode, since doing so requires restarting the cluster. 31 | for cc in python_config.CCS: 32 | 33 | # (1) Long days, static buffers. Show the cases where TCP ramp up is not 34 | # a problem. 35 | if cc in ["cubic"]: 36 | # Old optical switches. 37 | cnfs += [{"type": "strobe", "buffer_size": 16, 38 | "night_len_us": 1000. * python_config.TDF, 39 | "day_len_us": 9000. * python_config.TDF, 40 | "cc": cc}] 41 | # New optical switches, but using long days. 42 | cnfs += [{"type": "strobe", "buffer_size": 16, 43 | "night_len_us": python_config.RECONFIG_DELAY_us * \ 44 | python_config.TDF, 45 | "day_len_us": 9000. * python_config.TDF, "cc": cc}] 46 | 47 | # (2) Static buffers. Show that all the TCP variants perform poorly when 48 | # nights/days are short. 49 | # (3) reTCP. Show how much improvement reTCP offers with static buffers 50 | # (i.e., on its own). 51 | if cc in ["cubic", "retcp"]: 52 | for exp in range(2, 8): 53 | cnfs += [{"type": "strobe", "buffer_size": 2**exp, "cc": cc}] 54 | else: 55 | cnf = {"type": "strobe", "buffer_size": 2**4, "cc": cc} 56 | # For DCTCP, we will enable threshold-based ECN marking. 57 | dctcp = cc == "dctcp" 58 | if dctcp: 59 | cnf["ecn"] = python_config.DCTCP_THRESH 60 | cnfs += [cnf] 61 | 62 | # (4) Dynamic buffers. Show that dynamic buffers help TCP cubic when 63 | # nights/days are short. 64 | # (5) reTCP. Show how much improvement reTCP offers with dynamic 65 | # buffers. 66 | if cc in ["cubic", "retcp"]: 67 | for i in xrange(MAX_RESIZE + 1): 68 | if i % 500 == 0: 69 | cnf = {"type": "strobe", "buffer_size": 16, 70 | "queue_resize": True, "in_advance": i, "cc": cc} 71 | if dctcp: 72 | cnf["ecn"] = python_config.DCTCP_THRESH 73 | cnfs += [cnf] 74 | 75 | # For all configurations, enable the packet log. 76 | cnfs = [dict(cnf, packet_log=True) for cnf in cnfs] 77 | 78 | # Use the first experiment's CC mode, or "cubic" if no CC mode is specified. 79 | # This avoid unnecessarily restarting the cluster. 80 | maybe(lambda: common.initializeExperiment( 81 | "flowgrindd", cnfs[0].get("cc", "cubic"))) 82 | 83 | # Total number of experiments. 84 | tot = len(cnfs) 85 | for cnt, cnf in enumerate(cnfs, start=1): 86 | maybe(lambda: click_common.setConfig(cnf)) 87 | print("--- experiment {} of {}, config:\n{}".format(cnt, tot, cnf)) 88 | maybe(lambda: common.flowgrind( 89 | settings={"flows": [{"src": "r1", "dst": "r2"}]})) 90 | 91 | maybe(lambda: common.finishExperiment()) 92 | 93 | 94 | if __name__ == "__main__": 95 | main() 96 | -------------------------------------------------------------------------------- /experiments/buffers/sandbox.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # A simple configuration that sets up the testbed, launches flowgrindd on the 4 | # hosts, runs the Solstice scheduler, and exits, allowing the user to play 5 | # around manually. 6 | 7 | import sys 8 | sys.path.insert(0, '/etalon/experiments') 9 | from common import initializeExperiment 10 | from click_common import setConfig 11 | 12 | initializeExperiment('flowgrindd') 13 | 14 | config = { 15 | 'type': 'normal', 16 | 'buffer_size': 128, 17 | 'divert_acks': True 18 | } 19 | 20 | print '--- running test type %s...' % config['type'] 21 | print '--- setting switch buffer size to %d...' % config['buffer_size'] 22 | setConfig(config) 23 | print '--- done...' 24 | -------------------------------------------------------------------------------- /experiments/buffers/sequence_graphs/sg_cc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | from os import path 5 | import sys 6 | # Directory containing this program. 7 | PROGDIR = path.dirname(path.realpath(__file__)) 8 | # For python_config. 9 | sys.path.insert(0, path.join(PROGDIR, "..", "..", "..", "etc")) 10 | 11 | # Use non-interactive backend. 12 | import matplotlib 13 | matplotlib.use("Agg") 14 | 15 | import python_config 16 | import sg 17 | 18 | 19 | def main(): 20 | assert len(sys.argv) == 3, \ 21 | "Expected one argument: experiment data dir, output dir" 22 | edr, odr = sys.argv[1:] 23 | if not path.isdir(edr): 24 | print("The first argument must be a directory, but is: {}".format(edr)) 25 | sys.exit(-1) 26 | if path.exists(odr): 27 | if not path.isdir(odr): 28 | print("Output directory exists and is a file: {}".format(odr)) 29 | sys.exit(-1) 30 | else: 31 | os.makedirs(odr) 32 | 33 | # Create a graph for each TCP variant. 34 | for var in ["cubic"]: 35 | sg.seq( 36 | name="seq-dyn-{}".format(var), 37 | edr=edr, 38 | odr=odr, 39 | ptn="*-QUEUE-True-*-{}-*click.txt".format(var), 40 | key_fnc=lambda fn: int(round(float(fn.split("-")[6]) 41 | / python_config.TDF)), 42 | dur=1200, 43 | # chunk_mode=100, 44 | msg_len=116, 45 | log_pos="after") 46 | # ins=((2780, 2960), (200, 340)), 47 | #flt=lambda idx, label: idx < 10) 48 | # flt=lambda idx, label: idx in [0, 1, 3, 4, 5, 6, 7, 8, 9, 10]) 49 | 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /experiments/buffers/sim.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from collections import defaultdict 4 | import os 5 | from os import path 6 | import sys 7 | # Directory containing this program. 8 | PROGDIR = path.dirname(path.realpath(__file__)) 9 | 10 | from dotmap import DotMap 11 | import simpleplotlib 12 | simpleplotlib.default_options.rcParams['font.family'] = "Tahoma" 13 | from simpleplotlib import plot 14 | 15 | MSS_B_EXP = 1500 16 | MSS_B_TARGET = 9000 17 | CHOSEN_QUEUE_CAPS = [8, 16, 32, 64, 128] 18 | 19 | 20 | def main(): 21 | assert len(sys.argv) == 2, "Expected one argument: experiment data file" 22 | edf = sys.argv[1] 23 | if not path.isfile(edf): 24 | print("The first argument must be a file, but is: {}".format(edf)) 25 | sys.exit(-1) 26 | # Specify and create the output directory. 27 | odr = path.join(PROGDIR, "graphs", "nsdi2020") 28 | if path.exists(odr): 29 | if not path.isdir(odr): 30 | print("Output directory exists and is a file: {}".format(odr)) 31 | sys.exit(-1) 32 | else: 33 | os.makedirs(odr) 34 | 35 | # { num flows : { q len : list of results } } 36 | data = defaultdict(lambda: defaultdict(dict)) 37 | with open(edf) as f: 38 | for line in f: 39 | # date, swt_us, num_flows, q_len_p, delay_2way_ns, fct_us, num_rtx, \ 40 | # num_rto, num_syns, avt_rtt_us = line.strip().split() 41 | date, num_flows, swt_us, q_len_p, delay_2way_ns, fct_us, num_rtx, \ 42 | num_rto, num_syns, avt_rtt_us = line.strip().split() 43 | # Convert the queue length from being in terms of 1500-byte packets 44 | # to being in terms of 9000-bytes packets. 45 | q_len_p = float(q_len_p) * (float(MSS_B_EXP) / float(MSS_B_TARGET)) 46 | swt_us = float(swt_us) 47 | fct_s = float(fct_us) / 1e6 48 | record = (date, float(delay_2way_ns), fct_s, float(num_rtx), 49 | float(num_rto), float(num_syns), float(avt_rtt_us)) 50 | 51 | # Store this record if it is the smallest FCT for this q len. 52 | if ((swt_us in data[num_flows][q_len_p] and 53 | fct_s < data[num_flows][q_len_p][swt_us][2]) or 54 | swt_us not in data[num_flows][q_len_p]): 55 | data[num_flows][q_len_p][swt_us] = record 56 | 57 | 58 | for num_flows, q_len_results in data.items(): 59 | # for q_len_p, swt_us_results in q_len_results.items(): 60 | # for swt_us, val in swt_us_results.items(): 61 | # print val 62 | 63 | # assert False 64 | # { q len : list of pairs (switch time, FCT) }.items() 65 | lines = {q_len_p : [ 66 | (swt_us, fct_s) 67 | for swt_us, (_, _, fct_s, _, _, _, _) in swt_us_results.items()] 68 | for q_len_p, swt_us_results in q_len_results.items()}.items() 69 | # Pick only the lines we want. 70 | lines = [(q_len_p, res) for q_len_p, res in lines 71 | if q_len_p in CHOSEN_QUEUE_CAPS] 72 | # Sort the datapoints based on their x-valies. 73 | lines = sorted(lines, key=lambda a: a[0]) 74 | # lbls: list of q lens 75 | # lines: list of lists of pairs of (switch time, FCT) 76 | lbls, lines = zip(*lines) 77 | # xs: list of lists of switch times 78 | # ys: list of lists of FCTs 79 | xs = [[p[0] for p in sorted(val, key=lambda a: a[0])] for val in lines] 80 | ys = [[p[1] for p in sorted(val, key=lambda a: a[0])] for val in lines] 81 | 82 | options = DotMap() 83 | options.plot_type = "LINE" 84 | options.legend.options.labels = [ 85 | "{} packets".format(int(round(lbl))) for lbl in lbls] 86 | options.legend.options.fontsize = 18 87 | options.legend.options.ncol = 1 88 | options.series_options = [DotMap(linewidth=2, marker="o") 89 | for _ in range(len(xs))] 90 | options.output_fn = path.join(odr, "sim-{}-flows.pdf".format(num_flows)) 91 | options.x.label.xlabel = "Circuit uptime ($\mu$s)" 92 | options.y.label.ylabel = "Flow completion time (s)" 93 | options.x.label.fontsize = options.y.label.fontsize = 18 94 | options.x.ticks.major.options.labelsize = \ 95 | options.y.ticks.major.options.labelsize = 18 96 | options.x.log = True 97 | options.x.axis.show = options.y.axis.show = True 98 | options.x.axis.color = options.y.axis.color = "black" 99 | options.x.axis.stretch = 1.35 100 | # Flip the x-axis. 101 | options.x.limits = (max([max(vals) for vals in xs]) * 1.5, 102 | min([min(vals) for vals in xs]) * 0.5) 103 | options.y.limits = (0, max([max(vals) for vals in ys]) + 4) 104 | 105 | plot(xs, ys, options) 106 | 107 | 108 | if __name__ == "__main__": 109 | main() 110 | -------------------------------------------------------------------------------- /experiments/buffers/validation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os import path 4 | import sys 5 | # Directory containing this program. 6 | PROGDIR = path.dirname(path.realpath(__file__)) 7 | # For click_common and common. 8 | sys.path.insert(0, path.join(PROGDIR, "..")) 9 | 10 | import click_common 11 | import common 12 | 13 | CC = "cubic" 14 | BUFFER_SIZE = 128 15 | 16 | 17 | def main(): 18 | common.initializeExperiment("flowgrindd", cc=CC) 19 | 20 | for typ in ["no_circuit", "circuit", "strobe"]: 21 | cnf = {"type": typ, 22 | "cc": CC, 23 | "buffer_size": BUFFER_SIZE, 24 | "divert_acks": True} 25 | click_common.setConfig(cnf) 26 | print("--- config:\n{}".format(cnf)) 27 | common.flowgrind(settings={"flows": [{"src": "r1", "dst": "r2"}]}) 28 | 29 | common.finishExperiment() 30 | 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /experiments/buffers/validation_results.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import glob 4 | from os import path 5 | import sys 6 | # Directory containing this program. 7 | PROGDIR = path.dirname(path.realpath(__file__)) 8 | # For parse_logs. 9 | sys.path.insert(0, path.join(PROGDIR, "..")) 10 | 11 | import parse_logs 12 | 13 | 14 | def main(): 15 | assert len(sys.argv) == 2, "Expected one argument: experiment dir" 16 | edr = sys.argv[1] 17 | if not path.isdir(edr): 18 | print("The first argument must be a directory, but is: {}".format(edr)) 19 | sys.exit(-1) 20 | 21 | for ptn in ["*-no_circuit-*-flowgrind.txt", 22 | "*-circuit-*-flowgrind.txt", 23 | "*-strobe-*-flowgrind.txt"]: 24 | parse_logs.parse_validation_log(glob.glob(path.join(edr, ptn))[0]) 25 | 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /experiments/click_common.py: -------------------------------------------------------------------------------- 1 | 2 | from os import path 3 | import socket 4 | import sys 5 | # Directory containing this program. 6 | PROGDIR = path.dirname(path.realpath(__file__)) 7 | # For python_config. 8 | sys.path.insert(0, path.join(PROGDIR, "..", "etc")) 9 | import time 10 | 11 | import common 12 | from python_config import NUM_RACKS, TIMESTAMP, SCRIPT, TDF, EXPERIMENTS, \ 13 | CLICK_ADDR, CLICK_PORT, CLICK_BUFFER_SIZE, DEFAULT_CIRCUIT_CONFIG, \ 14 | CIRCUIT_LATENCY_s_TDF, RECONFIG_DELAY_us, PACKET_BW_Gbps_TDF, DEFAULT_CC 15 | 16 | CLICK_SOCKET = None 17 | FN_FORMAT = '' 18 | 19 | 20 | def initializeClickControl(): 21 | global CLICK_SOCKET 22 | print '--- connecting to click socket...' 23 | CLICK_SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 24 | CLICK_SOCKET.connect((CLICK_ADDR, CLICK_PORT)) 25 | CLICK_SOCKET.recv(CLICK_BUFFER_SIZE) 26 | print '--- done...' 27 | 28 | 29 | def clickWriteHandler(element, handler, value): 30 | message = "WRITE %s.%s %s\n" % (element, handler, value) 31 | print message.strip() 32 | CLICK_SOCKET.send(message) 33 | response = CLICK_SOCKET.recv(CLICK_BUFFER_SIZE).strip() 34 | print response 35 | assert int(response.split("\n")[-1].split(" ")[0]) == 200, response 36 | 37 | 38 | def clickReadHandler(element, handler): 39 | message = "READ %s.%s\n" % (element, handler) 40 | print message.strip() 41 | CLICK_SOCKET.send(message) 42 | # The return value is three lines. The last line is the actual value. 43 | data = int(CLICK_SOCKET.recv(CLICK_BUFFER_SIZE).strip().split("\n")[-1]) 44 | print data 45 | return data 46 | 47 | 48 | def setLog(log): 49 | print 'changing log fn to %s' % log 50 | clickWriteHandler('hsl', 'openLog', log) 51 | if log != '/tmp/hslog.log': 52 | EXPERIMENTS.append(log) 53 | time.sleep(0.1) 54 | 55 | 56 | def disableLog(): 57 | print 'disabling packet logging' 58 | clickWriteHandler('hsl', 'disableLog', '') 59 | time.sleep(0.1) 60 | 61 | 62 | def setQueueCap(s_cap, b_cap): 63 | # When resizing the small VOQ capacity, we need to explicitly reconfigure 64 | # the VOQs themselves. This is necessary because the Click RunSchedule 65 | # element does not set the VOQ capacity when in static VOQ mode. The best 66 | # solution would be to modify RunSchedule to always set the proper VOQ 67 | # capacity, but I do not want to risk introducing a bug, so I will do this 68 | # hack instead. 69 | for src in xrange(1, NUM_RACKS + 1): 70 | for dst in xrange(1, NUM_RACKS + 1): 71 | clickWriteHandler("hybrid_switch/q{}{}/q".format(src, dst), 72 | "resize_capacity", s_cap) 73 | clickWriteHandler("runner", "queue_capacity", "{},{}".format(s_cap, b_cap)) 74 | time.sleep(0.1) 75 | 76 | 77 | def setEstimateTrafficSource(source): 78 | clickWriteHandler('traffic_matrix', 'setSource', source) 79 | 80 | 81 | def setInAdvance(in_advance): 82 | clickWriteHandler('runner', 'setInAdvance', in_advance) 83 | 84 | 85 | def setQueueResize(b): 86 | clickWriteHandler('runner', 'setDoResize', 'true' if b else 'false') 87 | time.sleep(0.1) 88 | 89 | 90 | def getCounters(): 91 | print "--- getting Click counters..." 92 | circuit_bytes = [] 93 | packet_up_bytes = [] 94 | packet_down_bytes = [] 95 | for i in xrange(1, NUM_RACKS + 1): 96 | circuit_bytes.append( 97 | clickReadHandler('hybrid_switch/circuit_link%d/lu' % (i), 98 | 'total_bytes')) 99 | packet_up_bytes.append( 100 | clickReadHandler('hybrid_switch/packet_up_link%d/lu' % (i), 101 | 'total_bytes')) 102 | packet_down_bytes.append( 103 | clickReadHandler('hybrid_switch/ps/packet_link%d/lu' % (i), 104 | 'total_bytes')) 105 | print "--- done..." 106 | return (circuit_bytes, packet_up_bytes, packet_down_bytes) 107 | 108 | 109 | def clearCounters(): 110 | print("--- clearing Click counters...") 111 | for i in xrange(1, NUM_RACKS + 1): 112 | for j in xrange(1, NUM_RACKS + 1): 113 | clickWriteHandler('hybrid_switch/q%d%d/q' % (i, j), 114 | 'clear', "") 115 | clickWriteHandler('hybrid_switch/circuit_link%d/lu' % (i), 116 | 'clear', "") 117 | clickWriteHandler('hybrid_switch/packet_up_link%d/lu' % (i), 118 | 'clear', "") 119 | clickWriteHandler('hybrid_switch/ps/packet_link%d/lu' % (i), 120 | 'clear', "") 121 | clickWriteHandler('traffic_matrix', 'clear', "") 122 | print("--- done...") 123 | 124 | 125 | def divertACKs(divert): 126 | clickWriteHandler('divert_acks', 'switch', 1 if divert else 0) 127 | 128 | 129 | def setCircuitLinkDelay(delay): 130 | for i in xrange(1, NUM_RACKS + 1): 131 | clickWriteHandler('hybrid_switch/circuit_link%d/lu' % (i), 132 | 'latency', delay) 133 | 134 | 135 | def setPacketLinkBandwidth(bw): 136 | for i in xrange(1, NUM_RACKS + 1): 137 | clickWriteHandler('hybrid_switch/packet_up_link%d/lu' % (i), 138 | 'bandwidth', '%.1fGbps' % bw) 139 | clickWriteHandler('hybrid_switch/ps/packet_link%d/lu' % (i), 140 | 'bandwidth', '%.1fGbps' % bw) 141 | 142 | 143 | def setSolsticeThresh(thresh): 144 | clickWriteHandler('sol', 'setThresh', thresh) 145 | 146 | 147 | ## 148 | # Scheduling 149 | ## 150 | def enableSolstice(): 151 | clickWriteHandler('sol', 'setEnabled', 'true') 152 | time.sleep(0.1) 153 | 154 | 155 | def disableSolstice(): 156 | clickWriteHandler('sol', 'setEnabled', 'false') 157 | time.sleep(0.1) 158 | 159 | 160 | def disableCircuit(): 161 | setFixedSchedule('1 20000 %s' % (('-1/' * NUM_RACKS)[:-1])) 162 | 163 | 164 | def setStrobeSchedule(num_racks=NUM_RACKS, night_len_us=20, day_len_us=180): 165 | # Configuration that turns off all the circuit links. Remove trailing '/'. 166 | off_config = ('-1/' * num_racks)[:-1] 167 | # Day len, day config, night len, off config 168 | config_s = '%d %s %d %s ' 169 | # The first element is the number of configurations. There are num_racks - 1 170 | # configurations because there does not need to be a configuration where the 171 | # racks connect to themselves. Each configuration actually contains both a 172 | # day and a night. 173 | schedule = '%d ' % ((num_racks - 1) * 2) 174 | for i in xrange(num_racks - 1): 175 | day_config = '' 176 | for j in xrange(num_racks): 177 | day_config += '%d/' % ((i + 1 + j) % num_racks) 178 | # Remove trailing '/'. 179 | day_config = day_config[:-1] 180 | schedule += config_s % (day_len_us, day_config, night_len_us, off_config) 181 | # Remove trailing space. 182 | schedule = schedule[:-1] 183 | setFixedSchedule(schedule) 184 | 185 | 186 | def setFakeStrobeSchedule(num_racks_fake=NUM_RACKS, night_len_us=20, day_len_us=180): 187 | """ 188 | Set a schedule that, from the point of view of one node, appears to be a 189 | strobe schedule on a testbed with "num_racks_fake" racks. 190 | 191 | E.g., for setFakeSchedule(8, 3600, 400) will create this schedule: 192 | "14 193 | 3600 1/2/0 400 -1/-1/-1 # 1 194 | 3600 1/2/0 400 -1/-1/-1 # 2 195 | 3600 1/2/0 400 -1/-1/-1 # 3 196 | 3600 1/2/0 400 -1/-1/-1 # 4 197 | 3600 1/2/0 400 -1/-1/-1 # 5 198 | 3600 1/2/0 400 -1/-1/-1 # 6 199 | 3600 2/0/1 400 -1/-1/-1" # 7 200 | This schedule mimics an eight-rack cluster where each rack gets a circuit to 201 | every other rack for 180us (under time dilation). Values are the srcs, 202 | indices are the dsts. E.g., 1/2/0 means that 1->0, 2->1, and 0->2. Day/night 203 | pairs 1-6 are the "other" circuits. 7 is the circuit that our test flow will 204 | traverse. There are only seven configurations total for an eight-rack 205 | cluster because we do not need a configuration where each of the racks 206 | connects to itself. 207 | """ 208 | assert num_racks_fake >= 3, \ 209 | ("Can only fake testbeds with more than 3 racks, but " 210 | "\"num_racks_fake\" = {}").format(num_racks_fake) 211 | num_parts = num_racks_fake - 1 212 | schedule = "{} ".format(num_parts * 2) 213 | schedule += "{} 1/2/0 {} -1/-1/-1 ".format(day_len_us, night_len_us) * (num_parts - 1) 214 | schedule += "{} 2/0/1 {} -1/-1/-1".format(day_len_us, night_len_us) 215 | setFixedSchedule(schedule) 216 | 217 | 218 | def setCircuitSchedule(configuration): 219 | setFixedSchedule('1 %d %s' % (20 * TDF * 10 * 10, configuration)) 220 | 221 | 222 | def setFixedSchedule(schedule): 223 | disableSolstice() 224 | clickWriteHandler('runner', 'setSchedule', schedule) 225 | time.sleep(0.1) 226 | 227 | 228 | def setEceEnabled(enabled): 229 | """ Enable/disable ECE marking. """ 230 | clickWriteHandler('ecem', 'enabled', 'true' if enabled else 'false') 231 | time.sleep(0.1) 232 | 233 | 234 | def setEcnEnabled(enabled): 235 | """ Enable/disable threshold-based ECN marking. """ 236 | val = 'true' if enabled else 'false' 237 | for src in xrange(1, NUM_RACKS + 1): 238 | for dst in xrange(1, NUM_RACKS + 1): 239 | clickWriteHandler('hybrid_switch/q{}{}/q'.format(src, dst), 240 | 'marking_enabled', val) 241 | time.sleep(0.1) 242 | 243 | 244 | def setEcnThresh(threshs): 245 | """ Set the ECN marking threshold. """ 246 | num_threshs = len(threshs) 247 | assert num_threshs == 2, \ 248 | ("Must specify exactly two marking threshold (small and big), " 249 | "not {}: {}").format(num_threshs, threshs) 250 | s_thresh, b_thresh = threshs 251 | 252 | for src in xrange(1, NUM_RACKS + 1): 253 | for dst in xrange(1, NUM_RACKS + 1): 254 | clickWriteHandler('hybrid_switch/q{}{}/q'.format(src, dst), 255 | 'marking_threshold', s_thresh) 256 | clickWriteHandler("runner", "marking_threshold", 257 | "{},{}".format(s_thresh, b_thresh)) 258 | time.sleep(0.1) 259 | 260 | 261 | def setConfig(config): 262 | global FN_FORMAT 263 | c = {'type': 'normal', 'small_queue_cap': 16, 'big_queue_cap': 128, 264 | 'traffic_source': 'QUEUE', 'queue_resize': False, 265 | 'in_advance': 12000, 'cc': DEFAULT_CC, 'packet_log': True, 266 | 'divert_acks': False, 'circuit_link_delay': CIRCUIT_LATENCY_s_TDF, 267 | 'packet_link_bandwidth': PACKET_BW_Gbps_TDF, 'hdfs': False, 268 | 'thresh': 1000000, 'night_len_us': RECONFIG_DELAY_us * TDF, 269 | 'day_len_us': RECONFIG_DELAY_us * TDF * 9} 270 | 271 | c.update(config) 272 | clearCounters() 273 | setQueueCap(c['small_queue_cap'], c['big_queue_cap']) 274 | setEstimateTrafficSource(c['traffic_source']) 275 | setInAdvance(c['in_advance']) 276 | common.setCC(c['cc']) 277 | setSolsticeThresh(c['thresh']) 278 | 279 | t = c['type'] 280 | if t == 'normal': 281 | enableSolstice() 282 | if t == 'no_circuit': 283 | disableCircuit() 284 | if t == 'strobe': 285 | setStrobeSchedule(NUM_RACKS, night_len_us=c['night_len_us'], 286 | day_len_us=c['day_len_us']) 287 | if t == 'fake_strobe': 288 | setFakeStrobeSchedule(num_racks_fake=c['num_racks_fake'], 289 | night_len_us=c['night_len_us'], 290 | day_len_us=c['day_len_us']) 291 | if t == 'circuit': 292 | setCircuitSchedule(DEFAULT_CIRCUIT_CONFIG) 293 | if t == 'fixed': 294 | setFixedSchedule(c['fixed_schedule']) 295 | 296 | # Enable threshold-based ECN marking only if it is explicitly configured. 297 | ecn_enabled = 'ecn' in c 298 | if ecn_enabled: 299 | setEcnThresh(c['ecn']) 300 | setEcnEnabled(ecn_enabled) 301 | # If using reTCP, then enable ECE marking. 302 | setEceEnabled(c['cc'] == 'retcp') 303 | 304 | # Do this after passing through manual queue sizes and setting the ECN 305 | # marking threshold. 306 | setQueueResize(c['queue_resize']) 307 | 308 | divertACKs(c['divert_acks']) 309 | setCircuitLinkDelay(c['circuit_link_delay']) 310 | setPacketLinkBandwidth(c['packet_link_bandwidth']) 311 | 312 | FN_FORMAT = "{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-".format( 313 | TIMESTAMP, SCRIPT, t, 314 | c["small_queue_cap"], 315 | c["big_queue_cap"], 316 | c["traffic_source"], 317 | c["queue_resize"], 318 | c["in_advance"], 319 | c["cc"], 320 | c["circuit_link_delay"], 321 | c["packet_link_bandwidth"], 322 | c["hdfs"]) 323 | if t in ["fake_strobe", "strobe"]: 324 | FN_FORMAT += "{}-{}-".format(int(c["night_len_us"]), int(c["day_len_us"])) 325 | if t == "fake_strobe": 326 | FN_FORMAT += "{}-".format(c["num_racks_fake"]) 327 | 328 | FN_FORMAT += '%s.txt' 329 | if config and c['packet_log']: 330 | setLog('/tmp/' + FN_FORMAT % 'click') 331 | if not c['packet_log']: 332 | disableLog() 333 | -------------------------------------------------------------------------------- /experiments/hdfs/README.md: -------------------------------------------------------------------------------- 1 | # experiments/hdfs 2 | 3 | Runs HDFS experiments and graph them (section 6 of our paper). 4 | 5 | - ```hdfs_dfsioe.py```: Experiment file for HDFS experiments. 6 | 7 | - ```hdfs_graphs.py```: Generates graphs for HDFS experiments. Expects a 8 | directory of output data from ```hdfs_dfsioe.py``` as an argument. 9 | -------------------------------------------------------------------------------- /experiments/hdfs/graphs/hdfs_99th.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/hdfs/graphs/hdfs_99th.pdf -------------------------------------------------------------------------------- /experiments/hdfs/graphs/hdfs_throughput.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/hdfs/graphs/hdfs_throughput.pdf -------------------------------------------------------------------------------- /experiments/hdfs/graphs/hdfs_utilization.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/hdfs/graphs/hdfs_utilization.pdf -------------------------------------------------------------------------------- /experiments/hdfs/graphs/hdfs_writes_cdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukerjee/etalon/750ef82a1e6219154d5a69e4dc17f4b2c3beff37/experiments/hdfs/graphs/hdfs_writes_cdf.pdf -------------------------------------------------------------------------------- /experiments/hdfs/hdfs_dfsioe.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | sys.path.insert(0, '/etalon/experiments') 5 | from common import initializeExperiment, finishExperiment, dfsioe 6 | from click_common import setConfig 7 | 8 | CONFIGS = [ 9 | {'type': 'normal', 'traffic_source': 'QUEUE'}, 10 | {'type': 'resize', 'traffic_source': 'QUEUE', 'in_advance': 20000}, 11 | {'type': 'resize', 'traffic_source': 'QUEUE', 'in_advance': 20000, 'cc': 'retcp'}, 12 | ] 13 | 14 | for c in CONFIGS: 15 | c['packet_log'] = False 16 | if c['type'] == 'resize': 17 | c['type'] = 'normal' 18 | c['queue_resize'] = True 19 | else: 20 | c['queue_resize'] = False 21 | if 'cc' not in c: 22 | c['cc'] = 'reno' 23 | c['thresh'] = 8000 24 | 25 | for h in ['HDFS', 'reHDFS']: 26 | for c in CONFIGS: 27 | c['hdfs'] = h 28 | print '--- running %s, %s, %s, %s' % (h, c['traffic_source'], 29 | c['queue_resize'], c['cc']) 30 | if c['traffic_source'] == 'ADU': 31 | initializeExperiment(h + "_adu") 32 | else: 33 | initializeExperiment(h) 34 | setConfig(c) 35 | print '--- done initializing...' 36 | dfsioe('h21', h) 37 | 38 | finishExperiment() 39 | -------------------------------------------------------------------------------- /experiments/hdfs/hdfs_graphs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | sys.path.insert(0, '..') 5 | import glob 6 | import numpy as np 7 | 8 | from dotmap import DotMap 9 | from simpleplotlib import plot 10 | from parse_logs import parse_hdfs_logs, parse_hdfs_throughput 11 | 12 | bytes_units = 2.0**-30 13 | 14 | types = ['HDFS+static', 'HDFS+resize', 'HDFS+reTCP', 'reHDFS+static', 15 | 'reHDFS+resize', 'reHDFS+reTCP'] 16 | 17 | fn_keys = { 18 | 'normal-16-QUEUE-False': 'static', 19 | 'normal-16-QUEUE-True-20000-reno': 'resize', 20 | 'normal-16-QUEUE-True-20000-retcp': 'reTCP', 21 | 'normal-16-ADU-False': 'adu', 22 | 'normal-16-ADU-True-20000-reno': 'adu+resize', 23 | 'normal-16-ADU-True-20000-retcp': 'adu+reTCP', 24 | } 25 | 26 | files = [ 27 | '/tmp/*QUEUE-False*-HDFS-dfsioe', 28 | '/tmp/*QUEUE-True-20000-reno*-HDFS-dfsioe', 29 | '/tmp/*QUEUE-True-20000-retcp*-HDFS-dfsioe', 30 | '/tmp/*QUEUE-False*-reHDFS-dfsioe', 31 | '/tmp/*QUEUE-True-20000-reno*-reHDFS-dfsioe', 32 | '/tmp/*QUEUE-True-20000-retcp*-reHDFS-dfsioe', 33 | ] 34 | 35 | files_short = [files[0], files[3]] 36 | 37 | 38 | def get_default_plot_options(x, y): 39 | options = DotMap() 40 | options.plot_type = 'BAR' 41 | options.legend.options.labels = ['HDFS', 'HDFS + Resize', 42 | 'HDFS + reTCP', 'reHDFS', 43 | 'reHDFS + Resize', 44 | 'reHDFS + reTCP'] 45 | options.series.color_groups = [0, 0, 0, 1, 1, 1] 46 | options.legend.order = [0, 2, 4, 1, 3, 5] 47 | options.legend.options.fontsize = 19 48 | options.legend.options.ncol = 3 49 | options.x.ticks.major.show = False 50 | return options 51 | 52 | 53 | def graph_wct(data): 54 | x = data 55 | y = [[float(j) / (len(x[i])-1) * 100 for j in xrange(len(x[i]))] 56 | for i in xrange(len(x))] 57 | 58 | options = get_default_plot_options(x, y) 59 | options.plot_type = 'LINE' 60 | options.legend.options.labels = ['HDFS', 'reHDFS'] 61 | options.series_options = [DotMap(linewidth=5) for i in range(len(x))] 62 | options.output_fn = 'graphs/hdfs_writes_cdf.pdf' 63 | options.x.label.xlabel = 'HDFS write completion time (ms)' 64 | options.y.label.ylabel = 'CDF (%)' 65 | del options.series.color_groups 66 | del options.legend.options.ncol 67 | del options.x.ticks.major.show 68 | plot(x, y, options) 69 | 70 | 71 | def graph_tail(data): 72 | x = np.array([[0] for i in xrange(len(data))]) 73 | y = [np.percentile(d, 99) for d in data] 74 | 75 | options = get_default_plot_options(x, y) 76 | options.y.limits = [0, 1500] 77 | options.output_fn = 'graphs/hdfs_99th.pdf' 78 | options.y.label.ylabel = '99th percent. writes (ms)' 79 | options.y.ticks.major.show = False 80 | del options.legend.options.ncol 81 | del options.legend.order 82 | plot(x, y, options) 83 | 84 | 85 | def graph_throughput(data): 86 | x = np.array([[0] for i in xrange(len(data))]) 87 | y = data 88 | 89 | options = get_default_plot_options(x, y) 90 | options.horizontal_lines.lines = [80*8 + 10*8] 91 | options.legend.options.fontsize = 18 92 | options.y.label_offset = [-0.01, -.13] 93 | options.y.limits = [0, 1100] 94 | options.output_fn = 'graphs/hdfs_throughput.pdf' 95 | options.y.label.ylabel = 'Agg. tput. (Gbps)' 96 | options.y.ticks.major.show = False 97 | plot(x, y, options) 98 | 99 | 100 | def bytes_graph(): 101 | data = {} 102 | for fn in glob.glob(sys.argv[1] + '/*.counters.txt'): 103 | key = 'reHDFS+' if 'reHDFS' in fn else 'HDFS+' 104 | key += [k for n, k in fn_keys.items() if n in fn][0] 105 | c, p, _ = eval(open(fn).read()) 106 | c = sum([int(b.split('\n')[-1]) * bytes_units for b in c]) 107 | p = sum([int(b.split('\n')[-1]) * bytes_units for b in p]) 108 | data[key] = p, c 109 | 110 | y = [data[t] for t in types] 111 | x = np.array([[0, 1] for i in xrange(len(y))]) 112 | 113 | options = get_default_plot_options(x, y) 114 | options.bar_labels.show = False 115 | options.legend.options.fontsize = 18 116 | options.y.label_offset = [-.07, -.18] 117 | options.y.limits = [0, 40] 118 | options.x.ticks.major.labels = DotMap( 119 | text=['Packet', 'Circuit']) 120 | options.y.ticks.major.labels = DotMap( 121 | locations=[0, 5, 10, 15, 20, 25]) 122 | options.output_fn = 'graphs/hdfs_utilization.pdf' 123 | options.x.label.xlabel = 'Which switch' 124 | options.y.label.ylabel = 'Bytes sent (GB)' 125 | plot(x, y, options) 126 | 127 | if __name__ == '__main__': 128 | graph_wct([parse_hdfs_logs(sys.argv[1] + n) for n in files_short]) 129 | graph_tail([parse_hdfs_logs(sys.argv[1] + n) for n in files]) 130 | graph_throughput([parse_hdfs_throughput(sys.argv[1] + n) for n in files]) 131 | bytes_graph() 132 | -------------------------------------------------------------------------------- /libADU/Makefile: -------------------------------------------------------------------------------- 1 | all: libADU.so 2 | 3 | install: libADU.so 4 | cp libADU.so /usr/lib/ 5 | cp libADU.so /etalon/vhost/ 6 | 7 | libADU.so: libADU.c ../etc/c_config.h 8 | rm -f libADU.so* 9 | gcc -O3 -fPIC -shared -Werror -Wall -o libADU.so libADU.c -ldl 10 | 11 | clean: 12 | rm -f libADU.so* 13 | -------------------------------------------------------------------------------- /libADU/README.md: -------------------------------------------------------------------------------- 1 | # libADU 2 | 3 | libADU is a interposition library that wraps ```write```, ```send```, 4 | ```shutdown```, and ```close``` to inform our reconfigurable switch emulator of 5 | demand waiting on an endhost. ```write``` and ```send``` send a message to the 6 | reconfigurable switch indicating how much data was just passed into the socket 7 | buffer, and ```shutdown``` and ```close``` send a message to the reconfigurable 8 | switch indicating remaining data for this flow has been expunged. 9 | -------------------------------------------------------------------------------- /libADU/libADU.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "../etc/c_config.h" 18 | 19 | #define EXIT_FAILED -1 20 | 21 | struct traffic_info { 22 | struct in_addr src; 23 | struct in_addr dst; 24 | uint8_t proto; 25 | uint16_t sport; 26 | uint16_t dport; 27 | long size; 28 | }; 29 | 30 | int ctrl_sock = -1; 31 | 32 | // Find the library version of the function that we are wrapping 33 | static void get_next_fn(void** next_fn, char* fn) { 34 | char* msg; 35 | *next_fn = dlsym(RTLD_NEXT, fn); 36 | if ((msg = dlerror()) != NULL) { 37 | fprintf(stderr, "dlopen failed on %s: %s\n", fn, msg); 38 | exit(EXIT_FAILED); 39 | } 40 | } 41 | 42 | void get_sock_info(int fd, struct traffic_info *info) { 43 | struct sockaddr_in localAddress; 44 | struct sockaddr_in remoteAddress; 45 | socklen_t addressLength = sizeof(localAddress); 46 | 47 | getsockname(fd, (struct sockaddr*)&localAddress, &addressLength); 48 | getpeername(fd, (struct sockaddr*)&remoteAddress, &addressLength); 49 | 50 | int type; 51 | socklen_t type_length = sizeof(int); 52 | getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &type_length); 53 | 54 | info->src = localAddress.sin_addr; 55 | info->dst = remoteAddress.sin_addr; 56 | info->proto = (type == SOCK_STREAM) ? 6 : 0; 57 | info->sport = localAddress.sin_port; 58 | info->dport = remoteAddress.sin_port; 59 | } 60 | 61 | static void open_ctrl_socket() { 62 | if(ctrl_sock == -1){ 63 | struct addrinfo hints, *res, *p; 64 | 65 | char *host=SWITCH_CTRL_IP, *port=SWITCH_CTRL_PORT; 66 | 67 | memset(&hints, 0, sizeof(hints)); 68 | hints.ai_family = AF_INET; 69 | hints.ai_socktype = SOCK_STREAM; //TCP 70 | 71 | if (getaddrinfo(host, port, &hints, &res) != 0) { 72 | perror("getaddrinfo() failed"); 73 | exit(-1); 74 | } 75 | 76 | for(p = res; p != NULL; p = p->ai_next) { 77 | int (*next_socket)(int, int, int); 78 | get_next_fn((void**)&next_socket, "socket"); 79 | if ((ctrl_sock = next_socket(p->ai_family, p->ai_socktype, 80 | p->ai_protocol)) == -1) { 81 | perror("Could not open socket"); 82 | continue; 83 | } 84 | 85 | if (connect(ctrl_sock, p->ai_addr, p->ai_addrlen) == -1) { 86 | int (*next_close)(int); 87 | get_next_fn((void**)&next_close, "close"); 88 | next_close(ctrl_sock); 89 | perror("Could not connect to socket"); 90 | continue; 91 | } 92 | 93 | break; 94 | } 95 | freeaddrinfo(res); 96 | } 97 | } 98 | 99 | void send_adu_info(int fd, int true_size) { 100 | struct stat statbuf; 101 | fstat(fd, &statbuf); 102 | if (!S_ISSOCK(statbuf.st_mode)) 103 | return; 104 | 105 | if (fd == ctrl_sock) 106 | return; 107 | 108 | open_ctrl_socket(); 109 | 110 | struct traffic_info adu_info; 111 | get_sock_info(fd, &adu_info); 112 | adu_info.size = true_size; 113 | /* fprintf(stderr, "LOCAL_IP: %s REMOTE_IP: %s\n", adu_info.src, adu_info.dst); */ 114 | 115 | if (true_size <= 0) 116 | return; 117 | 118 | ssize_t (*next_write)(int, const void*, size_t); 119 | get_next_fn((void**)&next_write, "write"); 120 | ssize_t nbytes = next_write(ctrl_sock, &adu_info, sizeof(struct traffic_info)); 121 | if (nbytes != sizeof(struct traffic_info)){ 122 | fprintf(stderr, "Failed to send ctrl message\n"); 123 | exit(EXIT_FAILED); 124 | } 125 | } 126 | 127 | // Wrap up the write() call 128 | ssize_t write(int fd, const void *buffer, size_t size) { 129 | ssize_t (*next_write)(int, const void*, size_t); 130 | get_next_fn((void**)&next_write, "write"); 131 | 132 | ssize_t true_size = next_write(fd, buffer, size); 133 | 134 | send_adu_info(fd, true_size); 135 | 136 | return true_size; 137 | } 138 | 139 | // Wrap up the send() call 140 | ssize_t send(int fd, const void *buffer, size_t size, int flags) { 141 | ssize_t (*next_send)(int, const void*, size_t, int); 142 | get_next_fn((void**)&next_send, "send"); 143 | 144 | ssize_t true_size = next_send(fd, buffer, size, flags); 145 | 146 | send_adu_info(fd, true_size); 147 | 148 | return true_size; 149 | } 150 | 151 | // Wrap up the shutdown() call 152 | int shutdown(int fd, int how) { 153 | int (*next_shutdown)(int, int); 154 | get_next_fn((void**)&next_shutdown, "shutdown"); 155 | 156 | int rc = next_shutdown(fd, how); 157 | 158 | if (how == SHUT_WR || how == SHUT_RDWR) 159 | send_adu_info(fd, -1); 160 | 161 | return rc; 162 | } 163 | 164 | // Wrap up the close() call 165 | int close(int fd) { 166 | int (*next_close)(int); 167 | get_next_fn((void**)&next_close, "close"); 168 | 169 | int rc = next_close(fd); 170 | send_adu_info(fd, -1); 171 | 172 | return rc; 173 | } 174 | -------------------------------------------------------------------------------- /reHDFS/BlockPlacementPolicyRDCN.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.apache.hadoop.hdfs.server.blockmanagement; 19 | 20 | import org.apache.hadoop.classification.InterfaceAudience; 21 | import org.apache.hadoop.conf.Configuration; 22 | import org.apache.hadoop.fs.StorageType; 23 | import org.apache.hadoop.net.NetworkTopology; 24 | import org.apache.hadoop.net.Node; 25 | 26 | import java.util.*; 27 | 28 | /** 29 | * The class is responsible for choosing the desired number of targets 30 | * for placing block replicas. 31 | * The strategy is that it tries its best to make a single ring. 32 | */ 33 | @InterfaceAudience.Private 34 | public class BlockPlacementPolicyRDCN extends BlockPlacementPolicyDefault { 35 | 36 | @Override 37 | protected DatanodeStorageInfo chooseRandom(int numOfReplicas, 38 | String scope, 39 | Set excludedNodes, 40 | long blocksize, 41 | int maxNodesPerRack, 42 | List results, 43 | boolean avoidStaleNodes, 44 | EnumMap storageTypes) 45 | throws NotEnoughReplicasException { 46 | int numOfRacks = clusterMap.getNumOfRacks(); 47 | if (scope.contains("~/rack")) { 48 | int i = Integer.parseInt(scope.split("~/rack")[1]); 49 | scope = String.format("/rack%02d", (i % numOfRacks) + 1); 50 | } 51 | return super.chooseRandom(numOfReplicas, scope, excludedNodes, blocksize, 52 | maxNodesPerRack, results, avoidStaleNodes, 53 | storageTypes); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /reHDFS/README.md: -------------------------------------------------------------------------------- 1 | # reHDFS (Reconfigurable Datacenter Network HDFS) 2 | 3 | reHDFS is modified HDFS block placement policy (i.e., it tells HDFS where to 4 | place replicas on write). reHDFS' policy is simple, given a writer on rack 1, 5 | place the replica in rack 2. Writers in rack 2 place replicas in rack 3, 6 | etc. When compared with the default policy (which chooses random racks), reHDFS 7 | creates a ring workload, as opposed to an all-to-all workload. -------------------------------------------------------------------------------- /reTCP/.gitignore: -------------------------------------------------------------------------------- 1 | .*.cmd 2 | *.mod.c 3 | .tmp_versions 4 | Module.symvers 5 | modules.order 6 | -------------------------------------------------------------------------------- /reTCP/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += retcp.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /reTCP/README.md: -------------------------------------------------------------------------------- 1 | # reTCP (Reconfigurable Datacenter Network TCP) 2 | 3 | reTCP is a pluggable linux congestion control module that looks for changes in 4 | the ECE (ECN-Echo) bit in TCP ACKs. If the ECE bit transitions from 0 to 1, 5 | reTCP will multiply ```cwnd``` by a user-settable factor ```jump_up``` upon the 6 | next ```cwnd``` update (i.e., when ```.cong_avoid``` is called). When the ECE 7 | bit transitions from 1 to 0, ```cwnd``` is divided by a user-settable factor 8 | ```jump_down``` upon the next ```cwnd``` update. By default both parameters are 9 | 2. This seems to work well with our reconfigurable switch emulator. 10 | 11 | Additionally, a kernel change is needed to make sure changes in the ECE bit are 12 | propagated to the module without forcing ECN to be enabled (as this would cause 13 | the ```cwnd``` to decrease upon receiving packets with ECE set). 14 | -------------------------------------------------------------------------------- /reTCP/kernel-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c 2 | index 48e4e89..0edc33d 100644 3 | --- a/net/ipv4/tcp_input.c 4 | +++ b/net/ipv4/tcp_input.c 5 | @@ -3603,6 +3603,11 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) 6 | ack_ev_flags |= CA_ACK_ECE; 7 | } 8 | 9 | + // let TCP CC see an ECE even if ECN is disabled 10 | + if (tcp_hdr(skb)->ece && !tcp_hdr(skb)->syn) { 11 | + ack_ev_flags |= CA_ACK_ECE; 12 | + } 13 | + 14 | if (flag & FLAG_WIN_UPDATE) 15 | ack_ev_flags |= CA_ACK_WIN_UPDATE; 16 | 17 | -------------------------------------------------------------------------------- /reTCP/retcp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * reTCP 3 | * 4 | * Matt Mukerjee 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | static int jump_up __read_mostly = 2; 11 | static int jump_down __read_mostly = 2; 12 | 13 | module_param(jump_up, int, 0644); 14 | MODULE_PARM_DESC(jump_up, "CA jump up when given circuit"); 15 | module_param(jump_down, int, 0644); 16 | MODULE_PARM_DESC(jump_up, "CA jump down when loses circuit"); 17 | 18 | struct retcp { 19 | u32 have_circuit; 20 | u32 jumped; 21 | }; 22 | 23 | static void retcp_init(struct sock *sk) 24 | { 25 | struct retcp *ca = inet_csk_ca(sk); 26 | ca->have_circuit = 0; 27 | ca->jumped = 0; 28 | } 29 | 30 | static void retcp_in_ack(struct sock *sk, u32 flags) 31 | { 32 | struct retcp *ca = inet_csk_ca(sk); 33 | ca->have_circuit = flags & CA_ACK_ECE; 34 | } 35 | 36 | static void retcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) 37 | { 38 | struct retcp *ca = inet_csk_ca(sk); 39 | struct tcp_sock *tp = tcp_sk(sk); 40 | tcp_reno_cong_avoid(sk, ack, acked); 41 | if (ca->have_circuit && !ca->jumped) { 42 | tp->snd_cwnd *= jump_up; 43 | ca->jumped = 1; 44 | } 45 | if (!ca->have_circuit && ca->jumped) { 46 | tp->snd_cwnd /= jump_down; 47 | ca->jumped = 0; 48 | } 49 | } 50 | 51 | static struct tcp_congestion_ops retcp __read_mostly = { 52 | .name = "retcp", 53 | .owner = THIS_MODULE, 54 | .init = retcp_init, 55 | .in_ack_event = retcp_in_ack, 56 | .ssthresh = tcp_reno_ssthresh, 57 | .cong_avoid = retcp_cong_avoid, 58 | .undo_cwnd = tcp_reno_undo_cwnd, 59 | }; 60 | 61 | static int __init retcp_register(void) 62 | { 63 | return tcp_register_congestion_control(&retcp); 64 | } 65 | 66 | static void __exit retcp_unregister(void) 67 | { 68 | tcp_unregister_congestion_control(&retcp); 69 | } 70 | 71 | module_init(retcp_register); 72 | module_exit(retcp_unregister); 73 | 74 | MODULE_AUTHOR("Matt Mukerjee"); 75 | MODULE_LICENSE("GPL"); 76 | MODULE_DESCRIPTION("reTCP"); 77 | -------------------------------------------------------------------------------- /rpycd/README.md: -------------------------------------------------------------------------------- 1 | # RPYCD (RPyC daemon) 2 | 3 | The RPyC daemon runs on each non-switch node in order for the switch 4 | node to run commands remotely on the other nodes. It is installed as a 5 | service using systemctl in `etalon/bin/node_install.sh`. 6 | -------------------------------------------------------------------------------- /rpycd/rpycd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import datetime 4 | import multiprocessing 5 | import subprocess 6 | import sys 7 | # For python_config. 8 | sys.path.insert(0, '/etalon/etc') 9 | import time 10 | 11 | import rpyc 12 | from rpyc.utils import server 13 | 14 | import python_config 15 | 16 | 17 | class EtalonService(rpyc.Service): 18 | 19 | def log(self, msg): 20 | with open("/tmp/rpycd.log", "a+") as lgf: 21 | lgf.write("{}: {}\n".format( 22 | datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), msg)) 23 | 24 | def on_connect(self, conn): 25 | """ Drops all connections that aren't from the switch. """ 26 | assert (conn._config['endpoints'][1][0] == \ 27 | python_config.SWITCH_CONTROL_IP), \ 28 | "rpyc connection not from switch" 29 | 30 | def exposed_run(self, cmd): 31 | """ 32 | Run a command on the physical host, but do not wait for the command to 33 | complete. Note: Any errors or output from the command are hidden. 34 | """ 35 | self.log("Launching cmd: {}".format(cmd)) 36 | multiprocessing.Process(target=lambda : self.exposed_run_fully(cmd)).start() 37 | self.log("Launched cmd: {}".format(cmd)) 38 | 39 | def exposed_run_fully(self, cmd): 40 | """ Run a command to completion on the physical host. """ 41 | self.log("Running cmd: {}".format(cmd)) 42 | try: 43 | out = subprocess.check_output(cmd, shell=True) 44 | out_msg = "\n{}".format(out.strip()) if out != "" else "" 45 | return out 46 | except subprocess.CalledProcessError as exp: 47 | # TODO: Log exp.child_traceback. 48 | out_msg = "Error , returncode: {} , exception output:\n{}".format( 49 | exp.returncode, exp.output) 50 | raise exp 51 | finally: 52 | self.log("Finished cmd: {} , output: {}".format(cmd, out_msg)) 53 | 54 | def exposed_run_fully_host(self, hid, cmd): 55 | """ Run a command to completion in a host container. """ 56 | return self.exposed_run_fully( 57 | cmd=python_config.DOCKER_EXEC.format(id=hid, cmd=cmd)) 58 | 59 | def exposed_run_fully_host_ns(self, hid, cmd, timeout_s=0, interval_s=0): 60 | """ 61 | Run a command to completion on the physical host but in a container's 62 | namespace. If "timeout_s" is greater than 0, then attempt the command 63 | every "interval_s" seconds until it suceeds or "timeout_s" seconds have 64 | elapsed. 65 | """ 66 | assert timeout_s >= 0, \ 67 | "\"timeout_s\" must be >= 0, but is: {}".format(timeout_s) 68 | assert interval_s >= 0, \ 69 | "\"interval_s\" must be >= 0, but is: {}".format(interval_s) 70 | 71 | once = False 72 | count = 0 73 | start_s = time.time() 74 | current_s = start_s 75 | end_s = start_s + timeout_s 76 | while (not once) or (current_s - start_s < timeout_s): 77 | once = True 78 | try: 79 | # First, get the Docker container's PID, then run the command in 80 | # the container's namespace. 81 | return self.exposed_run_fully(python_config.NS_RUN.format( 82 | pid=self.exposed_run_fully( 83 | cmd=python_config.DOCKER_GET_PID.format( 84 | id=hid)).split()[0].strip(), 85 | cmd=cmd)) 86 | except subprocess.CalledProcessError: 87 | count += 1 88 | self.log(("Will try command \"{}\" on host \"{}\" again " 89 | "(attempt #{}) in {} second(s).").format( 90 | cmd, hid, count + 1, interval_s)) 91 | if current_s + interval_s > end_s: 92 | # If there is at least one interval remaining, then sleep for 93 | # "interval_s" seconds. 94 | time.sleep(interval_s) 95 | current_s = time.time() 96 | 97 | # If we are here, then the command never completed successfully. 98 | msg = ("Command \"{}\" on host \"{}\" did not complete successfully " 99 | "after {} attempt(s) in {} seconds!").format( 100 | cmd, hid, count, timeout_s) 101 | self.log(msg) 102 | raise RuntimeError(msg) 103 | 104 | 105 | if __name__ == '__main__': 106 | server.ThreadedServer(EtalonService, port=python_config.RPYC_PORT, 107 | protocol_config={"allow_all_attrs": True}).start() 108 | -------------------------------------------------------------------------------- /rpycd/rpycd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Etalon Docker RPyC Daemon 3 | Requires=network.target 4 | After=network.target 5 | 6 | [Service] 7 | ExecStart=/usr/bin/python /etalon/rpycd/rpycd.py 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /vhost/.gitignore: -------------------------------------------------------------------------------- 1 | HiBench.tar.gz 2 | hadoop-*.tar.gz 3 | etalon.img 4 | kill 5 | flowgrindd 6 | -------------------------------------------------------------------------------- /vhost/README.md: -------------------------------------------------------------------------------- 1 | # vhost 2 | 3 | Files used to build the docker container image (i.e., the "vhosts") for 4 | Etalon. ```etalon.dockerfile``` builds the image and ```config``` contains 5 | additional files needed by the image. See lower-level ```README.md``` for more 6 | details. -------------------------------------------------------------------------------- /vhost/config/hadoop_config/.gitignore: -------------------------------------------------------------------------------- 1 | slaves 2 | -------------------------------------------------------------------------------- /vhost/config/hadoop_config/README.md: -------------------------------------------------------------------------------- 1 | # vhost/config/hadoop_config 2 | 3 | Configuration files for hadoop/hdfs. The only important changes are: 4 | 5 | 1. A script that assigns nodes to racks, based on their IP. 6 | 7 | 2. Reducing replication factor down to 2 (as a third replica will be on the 8 | second's rack anyways). 9 | -------------------------------------------------------------------------------- /vhost/config/hadoop_config/core-site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | topology.script.file.name 22 | /usr/local/hadoop/etc/hadoop/topology.script.sh 23 | 24 | 25 | fs.defaultFS 26 | hdfs://nn.etalon.local:9000 27 | 28 | 29 | -------------------------------------------------------------------------------- /vhost/config/hadoop_config/hdfs-site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | dfs.replication 22 | 2 23 | 24 | 25 | dfs.namenode.name.dir 26 | file:///usr/local/hadoop/hadoop_data/hdfs-nn 27 | 28 | 29 | dfs.datanode.data.dir 30 | file:///usr/local/hadoop/hadoop_data/hdfs 31 | 32 | 33 | dfs.namenode.datanode.registration.ip-hostname-check 34 | false 35 | 36 | 37 | dfs.block.replicator.classname 38 | org.apache.hadoop.hdfs.server.blockmanagement.BlockPlacementPolicyDefault 39 | 40 | 41 | -------------------------------------------------------------------------------- /vhost/config/hadoop_config/mapred-site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | mapreduce.framework.name 22 | yarn 23 | 24 | 25 | mapreduce.jobtracker.address 26 | nn.etalon.local:54311 27 | 28 | 29 | -------------------------------------------------------------------------------- /vhost/config/hadoop_config/masters: -------------------------------------------------------------------------------- 1 | nn.etalon.local 2 | -------------------------------------------------------------------------------- /vhost/config/hadoop_config/topology.script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 10.1.3.16 --> /rack03 3 | # 10.1.4.27 --> /rack04 4 | echo $@ | xargs -n 1 | awk -F '.' '{printf "/rack%02d\n", $3}' 5 | -------------------------------------------------------------------------------- /vhost/config/hadoop_config/yarn-site.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | yarn.nodemanager.vmem-check-enabled 20 | false 21 | 22 | 23 | yarn.nodemanager.aux-services 24 | mapreduce_shuffle 25 | 26 | 27 | yarn.nodemanager.aux-services.mapreduce.shuffle.class 28 | org.apache.hadoop.mapred.ShuffleHandler 29 | 30 | 31 | yarn.resourcemanager.hostname 32 | nn.etalon.local 33 | 34 | 35 | -------------------------------------------------------------------------------- /vhost/config/hibench/README.md: -------------------------------------------------------------------------------- 1 | # vhost/config/hibench 2 | 3 | Configuration files for hibench hadoop benchmarking (DFSIOE). The only important 4 | change here is that we've modified DFSIOE to write 64 files that are each 5 | 400MB. We choose 64 files as YARN creates 64 mappers on our cluster (one per 6 | physical core). Thus we can complete this workload in one round of map. -------------------------------------------------------------------------------- /vhost/config/hibench/dfsioe.conf: -------------------------------------------------------------------------------- 1 | #DFSIOE 2 | #Unit: number_of_file -> as name suggests 3 | #Unit: filesize -> MB of file size 4 | hibench.dfsioe.tiny.read.number_of_files 16 5 | hibench.dfsioe.tiny.read.file_size 1 6 | hibench.dfsioe.tiny.write.number_of_files 16 7 | hibench.dfsioe.tiny.write.file_size 1 8 | 9 | hibench.dfsioe.small.read.number_of_files 32 10 | hibench.dfsioe.small.read.file_size 10 11 | hibench.dfsioe.small.write.number_of_files 32 12 | hibench.dfsioe.small.write.file_size 10 13 | 14 | hibench.dfsioe.large.read.number_of_files 64 15 | hibench.dfsioe.large.read.file_size 10 16 | hibench.dfsioe.large.write.number_of_files 64 17 | hibench.dfsioe.large.write.file_size 10 18 | 19 | hibench.dfsioe.huge.read.number_of_files 256 20 | hibench.dfsioe.huge.read.file_size 100 21 | hibench.dfsioe.huge.write.number_of_files 64 22 | hibench.dfsioe.huge.write.file_size 400 23 | 24 | hibench.dfsioe.gigantic.read.number_of_files 512 25 | hibench.dfsioe.gigantic.read.file_size 400 26 | hibench.dfsioe.gigantic.write.number_of_files 512 27 | hibench.dfsioe.gigantic.write.file_size 400 28 | 29 | hibench.dfsioe.bigdata.read.number_of_files 2048 30 | hibench.dfsioe.bigdata.read.file_size 1000 31 | hibench.dfsioe.bigdata.write.number_of_files 2048 32 | hibench.dfsioe.bigdata.write.file_size 1000 33 | 34 | hibench.dfsioe.read.number_of_files ${hibench.dfsioe.${hibench.scale.profile}.read.number_of_files} 35 | hibench.dfsioe.read.file_size ${hibench.dfsioe.${hibench.scale.profile}.read.file_size} 36 | hibench.dfsioe.write.number_of_files ${hibench.dfsioe.${hibench.scale.profile}.write.number_of_files} 37 | hibench.dfsioe.write.file_size ${hibench.dfsioe.${hibench.scale.profile}.write.file_size} 38 | 39 | # export for shell script 40 | hibench.workload.input ${hibench.hdfs.data.dir}/Dfsioe/Input 41 | hibench.workload.output ${hibench.hdfs.data.dir}/Dfsioe/Output 42 | 43 | -------------------------------------------------------------------------------- /vhost/config/hibench/hadoop.conf: -------------------------------------------------------------------------------- 1 | # Hadoop home 2 | hibench.hadoop.home /usr/local/hadoop 3 | 4 | # The path of hadoop executable 5 | hibench.hadoop.executable ${hibench.hadoop.home}/bin/hadoop 6 | 7 | # Hadoop configraution directory 8 | hibench.hadoop.configure.dir ${hibench.hadoop.home}/etc/hadoop 9 | 10 | # Hadoop release provider. Supported value: apache, cdh5, hdp 11 | hibench.hadoop.release apache 12 | 13 | # The root HDFS path to store HiBench data 14 | hibench.hdfs.master hdfs://nn.etalon.local:9000 15 | -------------------------------------------------------------------------------- /vhost/config/hibench/hibench.conf: -------------------------------------------------------------------------------- 1 | # Data scale profile. Available value is tiny, small, large, huge, gigantic and bigdata. 2 | # The definition of these profiles can be found in the workload's conf file i.e. conf/workloads/micro/wordcount.conf 3 | hibench.scale.profile huge 4 | # Mapper number in hadoop, partition number in Spark 5 | hibench.default.map.parallelism 128 6 | 7 | # Reducer nubmer in hadoop, shuffle partition number in Spark 8 | hibench.default.shuffle.parallelism 128 9 | 10 | 11 | #====================================================== 12 | # Report files 13 | #====================================================== 14 | # default report formats 15 | hibench.report.formats "%-12s %-10s %-8s %-20s %-20s %-20s %-20s\n" 16 | 17 | # default report dir path 18 | hibench.report.dir ${hibench.home}/report 19 | 20 | # default report file name 21 | hibench.report.name hibench.report 22 | 23 | # input/output format settings. Available formats: Text, Sequence. 24 | sparkbench.inputformat Sequence 25 | sparkbench.outputformat Sequence 26 | 27 | # hibench config folder 28 | hibench.configure.dir ${hibench.home}/conf 29 | 30 | # default hibench HDFS root 31 | hibench.hdfs.data.dir ${hibench.hdfs.master}/HiBench 32 | 33 | # path of hibench jars 34 | hibench.hibench.datatool.dir ${hibench.home}/autogen/target/autogen-7.1-SNAPSHOT-jar-with-dependencies.jar 35 | hibench.common.jar ${hibench.home}/common/target/hibench-common-7.1-SNAPSHOT-jar-with-dependencies.jar 36 | hibench.sparkbench.jar ${hibench.home}/sparkbench/assembly/target/sparkbench-assembly-7.1-SNAPSHOT-dist.jar 37 | hibench.streambench.stormbench.jar ${hibench.home}/stormbench/streaming/target/stormbench-streaming-7.1-SNAPSHOT.jar 38 | hibench.streambench.gearpump.jar ${hibench.home}/gearpumpbench/streaming/target/gearpumpbench-streaming-7.1-SNAPSHOT-jar-with-dependencies.jar 39 | hibench.streambench.flinkbench.jar ${hibench.home}/flinkbench/streaming/target/flinkbench-streaming-7.1-SNAPSHOT-jar-with-dependencies.jar 40 | 41 | #====================================================== 42 | # workload home/input/ouput path 43 | #====================================================== 44 | hibench.hive.home ${hibench.home}/hadoopbench/sql/target/${hibench.hive.release} 45 | hibench.hive.release apache-hive-0.14.0-bin 46 | hibench.hivebench.template.dir ${hibench.home}/hadoopbench/sql/hive_template 47 | hibench.bayes.dir.name.input ${hibench.workload.dir.name.input} 48 | hibench.bayes.dir.name.output ${hibench.workload.dir.name.output} 49 | 50 | hibench.mahout.release.apache apache-mahout-distribution-0.11.0 51 | hibench.mahout.release.hdp apache-mahout-distribution-0.11.0 52 | hibench.mahout.release.cdh5 mahout-0.9-cdh5.1.0 53 | hibench.mahout.release ${hibench.mahout.release.${hibench.hadoop.release}} 54 | hibench.mahout.home ${hibench.home}/hadoopbench/mahout/target/${hibench.mahout.release} 55 | 56 | hibench.masters.hostnames 57 | hibench.slaves.hostnames 58 | 59 | hibench.workload.input 60 | hibench.workload.output 61 | hibench.workload.dir.name.input Input 62 | hibench.workload.dir.name.output Output 63 | 64 | hibench.nutch.dir.name.input ${hibench.workload.dir.name.input} 65 | hibench.nutch.dir.name.output ${hibench.workload.dir.name.output} 66 | hibench.nutch.nutchindexing.dir ${hibench.home}/hadoopbench/nutchindexing/ 67 | hibench.nutch.release nutch-1.2 68 | hibench.nutch.home ${hibench.home}/hadoopbench/nutchindexing/target/${hibench.nutch.release} 69 | 70 | hibench.dfsioe.dir.name.input ${hibench.workload.dir.name.input} 71 | hibench.dfsioe.dir.name.output ${hibench.workload.dir.name.output} 72 | 73 | 74 | #====================================================== 75 | # Streaming General 76 | #====================================================== 77 | # Indicate whether in debug mode for correctness verfication (default: false) 78 | hibench.streambench.debugMode false 79 | hibench.streambench.sampleProbability 0.1 80 | hibench.streambench.fixWindowDuration 10000 81 | hibench.streambench.fixWindowSlideStep 10000 82 | 83 | 84 | #====================================================== 85 | # Kafka for streaming benchmarks 86 | #====================================================== 87 | hibench.streambench.kafka.home /PATH/TO/YOUR/KAFKA/HOME 88 | # zookeeper host:port of kafka cluster, host1:port1,host2:port2... 89 | hibench.streambench.zkHost 90 | # Kafka broker lists, written in mode host:port,host:port,.. 91 | hibench.streambench.kafka.brokerList 92 | hibench.streambench.kafka.consumerGroup HiBench 93 | # number of partitions of generated topic (default 20) 94 | hibench.streambench.kafka.topicPartitions 20 95 | # consumer group of the consumer for kafka (default: HiBench) 96 | hibench.streambench.kafka.consumerGroup HiBench 97 | # Set the starting offset of kafkaConsumer (default: largest) 98 | hibench.streambench.kafka.offsetReset largest 99 | 100 | 101 | #====================================================== 102 | # Data generator for streaming benchmarks 103 | #====================================================== 104 | # Interval span in millisecond (default: 50) 105 | hibench.streambench.datagen.intervalSpan 50 106 | # Number of records to generate per interval span (default: 5) 107 | hibench.streambench.datagen.recordsPerInterval 5 108 | # fixed length of record (default: 200) 109 | hibench.streambench.datagen.recordLength 200 110 | # Number of KafkaProducer running on different thread (default: 1) 111 | hibench.streambench.datagen.producerNumber 1 112 | # Total round count of data send (default: -1 means infinity) 113 | hibench.streambench.datagen.totalRounds -1 114 | # Number of total records that will be generated (default: -1 means infinity) 115 | hibench.streambench.datagen.totalRecords -1 116 | # default path to store seed files (default: ${hibench.hdfs.data.dir}/Streaming) 117 | hibench.streambench.datagen.dir ${hibench.hdfs.data.dir}/Streaming 118 | # default path setting for genearate data1 & data2 119 | hibench.streambench.datagen.data1.name Seed 120 | hibench.streambench.datagen.data1.dir ${hibench.streambench.datagen.dir}/${hibench.streambench.datagen.data1.name} 121 | hibench.streambench.datagen.data2_cluster.dir ${hibench.streambench.datagen.dir}/Kmeans/Cluster 122 | hibench.streambench.datagen.data2_samples.dir ${hibench.streambench.datagen.dir}/Kmeans/Samples 123 | 124 | #====================================================== 125 | # MetricsReader for streaming benchmarks 126 | #====================================================== 127 | # Number of sample records for `MetricsReader` (default: 5000000) 128 | hibench.streambench.metricsReader.sampleNum 5000000 129 | # Number of thread for `MetricsReader` (default: 20) 130 | hibench.streambench.metricsReader.threadNum 20 131 | # The dir where stored the report of benchmarks (default: ${hibench.home}/report) 132 | hibench.streambench.metricsReader.outputDir ${hibench.home}/report 133 | -------------------------------------------------------------------------------- /vhost/config/ssh/README.md: -------------------------------------------------------------------------------- 1 | # vhost/config/ssh 2 | 3 | A throw-away pub/priv key that HDFS uses to start itself on other vhosts. 4 | -------------------------------------------------------------------------------- /vhost/config/ssh/authorized_keys: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDvq8/0HkyD2QeJ0+aSsPsxhJCt094ihNxJ0d+9OiYZN5P4vWBfsoxd1Sy3t4iL3iiVcZCsiQ9XfeRRyc1dtdJ+5aQmGQDkleAfiOcS5wGGuo8S8HkE2Y8718AtsQP7gcTLrPQq4clx5oyk93rOW7Q+XrSGiKdbYCZw1ZMlhG67G1jXvT7n1QQRLToh26z+OBtmySaa4ox50cJguJw9OSFKZIIRP0ee34ToVHgVbOuV3qi6VjVRvkNM5SkpvmlJn8KEYJe88fMfnVhFZ2LMv7QNxFz417hTxxncnqE/SckC9Hb4Ux6sVYPMrRTovSOlJSXNo/EDr3ng7LuLMoj/4MmWJ2FmZG2viFFF2Lk9++4zlaMwqCbsmCDlnHKFY7onAqa/rpbCUFT1oornfMrL5ZsKrN2Jq8EEQxCYLMcJzoOs/0tOaxeOR3UmF+Fl+o64g5J5xyMoDhQQ/RBpbSj++MRoNGJUfI0lX6p+aEJMqfpMaQa2uZ1L6No/o9g3czYU2Z3Un6t0/GLEFZO5j//7ckeRaoWDvoKwnADFSew1zAFqKd4ye5B7d7Zht6WiFAadL0c9TL8MVP/bKpiK9ceVlCDBzef+CNOfvTpIcxSgMUvWUFfqnCGG8C+J4CCq29sGShAyr+IR3suQe/bgug6uVrbasSR/t+f3hA2Yzbn3xqxkpQ== dkimcs@gmail.com 2 | -------------------------------------------------------------------------------- /vhost/config/ssh/config: -------------------------------------------------------------------------------- 1 | Host * 2 | StrictHostKeyChecking no 3 | -------------------------------------------------------------------------------- /vhost/config/ssh/id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKQIBAAKCAgEA76vP9B5Mg9kHidPmkrD7MYSQrdPeIoTcSdHfvTomGTeT+L1g 3 | X7KMXdUst7eIi94olXGQrIkPV33kUcnNXbXSfuWkJhkA5JXgH4jnEucBhrqPEvB5 4 | BNmPO9fALbED+4HEy6z0KuHJceaMpPd6zlu0Pl60hoinW2AmcNWTJYRuuxtY170+ 5 | 59UEES06Idus/jgbZskmmuKMedHCYLicPTkhSmSCET9Hnt+E6FR4FWzrld6oulY1 6 | Ub5DTOUpKb5pSZ/ChGCXvPHzH51YRWdizL+0DcRc+Ne4U8cZ3J6hP0nJAvR2+FMe 7 | rFWDzK0U6L0jpSUlzaPxA6954Oy7izKI/+DJlidhZmRtr4hRRdi5PfvuM5WjMKgm 8 | 7Jgg5ZxyhWO6JwKmv66WwlBU9aKK53zKy+WbCqzdiavBBEMQmCzHCc6DrP9LTmsX 9 | jkd1JhfhZfqOuIOSeccjKA4UEP0QaW0o/vjEaDRiVHyNJV+qfmhCTKn6TGkGtrmd 10 | S+jaP6PYN3M2FNmd1J+rdPxixBWTuY//+3JHkWqFg76CsJwAxUnsNcwBaineMnuQ 11 | e3e2YbelohQGnS9HPUy/DFT/2yqYivXHlZQgwc3n/gjTn706SHMUoDFL1lBX6pwh 12 | hvAvieAgqtvbBkoQMq/iEd7LkHv24LoOrla22rEkf7fn94QNmM2598asZKUCAwEA 13 | AQKCAgAAwcsrWzkkpXfXoJq21eZFNsT6N5QZCdBkT0xknZ1nMPy/qWF3i+nTlgvJ 14 | HMRKpM3+AtjRIrGwtg4VkAydAikDC5ikIioVjr+hed44+g/QLCCtj6LiOAmyx7Cs 15 | tEVt8u/7MLWouQPY30CMeNejdTixhb0OO7pDmJ8mdhY75Rowbxqtu8RKGSslHr5z 16 | Qu1d9DFp9rKvhI2rCNWTVqHvs5F+Wajrgwan4s7ZqQKkFq77zzM7O3B5rJ8rzAGA 17 | R0Cv4dhMcL2p9B2veODBf10ZhnV8IaWlh5DmeteDEeEygPoojzsc964+HxsGd3l8 18 | brSBHgfriVwGhfRdmIHTI7q5DC220SJG4nbqvaDmYPB5Lcenbpk+ywr/l4haA3pQ 19 | YJIUWB5Fpp2cNEGo40vHrvvPdKiQ8VcOTAIjpYNypVtMtDPrgujV7wod1hPtB4RK 20 | ZqXTqwYktTz7Pr0dLIzH901eA89/nJEZC8oxhyei5ETeGsMkoJUVYneCS4KMIOnS 21 | q3iRethJlPnid9jXjPP60IQt9O/EXOBfF/GLnjWuRRD4rKJDblV5JHgfiJq1+15U 22 | kI0B4y5z9jETwXoD1B8qkmCvTltW7oU0MAkUg2hqPVtpnJ3gE5dBpISyWy4JT3k8 23 | ZYGgFinRRnLbFZgrwF2eMG9Zvv2pY94NCZ1smf0pQcxomTwEoQKCAQEA/wWXvlyY 24 | g6gnuqINEpSiY+IowFjocr+/jCnVnNGBdrvMz3A3Ib1t2X3uB2VBkHBKNG5BdIb0 25 | DSUFcj5gccd+uMl1QjRy6bNdmRHshaNVWM5oOXi96gorj99VZQmBDOmP0xDc1oIH 26 | RCMItyI03xtJDRyFSmLmHvHy6LC9yAAzHaNrwuVmiHYFKgnkkPM+EttJ41wodfuJ 27 | 8RBLgI3OWgju3frQQMxvgrKxipukSo2mCZNouXHI3QyhWw5l1xnXn2pIDi9VHBlS 28 | fD2+ETSj837AONNHl8fzRsTlNjVDNbZriAAuz90bLCJ9rLMtTj4uH/w6yuvPSFxz 29 | GSh8pVBvr7wO3QKCAQEA8Jclid4heVsyGpA+M8JtBGUHjIuvZIWTGwJfiIIayjpi 30 | UJsEi4OQf0ItsWKGCu/h+kqhLgfI6SCOBgpxFrUdLBtlEBcz3DIxEZVzWW4WrNqp 31 | Mohs9dYP4/jJucw9ZGsE0jf5pIKvm/HCfD6MgFytIOj/SQWDckuvGTRbn8kcle2D 32 | voP2DOTVbzYF2fSoNHq/7iM1ytRen3CASHO/vXy63fBmAsj9dBf8wQ8FWrKMjmb6 33 | HcQNmtyWZTA+awmmaCpetuWK8pSFigqUjtOoG86z0J0VEsVTgVsyQVwbSplb8mnA 34 | w2m3EZJiiFCeVOLT411mWyn3V3nvgDZt1Lqp7CC8aQKCAQEA6qasvz3YH5xEIhgl 35 | yyj0Ws+oC4RDLgJ9L2z23npvk16tz1lDgUoZn3ud3zQuJccMoD2rAJuER+v+nOYE 36 | O+0CDulfeXFlIuAliUjSxwxI5ZPWlrsglA6Tkqg+nN8n3UezTNRBPycZpHpP9KIG 37 | 4BrtFqFdi9qAP40Eo1Ks7TAx2F2cNBJ8S6e8jLfZlf4a6aLY09jiadzvSd5ieoEA 38 | lBuQHrQ/lsAFAs+FSTJ48xhu5HS8cZXe6nZKqcwPrOJcYpSahGLM/zPVj/gHwTEc 39 | glnoaOEPCPu0PzPAbCaKFwwo2VTBvgWGkQ+Tb6rWTx4rtsaRGlHrwns+dLGqLzpZ 40 | 6esyCQKCAQEA7N4ylCEBXeSeWK7Ivtbd1CTPoNzpypMD+OSGP/SwAYgnwWpNuDnI 41 | qrHyIKF6jQwNtQmC1LnPYU6u5V2kZ5Ha7dDu2D+vzEwSKVi1n0eWxYnmaR0OAZ9G 42 | N9F6chWWH+aJcZqwivorgOIt83r1g286+JrMtAzclEvVUYBN8Ovk2+ti+NAdECa/ 43 | x+UtL6ADdt+fVQqPbGlX26u8cRicnydNJzMGjBlsB7+SFlT+lYQShCT4fRPHvT8O 44 | RwOPgHyYvBdy9qjZ+uik7Pprto7GBNKmEG6HD9r80u+Bc/4F1JVWhD0ADqRnG8K+ 45 | hayxpatIp6IOIxUXMqWXWei8i2NX5mV4eQKCAQAEwtEFP9IEZ1kFzRLgoreOVHq1 46 | ZGWNLT01CODVBtb29riu+CTTN31FJ+3XunUz3pHNhCauXUO3WAHrBxp+nWfz5Knj 47 | WL4Ly+li98VTrnTZXZh0xWlBkJRXd3UdwxcCQRkwuv9VVLJVYh9Mz73xg4SiZhaH 48 | hpi78bCpbZUiXwMxdTEX4r3/YF1HoslE8GWv1nAhxO76LvMz1GCTo1flbllH6Fk6 49 | H/RthhmfcyN9RzOxiqt/Bz5Ne9OJkGNzvBkOVfa6sVkgsmnAds9RGKzdgZ052220 50 | rt5wg3BUaPR96dmfzPMZv8QHqP/2c5xIkOO2ESVc1GsqRLNVkIbASce88O1D 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /vhost/config/ssh/id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDvq8/0HkyD2QeJ0+aSsPsxhJCt094ihNxJ0d+9OiYZN5P4vWBfsoxd1Sy3t4iL3iiVcZCsiQ9XfeRRyc1dtdJ+5aQmGQDkleAfiOcS5wGGuo8S8HkE2Y8718AtsQP7gcTLrPQq4clx5oyk93rOW7Q+XrSGiKdbYCZw1ZMlhG67G1jXvT7n1QQRLToh26z+OBtmySaa4ox50cJguJw9OSFKZIIRP0ee34ToVHgVbOuV3qi6VjVRvkNM5SkpvmlJn8KEYJe88fMfnVhFZ2LMv7QNxFz417hTxxncnqE/SckC9Hb4Ux6sVYPMrRTovSOlJSXNo/EDr3ng7LuLMoj/4MmWJ2FmZG2viFFF2Lk9++4zlaMwqCbsmCDlnHKFY7onAqa/rpbCUFT1oornfMrL5ZsKrN2Jq8EEQxCYLMcJzoOs/0tOaxeOR3UmF+Fl+o64g5J5xyMoDhQQ/RBpbSj++MRoNGJUfI0lX6p+aEJMqfpMaQa2uZ1L6No/o9g3czYU2Z3Un6t0/GLEFZO5j//7ckeRaoWDvoKwnADFSew1zAFqKd4ye5B7d7Zht6WiFAadL0c9TL8MVP/bKpiK9ceVlCDBzef+CNOfvTpIcxSgMUvWUFfqnCGG8C+J4CCq29sGShAyr+IR3suQe/bgug6uVrbasSR/t+f3hA2Yzbn3xqxkpQ== dkimcs@gmail.com 2 | -------------------------------------------------------------------------------- /vhost/etalon.dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | MAINTAINER Matt Mukerjee "mukerjee@cs.cmu.edu" 4 | 5 | # Install dependencies. 6 | RUN apt-get update && apt-get install -y \ 7 | bc inetutils-ping libcurl4-gnutls-dev libgsl-dev libpcap-dev \ 8 | libxmlrpc-core-c3-dev net-tools openssh-server openjdk-8-jdk python \ 9 | tcpdump uuid-dev && \ 10 | rm -rf /var/lib/apt/lists/* 11 | 12 | # Download pipework. 13 | WORKDIR /usr/local/bin 14 | ADD https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework /usr/local/bin/ 15 | RUN chmod +x pipework 16 | 17 | # Copy libVT. 18 | COPY libVT.so /usr/lib/libVT.so 19 | 20 | # Copy custom flowgrind. 21 | COPY flowgrindd /usr/local/sbin/flowgrindd 22 | 23 | CMD pipework --wait -i eth1 && \ 24 | pipework --wait -i eth2 && \ 25 | sleep infinity 26 | --------------------------------------------------------------------------------