Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions infinimetrics/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class OperatorConfig:
OUTPUTS = "outputs"
ATTRIBUTES = "attributes"
TOLERANCE = "tolerance"
WARMUP_ITERATIONS = "warmup_iterations"
MEASURED_ITERATIONS = "measured_iterations"
INFINICORE_OP = "infinicore_op"
TORCH_OP = "torch_op"

Expand All @@ -142,6 +144,46 @@ class TensorSpec:
INIT_MODE = "init_mode"


class AttributeSpec:
"""Operator attribute field names."""

NAME = "name"
VALUE = "value"


class MetricSpec:
"""Metric result field names."""

NAME = "name"
VALUE = "value"
TYPE = "type"
RAW_DATA_URL = "raw_data_url"
UNIT = "unit"


class MetricType:
"""Metric value representations."""

SCALAR = "scalar"


class OperatorMetric:
"""Metric names emitted by operator adapters."""

LATENCY = "operator.latency"
ACCURACY = "operator.tensor_accuracy"
FLOPS = "operator.flops"
BANDWIDTH = "operator.bandwidth"


class BandwidthField:
"""Memory bandwidth calculation field names."""

READ_BYTES = "read_bytes"
WRITE_BYTES = "write_bytes"
TOTAL_BYTES = "total_bytes"


class InfiniCoreResult:
"""InfiniCore test result field names"""

Expand All @@ -165,6 +207,33 @@ class InfiniCoreResult:
DEFAULT_TOLERANCE = {"atol": 1e-3, "rtol": 1e-3}


# InfiniOps runtime mappings. PyTorch dtype objects intentionally remain in
# the adapter so importing common constants does not import PyTorch.
INFINIOPS_PLATFORM_TO_TORCH_DEVICE = {
"nvidia": "cuda",
"metax": "cuda",
"iluvatar": "cuda",
"hygon": "cuda",
"moore": "musa",
"cambricon": "mlu",
"ascend": "npu",
"cpu": "cpu",
}

INFINIOPS_DEVICE_PLUGIN_MODULES = {
"mlu": "torch_mlu",
"npu": "torch_npu",
"musa": "torch_musa",
}

INFINIOPS_STREAM_ACCESSORS = {
"npu": ("npu", "npu_stream"),
"cuda": ("cuda", "cuda_stream"),
"mlu": ("mlu", "mlu_stream"),
"musa": ("musa", "musa_stream"),
}


# ============================================================
# Hardware Test Adapter Constants
# ============================================================
Expand Down
8 changes: 8 additions & 0 deletions infinimetrics/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# test_type must use TestCategory enum (not string literals)
_ADAPTER_REGISTRY = {
(TestCategory.OPERATOR, "infinicore"): lambda: _create_infinicore_adapter(),
(TestCategory.OPERATOR, "infiniops"): lambda: _create_infiniops_adapter(),
(TestCategory.HARDWARE, "cudaunified"): lambda: _create_hardware_adapter(),
(TestCategory.COMM, "nccltest"): lambda: _create_nccltests_adapter(),
(TestCategory.INFER, "infinilm"): lambda: _create_inference_adapter(),
Expand All @@ -40,6 +41,13 @@ def _create_infinicore_adapter():
return InfiniCoreAdapter()


def _create_infiniops_adapter():
"""Create InfiniOps adapter (lazy import)."""
from infinimetrics.operators.infiniops_adapter import InfiniOpsAdapter

return InfiniOpsAdapter()


def _create_nccltests_adapter():
"""Create NCCL communication adapter."""
from infinimetrics.communication.nccl_adapter import NcclTestsAdapter
Expand Down
7 changes: 7 additions & 0 deletions infinimetrics/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ def execute(self) -> TestResult:
test_result.result_file = result_file
test_result.duration = time.time() - start_time

# Extract result_code and error_msg from adapter response
if isinstance(response, dict):
if "result_code" in response:
test_result.result_code = response["result_code"]
if "error_msg" in response:
test_result.error_msg = response["error_msg"]

logger.info(
f"Executor: {self.testcase} completed in {test_result.duration:.2f}s"
)
Expand Down
15 changes: 14 additions & 1 deletion infinimetrics/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,23 @@
FLOPSCalculator,
calculate_bandwidth,
)
from infinimetrics.operators.infinicore_adapter import InfiniCoreAdapter

__all__ = [
"FLOPSCalculator",
"calculate_bandwidth",
"InfiniCoreAdapter",
"InfiniOpsAdapter",
]


def __getattr__(name):
"""Load adapters only when callers explicitly request them."""
if name == "InfiniCoreAdapter":
from infinimetrics.operators.infinicore_adapter import InfiniCoreAdapter

return InfiniCoreAdapter
if name == "InfiniOpsAdapter":
from infinimetrics.operators.infiniops_adapter import InfiniOpsAdapter

return InfiniOpsAdapter
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
56 changes: 41 additions & 15 deletions infinimetrics/operators/flops_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

from typing import Dict, List, Optional, Callable

from infinimetrics.common.constants import DTYPE_BYTES_MAP
from infinimetrics.common.constants import (
BandwidthField,
DTYPE_BYTES_MAP,
TensorSpec,
)


class FLOPSCalculator:
Expand Down Expand Up @@ -103,22 +107,22 @@ def get_flops(
@staticmethod
def _get_tensor_size(tensor: Dict) -> int:
"""Get total number of elements in tensor"""
shape = tensor.get("shape", [])
shape = tensor.get(TensorSpec.SHAPE, [])
size = 1
for dim in shape:
size *= dim
return size


# Register matrix operations
@FLOPSCalculator.register(["matmul", "bmm", "batchmm"])
@FLOPSCalculator.register(["matmul", "mm", "bmm", "batchmm"])
def _matmul_flops(inputs: List[Dict], outputs: List[Dict]) -> float:
"""Matrix Multiplication: C = A @ B (FLOPS = 2 * M * N * K)"""
if len(inputs) < 2:
return 0.0

a_shape = inputs[0].get("shape", [])
b_shape = inputs[1].get("shape", [])
a_shape = inputs[0].get(TensorSpec.SHAPE, [])
b_shape = inputs[1].get(TensorSpec.SHAPE, [])

if len(a_shape) == 2 and len(b_shape) == 2:
m, k = a_shape
Expand All @@ -135,14 +139,14 @@ def _matmul_flops(inputs: List[Dict], outputs: List[Dict]) -> float:
return 0.0


@FLOPSCalculator.register(["addmm", "linear"])
@FLOPSCalculator.register(["addmm"])
def _addmm_flops(inputs: List[Dict], outputs: List[Dict]) -> float:
"""AddMM: C = beta * bias + alpha * (input @ weight)"""
if len(inputs) < 3:
return 0.0

input_shape = inputs[1].get("shape", [])
weight_shape = inputs[2].get("shape", [])
input_shape = inputs[1].get(TensorSpec.SHAPE, [])
weight_shape = inputs[2].get(TensorSpec.SHAPE, [])

if len(input_shape) >= 2 and len(weight_shape) >= 2:
m, k = input_shape[-2], input_shape[-1]
Expand All @@ -158,6 +162,28 @@ def _addmm_flops(inputs: List[Dict], outputs: List[Dict]) -> float:
return 0.0


@FLOPSCalculator.register(["linear"])
def _linear_flops(inputs: List[Dict], outputs: List[Dict]) -> float:
"""Linear: output = input @ weight + bias."""
if len(inputs) < 2:
return 0.0

input_shape = inputs[0].get(TensorSpec.SHAPE, [])
weight_shape = inputs[1].get(TensorSpec.SHAPE, [])
output_shape = outputs[0].get(TensorSpec.SHAPE, []) if outputs else []
if len(input_shape) < 2 or len(weight_shape) < 2 or not output_shape:
return 0.0

k = input_shape[-1]
n = output_shape[-1]
batch = 1
for dim in input_shape[:-1]:
batch *= dim

bias_flops = batch * n if len(inputs) >= 3 else 0
return 2.0 * batch * n * k + bias_flops


@FLOPSCalculator.register(["conv2d", "conv2d_backward"])
def _conv2d_flops(inputs: List[Dict], outputs: List[Dict]) -> float:
"""
Expand All @@ -170,11 +196,11 @@ def _conv2d_flops(inputs: List[Dict], outputs: List[Dict]) -> float:
return 0.0

# Input: [N, C_in, H_in, W_in]
input_shape = inputs[0].get("shape", [])
input_shape = inputs[0].get(TensorSpec.SHAPE, [])
# Weight: [C_out, C_in, K_h, K_w]
weight_shape = inputs[1].get("shape", [])
weight_shape = inputs[1].get(TensorSpec.SHAPE, [])
# Output: [N, C_out, H_out, W_out]
output_shape = outputs[0].get("shape", [])
output_shape = outputs[0].get(TensorSpec.SHAPE, [])

if len(input_shape) != 4 or len(weight_shape) != 4 or len(output_shape) != 4:
return 0.0
Expand Down Expand Up @@ -214,7 +240,7 @@ def calculate_bandwidth(
"""

def get_tensor_bytes(tensor: Dict) -> int:
dtype = tensor.get("dtype", "float32").lower()
dtype = tensor.get(TensorSpec.DTYPE, "float32").lower()
bytes_per_element = DTYPE_BYTES_MAP.get(dtype, 4)
size = FLOPSCalculator._get_tensor_size(tensor)
return size * bytes_per_element
Expand All @@ -223,7 +249,7 @@ def get_tensor_bytes(tensor: Dict) -> int:
write_bytes = sum(get_tensor_bytes(out) for out in outputs)

return {
"read_bytes": read_bytes,
"write_bytes": write_bytes,
"total_bytes": read_bytes + write_bytes,
BandwidthField.READ_BYTES: read_bytes,
BandwidthField.WRITE_BYTES: write_bytes,
BandwidthField.TOTAL_BYTES: read_bytes + write_bytes,
}
Loading
Loading