├── .gitignore ├── LICENSE ├── Readme.md ├── conf.go ├── flow.go ├── flowgen.go ├── ideal.go ├── logging.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | ideal 2 | *.txt 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ideal-simulator is distributed under the following BSD3 license: 2 | 3 | Copyright (c) 2016, Akshay Narayan. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 15 | * The names of authors or their organizations may not be used to endorse or 16 | promote products derived from this software without specific prior written 17 | permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Flow-level simulator for the "ideal" algorithm outlined by [pfabric](http://conferences.sigcomm.org/sigcomm/2013/papers/sigcomm/p435.pdf). 2 | 3 | Algorithm 4 | ========= 5 | 6 | This is an algorithm that: 7 | 1. simulates the network topology as a "big switch". 8 | 2. greedily schedules flows in SRPT (shortest remaining processing time) order 9 | 10 | This gives at least a 2-approximation of the optimal average FCT, which is [NP-hard to compute](http://dl.acm.org/citation.cfm?doid=378420.378792). 11 | 12 | The following, the pFabric ideal algorithm, is run whenever either (a) a new flow enters the network or (b) a flow finishes. 13 | 14 | Input: List of active flows, F: (source, destination, size). 15 | Output: Set of flows, S, that are scheduled in this iteration. 16 | 17 | S = {} 18 | all sources are not busy 19 | all destinations are not busy 20 | for each flow f in F in order of increasing remaining size: 21 | if f.source and f.destination are not busy: 22 | add f to S 23 | mark f.source and f.destination as busy 24 | 25 | We make an improvement in our implementation. When a flow arrives in the network, only its source is marked as busy. After one propagation delay, the destination is also marked as busy. Similarly, once a flow transmits its last byte, its source is immediately made available to schedule another flow, while its destination remains marked busy until the last byte arrives. 26 | 27 | This improvement prevents the wastage of a propagation delay amount of time at the source and destination at the end and beginning of a flow, respectively. 28 | 29 | Running 30 | ======= 31 | 32 | The simulator takes one argument, the path to a configuration file. The configuration file consists of lines, each of which must be either a parameter definition or a switch. The format of the two types of lines follows. 33 | 34 | Switch: ```[Switch Name]``` 35 | 36 | Parameter definition: ```[Field Name] [Value]``` 37 | 38 | A list of valid parameters to define and switches to use follows. 39 | 40 | Switches 41 | -------- 42 | 43 | Note that for each switch, no parameter other than the required parameter for that switch should be defined. 44 | 45 | - "Read": Read flows from a trace file and run the ideal algorithm. The parameters "Bandwidth" and "TraceFile" must be defined. 46 | - "Generate": Generate flows using a Poisson arrival process and run the ideal algorithm. The parameters "Bandwidth", "Load", "NumFlows", and "CDF" must be defined. 47 | - "GenerateOnly": Generate flows using a Poisson arrival process and exit. The parameters are the same as in "Generate". 48 | 49 | Parameters 50 | ---------- 51 | 52 | - "TraceFile": The path to a file containing the list of flows to simulate. This file should have the following format: 53 | 54 | ```[id (ignored)] [size (bytes)] [source] [destination] [start time (microseconds)]``` 55 | 56 | The source and destination fields are currently hardcoded to correspond to a 144-host topology with 9 racks of 16 nodes each. In-rack propagation delay is set to be 440 ns, and inter-rack propagation delay is set to be 2040 ns. 57 | 58 | - "Bandwidth": Topology bandwidth in gigabits. Note that this parameter is required for all experiments. 59 | - "NumFlows": The number of flows to generate for simulation. 60 | - "Load": The target load at which flows should be generated. 61 | - "CDF": The path to a CDF file defining the flow size distribution with which to generate flows. This file should have the following format: 62 | 63 | ```[Flow Size (packets)] [CDF Value]``` 64 | 65 | Example Configuration 66 | --------------------- 67 | 68 | Configuration file: 69 | ``` 70 | Bandwidth 40 71 | GenerateOnly 72 | Load 0.9 73 | NumFlows 1000000 74 | CDF imc10.cdf 75 | ``` 76 | 77 | CDF File (imc10.cdf above): 78 | ``` 79 | 1 1 0 80 | 1 1 0.500000 81 | 2 1 0.600000 82 | 3 1 0.700000 83 | 5 1 0.750000 84 | 7 1 0.800000 85 | 40 1 0.812500 86 | 72 1 0.825000 87 | 137 1 0.850000 88 | 267 1 0.900000 89 | 1187 1 0.95000 90 | 2107 1 1.0 91 | ``` 92 | 93 | -------------------------------------------------------------------------------- /conf.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // read in conf file 11 | 12 | type Conf struct { 13 | Bandwidth float64 14 | 15 | Read bool 16 | TraceFileName string 17 | 18 | GenerateOnly bool 19 | 20 | Generate bool 21 | Load float64 22 | NumFlows uint 23 | CDFFileName string 24 | } 25 | 26 | func (c Conf) assert_generation() { 27 | if !c.Generate { 28 | panic("Conflicting options") 29 | } 30 | } 31 | 32 | func (c Conf) assert_readtrace() { 33 | if !c.Read { 34 | panic("Conflicting options") 35 | } 36 | } 37 | 38 | func readConf(fn string) Conf { 39 | file, ok := os.Open(fn) 40 | check(ok) 41 | defer file.Close() 42 | 43 | c := Conf{} 44 | defer func() { 45 | if c.Bandwidth == 0 { 46 | panic("Invalid configuration") 47 | } else if c.Read == c.Generate { 48 | panic("Conflicting options") 49 | } 50 | }() 51 | 52 | scanner := bufio.NewScanner(file) 53 | for scanner.Scan() { 54 | l := strings.Split(scanner.Text(), " ") 55 | switch { 56 | case l[0][0] == '#': 57 | continue 58 | case l[0] == "Bandwidth": 59 | b, ok := strconv.ParseFloat(l[1], 64) 60 | check(ok) 61 | c.Bandwidth = b 62 | 63 | case l[0] == "Read": 64 | c.Read = true 65 | defer func() { 66 | if c.TraceFileName == "" { 67 | panic("Invalid configuration") 68 | } 69 | }() 70 | case l[0] == "TraceFile": 71 | c.TraceFileName = l[1] 72 | defer c.assert_readtrace() 73 | 74 | case l[0] == "GenerateOnly": 75 | c.GenerateOnly = true 76 | fallthrough 77 | case l[0] == "Generate": 78 | c.Generate = true 79 | defer func() { 80 | if c.CDFFileName == "" || c.Load == 0 || c.NumFlows == 0 { 81 | panic("Invalid configuration") 82 | } 83 | }() 84 | case l[0] == "Load": 85 | l, ok := strconv.ParseFloat(l[1], 64) 86 | check(ok) 87 | c.Load = l 88 | defer c.assert_generation() 89 | case l[0] == "NumFlows": 90 | n, ok := strconv.ParseUint(l[1], 10, 32) 91 | check(ok) 92 | c.NumFlows = uint(n) 93 | defer c.assert_generation() 94 | case l[0] == "CDF": 95 | c.CDFFileName = l[1] 96 | defer c.assert_generation() 97 | } 98 | } 99 | 100 | return c 101 | } 102 | -------------------------------------------------------------------------------- /flow.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // define data types 4 | 5 | import ( 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type SortedFlows []*Flow 12 | 13 | func (sf SortedFlows) Len() int { 14 | return len(sf) 15 | } 16 | 17 | func (sf SortedFlows) Less(i, j int) bool { 18 | return (sf[i].TimeRemaining < sf[j].TimeRemaining) 19 | } 20 | 21 | func (sf SortedFlows) Swap(i, j int) { 22 | tmp := sf[i] 23 | sf[i] = sf[j] 24 | sf[j] = tmp 25 | } 26 | 27 | type Flow struct { 28 | Start float64 29 | Size uint 30 | Source uint8 31 | Dest uint8 32 | End float64 33 | TimeRemaining float64 34 | OracleFct float64 35 | LastTime float64 36 | FinishEvent *Event 37 | FinishSending bool 38 | Finish bool 39 | } 40 | 41 | func flowToString(f *Flow) string { 42 | return fmt.Sprintf("%d %d %d %f %f %f %f %f\n", f.Source, f.Dest, f.Size, f.Start, f.OracleFct, f.End, f.End-f.Start, calculateFlowSlowdown(f)) 43 | } 44 | 45 | func calculateFlowSlowdown(f *Flow) float64 { 46 | if f.End < f.Start { 47 | panic("flow has negative fct") 48 | } 49 | 50 | fct := (f.End - f.Start) 51 | slowdown := fct / f.OracleFct 52 | switch { 53 | case slowdown >= 1: 54 | return slowdown 55 | case slowdown < 0.999: 56 | panic("flow has fct better than oracle") 57 | default: 58 | return 1.000000 59 | } 60 | } 61 | 62 | func makeFlow(l string) *Flow { 63 | sp := strings.Split(l, " ") 64 | 65 | size, err := strconv.ParseUint(sp[1], 10, 32) 66 | check(err) 67 | src, err := strconv.ParseUint(sp[2], 10, 8) 68 | check(err) 69 | dst, err := strconv.ParseUint(sp[3], 10, 8) 70 | check(err) 71 | time, err := strconv.ParseFloat(sp[4], 64) 72 | check(err) 73 | 74 | return &Flow{Start: time, Size: uint(size), Source: uint8(src), Dest: uint8(dst), LastTime: 0, FinishEvent: nil} 75 | } 76 | 77 | type EventType int 78 | 79 | const ( 80 | FlowArrival EventType = iota 81 | FlowSourceFree 82 | FlowDestFree 83 | ) 84 | 85 | type Event struct { 86 | Time float64 87 | Flow *Flow 88 | Type EventType 89 | Cancelled bool 90 | } 91 | 92 | func makeArrivalEvent(f *Flow) *Event { 93 | return &Event{Time: f.Start, Flow: f, Type: FlowArrival, Cancelled: false} 94 | } 95 | 96 | func makeCompletionEvent(t float64, f *Flow, ty EventType) *Event { 97 | return &Event{Time: t, Flow: f, Type: ty, Cancelled: false} 98 | } 99 | 100 | type EventQueue []*Event 101 | 102 | func (e EventQueue) Len() int { 103 | return len(e) 104 | } 105 | 106 | func (e EventQueue) Less(i, j int) bool { 107 | return (e[i].Time < e[j].Time) 108 | } 109 | 110 | func (e EventQueue) Swap(i, j int) { 111 | tmp := e[i] 112 | e[i] = e[j] 113 | e[j] = tmp 114 | } 115 | 116 | func (e *EventQueue) Push(x interface{}) { 117 | ev := x.(*Event) 118 | *e = append(*e, ev) 119 | } 120 | 121 | func (e *EventQueue) Pop() interface{} { 122 | old := *e 123 | n := len(old) 124 | ev := old[n-1] 125 | *e = old[0 : n-1] 126 | return ev 127 | } 128 | -------------------------------------------------------------------------------- /flowgen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "container/heap" 6 | "math/rand" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // generate flows 13 | 14 | // read input flow trace 15 | 16 | type FlowReader struct { 17 | file string 18 | } 19 | 20 | func makeFlowReader(fn string) FlowReader { 21 | return FlowReader{file: fn} 22 | } 23 | 24 | func (fr FlowReader) makeFlows(logger chan LogEvent) EventQueue { 25 | eventQueue := make(EventQueue, 0) 26 | file, err := os.Open(fr.file) 27 | check(err) 28 | defer file.Close() 29 | 30 | scanner := bufio.NewScanner(file) 31 | for scanner.Scan() { 32 | flow := makeFlow(scanner.Text()) 33 | eventQueue = append(eventQueue, makeArrivalEvent(flow)) 34 | } 35 | 36 | return eventQueue 37 | } 38 | 39 | // read flow size CDF file and load for Poisson arrival 40 | 41 | type CDF struct { 42 | values []uint 43 | distrs []float64 44 | } 45 | 46 | func readCDF(fn string) CDF { 47 | file, err := os.Open(fn) 48 | check(err) 49 | defer file.Close() 50 | 51 | vals := make([]uint, 16) 52 | cdfs := make([]float64, 16) 53 | scanner := bufio.NewScanner(file) 54 | for scanner.Scan() { 55 | sp := strings.Split(scanner.Text(), " ") 56 | // structure of line: 57 | v, ok := strconv.ParseUint(sp[0], 10, 32) 58 | check(ok) 59 | c, ok := strconv.ParseFloat(sp[2], 64) 60 | check(ok) 61 | 62 | if v == 0 { 63 | panic("invalid cdf flow size") 64 | } 65 | 66 | vals = append(vals, uint(v)*1460) 67 | cdfs = append(cdfs, c) 68 | } 69 | 70 | return CDF{values: vals, distrs: cdfs} 71 | } 72 | 73 | func (cdf CDF) meanFlowSize() float64 { 74 | avg := 0.0 75 | lastCdf := 0.0 76 | for i := 0; i < len(cdf.values); i++ { 77 | avg += float64(cdf.values[i]) * (cdf.distrs[i] - lastCdf) 78 | lastCdf = cdf.distrs[i] 79 | } 80 | return avg 81 | } 82 | 83 | func (cdf CDF) value() uint { 84 | rand := rand.Float64() 85 | for i := 0; i < len(cdf.values); i++ { 86 | if cdf.distrs[i] >= rand { 87 | if cdf.values[i] == 0 { 88 | panic("invalid flow size") 89 | } 90 | return cdf.values[i] 91 | } 92 | } 93 | 94 | panic("reached end of cdf function without value") 95 | } 96 | 97 | type FlowGenerator struct { 98 | load float64 99 | bandwidth float64 100 | cdf CDF 101 | numFlows uint 102 | } 103 | 104 | func makeFlowGenerator(load float64, bw float64, cdfFile string, nf uint) FlowGenerator { 105 | return FlowGenerator{load: load, bandwidth: bw, cdf: readCDF(cdfFile), numFlows: nf} 106 | } 107 | 108 | func makeCreationEvent(f *Flow) *Event { 109 | return &Event{Time: f.Start, Flow: f, Type: FlowArrival, Cancelled: false} 110 | } 111 | 112 | func (fg FlowGenerator) makeFlows(logger chan LogEvent) EventQueue { 113 | lambda := (fg.bandwidth * 1e9 * fg.load) / (fg.cdf.meanFlowSize() * 1500 * 8) 114 | lambda /= 143 115 | 116 | creationQueue := make(EventQueue, 0) 117 | defer func() { 118 | creationQueue = nil 119 | }() 120 | 121 | heap.Init(&creationQueue) 122 | for i := 0; i < NUM_HOSTS; i++ { 123 | for j := 0; j < NUM_HOSTS; j++ { 124 | if i == j { 125 | continue 126 | } 127 | f := &Flow{Start: 1e6 + (rand.ExpFloat64()/lambda)*1e6, Size: fg.cdf.value(), Source: uint8(i), Dest: uint8(j), LastTime: 0, FinishEvent: nil} 128 | heap.Push(&creationQueue, makeCreationEvent(f)) 129 | } 130 | } 131 | 132 | eventQueue := make(EventQueue, 0) 133 | for uint(len(eventQueue)) < fg.numFlows { 134 | ev := heap.Pop(&creationQueue).(*Event) 135 | logger <- LogEvent{Time: 0, Type: LOG_FLOW_GEN, Flow: ev.Flow} 136 | eventQueue = append(eventQueue, makeArrivalEvent(ev.Flow)) 137 | 138 | nextTime := ev.Time + (rand.ExpFloat64()/lambda)*1e6 139 | f := &Flow{Start: nextTime, Size: fg.cdf.value(), Source: ev.Flow.Source, Dest: ev.Flow.Dest, LastTime: 0, FinishEvent: nil} 140 | heap.Push(&creationQueue, makeCreationEvent(f)) 141 | } 142 | 143 | return eventQueue 144 | } 145 | -------------------------------------------------------------------------------- /ideal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // run ideal event loop until completion. 4 | 5 | import ( 6 | "container/heap" 7 | "container/list" 8 | "sort" 9 | ) 10 | 11 | const ( 12 | NUM_HOSTS = 144 13 | PROPAGATION_DELAY = 0.4 // microseconds 14 | HOSTS_IN_RACK = 16 15 | ) 16 | 17 | func removeFromActiveFlows(ls *list.List, f *Flow) { 18 | for e := ls.Front(); e != nil; e = e.Next() { 19 | if e.Value.(*Flow) == f { 20 | ls.Remove(e) 21 | break 22 | } 23 | } 24 | } 25 | 26 | func getOracleFCT(flow *Flow, bw float64) (float64, float64) { 27 | pd := PROPAGATION_DELAY * 4 28 | td := (float64(flow.Size) / (bw * 1e9 / 8)) * 1e6 // microseconds 29 | return pd, td 30 | } 31 | 32 | // input: linked list of flows 33 | // output: sorted slice of flows, number of flows 34 | func getSortedFlows(actives *list.List) SortedFlows { 35 | sortedFlows := make(SortedFlows, actives.Len()) 36 | 37 | i := 0 38 | for e := actives.Front(); e != nil; e = e.Next() { 39 | sortedFlows[i] = e.Value.(*Flow) 40 | i++ 41 | } 42 | 43 | sort.Sort(sortedFlows) 44 | return sortedFlows 45 | } 46 | 47 | // input: eventQueue of FlowArrival events, topology bandwidth (to determine oracle FCT) 48 | // output: slice of pointers to completed Flows 49 | func ideal(bandwidth float64, logger chan LogEvent, eventQueue EventQueue) []*Flow { 50 | heap.Init(&eventQueue) 51 | 52 | activeFlows := list.New() 53 | completedFlows := make([]*Flow, 0) 54 | var srcPorts [NUM_HOSTS]*Flow 55 | var dstPorts [NUM_HOSTS]*Flow 56 | var currentTime float64 57 | 58 | for len(eventQueue) > 0 { 59 | ev := heap.Pop(&eventQueue).(*Event) 60 | if ev.Cancelled { 61 | continue 62 | } 63 | 64 | if ev.Time < currentTime { 65 | panic("going backwards!") 66 | } 67 | 68 | currentTime = ev.Time 69 | flow := ev.Flow 70 | 71 | switch ev.Type { 72 | case FlowArrival: 73 | logger <- LogEvent{Time: currentTime, Type: LOG_FLOW_ARRIVAL, Flow: flow} 74 | prop_delay, trans_delay := getOracleFCT(flow, bandwidth) 75 | flow.TimeRemaining = trans_delay 76 | flow.OracleFct = prop_delay + trans_delay 77 | activeFlows.PushBack(flow) 78 | case FlowSourceFree: 79 | flow.FinishSending = true 80 | flow.FinishEvent = makeCompletionEvent(currentTime+2*PROPAGATION_DELAY, flow, FlowDestFree) 81 | heap.Push(&eventQueue, flow.FinishEvent) 82 | case FlowDestFree: 83 | if !flow.FinishSending { 84 | panic("finish without finishSending") 85 | } 86 | removeFromActiveFlows(activeFlows, flow) 87 | flow.End = currentTime + 2*PROPAGATION_DELAY // send an ACK 88 | flow.Finish = true 89 | completedFlows = append(completedFlows, flow) 90 | logger <- LogEvent{Time: currentTime, Type: LOG_FLOW_FINISHED, Flow: flow} 91 | } 92 | 93 | for i := 0; i < len(srcPorts); i++ { 94 | if srcPorts[i] != nil { 95 | inProgressFlow := srcPorts[i] 96 | 97 | if inProgressFlow.LastTime == 0 { 98 | panic("flow in progress without LastTime set") 99 | } 100 | 101 | if inProgressFlow.FinishEvent == nil { 102 | panic("flow in progress without FinishEvent set") 103 | } 104 | 105 | inProgressFlow.TimeRemaining -= (currentTime - inProgressFlow.LastTime) 106 | inProgressFlow.LastTime = 0 107 | 108 | if !inProgressFlow.FinishSending { 109 | inProgressFlow.FinishEvent.Cancelled = true 110 | inProgressFlow.FinishEvent = nil 111 | } 112 | } 113 | srcPorts[i] = nil 114 | dstPorts[i] = nil 115 | } 116 | 117 | sortedActiveFlows := getSortedFlows(activeFlows) 118 | for _, f := range sortedActiveFlows { 119 | src := f.Source 120 | dst := f.Dest 121 | 122 | if f.FinishSending { 123 | if f.Finish { 124 | panic("finished flow in actives") 125 | } 126 | 127 | if srcPorts[src] != nil || dstPorts[dst] != nil { 128 | panic("ports taken on still sending flow") 129 | } 130 | 131 | dstPorts[dst] = f 132 | continue 133 | } 134 | 135 | if srcPorts[src] == nil && dstPorts[dst] == nil { 136 | //this flow gets scheduled. 137 | f.LastTime = currentTime 138 | srcPorts[src] = f 139 | dstPorts[dst] = f 140 | 141 | if f.FinishEvent != nil { 142 | panic("flow being scheduled, finish event non-nil") 143 | } 144 | 145 | f.FinishEvent = makeCompletionEvent(currentTime+f.TimeRemaining, f, FlowSourceFree) 146 | heap.Push(&eventQueue, f.FinishEvent) 147 | } 148 | } 149 | } 150 | 151 | return completedFlows 152 | } 153 | -------------------------------------------------------------------------------- /logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type LogEventType int 8 | 9 | const ( 10 | LOG_FLOW_ARRIVAL LogEventType = iota 11 | LOG_FLOW_GEN 12 | LOG_FLOW_FINISHED 13 | OTHER 14 | ) 15 | 16 | type LogEvent struct { 17 | Time float64 18 | Type LogEventType 19 | Flow *Flow 20 | msg string 21 | } 22 | 23 | func log(lgs chan LogEvent, done chan bool) { 24 | backlog := uint(0) 25 | for l := range lgs { 26 | f := l.Flow 27 | t := l.Time 28 | switch l.Type { 29 | case LOG_FLOW_GEN: 30 | fmt.Printf("generated: %d %d %d %f\n", f.Size, f.Source, f.Dest, f.Start) 31 | case LOG_FLOW_ARRIVAL: 32 | backlog += f.Size 33 | fmt.Printf("backlog %.6f %d : starting %d %d %d\n", t, backlog, f.Source, f.Dest, f.Size) 34 | case LOG_FLOW_FINISHED: 35 | backlog -= f.Size 36 | fmt.Print(flowToString(f)) 37 | } 38 | } 39 | done <- false 40 | } 41 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // read in input 4 | // initialize objects 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | ) 10 | 11 | func check(e error) { 12 | if e != nil { 13 | panic(e) 14 | } 15 | } 16 | 17 | func main() { 18 | args := os.Args[1:] 19 | conf := readConf(args[0]) 20 | 21 | var logger = make(chan LogEvent, 1000) 22 | loggingDone := make(chan bool) 23 | go log(logger, loggingDone) 24 | 25 | var eventQueue EventQueue 26 | switch { 27 | case conf.Read: 28 | fr := makeFlowReader(conf.TraceFileName) 29 | eventQueue = fr.makeFlows(logger) 30 | case conf.GenerateOnly: 31 | fg := makeFlowGenerator(conf.Load, conf.Bandwidth, conf.CDFFileName, conf.NumFlows) 32 | eventQueue = fg.makeFlows(logger) 33 | close(logger) 34 | <-loggingDone 35 | return 36 | case conf.Generate: 37 | fg := makeFlowGenerator(conf.Load, conf.Bandwidth, conf.CDFFileName, conf.NumFlows) 38 | eventQueue = fg.makeFlows(logger) 39 | default: 40 | panic("Invalid configuration") 41 | } 42 | 43 | flows := ideal(conf.Bandwidth, logger, eventQueue) 44 | 45 | close(logger) 46 | <-loggingDone 47 | 48 | numFlows := len(flows) 49 | 50 | slowdown := 0.0 51 | for i := 0; i < numFlows; i++ { 52 | slowdown += calculateFlowSlowdown(flows[i]) 53 | } 54 | fmt.Println(slowdown / float64(numFlows)) 55 | } 56 | --------------------------------------------------------------------------------