import sys import sqlite3 import argparse from dramsys.common.memUtil import * from math import * metrics = [] threadMetrics = [] def metric(function): metrics.append(function) return function def threadMetric(function): threadMetrics.append(function) return function def getThreads(connection): cthread = getControllerThread(connection) cursor = connection.cursor() cursor.execute("SELECT DISTINCT(Thread) FROM Transactions WHERE Thread != " + str(cthread) + " ORDER BY Thread") result = [] for currentRow in cursor: result.append(currentRow[0]) return result @metric def trace_length_in_ns(connection): cursor = connection.cursor() cursor.execute("SELECT MAX(PhaseEnd) / 1000 FROM Phases") result = cursor.fetchone() return result[0] @metric def command_bus_utilisation_in_percent(connection): cursor = connection.cursor() if getRowColumnCommandBus(connection): cursor.execute(""" SELECT SUM(CommandLengths.Length) FROM Phases INNER JOIN CommandLengths ON Phases.PhaseName = CommandLengths.Command WHERE PhaseName <> 'RD' AND PhaseName <> 'RDA' AND PhaseName <> 'WR' AND PhaseName <> 'WRA' """) rowBusUtil = cursor.fetchone()[0] if rowBusUtil is None: rowBusUtil = 0 cursor.execute(""" SELECT SUM(CommandLengths.Length) FROM Phases INNER JOIN CommandLengths ON Phases.PhaseName = CommandLengths.Command WHERE PhaseName = 'RD' OR PhaseName = 'RDA' OR PhaseName = 'WR' OR PhaseName = 'WRA' """) columnBusUtil = cursor.fetchone()[0] if columnBusUtil is None: columnBusUtil = 0 clk, _ = getClock(connection) traceEnd = getTraceEndTime(connection) rowBusOccupied = rowBusUtil * clk / traceEnd * 100 columnBusOccupied = columnBusUtil * clk / traceEnd * 100 return "row commands: {}, column commands: {}".format(rowBusOccupied, columnBusOccupied) else: cursor.execute(""" SELECT SUM(CommandLengths.Length) FROM Phases INNER JOIN CommandLengths ON Phases.PhaseName = CommandLengths.Command """) util = cursor.fetchone()[0] if util is None: util = 0 clk, _ = getClock(connection) traceEnd = getTraceEndTime(connection) commandBusOccupied = util * clk / traceEnd * 100 return ": {}".format(commandBusOccupied) @metric def average_response_latency_in_ns(connection): cursor = connection.cursor() cursor.execute(""" SELECT AVG(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp WHERE Req.PhaseName = 'REQ' AND Resp.PhaseName='RESP' AND Req.Transact = Resp.Transact """) result = cursor.fetchone() return round(result[0], 1) @metric def average_rd_response_latency_in_ns(connection): cursor = connection.cursor() cursor.execute(""" SELECT AVG(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp INNER JOIN Transactions ON Req.Transact = Transactions.ID WHERE Req.PhaseName = 'REQ' AND Resp.PhaseName = 'RESP' AND Req.Transact = Resp.Transact AND Transactions.Command = 'R' """) result = cursor.fetchone() return round(result[0], 1) @metric def average_wr_response_latency_in_ns(connection): cursor = connection.cursor() cursor.execute(""" SELECT AVG(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp INNER JOIN Transactions ON Req.Transact = Transactions.ID WHERE Req.PhaseName = 'REQ' AND Resp.PhaseName = 'RESP' AND Req.Transact = Resp.Transact AND Transactions.Command = 'W' """) result = cursor.fetchone() return round(result[0], 1) @metric def max_response_latency_in_ns(connection): cursor = connection.cursor() cursor.execute(""" SELECT MAX(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp WHERE Req.PhaseName = 'REQ' AND Resp.PhaseName = 'RESP' AND Req.Transact = Resp.Transact """) result = cursor.fetchone() return result[0] @metric def max_rd_response_latency_in_ns(connection): cursor = connection.cursor() cursor.execute(""" SELECT MAX(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp INNER JOIN Transactions ON Req.Transact = Transactions.ID WHERE Req.PhaseName = 'REQ' AND Resp.PhaseName = 'RESP' AND Req.Transact = Resp.Transact AND Transactions.Command = 'R' """) result = cursor.fetchone() return round(result[0], 1) @metric def max_wr_response_latency_in_ns(connection): cursor = connection.cursor() cursor.execute(""" SELECT MAX(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp INNER JOIN Transactions ON Req.Transact = Transactions.ID WHERE Req.PhaseName = 'REQ' AND Resp.PhaseName = 'RESP' AND Req.Transact = Resp.Transact AND Transactions.Command = 'W' """) result = cursor.fetchone() return round(result[0], 1) @metric def trans_with_max_response_latency(connection): cursor = connection.cursor() cursor.execute(""" SELECT Req.Transact, MAX(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp WHERE Req.PhaseName = 'REQ' AND Resp.PhaseName = 'RESP' AND Req.Transact = Resp.Transact """) result = cursor.fetchone() return result[0] @metric def memory_active_in_clks(connection): cursor = connection.cursor() cursor.execute("SELECT SUM(DataStrobeEnd - DataStrobeBegin) FROM Phases") active = cursor.fetchone()[0] if getPseudoChannelMode(connection): active /= 2 clk, _ = getClock(connection) return active / clk @metric def memory_total_in_clks(connection): cursor = connection.cursor() cursor.execute("SELECT MAX(DataStrobeEnd) FROM Phases") total = cursor.fetchone() clk, _ = getClock(connection) return total[0] / clk @metric def memory_idle_in_clks(connection): # This complex query identifies idle times when the DRAM is not used. # The code works also if schedulers are used. cursor = connection.cursor() # Create a table with transactions sorted by BeginRequest: # (RowNum, BeginRequest) query = """DROP TABLE IF EXISTS transactionsInRequestOrder;""" cursor.execute(query) query = """ CREATE TEMPORARY TABLE transactionsInRequestOrder AS SELECT ROW_NUMBER () OVER (ORDER BY p.PhaseBegin) RowNum, p.PhaseBegin AS BeginRequest FROM Phases p WHERE p.PhaseName = 'REQ'; """ cursor.execute(query) # Create a table with transactions sorted by BeginResponse: # (RowNum, ID, EndResponse) query = """DROP TABLE IF EXISTS transactionsInResponseOrder;""" cursor.execute(query) query = """ CREATE TEMPORARY TABLE transactionsInResponseOrder AS SELECT ROW_NUMBER () OVER (ORDER BY q.PhaseBegin) RowNum, q.PhaseEnd AS EndResponse FROM Phases q WHERE q.PhaseName = 'RESP'; """ cursor.execute(query) # Sum up the idle times: query = """ SELECT SUM(c.BeginRequest - b.EndResponse) AS idle FROM transactionsInRequestOrder AS a, transactionsInResponseOrder AS b, transactionsInRequestOrder AS c WHERE a.RowNum = b.RowNum AND c.RowNum = (a.RowNum + 1) AND c.BeginRequest > b.EndResponse; """ cursor.execute(query) idle = cursor.fetchone() cursor.execute("""SELECT MIN(PhaseBegin) FROM Phases WHERE PhaseName = 'REQ'""") idle_start = cursor.fetchone() clk, unit = getClock(connection) if idle[0] is None: return idle_start[0] / clk else: return (idle[0] + idle_start[0]) / clk @metric def memory_delayed_in_clks(connection): total = memory_total_in_clks(connection) active = memory_active_in_clks(connection) idle = memory_idle_in_clks(connection) return total - active - idle @metric def delayed_reasons(connection): cursor = connection.cursor() # Create a table with sorted data strobes: # (RowNum, ID, Begin, End) cursor.execute("DROP TABLE IF EXISTS dataStrobesInOrder;") cursor.execute(""" CREATE TEMPORARY TABLE dataStrobesInOrder AS SELECT ROW_NUMBER () OVER (ORDER BY DataStrobeBegin) RowNum, Transact AS ID, DataStrobeBegin AS Begin, DataStrobeEnd AS End, Row, Bank, PhaseName AS Command FROM Phases WHERE DataStrobeBegin <> 0 AND DataStrobeEnd <> 0; """) # Create a table with transactions sorted by BeginRequest: # (RowNum, BeginRequest) cursor.execute("""DROP TABLE IF EXISTS transactionsInRequestOrder;""") query = """ CREATE TEMPORARY TABLE transactionsInRequestOrder AS SELECT ROW_NUMBER () OVER (ORDER BY PhaseBegin) RowNum, PhaseBegin AS BeginRequest FROM Phases WHERE PhaseName = 'REQ'; """ cursor.execute(query) # Create a table with transactions sorted by BeginResponse: # (RowNum, ID, EndResponse) query = """DROP TABLE IF EXISTS transactionsInResponseOrder;""" cursor.execute(query) query = """ CREATE TEMPORARY TABLE transactionsInResponseOrder AS SELECT ROW_NUMBER () OVER (ORDER BY PhaseBegin) RowNum, Transact AS ID, PhaseEnd AS EndResponse FROM Phases WHERE PhaseName = 'RESP'; """ cursor.execute(query) # Create a table with transaction IDs that start an idle time: # (ID) query = """DROP TABLE IF EXISTS idleGaps;""" cursor.execute(query) query = """ CREATE TEMPORARY TABLE idleGaps AS SELECT b.ID AS ID FROM transactionsInRequestOrder AS a, transactionsInResponseOrder AS b, transactionsInRequestOrder AS c WHERE a.RowNum = b.RowNum AND c.RowNum = (a.RowNum + 1) AND c.BeginRequest > b.EndResponse; """ cursor.execute(query) # Create a table which features IDs that form gaps on the data bus: # (gapBeginID, gapEndID) query = """DROP TABLE IF EXISTS delayedDataBusGaps;""" cursor.execute(query) query = """ CREATE TEMPORARY TABLE delayedDataBusGaps AS SELECT a.ID AS gapBeginID, b.ID AS gapEndID, a.Command AS gapBeginCommand, b.Command AS gapEndCommand FROM dataStrobesInOrder a, dataStrobesInOrder b WHERE a.RowNum = b.RowNum - 1 AND b.Begin > a.End AND a.ID NOT IN (SELECT ID FROM idleGaps); """ cursor.execute(query) # Count RW query = """ SELECT COUNT(*) FROM delayedDataBusGaps WHERE (gapBeginCommand = 'RD' OR gapBeginCommand = 'RDA') AND (gapEndCommand = 'WR' OR gapEndCommand = 'WRA'); """ cursor.execute(query) RW = cursor.fetchone()[0] # Count WR query = """ SELECT COUNT(*) FROM delayedDataBusGaps WHERE (gapBeginCommand = 'WR' OR gapBeginCommand = 'WRA') AND (gapEndCommand = 'RD' OR gapEndCommand = 'RDA'); """ cursor.execute(query) WR = cursor.fetchone()[0] # Count RR Miss query = """ SELECT COUNT(*) FROM ( SELECT COUNT(*) FROM delayedDataBusGaps d, Phases p WHERE (d.gapBeginCommand = 'RD' OR d.gapBeginCommand = 'RDA') AND (d.gapEndCommand = 'RD' OR d.gapEndCommand = 'RDA') AND p.Transact = d.gapEndID AND p.PhaseName = 'ACT' GROUP BY d.gapBeginID ) """ cursor.execute(query) RRM = cursor.fetchone()[0] # Count WW Miss query = """ SELECT COUNT(*) FROM ( SELECT COUNT(*) FROM delayedDataBusGaps d, Phases p WHERE (d.gapBeginCommand = 'WR' OR d.gapBeginCommand = 'WRA') AND (d.gapEndCommand = 'WR' OR d.gapEndCommand = 'WRA') AND p.Transact = d.gapEndID AND p.PhaseName = 'ACT' GROUP BY d.gapBeginID ) """ cursor.execute(query) WWM = cursor.fetchone()[0] # Count All Gaps query = """ SELECT COUNT(*) FROM delayedDataBusGaps; """ cursor.execute(query) total = cursor.fetchone()[0] other = total - RW - WR - RRM - WWM RW /= total / 100.0 WR /= total / 100.0 RRM /= total / 100.0 WWM /= total / 100.0 other /= total / 100.0 result = "RW: {}, WR: {}, RRM: {}, WWM: {}, Other: {}".format(RW, WR, RRM, WWM, other) return result @metric def memory_idle_in_percent(connection): total = memory_total_in_clks(connection) idle = memory_idle_in_clks(connection) return (idle / total) * 100 @metric def memory_active_in_percent(connection): total = memory_total_in_clks(connection) active = memory_active_in_clks(connection) return (active / total) * 100 @metric def memory_delayed_in_percent(connection): total = memory_total_in_clks(connection) delayed = memory_delayed_in_clks(connection) return (delayed / total) * 100 @metric def memory_idle_active_delayed_check(connection): return memory_idle_in_percent(connection) + memory_active_in_percent(connection) \ + memory_delayed_in_percent(connection) @metric def memory_utilisation_percent_without_idle(connection): total = memory_total_in_clks(connection) active = memory_active_in_clks(connection) idle = memory_idle_in_clks(connection) return (active / (total - idle)) * 100 @metric def memory_utilisation_in_Gibps_without_idle(connection): # This function calculates the memory utilisation in Gibit/s considering # the memory_utilisation_percent_new function result. maxDataRate = maximum_data_rate(connection) memoryPercent = memory_utilisation_percent_without_idle(connection) return (memoryPercent / 100) * (maxDataRate / 1024) @metric def memory_utilisation_percent_including_idle(connection): cursor = connection.cursor() cursor.execute("""SELECT SUM(DataStrobeEnd - DataStrobeBegin) FROM Phases""") active = cursor.fetchone()[0] if getPseudoChannelMode(connection): active /= 2 cursor = connection.cursor() cursor.execute("""SELECT MAX(DataStrobeEnd) FROM Phases""") total = cursor.fetchone()[0] return (active / total) * 100 # def refreshMissDecision(connection, calculatedMetrics): # cursor = connection.cursor() # cursor.execute("""SELECT Phases.ID, PhaseBegin, PhaseEnd, TBank FROM Phases INNER JOIN Transactions # ON Transactions.ID = Phases.Transact WHERE PhaseName IN ('REFAB')' """) # queryMinREQ = """SELECT ID, MIN(PhaseBegin) FROM (SELECT Transactions.ID, PhaseBegin FROM Transactions # INNER JOIN ranges ON ranges.id = Transactions.Range INNER JOIN Phases ON Phases.Transact = Transactions.ID # WHERE TThread != 0 AND TBank = :bank and PhaseName = "REQ" and ranges.begin < :begin AND ranges.end > :end)""" # # queryMinRESP = """SELECT ID, MIN(PhaseBegin) FROM (SELECT Transactions.ID, PhaseBegin FROM Transactions # INNER JOIN ranges ON ranges.id = Transactions.Range INNER JOIN Phases ON Phases.Transact = Transactions.ID # WHERE TThread != 0 AND TBank = :bank AND PhaseName = "RESP" AND ranges.begin < :begin AND ranges.end > :end) """ # # missDecisions = 0 # totalDecisions = 0 # # for refresh in cursor: # id = refresh[0] # begin = refresh[1] # end = refresh[2] # bank = refresh[3] # # print('Refresh: {0} {1} {2} {3}'.format(id,begin,end,bank)) # # cursorMinREQ = connection.cursor() # cursorMinRESP = connection.cursor() # # cursorMinREQ.execute(queryMinREQ, {"bank": bank, "begin": begin, "end": end}) # cursorMinRESP.execute(queryMinRESP, {"bank": bank, "begin": begin, "end": end}) # # earliestReq = cursorMinREQ.fetchone() # earliestResp = cursorMinRESP.fetchone() # if earliestReq[0] is not None: # totalDecisions = totalDecisions + 1 # if earliestReq[0] != earliestResp[0]: # missDecisions = missDecisions + 1 # # print("earliest Req: {0}| earliest Res: {1}".format(earliestReq[0], earliestResp[0])) # # if totalDecisions != 0: # # calculatedMetrics.append(("Total Missdecisions", missDecisions)) # calculatedMetrics.append(("Relative Missdecisions", 1.0*missDecisions/totalDecisions)) # else: # calculatedMetrics.append(("Total Missdecisions", 0)) # calculatedMetrics.append(("Relative Missdecisions", 0)) # @threadMetric # FIXME: Fix thread metrics # def average_response_latency_in_ns(connection, thread): # cursor = connection.cursor() # query = """SELECT AVG(Resp.PhaseBegin - Req.PhaseBegin) / 1000 FROM Phases Req, Phases Resp INNER JOIN Transactions # ON Req.Transact = Transactions.ID WHERE Req.Transact = Resp.Transact AND Req.PhaseName = 'REQ' # AND Resp.PhaseName = 'RESP' AND Thread = :Thread""" # # cursor.execute(query, {"Thread": thread}) # result = cursor.fetchone() # return round(result[0], 1) def addStallTime(times, begin, end): time = begin while time <= end: if time in times: times[time] = times[time] + 1 else: times[time] = 1 time = time + 1 # @threadMetric # FIXME: Fix thread metrics # def parallelism(connection, thread): # cursor = connection.cursor() # stalltimes = {} # query = """SELECT Transactions.ID, MIN(PhaseBegin) / :clk, MAX(PhaseEnd) / :clk # FROM Phases INNER JOIN Transactions ON Phases.Transact = Transactions.ID # WHERE PhaseName NOT IN ('REQ','RESP') AND Thread = :Thread GROUP BY Transactions.ID """ # # clk, unit = getClock(connection) # cursor.execute(query, {"Thread": thread, "clk": clk}) # for currentRow in cursor: # addStallTime(stalltimes, currentRow[1], currentRow[2]) # para = 0 # for time in stalltimes: # para = para + stalltimes[time] # return round(para / len(stalltimes), 2) # @threadMetric # FIXME: Fix thread metrics # def thread_conclusion_in_ns(connection, thread): # cursor = connection.cursor() # query = """ # SELECT max(PhaseEnd) / 1000 FROM Phases INNER JOIN Transactions # ON Phases.Transact = Transactions.ID WHERE Thread = :Thread """ # cursor.execute(query, {"Thread": thread}) # result = cursor.fetchone() # return result[0] @metric def REQ_count(connection): return get_phase_occurrences(connection, 'REQ') @metric def RESP_count(connection): return get_phase_occurrences(connection, 'RESP') # @metric # def ACTB_count(connection): # return get_phase_occurrences(connection, 'ACTB') @metric def ACT_count(connection): return get_phase_occurrences(connection, 'ACT') # @metric # def PREB_count(connection): # return get_phase_occurrences(connection, 'PREB') @metric def PREPB_count(connection): return get_phase_occurrences(connection, 'PREPB') + get_phase_occurrences(connection, 'PRE') @metric def PRESB_count(connection): return get_phase_occurrences(connection, 'PRESB') @metric def PREAB_count(connection): return get_phase_occurrences(connection, 'PREAB') + get_phase_occurrences(connection, 'PREA') @metric def REFPB_count(connection): return get_phase_occurrences(connection, 'REFPB') + get_phase_occurrences(connection, 'REFB') @metric def REFP2B_count(connection): return get_phase_occurrences(connection, 'REFP2B') @metric def REFSB_count(connection): return get_phase_occurrences(connection, 'REFSB') @metric def REFAB_count(connection): return get_phase_occurrences(connection, 'REFAB') + get_phase_occurrences(connection, 'REFA') @metric def RFMPB_count(connection): return get_phase_occurrences(connection, 'RFMPB') @metric def RFMP2B_count(connection): return get_phase_occurrences(connection, 'RFMP2B') @metric def RFMSB_count(connection): return get_phase_occurrences(connection, 'RFMSB') @metric def RFMAB_count(connection): return get_phase_occurrences(connection, 'RFMAB') @metric def RD_count(connection): return get_phase_occurrences(connection, 'RD') @metric def RDA_count(connection): return get_phase_occurrences(connection, 'RDA') @metric def WR_count(connection): return get_phase_occurrences(connection, 'WR') @metric def WRA_count(connection): return get_phase_occurrences(connection, 'WRA') @metric def PDNA_count(connection): return get_phase_occurrences(connection, 'PDNA') @metric def PDNB_count(connection): return get_phase_occurrences(connection, 'PDNAB') @metric def PDNP_count(connection): return get_phase_occurrences(connection, 'PDNP') @metric def PDNPB_count(connection): return get_phase_occurrences(connection, 'PDNPB') @metric def SREF_count(connection): return get_phase_occurrences(connection, 'SREF') @metric def SREFB_count(connection): return get_phase_occurrences(connection, 'SREFB') @metric def number_of_accesses(connection): return RD_count(connection) + RDA_count(connection) + WR_count(connection) + WRA_count(connection) @metric def accesses_per_activate(connection): return round(float(number_of_accesses(connection)) / ACT_count(connection), 1) @metric def bank_overlap_ratio(connection): # Calculates how many banks are open in parallel clk, unit = getClock(connection) numberOfRanks = getNumberOfRanks(connection) numberOfBankGroups = getNumberOfBankGroups(connection) numberOfBanks = getNumberOfBanks(connection) per2BankOffset = getPer2BankOffset(connection) banksPerRank = int(numberOfBanks / numberOfRanks) banksPerGroup = int(numberOfBanks / numberOfBankGroups) cursor = connection.cursor() traceEnd = getTraceEndTime(connection) trace = [] cursor.execute(""" SELECT PhaseBegin, PhaseName, Rank, BankGroup, Bank FROM Phases WHERE (PhaseName = 'ACT' OR PhaseName = 'PREPB' OR PhaseName = 'PREP2B' OR PhaseName = 'PRESB' OR PhaseName = 'PREAB' OR PhaseName = 'RDA' OR PhaseName = 'WRA') ORDER BY PhaseBegin """) # prevPhase = "PREAB" # prevTime = 0 # clk of command, name, rank, bank group, bank for c in cursor: trace.append([(int(c[0] / clk)), c[1], c[2], c[3], c[4]]) # Insert a pseudo precharge all to mark the end of the trace on all ranks for i in range(numberOfRanks): trace.append([traceEnd, "PREAB", i, 0, 0]) bankCounter = [False] * numberOfBanks # + 1 because we have #banks + 1 active states (0 to all banks active) bankTime = [0.0] * (numberOfBanks + 1) currentTime = 0 for t in trace: interval = t[0] - currentTime bankTime[sum(bankCounter)] += interval currentTime = t[0] if t[1] == "ACT": bankCounter[t[4]] = True elif t[1] in ["PREPB", "RDA", "WRA"]: bankCounter[t[4]] = False elif t[1] == "PREP2B": bankCounter[t[4]] = False bankCounter[t[4] + per2BankOffset] = False elif t[1] == "PRESB": for i in range(t[4], t[4] + banksPerRank, banksPerGroup): bankCounter[i] = False elif t[1] == "PREAB": for i in range(t[2] * banksPerRank, (t[2] + 1) * banksPerRank): bankCounter[i] = False else: print("ERROR") return 0 for i in range(numberOfBanks + 1): bankTime[i] = round(bankTime[i] / traceEnd * 100, 2) return ",".join(format(x, "6.2f") for x in bankTime) # @metric # def number_of_precharges(connection): # cursor = connection.cursor() # cursor.execute("SELECT COUNT(*) FROM Phases WHERE PhaseName IN ('PRE','PREA','RDA','WRA')") # result = cursor.fetchone() # return result[0] def time_in_PDNA_in_ns(connection): return get_total_time_in_phase(connection, "PDNA") def time_in_PDNA_percent(connection): return (time_in_PDNA_in_ns(connection) * 1.0 / trace_length_in_ns(connection)) * 100 def time_in_PDNP_in_ns(connection): return get_total_time_in_phase(connection, "PDNP") def time_in_PDNP_percent(connection): return (time_in_PDNP_in_ns(connection) * 1.0 / trace_length_in_ns(connection)) * 100 def time_in_SREF_in_ns(connection): return get_total_time_in_phase(connection, "SREF") def time_in_SREF_percent(connection): return (time_in_SREF_in_ns(connection) * 1.0 / trace_length_in_ns(connection)) * 100 def time_in_PDNAB_in_ns(connection): return get_total_time_in_phase(connection, "PDNAB") def time_in_PDNAB_percent(connection): totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) return (time_in_PDNAB_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 def time_in_PDNPB_in_ns(connection): return get_total_time_in_phase(connection, "PDNPB") def time_in_PDNPB_percent(connection): totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) return (time_in_PDNPB_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 def time_in_SREFB_in_ns(connection): return get_total_time_in_phase(connection, "SREFB") def time_in_SREFB_percent(connection): totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) return (time_in_SREFB_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 @metric def time_in_power_down_states_in_ns(connection): mcconfig = MCConfig(connection) # bankwiseLogic = mcconfig.getValue("BankwiseLogic") bankwiseLogic = "0" if bankwiseLogic == "0": totalTimeInPDNA = time_in_PDNA_in_ns(connection) totalTimeInPDNP = time_in_PDNP_in_ns(connection) totalTimeInSREF = time_in_SREF_in_ns(connection) totalTimePdnStates = totalTimeInPDNA + totalTimeInPDNP + totalTimeInSREF else: totalTimeInPDNAB = time_in_PDNAB_in_ns(connection) totalTimeInPDNPB = time_in_PDNPB_in_ns(connection) totalTimeInSREFB = time_in_SREFB_in_ns(connection) totalTimePdnStates = totalTimeInPDNAB + totalTimeInPDNPB + totalTimeInSREFB return totalTimePdnStates @metric def time_in_power_down_states_percent(connection): mcconfig = MCConfig(connection) # bankwiseLogic = mcconfig.getValue("BankwiseLogic") bankwiseLogic = "0" if bankwiseLogic == "0": totalTimeAllBanks = trace_length_in_ns(connection) else: totalTimeAllBanks = trace_length_in_ns(connection) * getNumberOfBanks(connection) return (time_in_power_down_states_in_ns(connection) * 1.0 / totalTimeAllBanks) * 100 def passRatio(connection): numberOfPassWins = {} numberOfPassLosses = {} for thread in getThreads(connection): numberOfPassWins[thread] = 0 numberOfPassLosses[thread] = 0 for bankNumber in range(getNumberOfBanks(connection)): cursor = connection.cursor() query = """SELECT begin,end,transactions.ID,TThread, TBank from transactions inner join ranges on transactions.range = ranges.id WHERE TBank = :Bank AND TThread != 0""" cursor.execute(query, {"Bank": bankNumber}) for passedRequest in cursor: passedBegin = passedRequest[0] passedEnd = passedRequest[1] passedId = passedRequest[2] passedThread = passedRequest[3] cursor2 = connection.cursor() query2 = """SELECT begin,end,transactions.ID,TThread, TBank from transactions inner join ranges on transactions.range = ranges.id WHERE TBank = :Bank AND :Begin 0): passRatio = numberOfPassWins[thread]*1.0/(numberOfPassWins[thread]+numberOfPassLosses[thread]) else: passRatio = 0.5 # print("Thread {0} passed other threads {1} times and was passed {2} times. Pass ratio is {3}".format(thread, numberOfPassWins[thread], numberOfPassLosses[thread], passRatio)) result.append(("Thread {0} pass ratio".format(thread), passRatio)) return result def getMetrics(pathToTrace): selectedMetrics = [] connection = sqlite3.connect(pathToTrace) mcconfig = MCConfig(connection) # bankwiseLogic = mcconfig.getValue("BankwiseLogic") bankwiseLogic = "0" if bankwiseLogic == "0": pdnMetrics = [time_in_PDNA_in_ns, time_in_PDNA_percent, time_in_PDNP_in_ns, time_in_PDNP_percent, time_in_SREF_in_ns, time_in_SREF_percent] else: pdnMetrics = [time_in_PDNAB_in_ns, time_in_PDNAB_percent, time_in_PDNPB_in_ns, time_in_PDNPB_percent, time_in_SREFB_in_ns, time_in_SREFB_percent] for metric in pdnMetrics: if metric not in metrics: metrics.append(metric) if len(getThreads(connection)) >= 1: for metric in metrics: res = metric.__name__.replace("_", " ") selectedMetrics.append(res) if len(getThreads(connection)) > 1: for thread in getThreads(connection): for threadMetric in threadMetrics: res = "Thread {0}: {1}".format(thread, threadMetric.__name__.replace("_", " ")) selectedMetrics.append(res) res = "pass ratio" selectedMetrics.append(res) connection.close() return selectedMetrics def calculateMetrics(pathToTrace, selectedMetrics=None): if selectedMetrics is None: selectedMetrics = [] calculatedMetrics = [] connection = sqlite3.connect(pathToTrace) mcconfig = MCConfig(connection) # bankwiseLogic = mcconfig.getValue("BankwiseLogic") pdnMetrics = [time_in_PDNA_in_ns, time_in_PDNA_percent, time_in_PDNP_in_ns, time_in_PDNP_percent, time_in_SREF_in_ns, time_in_SREF_percent] for m in pdnMetrics: if m not in metrics: metrics.append(m) print("================================") print("Calculating metrics for {0}".format(pathToTrace)) print("Number of threads is {0}".format(len(getThreads(connection)))) if not selectedMetrics: selectedMetrics = [0] * (len(metrics) + len(getThreads(connection)) * len(threadMetrics) + 1) for i in range(len(selectedMetrics)): selectedMetrics[i] = True if len(getThreads(connection)) >= 1: for metric in metrics: if selectedMetrics[metrics.index(metric)]: mres = metric(connection) mname = metric.__name__.replace("_", " ") res = (mname, mres) if metric.__name__ == "bank_overlap_ratio": values = mres.split(",") nbanks = 0 for v in values: name = mname + " (" + str(nbanks) + " banks active)" nbanks = nbanks + 1 r = (name, float(v)) calculatedMetrics.append(r) elif metric.__name__ == "delayed_reasons" or metric.__name__ == "command_bus_utilisation_in_percent": values = mres.split(",") for v in values: name = mname + " (" + v.partition(":")[0].strip() + ")" value = v.partition(":")[2].strip() r = (name, float(value)) calculatedMetrics.append(r) else: calculatedMetrics.append(res) print("{0}: {1}".format(res[0], res[1])) if len(getThreads(connection)) > 1: for thread in getThreads(connection): for metric in threadMetrics: if selectedMetrics[len(metrics) + len(threadMetrics) * (thread - 1) + threadMetrics.index(metric)]: mres = metric(connection, thread) mname = "Thread {0} - {1}".format(thread, metric.__name__.replace("_", " ")) res = (mname, mres) calculatedMetrics.append(res) print("{0}: {1}".format(res[0], res[1])) # if selectedMetrics[len(selectedMetrics) - 1]: # FIXME: Add pass ratio # calculatedMetrics.extend(passRatio(connection)) # refreshMissDecision(connection, calculatedMetrics) if len(getThreads(connection)) == 0: res = ("No accesses were performed for this channel, number of metrics generated", 0.0) calculatedMetrics.append(res) # refreshMissDecision(connection, calculatedMetrics) connection.close() return calculatedMetrics def calculateMetricsFromFuncs(pathToTrace, selectedMetrics): calculatedMetrics = [] connection = sqlite3.connect(pathToTrace) mcconfig = MCConfig(connection) print("================================") print("Calculating metrics for {0}".format(pathToTrace)) print("Number of threads is {0}".format(len(getThreads(connection)))) if not selectedMetrics: selectedMetrics = [0] * (len(metrics) + len(getThreads(connection)) * len(threadMetrics) + 1) for i in range(len(selectedMetrics)): selectedMetrics[i] = True for metric in selectedMetrics: mres = metric(connection) mname = metric.__name__.replace("_", " ") res = (mname, mres) if metric.__name__ == "bank_overlap_ratio": values = mres.split(",") nbanks = 0 for v in values: name = mname + " (" + str(nbanks) + " banks active)" nbanks = nbanks + 1 r = (name, float(v)) calculatedMetrics.append(r) else: calculatedMetrics.append(res) print("{0}: {1}".format(res[0], res[1])) # refreshMissDecision(connection, calculatedMetrics) connection.close() return calculatedMetrics def main(): """ Only non-threaded metrics are implemented for selection through command line """ parser = argparse.ArgumentParser(description="Calculates metrics of a given .tdb file") parser.add_argument('path', type=str, help="The path to the .tdb file to be used") dic_metric_functions = {} for m in metrics: parser.add_argument("--" + m.__name__, action='store_true') dic_metric_functions[m.__name__] = m arg_namespace = parser.parse_args(sys.argv[1:]) selected_metrics = [] for k, v in arg_namespace.__dict__.items(): if k == 'path': continue if v: selected_metrics.append(dic_metric_functions[k]) if not selected_metrics: calculateMetrics(arg_namespace.path) else: calculateMetricsFromFuncs(arg_namespace.path, selected_metrics) if __name__ == "__main__": main()