operator.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. from dataclasses import dataclass
  2. from typing import Dict, Optional, Tuple
  3. # This class holds information about a single operator used to determine
  4. # the outcome of a selective/custom PyTorch build that doesn't include
  5. # registration code for all the supported operators. This is done to
  6. # reduce the size of the generated binary so that it can be deployed in
  7. # situations where binary size comes at a premium.
  8. #
  9. @dataclass(frozen=True)
  10. class SelectiveBuildOperator:
  11. # The name of the operator. This includes the aten::, etc... prefix
  12. # The operator name may or may not have the overload name. If this
  13. # operator name does not specify an overload name, the way to determine
  14. # if this entry refers to the family of operators with this base name
  15. # or just the operator with this name is to look at the value of the
  16. # 'include_all_overloads' flag in this class.
  17. name: str
  18. # True if this is a root operator (i.e. called directly from a
  19. # TorchScript model, etc...). An operator is considered to be a
  20. # root operator if it is called directly from any one of the models
  21. # that this instance of the pytorch library was built for. Hence, it
  22. # may not be a root operator in all of the models that are used in
  23. # this instance of the pytorch library.
  24. is_root_operator: bool
  25. # Is this operator used for on-device training? If True, then we need to
  26. # use the information to generate code in VariableType_N.cpp for registration
  27. # of training related operators. Again, this is True if this operator
  28. # is used for training in one or more models used by this instance of the
  29. # pytorch library.
  30. is_used_for_training: bool
  31. # If True, it indicates that this operator instance (object) refers to an
  32. # operator without the overload name and should apply to all overloads
  33. # which have this operator name as the base name. This flag is applicable
  34. # only for objects that have operator names without a DOT (period) character
  35. # in them.
  36. #
  37. # Note: This flag is a temporary workaround to grandfather in the current
  38. # static selective (custom) build mechanism, which largely ignores overload
  39. # names when determining whether to select operators for registration
  40. # purposes.
  41. include_all_overloads: bool
  42. # Debug Information at the operator level
  43. _debug_info: Optional[Tuple[str, ...]]
  44. @staticmethod
  45. def from_yaml_dict(
  46. op_name: str, op_info: Dict[str, object]
  47. ) -> "SelectiveBuildOperator":
  48. allowed_keys = {
  49. "name",
  50. "is_root_operator",
  51. "is_used_for_training",
  52. "include_all_overloads",
  53. "debug_info",
  54. }
  55. if len(set(op_info.keys()) - allowed_keys) > 0:
  56. raise Exception(
  57. "Got unexpected top level keys: {}".format(
  58. ",".join(set(op_info.keys()) - allowed_keys),
  59. )
  60. )
  61. if "name" in op_info:
  62. assert op_name == op_info["name"]
  63. is_root_operator = op_info.get("is_root_operator", True)
  64. assert isinstance(is_root_operator, bool)
  65. is_used_for_training = op_info.get("is_used_for_training", True)
  66. assert isinstance(is_used_for_training, bool)
  67. include_all_overloads = op_info.get("include_all_overloads", True)
  68. assert isinstance(include_all_overloads, bool)
  69. debug_info: Optional[Tuple[str, ...]] = None
  70. if "debug_info" in op_info:
  71. di_list = op_info["debug_info"]
  72. assert isinstance(di_list, list)
  73. debug_info = tuple(map(lambda x: str(x), di_list))
  74. return SelectiveBuildOperator(
  75. name=op_name,
  76. is_root_operator=is_root_operator,
  77. is_used_for_training=is_used_for_training,
  78. include_all_overloads=include_all_overloads,
  79. _debug_info=debug_info,
  80. )
  81. @staticmethod
  82. def from_legacy_operator_name_without_overload(
  83. name: str,
  84. ) -> "SelectiveBuildOperator":
  85. return SelectiveBuildOperator(
  86. name=name,
  87. is_root_operator=True,
  88. is_used_for_training=True,
  89. include_all_overloads=True,
  90. _debug_info=None,
  91. )
  92. def to_dict(self) -> Dict[str, object]:
  93. ret: Dict[str, object] = {
  94. "is_root_operator": self.is_root_operator,
  95. "is_used_for_training": self.is_used_for_training,
  96. "include_all_overloads": self.include_all_overloads,
  97. }
  98. if self._debug_info is not None:
  99. ret["debug_info"] = self._debug_info
  100. return ret
  101. def merge_debug_info(
  102. lhs: Optional[Tuple[str, ...]],
  103. rhs: Optional[Tuple[str, ...]],
  104. ) -> Optional[Tuple[str, ...]]:
  105. # Ensure that when merging, each entry shows up just once.
  106. if lhs is None and rhs is None:
  107. return None
  108. return tuple(set((lhs or ()) + (rhs or ())))
  109. def combine_operators(
  110. lhs: "SelectiveBuildOperator", rhs: "SelectiveBuildOperator"
  111. ) -> "SelectiveBuildOperator":
  112. if str(lhs.name) != str(rhs.name):
  113. raise Exception(
  114. "Expected both arguments to have the same name, but got '{}' and '{}' instead".format(
  115. str(lhs.name),
  116. str(rhs.name),
  117. )
  118. )
  119. return SelectiveBuildOperator(
  120. name=lhs.name,
  121. # Consider this operator to be a root operator if it is a
  122. # root operator in any of the models used in this instance of
  123. # the pytorch library.
  124. is_root_operator=lhs.is_root_operator or rhs.is_root_operator,
  125. # Consider this operator to be a training operator if it is
  126. # an operator used for training in any of the models used
  127. # in this instance of the pytorch library.
  128. is_used_for_training=lhs.is_used_for_training or rhs.is_used_for_training,
  129. include_all_overloads=lhs.include_all_overloads or rhs.include_all_overloads,
  130. _debug_info=merge_debug_info(lhs._debug_info, rhs._debug_info),
  131. )
  132. def merge_operator_dicts(
  133. lhs: Dict[str, SelectiveBuildOperator],
  134. rhs: Dict[str, SelectiveBuildOperator],
  135. ) -> Dict[str, SelectiveBuildOperator]:
  136. operators: Dict[str, SelectiveBuildOperator] = {}
  137. for (op_name, op) in list(lhs.items()) + list(rhs.items()):
  138. new_op = op
  139. if op_name in operators:
  140. new_op = combine_operators(operators[op_name], op)
  141. operators[op_name] = new_op
  142. return operators
  143. def strip_operator_overload_name(op_name: str) -> str:
  144. return op_name.split(".")[0]