1137 lines
36 KiB
Python
1137 lines
36 KiB
Python
import sys
|
|
import sqlite3
|
|
import argparse
|
|
from 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()
|
|
cursor = connection.cursor()
|
|
cursor.execute("""SELECT MAX(DataStrobeEnd) FROM Phases""")
|
|
total = cursor.fetchone()
|
|
return (active[0] / total[0]) * 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()
|
|
cursor.execute("SELECT TraceEnd FROM GeneralInfo")
|
|
traceEndTMP = cursor.fetchone()
|
|
traceEnd = int(traceEndTMP[0] / clk)
|
|
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<begin AND end<:End AND TThread != :Thread AND TThread != 0"""
|
|
cursor2.execute(query2, {"Bank": bankNumber, "Thread": passedThread, "Begin": passedBegin, "End": passedEnd})
|
|
|
|
for passingRequest in cursor2:
|
|
numberOfPassLosses[passedThread] += 1
|
|
numberOfPassWins[passingRequest[3]] += 1
|
|
# print("""Transaction {0} (thread:{1} begin:{2} end:{3}) was passed by Transaction {4} (thread:{5} begin:{6} end:{7}) on bank {8}""".format(passedId,passedThread, passedBegin,passedEnd,
|
|
# passingRequest[2], passingRequest[3], passingRequest[0], passingRequest[1], bankNumber))
|
|
|
|
result = []
|
|
for thread in getThreads(connection):
|
|
totalPassedInvolved = numberOfPassWins[thread]+numberOfPassLosses[thread]
|
|
if(totalPassedInvolved > 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
|
|
|
|
|
|
if __name__ == "__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)
|