insnmeas.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. # -*- coding: utf-8 -*-
  2. #
  3. # AWL simulator - Instruction timing measurement
  4. #
  5. # Copyright 2019 Michael Buesch <m@bues.ch>
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License along
  18. # with this program; if not, write to the Free Software Foundation, Inc.,
  19. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. #
  21. from __future__ import division, absolute_import, print_function, unicode_literals
  22. #from awlsim.common.cython_support cimport * #@cy
  23. from awlsim.common.compat import *
  24. from awlsim.common.util import *
  25. from awlsim.common.exceptions import *
  26. from awlsim.common.monotonic import * #+cimport
  27. #from awlsim.core.insnmeas cimport * #@cy
  28. from awlsim.core.instructions.main import * #+cimport
  29. from awlsim.core.instructions.types import * #+cimport
  30. import time
  31. __all__ = [
  32. "InsnMeas",
  33. ]
  34. class InsnMeasData(object): #+cdef
  35. def __init__(self):
  36. self.measured = False
  37. self.measStart = 0.0
  38. self.cumRt = 0.0
  39. self.count = 0
  40. self.minRt = 9999.0
  41. self.maxRt = 0.0
  42. @property
  43. def avgRt(self):
  44. if self.count == 0:
  45. return 9999.0
  46. return self.cumRt / float(self.count)
  47. def subtractCal(self, calOffset):
  48. new = InsnMeasData()
  49. new.minRt = max(1.0e-9, self.minRt - calOffset)
  50. new.maxRt = max(1.0e-9, self.maxRt - calOffset)
  51. new.cumRt = max(1.0e-9, self.avgRt - calOffset)
  52. new.count = 1
  53. return new
  54. def dump(self, name):
  55. name += " " * (6 - len(name))
  56. return "%s: min: %.3f us, max: %.3f us, avg: %.3f us" % (
  57. name,
  58. self.minRt * 1.0e6,
  59. self.maxRt * 1.0e6,
  60. self.avgRt * 1.0e6)
  61. class InsnMeas(object): #+cdef
  62. def __init__(self):
  63. self.__perf_counter = time.perf_counter
  64. self.__data = [None] * u32_to_s16(AwlInsnTypes.NR_TYPES + 1) #+suffix-u
  65. for i in range(AwlInsnTypes.NR_TYPES + 1):
  66. self.__data[i] = InsnMeasData()
  67. self.__runOffsetCal()
  68. def __runOffsetCal(self):
  69. calTime = 3.0
  70. printInfo("Running instruction measurement "
  71. "offset calibration (takes %.1f s)..." % (calTime))
  72. calEnd = monotonic_time() + calTime
  73. while monotonic_time() < calEnd:
  74. self.meas(True, AwlInsnTypes.NR_TYPES)
  75. self.meas(False, AwlInsnTypes.NR_TYPES)
  76. printInfo("Instruction measurement cal offset = %.3f us" % (
  77. self.__calOffset * 1.0e6))
  78. def meas(self, begin, insnType): #@nocy
  79. #@cy cdef void meas(self, _Bool begin, uint32_t insnType):
  80. #@cy cdef InsnMeasData measData
  81. #@cy cdef double rt
  82. #@cy cdef double now
  83. now = self.__perf_counter()
  84. measData = self.__data[insnType]
  85. if begin:
  86. measData.measStart = now
  87. else:
  88. rt = now - measData.measStart
  89. measData.cumRt += rt
  90. measData.count += 1
  91. measData.minRt = min(measData.minRt, rt)
  92. measData.maxRt = max(measData.maxRt, rt)
  93. measData.measured = True
  94. @property
  95. def haveAnyMeasurements(self):
  96. return any(self.__data[i].measured
  97. for i in range(AwlInsnTypes.NR_TYPES))
  98. @property
  99. def __calOffset(self):
  100. cal = self.__data[AwlInsnTypes.NR_TYPES]
  101. calOffset = cal.avgRt
  102. return calOffset
  103. @property
  104. def __allMeasData(self):
  105. calOffset = self.__calOffset
  106. for insnType in range(AwlInsnTypes.NR_TYPES):
  107. measData = self.__data[insnType]
  108. if measData.measured:
  109. measData = measData.subtractCal(calOffset)
  110. yield insnType, measData
  111. def dump(self):
  112. if not self.haveAnyMeasurements:
  113. return
  114. ret = []
  115. ret.append("Instruction time measurements:")
  116. for insnType, measData in self.__allMeasData:
  117. name = AwlInsnTypes.type2name_german[insnType]
  118. ret.append(measData.dump(name))
  119. return "\n".join(ret) + "\n"
  120. def dumpCSV(self):
  121. if not self.haveAnyMeasurements:
  122. return ""
  123. ret = [ "instruction type;"
  124. "instruction name;"
  125. "minimum runtime (µs);"
  126. "maximum runtime (µs);"
  127. "average runtime (µs)" ]
  128. for insnType, measData in self.__allMeasData:
  129. name = AwlInsnTypes.type2name_german[insnType]
  130. ret.append("%d; %s;%.3f;%.3f;%.3f" % (
  131. insnType,
  132. AwlInsnTypes.type2name_german[insnType],
  133. measData.minRt * 1.0e6,
  134. measData.maxRt * 1.0e6,
  135. measData.avgRt * 1.0e6))
  136. return "\n".join(ret) + "\n"