source: benchmark/convoy/genConvoyStats.py@ 1afda5a2

ADT ast-experimental stuck-waitfor-destruct
Last change on this file since 1afda5a2 was 8e64cb4, checked in by caparsons <caparson@…>, 4 years ago

added single run mode and added sample for single run to data

  • Property mode set to 100644
File size: 7.3 KB
Line 
1#!/usr/bin/env python3
2
3import os
4import sys
5import math
6import argparse
7import statistics as st
8
9# Parsing Logic
10parser = argparse.ArgumentParser(prog = 'GenConvoyStats',
11 description = 'Analyzes handoff matrix output and uses Markov chain modelling to provide upper and lower bounds on the largest long term convoy',
12 epilog = '')
13
14parser.add_argument("Filename")
15parser.add_argument("-r", "--RunsPerNumThds", default=5, help="Number of trials per # of threads. Default is 5")
16parser.add_argument("-m", "--MaxThreads", default=32, help="Maximum number of threads. Default is 32")
17parser.add_argument("-o", "--OutputFile", default='', help="File to write output to")
18parser.add_argument("-v", "--Verbose", help="Verbose output. Will print per run stats alongside aggregates", action='count', default=0)
19parser.add_argument("-s", "--Single", help="Indicates that input only contains a single run. Assumes the number of threads is the value passed to -m", action='count', default=0)
20
21args = parser.parse_args()
22
23# handoff data
24handoff = []
25sumPerRow = []
26handoffTotal = 0
27maxCycleSum = 0
28maxCycle = []
29
30# per thread data (across runs)
31minBound = []
32maxBound = []
33expected = []
34
35# input file descriptor
36readFile = open(args.Filename, "r")
37
38writeFile = False
39if args.OutputFile != '':
40 writeFile = open(args.Filename, "w")
41
42def reset():
43 global handoff, sumPerRow, handoffTotal, readFile, maxCycleSum, maxCycle
44 handoff.clear()
45 sumPerRow.clear()
46 handoffTotal = 0
47 maxCycleSum = 0
48 maxCycle.clear()
49
50def thdReset():
51 global minBound, maxBound, expected
52 minBound.clear()
53 maxBound.clear()
54 expected.clear()
55
56def output(string):
57 global writeFile
58 if writeFile:
59 writeFile.write(string + '\n')
60
61 print(string)
62
63# reads in handoff matrix for a single run and accumulates row/matrix total at the same time
64def readInMatrix(currThds):
65 global handoff, sumPerRow, handoffTotal, readFile
66 for i in range(currThds):
67 line = readFile.readline()
68
69 # Deal with EOF
70 if not line:
71 print("Incorrect arguments or file format: error in readInMatrix")
72 sys.exit(1)
73
74 # deal with any empty lines
75 while line == '\n':
76 line = readFile.readline()
77
78 row = []
79 rowSum = 0
80
81 # convert row into list of ints and accumulate
82 for val in line.replace(',','').split():
83 row.append(int(val))
84 rowSum += int(val)
85
86 #store row in global state
87 handoff.append(row)
88 sumPerRow.append(rowSum)
89 handoffTotal += rowSum
90
91# moves current non empty line in readFile to line after line described by first two chars
92def goToLineByStartingChars(string):
93 global readFile
94 # find start of relevant data
95 line = ""
96 while True:
97 line = readFile.readline()
98 if not line:
99 print("Incorrect arguments or file format: error in goToLineByStartingChars")
100 sys.exit(1)
101
102 # strip after checking for EOF so we can distinguish EOF vs empty line
103 line = line.strip()
104
105 # discard lines until we see the column line in output
106 if line and line[0:len(string)] == string:
107 break
108
109# recursively find largest cycle included a specific node using DFS
110def findLargestCycle(startNode, currNode, visited, globalVisited, currSum, currCycle):
111 global handoff, maxCycle, maxCycleSum
112
113 # print("CurrNode: " + str(currNode))
114 # print(currCycle)
115 # if we visit a node from a previous call then return since we have already
116 # looked at all cycles containing that node
117 if globalVisited[currNode]:
118 # print("globalVisited")
119 return
120
121 # found a cycle
122 if visited[currNode]:
123 # print("LocalVisited, curr: " + str(currSum) + ", max: " + str(maxCycleSum) + ", Start: " + str(startNode) )
124 # if the cycle contains the start node check if it is our new max cycle
125 if currNode == startNode and currSum > maxCycleSum:
126 # print("NewMax")
127 maxCycleSum = currSum
128 maxCycle = currCycle.copy()
129 return
130
131 visited[currNode] = True
132
133 for idx, val in enumerate(handoff[currNode]):
134 # continue if no edge
135 if val == 0:
136 continue
137
138 currCycle.append(currNode)
139 findLargestCycle(startNode, idx, visited, globalVisited, currSum + val, currCycle)
140 currCycle.pop()
141
142 visited[currNode] = False
143
144
145
146def analyzeRun():
147 global handoff, sumPerRow, handoffTotal, maxCycle, maxCycleSum, minBound, maxBound, expected
148 currThds = len(handoff)
149
150 # find largest cycle
151 globalVisited = [False] * currThds
152 for i in range(currThds):
153 visited = [False] * currThds
154 findLargestCycle(i, i, visited, globalVisited, 0, [])
155 globalVisited[i] = True
156
157 # calculate stats
158 cycleHandoffs = []
159 cycleHandoffs.append(handoff[maxCycle[-1]][maxCycle[0]])
160 sumOfMaxCycleRows = sumPerRow[maxCycle[0]]
161
162 # expected handoff is MULT P( handoff ) for each handoff in max cycle
163 expectedConvoy = handoff[maxCycle[-1]][maxCycle[0]] / sumPerRow[maxCycle[-1]]
164 for idx, val in enumerate(maxCycle[1:]):
165 cycleHandoffs.append(handoff[maxCycle[idx]][val])
166 sumOfMaxCycleRows += sumPerRow[val]
167 expectedConvoy = expectedConvoy * handoff[maxCycle[idx]][val] / sumPerRow[maxCycle[idx]]
168
169 # adjust expected bound
170 # if max cycle contains all nodes, sumOfMaxCycleRows / handoffTotal == 1
171 # else this adjusts the expected bound to compensate for the cycle not visiting all nodes
172 # also mult by 100 to turn into percentage from decimal
173 expectedConvoy = expectedConvoy * sumOfMaxCycleRows * 100 / handoffTotal
174
175 # upper bound is the percentage of all handoffs that occur in the maximum possible cycle
176 # The cycle is possible min(cycleHandoffs) number of times
177 maxFeasibleHandoff = min(cycleHandoffs)
178 upperBoundConvoy = maxFeasibleHandoff * len(maxCycle) * 100 / handoffTotal
179 lowerBoundConvoy = 1
180 for val in maxCycle:
181 lowerBoundConvoy = lowerBoundConvoy * maxFeasibleHandoff / sumPerRow[val]
182
183 # adjust lower bound. See comment for expectedConvoy adjustment to explain why
184 lowerBoundConvoy = lowerBoundConvoy * sumOfMaxCycleRows * 100 / handoffTotal
185
186 maxBound.append(upperBoundConvoy)
187 minBound.append(lowerBoundConvoy)
188 expected.append(expectedConvoy)
189
190 if args.Verbose or args.Single:
191 output('Convoying bounds: {:.2f}%-{:.2f}%, Expected convoying: {:.2f}%'.format(lowerBoundConvoy,upperBoundConvoy, expectedConvoy))
192
193
194if args.Single:
195 output("N: " + str(args.MaxThreads))
196 goToLineByStartingChars(str(args.MaxThreads)+' ')
197 readInMatrix(int(args.MaxThreads))
198 analyzeRun()
199 reset()
200else:
201 for i in range(args.MaxThreads):
202 output("N: " + str(i+1))
203
204 goToLineByStartingChars(str(i+1)+' ')
205 for j in range(args.RunsPerNumThds):
206 readInMatrix(i+1)
207 analyzeRun()
208 reset()
209
210 output('Mean convoying bounds: {:.2f}%-{:.2f}%, Mean expected convoying: {:.2f}%'.format(st.mean(minBound), st.mean(maxBound),st.mean(expected)))
211 output('Median convoying bounds: {:.2f}%-{:.2f}%, Median expected convoying: {:.2f}%'.format(st.median(minBound), st.median(maxBound),st.median(expected)))
212 output('')
213 thdReset()
214
215readFile.close()
216
217if writeFile:
218 writeFile.close()
Note: See TracBrowser for help on using the repository browser.