Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/Tensile/SolutionStructs/Problem.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1447 lines
60 KiB
Python

################################################################################
#
# Copyright (C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
################################################################################
from rocisa.enum import DataTypeEnum
from collections import OrderedDict
from collections.abc import Mapping
from typing import List
from Tensile.Activation import ActivationType
from Tensile.Common import fastdeepcopy as deepcopy
from Tensile.Common.Constants import INDEX_CHARS
from Tensile.Common.DataType import DataType
from Tensile.Common.Utilities import assignParameterWithDefault, printWarning, print2, printExit
class ProblemSizeRange:
def __init__(self, problemType, config):
self.totalIndices = 1+max(problemType["IndexAssignmentsA"]) + problemType["NumIndicesLD"]
if len(config) < self.totalIndices:
for i in range(len(config), self.totalIndices):
if i < self.totalIndices - problemType["NumIndicesLD"]:
config.append(0)
else:
config.append([0])
self.indexMax = []
self.indexIsSized = []
self.indicesSized = []
self.indicesMapped = []
for i in range(0, self.totalIndices):
dim = deepcopy(config[i])
if isinstance(dim, list):
if len(dim) == 1:
self.indicesSized.append([dim[0], 1, 0, dim[0]])
elif len(dim) == 2:
self.indicesSized.append([dim[0], dim[0], 0, dim[1]])
elif len(dim) == 3:
self.indicesSized.append([dim[0], dim[1], 0, dim[2]])
elif len(dim) == 4:
self.indicesSized.append([dim[0], dim[1], dim[2], dim[3]])
else:
printExit("dimension[%u] config (%s) has %u descriptors rather than 1-4."
% ( i, dim, len(dim) ))
self.indexIsSized.append(True)
self.indexMax.append(self.indicesSized[len(self.indicesSized)-1][3])
elif isinstance(dim, int):
self.indicesMapped.append(dim)
self.indexIsSized.append(False)
self.indexMax.append(self.indicesSized[self.indicesMapped[ \
len(self.indicesMapped)-1]][3])
# max num elements in each tensor
self.maxNumElements = [ 1, 1, 1 ]
for i in range(0, problemType["NumIndicesC"]):
self.maxNumElements[0] *= self.indexMax[i]
for i in problemType["IndexAssignmentsA"]:
self.maxNumElements[1] *= self.indexMax[i]
for i in problemType["IndexAssignmentsB"]:
self.maxNumElements[2] *= self.indexMax[i]
self.totalProblemSizes = 1
self.numProblemSizes = [] # per index
self.problemSizeToIndex = []
self.problemIndexToSize = []
sizedIdx = 0
for i in range(0, len(self.indexIsSized)):
self.problemSizeToIndex.append({})
self.problemIndexToSize.append({})
if self.indexIsSized[i]:
self.numProblemSizes.append(0)
index = self.indicesSized[sizedIdx]
sizedIdx += 1
currentSize = index[0]
currentIncrement = index[1]
while currentSize <= index[3]:
currentSize += currentIncrement
currentIncrement += index[2]
self.numProblemSizes[i] += 1
else:
self.numProblemSizes.append(1)
self.totalProblemSizes *= self.numProblemSizes[i]
########################################
# enumerate problem sizes
currentSizedIndexSizes = []
currentSizedIndexIncrements = []
for i in range(0, len(self.indicesSized)):
currentSizedIndexSizes.append(self.indicesSized[i][0])
currentSizedIndexIncrements.append(self.indicesSized[i][1])
# iterate over all problem sizes
self.problemSizes = []
moreProblemSizes = True
problemIdx = 0
problemSize = [0]*self.totalIndices
while moreProblemSizes:
#/ convert current sized and mapped indices to full sizes
currentSizedIdx = 0
currentMappedIdx = 0
for i in range(0, self.totalIndices):
if self.indexIsSized[i]:
problemSize[i] = currentSizedIndexSizes[currentSizedIdx]
currentSizedIdx+=1
else:
problemSize[i] = problemSize[self.indicesMapped[currentMappedIdx]]
currentMappedIdx+=1
self.problemSizes.append(tuple(problemSize))
#/ increment sizes for next benchmark
currentSizedIndexSizes[0] += currentSizedIndexIncrements[0]
currentSizedIndexIncrements[0] += self.indicesSized[0][2]
for i in range(1, len(self.indicesSized)+1):
# if prior index past max, reset to min and increment next index
if currentSizedIndexSizes[i-1] > self.indicesSized[i-1][3]:
#/ reset prior index
currentSizedIndexSizes[i-1] = self.indicesSized[i-1][0]
currentSizedIndexIncrements[i-1] = self.indicesSized[i-1][1]
# increment next index
if i >= len(self.indicesSized):
moreProblemSizes = False
else:
currentSizedIndexSizes[i] += currentSizedIndexIncrements[i]
currentSizedIndexIncrements[i] += self.indicesSized[i][2]
problemIdx+=1
########################################
# YAML format
def __str__(self):
state = "[ "
sizedIdx = 0
mappedIdx = 0
for i in range(0, len(self.indexIsSized)):
if self.indexIsSized[i]:
indices = self.indicesSized[sizedIdx]
state += "[ %u, %u, %u, %u ]" \
% (indices[0], indices[1], indices[2], indices[3])
sizedIdx += 1
else:
indices = self.indicesSized[self.indicesMapped[mappedIdx]]
state += str(self.indicesMapped[mappedIdx])
mappedIdx += 1
if i < len(self.indexIsSized)-1:
state += ", "
state += " ]"
return state
class Problem:
""" Problem sizes, strides, padding and other info"""
def __init__(self, sizes=None, stridesA=None, stridesB=None, stridesC=None, stridesD=None, stridesGate=None, count=None):
self.sizes = tuple(sizes) if sizes else None
self.stridesA = tuple(stridesA) if stridesA else None
self.stridesB = tuple(stridesB) if stridesB else None
self.stridesC = tuple(stridesC) if stridesC else None
self.stridesD = tuple(stridesD) if stridesD else None
self.stridesGate = tuple(stridesGate) if stridesGate else None
self.count = count
def __str__(self):
rv= "{ sizes:" + str(list(self.sizes))
if self.stridesA:
rv += ", stridesA:" + str(list(self.stridesA))
if self.stridesB:
rv += ", stridesB:" + str(list(self.stridesB))
if self.stridesC:
rv += ", stridesC:" + str(list(self.stridesC))
if self.stridesD:
rv += ", stridesD:" + str(list(self.stridesD))
if self.stridesGate:
rv += ", stridesGate:" + str(list(self.stridesGate))
rv += " }"
return rv
class ExactList(Problem):
def __init__(self, e, problemType):
if len(e) == problemType["TotalIndices"]:
if -1 in e:
printExit("ExactSize %s contains -1" % (e))
if problemType["OperationType"] == "GEMM":
e += [-1, -1, -1, -1]
e = ExactList.convertLeadingDims(problemType, tuple(e))
sizes=e
elif len(e) == (problemType["TotalIndices"] + problemType["NumIndicesLD"]):
sizes = ExactList.convertLeadingDims(problemType, tuple(e))
else:
printExit("ExactSize %s doesn't match indices of ProblemType %s, totalIndices=%d, len e=%d, NumIndicesLD = %d" \
% (e, problemType, problemType["TotalIndices"], len(e), problemType["NumIndicesLD"]) )
# TODO- pass strides here, remove calls to convertLeadingDims
Problem.__init__(self, sizes=sizes)
def __str__(self):
return str(list(self.sizes))
@staticmethod
def convertLeadingDims(problemType, problemSize, stridesA = None, stridesB = None, stridesC = None, stridesD = None):
# FIXME-problem: refactor to eliminate max, pass strides in strideB parm rather than hacked
# onto the end of the sizes list
predStridesD = stridesD is not None and stridesD[1] != -1
predStridesC = stridesC is not None and stridesC[1] != -1
predStridesA = stridesA is not None and stridesA[1] != -1
predStridesB = stridesB is not None and stridesB[1] != -1
return problemSize[:problemType["NumIndicesC"]+1] + \
(max(problemSize[0], problemSize[problemType["IndexAssignmentsLD"][0]]) if not predStridesD else stridesD[1], ) + \
(max(problemSize[0], problemSize[problemType["IndexAssignmentsLD"][1]]) if not predStridesC else stridesC[1], ) + \
(max(problemSize[problemType["IndexAssignmentsLD"][2]],
problemSize[problemType["IndexAssignmentsA"][0]]) if not predStridesA else stridesA[1], ) + \
(max(problemSize[problemType["IndexAssignmentsLD"][3]],
problemSize[problemType["IndexAssignmentsB"][0]]) if not predStridesB else stridesB[1], )
class ExactDict(Problem):
AllowedFields = [ 'count', 'sizes', 'stridesA', 'stridesB', 'stridesC', 'stridesD', 'stridesGate' ]
def __init__(self, e, problemType):
Problem.__init__(self)
for f in e:
if f in ExactDict.AllowedFields:
setattr(self, f, e[f])
else:
raise RuntimeError ("specified field '%s' is not a valid Exact dict field"%f)
if problemType:
if "OperationType" in problemType and problemType["OperationType"] == "GEMM":
sizesTuple = tuple(self.sizes + [-1, -1, -1, -1])
self.sizes = ExactList.convertLeadingDims(problemType, sizesTuple, self.stridesA, self.stridesB, self.stridesC, self.stridesD)
if problemType:
if "OperationType" in problemType and problemType["OperationType"] == "GEMM":
if len(self.sizes) != (problemType["TotalIndices"] + problemType["NumIndicesLD"]):
# FIXME-ExactDict size descriptor still (but preferrably not so) uses 8-tuple for GEMM problems
raise RuntimeError ("specified size=%s does not have enough indices for problem (expected %d, got %d)" \
% (self.sizes, problemType["TotalIndices"]+problemType["NumIndicesLD"], len(self.sizes)))
elif len(self.sizes) != problemType["TotalIndices"]:
raise RuntimeError ("specified size=%s does not have enough indices for problem (expected %d, got %d)" \
% (self.sizes, problemType["TotalIndices"], len(self.sizes)))
################################################################################
# ProblemSizes
################################################################################
"""
Adapter class for class `ProblemSizes`. It satisfies the implicit usage requirement
of ClientWriter.writeClientConfig() by converting ExactLogic to list of `Problem` objects
"""
class ProblemSizesMock:
def __init__(self, exactLogic):
self.problems = [Problem(problem) for problem, solution in exactLogic]
class ProblemSizesMockDummy:
def __init__(self):
self.problems = [Problem(sizes=[128, 128, 1, 512])]
class ProblemSizes:
########################################
def __init__(self, problemType, config):
self.problemType = problemType
self.ranges = []
self.exacts = []
self.minStrides = None
if config:
for dictionary in config:
for sizeTypeKey in dictionary:
#print ("PROBLEM parsed:", sizeTypeKey, dictionary[sizeTypeKey])
if sizeTypeKey == "Range":
psr = ProblemSizeRange(problemType, dictionary[sizeTypeKey])
self.ranges.append( psr )
elif sizeTypeKey == "Exact":
e= dictionary[sizeTypeKey]
if isinstance(e,list):
self.exacts.append(ExactList(e, problemType))
elif isinstance(e,dict):
self.exacts.append(ExactDict(e, problemType))
else:
printExit("Unsupported Exact type==%s"%type(e))
elif sizeTypeKey == "MinStride":
e = dictionary[sizeTypeKey]
if len(e) != problemType["TotalIndices"]:
printExit("MinStride %s doesn't match indices of ProblemType %s" \
% (e, problemType) )
if self.minStrides:
printExit("Only one MinStride command is allowed in a ProblemsSizes definition. Previous minStrides:%s, New minstride:%s" \
% (self.minStrides, e) )
self.minStrides=(tuple(e))
else:
printExit("ProblemSize Type %s not supported"%sizeTypeKey)
if not self.minStrides:
# set harmless default mins of 0
self.minStrides = ([0]* problemType["TotalIndices"])
# not the ideal spot, but convert leading dims that are below the minimum size
if problemType["OperationType"] == "GEMM":
for i in range(0, len(self.ranges)):
self.ranges[i].problemSizes[:] = \
[ExactList.convertLeadingDims(self.problemType, problemSize) for problemSize in self.ranges[i].problemSizes]
self.problems = OrderedDict()
for sizeRange in self.ranges:
for rangeSize in sizeRange.problemSizes:
self.problems.update({Problem(rangeSize) : 1})
for e in self.exacts:
self.problems.update({e : 1})
self.problems = list(self.problems.keys())
self.totalProblemSizes = len(self.problems)
# max sizes
self.maxD = 0
self.maxC = 0
self.maxA = 0
self.maxB = 0
for problem in self.problems:
problemSize = problem.sizes # FIXME-problem. This should use problem.strides*
sizeLdd = problemSize[self.problemType["IndexAssignmentsLD"][0]] if problemType["OperationType"] == "GEMM" else problemSize[0]
sizeD = max(self.minStrides[0], sizeLdd)
for i in range(1, problemType["NumIndicesC"]):
sizeD *= max(self.minStrides[i], problemSize[i])
sizeLdc = problemSize[self.problemType["IndexAssignmentsLD"][1]] if problemType["OperationType"] == "GEMM" else problemSize[0]
sizeC = max(self.minStrides[0], sizeLdc)
for i in range(1, problemType["NumIndicesC"]):
sizeC *= max(self.minStrides[i], problemSize[i])
sizeLda = problemSize[self.problemType["IndexAssignmentsLD"][2]] \
if problemType["OperationType"] == "GEMM" \
else problemSize[self.problemType["IndexAssignmentsA"][0]]
sizeA = max(self.minStrides[self.problemType["IndexAssignmentsA"][0]], sizeLda)
for i in self.problemType["IndexAssignmentsA"][1:]:
sizeA *= max(self.minStrides[i], problemSize[i])
sizeLdb = problemSize[self.problemType["IndexAssignmentsLD"][3]] \
if problemType["OperationType"] == "GEMM" \
else problemSize[self.problemType["IndexAssignmentsB"][0]]
sizeB = max(self.minStrides[self.problemType["IndexAssignmentsB"][0]], sizeLdb)
for i in self.problemType["IndexAssignmentsB"][1:]:
sizeB *= max(self.minStrides[i], problemSize[i])
self.maxD = max(self.maxD, sizeD)
self.maxC = max(self.maxC, sizeC)
self.maxA = max(self.maxA, sizeA)
self.maxB = max(self.maxB, sizeB)
def __str__(self):
s = "ProblemSizes\n"
for sizeRange in self.ranges:
s += " %s" % sizeRange
return s
# Get real Dataype for A and B when using mix-types (i.g. Float8BFloat8);
def getRealDataTypeA(dataType):
if dataType.isFloat8BFloat8():
return DataType(DataTypeEnum.Float8)
elif dataType.isBFloat8Float8():
return DataType(DataTypeEnum.BFloat8)
elif dataType.isFloat8BFloat8_fnuz():
return DataType(DataTypeEnum.Float8_fnuz)
elif dataType.isBFloat8Float8_fnuz():
return DataType(DataTypeEnum.BFloat8_fnuz)
else:
return dataType
def getRealDataTypeB(dataType):
if dataType.isFloat8BFloat8():
return DataType(DataTypeEnum.BFloat8)
elif dataType.isBFloat8Float8():
return DataType(DataTypeEnum.Float8)
elif dataType.isFloat8BFloat8_fnuz():
return DataType(DataTypeEnum.BFloat8_fnuz)
elif dataType.isBFloat8Float8_fnuz():
return DataType(DataTypeEnum.Float8_fnuz)
else:
return dataType
################################################################################
# ProblemType
# name of solution should begin with name of problemType, and arguments can be listed out explicitly
################################################################################
# Default Problem Type
################################################################################
_defaultProblemType = {
# =GEMM uses TransposeA,B parameters and makes the problem type more readable for users
# =TensorContraction requires specifying
"OperationType": "GEMM", # GEMM, TensorContraction, ConvolutionForward, ConvolutionBackwardData, ConvolutionBackwardWeights
"DataType": 0, # data types can specified by a variety of ways, such as "s", as listed in SolutionStructs.py::DataType
"DataTypeA": 0, # A data type can specified by a variety of ways, such as "s", as listed in SolutionStructs.py::DataType
"DataTypeB": 0, # B data type can specified by a variety of ways, such as "s", as listed in SolutionStructs.py::DataType
"MacDataTypeA": 0, # A data type which is used for mac/mfma/wmma computation.
"MacDataTypeB": 0, # B data type which is used for mac/mfma/wmma computation.
"DataTypeE": 0, # E data type can specified by a variety of ways, such as "s", as listed in SolutionStructs.py::DataType
"DataTypeAmaxD": 0, # AmaxD data type can specified by a variety of ways, such as "s", as listed in SolutionStructs.py::DataType
"DestDataType": 0, # destination data types can specified by a variety of ways, such as "s", as listed in SolutionStructs.py::DataType
"ComputeDataType": 0, # compute data types can specified by a variety of ways, such as "s", as listed in SolutionStructs.py::DataType
"F32XdlMathOp": 0, # reducing intermediate precision from f32 to a specific type, such as "x", as listed in SolutionStructs.py::DataType.
# in:f32, intermediate:xf32, out:f32. f32 = xf32(f32) * xf32(f32)
"UseBeta": True, # =True use beta parameter (asm will check for B=0 and optimize the write for that), =False don't use beta parameter
"UseE": False, # =True use output E to output gemm results before activation
"Gradient": False, # =True set globalWriteElements to gradient mode
"UseBias": 0, # =1 support bias vector on M direction, =2 support bias vector on N direction, =3 support bias vector on both M,N direction
"UseGateResidual": False, # =True apply gate residual: D = gate * spmm_result + gate
"BiasSrc": "D", # This parameter is used in gradient + bias. Support A, B, D.
"UseScaleAB": "", # Support "", "Scalar", and "Vector"
"UseScaleCD": False, # =True use scaleC, scaleD
"UseScaleAlphaVec": 0, # =1 support alpha vector on M direction, =2 support bias vector on N direction, =3 support alpha vector on both M,N direction
"HighPrecisionAccumulate": False, # f32 += f16*f16
"SilentHighPrecisionAccumulate": False, # Keep kernel names the same for HPA mode. Useful for testing.
"Sparse": 0, # 4:2 Structured Sparse A Matrix, 0=Non Sparse, 1=Sparse Matrix A, 2=Sparse Matrix B
"ComplexConjugateA": False, # complex data should be conjugated for "C" transpose case
"ComplexConjugateB": False,
"StochasticRounding": False, # By default, IEEE RNE rounding
# for OperationType == GEMM
"TransposeA": False, # =True means transA="T" or "C", =False means transA = "N"
"TransposeB": True,
"Batched": False, # add batching dimension
"StridedBatched": True, # use to select general batch or strided batch
"GroupedGemm": False, # use to select general batch or strided batch
# for OperationType == TensorContraction
# - Indices < NumIndicesC are Free or Batch indices and appear in C and D
# - Indices which appear in both A and B, and are < NumIndicesC are batch. A and B must have same number of batch indices.
# - Indices which appear in both A and B, and are >= NumIndicesC are summation. A and B must have same number of summation indices.
# - Indices which appear in A or B (but not both), are Free. A and B may have different numbers of free indices.
# - Summation loops are nested from smallest index number to largest, with the largest summation index as the 'unroll' loop.
# - Memory order of C and D matrices is always 0..NumIndicesC-1, with 0 as the fastest-moving.
# - By choosing index assignments the output can be 'transposed'. For example if IA=[1,2] IB=[0,2] then 0 is the coalesced dim for C/D.
# - Likewise batch index may be assigned between two free indices to control the output order, ie to write in CNHW format.
# - For example : IA=[0,1,3] IB=[2,1,3]. 0,2 are free indices; 1 is batch.
"IndexAssignmentsA": [0, 2],
"IndexAssignmentsB": [1, 2],
"NumIndicesC": 2,
# use initial strides for AB.
# This has some performance impact for the increased flexibility:
# - Additional strides will be passed into the kernel and will occupy SGPR registers
# - GlobalReadWidth must be 1 (since elements are not guaranteed to be adjacent in memory)
"UseInitialStridesAB": False,
# use initial strides for CD.
# This has some performance impact for the increased flexibility:
# - Additional strides will be passed into the kernel and will occupy SGPR registers
# - Additional multiply on the store address path
# -VectorStore must be 0. If VectorStore is -1, it will be silently set to 0 internally.
"UseInitialStridesCD": False,
"AllowNoFreeDims": False, # allow A or B to specify no free dims
# (if false, A and B must have at least one free dim)
# (if true, A and B must have at least one free or batch dim)
# SetConstStride* sets the specified stride in the problem.
# These no longer generate predicates - see AssertStrideEqualA/B below
# List of pairs of [index, constValue].
# Index is a member of the global index assignments (not an offset into IndexAssignmentsA/B)
# EX: SetConstStrideA: [ [3, 1], [2, 4] ] sets
# strideA for index3 to constant '1' and stride for index2 to constant '4'.
"SetConstStrideA": [],
"SetConstStrideB": [],
"SetConstStrideBias": [],
"SetConstStrideGate": [],
# Summation dimension indices
"MirrorDimsA": [],
"MirrorDimsMXSA": [],
"MirrorDimsB": [],
"MirrorDimsMXSB": [],
"MirrorDimsMetadata": [],
# for LD description
"NumIndicesLD": 4,
"IndexAssignmentsLD": [3, 4, 5, 6], # order is LDD, LDC, LDA, LDB
# Tile aware solution selection
"TileAwareSelection": False,
# Activation
"Activation": False,
"ActivationNoGuard": False,
# AmaxD
"OutputAmaxD": False,
# For kernels putting arguments in workspaces instead of kernel arguments, they can choose to support user arguments input instead.
"SupportUserArgs": True,
"SwizzleTensorA": False,
"SwizzleTensorB": False,
"isMixMode": False, # True means this is a mix-mode problem, i.e. Float8BFloat6 or BFloat6Float8
"MetadataLayout": 0,
# MX Block
"MXBlockA": 0,
"MXBlockB": 0,
"DataTypeMXSA": "E8",
"DataTypeMXSB": "E8",
}
# The supported typed GEMM, each entry is (Ti, To, Tc).
# DataType (Ti) = The data-type of the input matrices: A/B
# DestDataType (To) = The data-type of the output matrices: C/D
# ComputeDataType (Tc) = The data-type of computation: alpha/beta:
# Cinternal: basically should == ComputeDataType
# This is used in _checkIfSupportedGEMMType()
_validGEMMTypes = [
("H", "H", "H", "H"),
("S", "S", "S", "S"),
("D", "D", "D", "D"),
("C", "C", "C", "C"),
("Z", "Z", "Z", "Z"),
("H", "H", "H", "S"),
("H", "H", "S", "S"),
("B", "B", "B", "S"),
("B", "B", "S", "S"),
("B", "B", "H", "S"),
("I8", "I8", "I", "I"),
("4xi8", "4xi8", "I", "I"),
("I8", "I8", "I8", "I"),
("I8", "I8", "I", "S"),
("I8", "I8", "I8", "S"),
("I8", "I8", "H", "S"),
("I8", "I8", "B", "S"),
("F8", "F8", "S", "S"),
("B8", "B8", "S", "S"),
("F8", "B8", "S", "S"),
("B8", "F8", "S", "S"),
("F8", "F8", "H", "S"),
("B8", "B8", "H", "S"),
("F8", "B8", "H", "S"),
("B8", "F8", "H", "S"),
("B8", "B8", "B", "S"),
("H", "H", "F8", "S"),
("F8", "F8", "B", "S"),
("F8", "B8", "B", "S"),
("B8", "F8", "B", "S"), # in/out are both R8
("F8", "F8", "F8", "S"),
("B8", "B8", "B8", "S"),
("F8", "B8", "B8", "S"),
("B8", "F8", "B8", "S"),
("F8", "F8", "B8", "S"),
("B8", "B8", "F8", "S"),
("F8", "B8", "F8", "S"),
("B8", "F8", "F8", "S"), # F8 NANOO
("F8N", "F8N", "S", "S"),
("B8N", "B8N", "S", "S"),
("F8N", "B8N", "S", "S"),
("B8N", "F8N", "S", "S"),
("F8N", "F8N", "H", "S"),
("B8N", "B8N", "H", "S"),
("F8N", "B8N", "H", "S"),
("B8N", "F8N", "H", "S"),
("B8N", "B8N", "B", "S"),
("H", "H", "F8N", "S"),
("F8N", "F8N", "B", "S"),
("F8N", "B8N", "B", "S"),
("B8N", "F8N", "B", "S"), # in/out are both R8
("F8N", "F8N", "F8N", "S"),
("B8N", "B8N", "B8N", "S"),
("F8N", "B8N", "B8N", "S"),
("B8N", "F8N", "B8N", "S"),
("F8N", "F8N", "B8N", "S"),
("B8N", "B8N", "F8N", "S"),
("F8N", "B8N", "F8N", "S"),
("B8N", "F8N", "F8N", "S"),
("F6", "F6", "S", "S"),
("B6", "B6", "S", "S"),
("F6", "B6", "S", "S"),
("B6", "F6", "S", "S"),
("F8", "F6", "S", "S"),
("F6", "F8", "S", "S"),
("F8", "F4", "S", "S"),
("F4", "F8", "S", "S"),
("F6", "F4", "S", "S"),
("F4", "F6", "S", "S"),
("F6", "F4", "B", "S"),
("F4", "F6", "B", "S"),
("B6", "F4", "S", "S"),
("F4", "B6", "S", "S"),
("B6", "F4", "B", "S"),
("F4", "B6", "B", "S"),
("F8", "B6", "S", "S"),
("B6", "F8", "S", "S"),
("F6", "B8", "S", "S"),
("B8", "F6", "S", "S"),
("F4", "B8", "S", "S"),
("B8", "F4", "S", "S"),
("F4", "F4", "S", "S"),
("F4", "F4", "H", "S"),
("F4", "F4", "B", "S"),
("F4", "F4", "F8", "S"),
("F6", "F6", "H", "S"),
("F6", "F6", "B", "S"),
("F6", "F6", "F8", "S"),
("F6", "F6", "B8", "S"),
("B6", "B6", "H", "S"),
("B6", "B6", "B", "S"),
("B6", "B6", "F8", "S"),
("B6", "B6", "B8", "S"),
("F6", "B6", "H", "S"),
("F6", "B6", "B", "S"),
("F6", "B6", "F8", "S"),
("F6", "B6", "B8", "S"),
("B6", "F6", "H", "S"),
("B6", "F6", "B", "S"),
("B6", "F6", "F8", "S"),
("B6", "F6", "B8", "S"),
]
# All HPA types are listed here (HPA=T). The name of the library logic files for these types is:
# *_TiToTc_BH*.yaml where Ti, To, and Tc are the data types of A/B, C/D, and computation, respectively.
# The name of the library logic files for non-HPA (HPA=F) types is: *_TiB*.yaml.
_HPATypes = [
("H", "H", "S", "S"),
("H", "H", "H", "S"),
("B", "B", "B", "S"),
("B", "B", "S", "S"),
("B", "B", "H", "S"),
("I8", "I8", "I", "I"),
("4xi8", "4xi8", "I", "I"),
("I8", "I8", "I", "S"),
("I8", "I8", "I8", "S"),
("I8", "I8", "H", "S"),
("I8", "I8", "B", "S"),
("F8", "F8", "S", "S"),
("B8", "B8", "S", "S"),
("F8", "B8", "S", "S"),
("B8", "F8", "S", "S"),
("F8", "F8", "H", "S"),
("B8", "B8", "H", "S"),
("F8", "B8", "H", "S"),
("B8", "F8", "H", "S"),
("H", "H", "F8", "S"),
("F8", "F8", "B", "S"),
("F8", "B8", "B", "S"), # in/out are both R8
("F8", "F8", "F8", "S"),
("B8", "B8", "B8", "S"),
("F8", "B8", "B8", "S"),
("B8", "F8", "B8", "S"),
("F8", "F8", "B8", "S"),
("B8", "B8", "F8", "S"),
("F8", "B8", "F8", "S"),
("B8", "F8", "F8", "S"),
("F8N", "F8N", "S", "S"),
("B8N", "B8N", "S", "S"),
("F8N", "B8N", "S", "S"),
("B8N", "F8N", "S", "S"),
("F8N", "F8N", "H", "S"),
("B8N", "B8N", "H", "S"),
("F8N", "B8N", "H", "S"),
("B8N", "F8N", "H", "S"),
("H", "H", "F8N", "S"),
("F8N", "F8N", "B", "S"),
("F8N", "B8N", "B", "S"), # in/out are both R8
("F8N", "F8N", "F8N", "S"),
("B8N", "B8N", "B8N", "S"),
("F8N", "B8N", "B8N", "S"),
("B8N", "F8N", "B8N", "S"),
("F8N", "F8N", "B8N", "S"),
("B8N", "B8N", "F8N", "S"),
("F8N", "B8N", "F8N", "S"),
("B8N", "F8N", "F8N", "S"),
("F6", "F6", "S", "S"),
("B6", "B6", "S", "S"),
("F6", "B6", "S", "S"),
("B6", "F6", "S", "S"),
("F8", "F6", "S", "S"),
("F6", "F8", "S", "S"),
("F8", "F4", "S", "S"),
("F4", "F8", "S", "S"),
("F6", "F4", "S", "S"),
("F4", "F6", "S", "S"),
("F6", "F4", "B", "S"),
("F4", "F6", "B", "S"),
("B6", "F4", "S", "S"),
("F4", "B6", "S", "S"),
("B6", "F4", "B", "S"),
("F4", "B6", "B", "S"),
("F4", "F4", "S", "S"),
("F4", "F4", "H", "S"),
("F4", "F4", "B", "S"),
("F4", "F4", "F8", "S"),
("F6", "F6", "H", "S"),
("F6", "F6", "B", "S"),
("F6", "F6", "F8", "S"),
("F6", "F6", "B8", "S"),
("B6", "B6", "H", "S"),
("B6", "B6", "B", "S"),
("B6", "B6", "F8", "S"),
("B6", "B6", "B8", "S"),
("F6", "B6", "H", "S"),
("F6", "B6", "B", "S"),
("F6", "B6", "F8", "S"),
("F6", "B6", "B8", "S"),
("B6", "F6", "H", "S"),
("B6", "F6", "B", "S"),
("B6", "F6", "F8", "S"),
("B6", "F6", "B8", "S"),
]
def problemTypeToEnum(problemType):
problemType["DataType"] = \
problemType["DataType"].value
problemType["MacDataTypeA"] = \
problemType["MacDataTypeA"].value
problemType["MacDataTypeB"] = \
problemType["MacDataTypeB"].value
problemType["DataTypeA"] = \
problemType["DataTypeA"].value
problemType["DataTypeB"] = \
problemType["DataTypeB"].value
problemType["DataTypeE"] = \
problemType["DataTypeE"].value
problemType["DataTypeAmaxD"] = \
problemType["DataTypeAmaxD"].value
problemType["DestDataType"] = \
problemType["DestDataType"].value
problemType["ComputeDataType"] = \
problemType["ComputeDataType"].value
problemType["BiasDataTypeList"] = \
[btype.value for btype in problemType["BiasDataTypeList"]]
if "GateResidualDataTypeList" in problemType:
problemType["GateResidualDataTypeList"] = \
[gtype.value for gtype in problemType["GateResidualDataTypeList"]]
problemType["ActivationComputeDataType"] = \
problemType["ActivationComputeDataType"].value
problemType["ActivationType"] = \
problemType["ActivationType"].value
problemType["F32XdlMathOp"] = \
problemType["F32XdlMathOp"].value
if "DataTypeMetadata" in problemType:
problemType["DataTypeMetadata"] = \
problemType["DataTypeMetadata"].value
if "DataTypeMXSA" in problemType:
problemType["DataTypeMXSA"] = \
problemType["DataTypeMXSA"].value
else:
problemType["DataTypeMXSA"] = DataTypeEnum.E8
if "DataTypeMXSB" in problemType:
problemType["DataTypeMXSB"] = \
problemType["DataTypeMXSB"].value
else:
problemType["DataTypeMXSB"] = DataTypeEnum.E8
# Pre-compute expected types for ProblemType parameters from _defaultProblemType.
# Note: For _defaultProblemType, the values are defaults (not lists of allowed values),
# so we just take type(defaultValue) directly.
_expectedProblemTypeParamTypes = {
key: {type(value)} for key, value in _defaultProblemType.items()
}
def validateProblemTypeParameterTypes(state, srcFile="", *, raiseOnMismatch: bool = True,
keyPathPrefix: str = "ProblemType"):
"""Validate that every ProblemType parameter has the correct Python type.
Checks ProblemType parameters against ``_defaultProblemType``. ``bool``
where ``int`` is expected (or vice versa) is the canonical YAML
collapse this gate targets; ``type()`` (not ``isinstance``) keeps the
two distinct.
Two consumption modes:
- ``raiseOnMismatch=True`` (default, input-YAML path): raises a
:class:`ConfigTypeError` on the first mistyped key encountered.
- ``raiseOnMismatch=False`` (library-logic path): mismatches are only
appended to the module-level ``_typeMismatchCollector`` (reported
later by the aggregate create-library gate). Never raises here.
Args:
state: The ProblemType state dict (parameter name -> value).
srcFile: The YAML source file path, included in messages.
raiseOnMismatch: see above. Default True.
keyPathPrefix: prefix for the error keypath (default "ProblemType").
"""
# _skipTypeCheck lives in Common/ValidParameters (Common -> Solution
# import direction), but the type-mismatch collector still lives in
# Solution. Import the collector inside the function to avoid the
# historical Naming -> Problem -> Solution -> Naming circular dep.
from Tensile.SolutionStructs.Solution import _typeMismatchCollector
from Tensile.Common.ValidParameters import _skipTypeCheck
from Tensile.Common.TypeValidationErrors import (
ConfigTypeError, formatMismatch,
)
for key, value in state.items():
if key not in _expectedProblemTypeParamTypes or key in _skipTypeCheck:
continue
expectedTypes = _expectedProblemTypeParamTypes[key]
actualType = type(value)
# Use type() not isinstance() so bool/int are distinguished.
if actualType not in expectedTypes:
if raiseOnMismatch:
raise ConfigTypeError(formatMismatch(srcFile, f"{keyPathPrefix}.{key}", value, expectedTypes))
else:
expectedStr = " or ".join(sorted(t.__name__ for t in expectedTypes))
collectorKey = (key, actualType.__name__, expectedStr)
if collectorKey not in _typeMismatchCollector:
_typeMismatchCollector[collectorKey] = {
"count": 0,
"values": set(),
"files": set(),
}
entry = _typeMismatchCollector[collectorKey]
entry["count"] += 1
entry["values"].add(repr(value))
if srcFile:
entry["files"].add(srcFile)
class ProblemType(Mapping):
########################################
@classmethod
def FromDefaultConfig(printIndexAssignmentInfo: bool):
return ProblemType(_defaultProblemType, printIndexAssignmentInfo)
def __init__(
self,
config,
printIndexAssignmentInfo: bool,
srcFile: str = "",
*,
raiseOnTypeMismatch: bool = True,
):
self.state = {}
for key in _defaultProblemType:
assignParameterWithDefault(self.state, key, config, _defaultProblemType)
# Validate parameter types against the _defaultProblemType registry
validateProblemTypeParameterTypes(
self.state,
srcFile=srcFile,
raiseOnMismatch=raiseOnTypeMismatch,
)
# adjusting all data types
if "DataType" in config:
self["DataType"] = DataType(config["DataType"])
self["DataTypeA"] = self["DataType"]
self["DataTypeB"] = self["DataType"]
self["MacDataTypeA"] = self["DataType"]
self["MacDataTypeB"] = self["DataType"]
else:
raise Exception("NO data type specified")
self["DataType"] = DataType(0)
self["DataTypeA"] = DataType(0)
self["DataTypeB"] = DataType(0)
self["MacDataTypeA"] = DataType(0)
self["MacDataTypeB"] = DataType(0)
if "MacDataTypeA" in config:
self["MacDataTypeA"] = DataType(config["MacDataTypeA"])
self["MacDataTypeA"] = getRealDataTypeA(self["MacDataTypeA"])
if "MacDataTypeB" in config:
self["MacDataTypeB"] = DataType(config["MacDataTypeB"])
self["MacDataTypeB"] = getRealDataTypeB(self["MacDataTypeB"])
if "DataTypeA" in config:
self["DataTypeA"] = DataType(config["DataTypeA"])
else:
self["DataTypeA"] = self["MacDataTypeA"]
self["DataTypeA"] = getRealDataTypeA(self["DataTypeA"])
if "DataTypeB" in config:
self["DataTypeB"] = DataType(config["DataTypeB"])
else:
self["DataTypeB"] = self["MacDataTypeB"]
self["DataTypeB"] = getRealDataTypeB(self["DataTypeB"])
if "DestDataType" in config:
self["DestDataType"] = DataType(config["DestDataType"])
else:
if "DataType" in config:
self["DestDataType"] = DataType(config["DataType"])
else:
raise Exception("NO dest data type or data type specified")
self["DataType"] = DataType(0)
self["DataTypeE"] = self["DestDataType"]
if "DataTypeE" in config:
self["DataTypeE"] = DataType(config["DataTypeE"])
if "ComputeDataType" in config:
self["ComputeDataType"] = DataType(config["ComputeDataType"])
else:
if "DestDataType" in config:
self["ComputeDataType"] = DataType(config["DestDataType"])
else:
if "DataType" in config:
self["ComputeDataType"] = DataType(config["DataType"])
else:
raise Exception("NO compute data type, or dest data type, or data type specified")
self["DataType"] = DataType(0)
self["isMixMode"] = True if not self["Sparse"] and self["MacDataTypeA"].value != self["MacDataTypeB"].value else False
if "DataTypeMXSA" in config:
self["DataTypeMXSA"] = DataType(config["DataTypeMXSA"])
else:
self["DataTypeMXSA"] = DataType(DataTypeEnum.E8)
if "DataTypeMXSB" in config:
self["DataTypeMXSB"] = DataType(config["DataTypeMXSB"])
else:
self["DataTypeMXSB"] = DataType(DataTypeEnum.E8)
# Just like DataTypeE is DestDataType by default; DataTypeAmaxD if ComputeDataType by default.
# So far we don't have to set it in config yamls
self["DataTypeAmaxD"] = self["ComputeDataType"]
if "DataTypeAmaxD" in config:
self["DataTypeAmaxD"] = DataType(config["DataTypeAmaxD"])
if self["Sparse"]:
self["DataTypeMetadata"] = DataType("I8")
if "F32XdlMathOp" in config:
self["F32XdlMathOp"] = DataType(config["F32XdlMathOp"])
else:
self["F32XdlMathOp"] = DataType(DataTypeEnum.Float)
# Modifying ComputeDataType for HHH+HPA: if (HHH+HPA), convert it to HHS_BH by setting ComputeDataType to S.
if self["ComputeDataType"].isHalf() and self["DataType"].isHalf() and self["HighPrecisionAccumulate"]:
printWarning("Inconsistent DataTypes: DataType == f16, DestType == f16, ComputeDataType == f16, but HPA == True (HHH+HPA, no such a type); Converting HHH+HPA to HHS_BH by setting compute data type to f32.")
self["ComputeDataType"] = DataType('s')
# Modifying ComputeDataType for BBB+HPA: if (BBB+HPA), convert it to BBS_BH by setting ComputeDataType to S.
if self["ComputeDataType"].isBFloat16() and self["DataType"].isBFloat16() and self["HighPrecisionAccumulate"]:
printWarning("Inconsistent DataTypes: DataType == bf16, DestType == bf16, ComputeDataType == bf16, but HPA == True (BBB+HPA, no such a type); Converting BBB+HPA to BBS_BH by setting compute data type to f32.")
self["ComputeDataType"] = DataType('s')
# Modifying ComputeDataType for I8I8I_BH: if (I8I8I8+HPA), convert it to I8I8I_BH by setting ComputeDataType to i.
if self["ComputeDataType"].isInt8() and DataType(config["DataType"]).isInt8() and self["HighPrecisionAccumulate"]:
print2("DataType == i8 and HPA == True; setting compute data type to int32")
self["ComputeDataType"] = DataType('i')
if self["OperationType"] == "GEMM":
self._checkIfSupportedGEMMType()
self.initGEMM()
else:
raise Exception("Unsupported OperationType = %s" % self["OperationType"])
self.state["AssignedDerivedParameters"] = False
ProblemType.assignDerivedParameters(self.state, printIndexAssignmentInfo)
for tc in ('A', 'B'):
for sc in self["SetConstStride%s"%tc] :
(anchorDim, stride) = sc[:2]
if anchorDim not in self.state["IndexAssignments%s"%tc]:
raise Exception("SetConstStride%s=%s anchorDim=%u is not in IndexAssignments%s"%(tc, sc, anchorDim, tc))
# Bias
# If compute data type is not equal to dest data type, tensile will run conversion kernel.
# In this case we don't need to apply bias in beta only kernel.
if "UseBias" in config:
if self["ComputeDataType"] != self["DestDataType"]:
self["BetaOnlyUseBias"] = False
else:
self["BetaOnlyUseBias"] = True if self["UseBias"] > 0 else False
if "BiasDataTypeList" in config:
self["BiasDataTypeList"] = [DataType(btype) for btype in config["BiasDataTypeList"]]
self["BiasDataTypeList"].sort() # Make name unique
else:
self["BiasDataTypeList"] = getBiasDataTypeListDefault(self)
else:
self["BetaOnlyUseBias"] = False
self["BiasDataTypeList"] = []
# Gate Residual
if "UseGateResidual" in config and config["UseGateResidual"]:
if "GateResidualDataTypeList" in config:
self["GateResidualDataTypeList"] = [DataType(gtype) for gtype in config["GateResidualDataTypeList"]]
self["GateResidualDataTypeList"].sort() # Make name unique
else:
self["GateResidualDataTypeList"] = getGateResidualDataTypeListDefault(self)
else:
self["GateResidualDataTypeList"] = []
# Activation
# Currently, ActivationType supports only 'all' and 'hipblaslt_all', and is active only when the Activation configuration is set to True.
# Otherwise, ActivationType will be set to 'none'.
if "Activation" in config:
typeStr = config.get("ActivationType", 'none')
if typeStr not in ['all', 'hipblaslt_all']:
typeStr = 'none'
else:
typeStr = 'none'
self["ActivationType"] = ActivationType(typeStr)
if "ActivationComputeDataType" in config:
self["ActivationComputeDataType"] = DataType(config["ActivationComputeDataType"])
else:
self["ActivationComputeDataType"] = self["ComputeDataType"]
if self["ActivationType"] != 'none':
# This is a dummy guard in case we currently don't have a converter to convert data from compute type to activation compute type
if self["ActivationComputeDataType"] not in [self["ComputeDataType"], self["DestDataType"]]:
printWarning("TensileLite currently only supports ActivationComputeDataType (%s) = ComputeDataType (%s) or DestDataType (%s). \
ActivationComputeDataType will be set to ComputeDataType automatically."%(self["ActivationComputeDataType"].toChar(), \
self["ComputeDataType"], \
self["DestDataType"]))
self["ActivationComputeDataType"] = self["ComputeDataType"]
if (self["ActivationComputeDataType"].numRegisters() != self["ComputeDataType"].numRegisters()) and \
(self["DataType"].numRegisters() < self["DestDataType"].numRegisters()):
printWarning("TensileLite only supports ActivationComputeDataType = ComputeDataType if DestDataType > DataType. \
ActivationComputeDataType will be set to ComputeDataType automatically.")
self["ActivationComputeDataType"] = self["ComputeDataType"]
if "UseE" in config:
if config["UseE"]:
if self["ActivationType"] == 'none':
printWarning("Use E is disabled cause Activation is set to False.")
self["UseE"] = False
else:
self["UseE"] = config["UseE"]
else:
self["UseE"] = config["UseE"]
if "Gradient" in config:
if config["Gradient"]:
if (not self["UseBias"]) and self["ActivationType"] == 'none':
printWarning("Gradient is disabled cause bias and activation are both disabled.")
self["Gradient"] = False
if self["ActivationType"] != 'none' and self["UseE"] == False:
printWarning("Use E is enabled cause Activation is enabled.")
self["UseE"] = True
elif self["ActivationType"] == 'none' and self["UseE"] == True:
printWarning("Use E is disabled cause Activation is disabled.")
self["UseE"] = False
# if self["UseScaleAlphaVec"]:
# printWarning("Use scaleAlphaVec is disabled cause Gradient is enabled.")
# self["UseScaleAlphaVec"] = False
self["Gradient"] = config["Gradient"]
# Need gradient info
biasSrcList = ["A", "B", "D"]
if "BiasSrc" in config:
if not self["Gradient"] and config["BiasSrc"] != "D":
printWarning("BiasSrc is set to D cause Gradient is disabled.")
self["BiasSrc"] = "D"
elif self["Gradient"]:
# # Currently only supports D :)
# if config["BiasSrc"] != "D":
# raise Exception("BiasSrc currently only supports D.")
if config["BiasSrc"] not in biasSrcList:
raise Exception("BiasSrc only supports A, B, D.")
if "ActivationNoGuard" in config:
self["ActivationNoGuard"] = config["ActivationNoGuard"]
if self["ActivationNoGuard"]:
if self["ActivationType"] == 'none':
printWarning("ActivationNoGuard is set to False cause Acivation is off.")
self["ActivationNoGuard"] = False
if (not self["Gradient"]):
printWarning("ActivationNoGuard is set to False cause Gradient is off.")
self["ActivationNoGuard"] = False
################################################################################
# Function checkIfSupportedGEMMType:
# Assures 3 data-types are valid, supported and well-assigned
# See the discussion in ValidParameters.py for validGEMMTypes
################################################################################
def _checkIfSupportedGEMMType(self):
inTypeA = self["MacDataTypeA"]
inTypeB = self["MacDataTypeB"]
outType = self["DestDataType"]
computeType = self["ComputeDataType"]
gemmType = ( inTypeA.toChar(), inTypeB.toChar(), outType.toChar(), computeType.toChar() )
if gemmType not in _validGEMMTypes:
raise Exception("This typed-GEMM (Ti, To, Tc) = (%s, %s, %s) is not supported yet."%(gemmType[0], gemmType[1], gemmType[2]))
########################################
def initGEMM(self):
sumIdx = 3 if self["Batched"] else 2
self["IndexAssignmentsA"] = [0, sumIdx] # N
self["IndexAssignmentsB"] = [sumIdx, 1] # N
if self.state["Sparse"] == 2:
if self.state["MetadataLayout"]:
self["IndexAssignmentsMetadata"] = [1, sumIdx] # T (ref B)
else:
self["IndexAssignmentsMetadata"] = [sumIdx, 1] # N (ref B)
else:
if self.state["MetadataLayout"]:
self["IndexAssignmentsMetadata"] = [0, sumIdx] # N (ref A)
else:
self["IndexAssignmentsMetadata"] = [sumIdx, 0] # T (ref A)
if self["TransposeA"]:
self["IndexAssignmentsA"] = [sumIdx, 0] # T
if self["TransposeB"]:
self["IndexAssignmentsB"] = [1, sumIdx] # T
if self["Batched"]:
self["IndexAssignmentsA"].append(2)
self["IndexAssignmentsB"].append(2)
self["IndexAssignmentsMetadata"].append(2)
self["NumIndicesC"] = 3
else:
self["NumIndicesC"] = 2
self["NumIndicesLD"] = 4
self["IndexAssignmentsLD"][0] = self["NumIndicesC"] + 1
for i in range(1, len(self["IndexAssignmentsLD"])):
self["IndexAssignmentsLD"][i] = self["IndexAssignmentsLD"][i-1] + 1
if self["MXBlockA"]:
self["IndexAssignmentsMXSA"] = deepcopy(self["IndexAssignmentsA"])
if self["MXBlockB"]:
self["IndexAssignmentsMXSB"] = deepcopy(self["IndexAssignmentsB"])
########################################
def isGEMM(self):
return self.operationType == 0
########################################
# determine d0, d1, dU
@staticmethod
def assignDerivedParameters(state, printIndexAssignmentInfo: bool=False):
if "AssignedDerivedParameters" in state:
if state["AssignedDerivedParameters"]:
return
state["AssignedDerivedParameters"] = False
state["TotalIndices"] = max(max(state["IndexAssignmentsA"])+1, \
max(state["IndexAssignmentsB"])+1)
# determine num free, batch
state["IndicesFree"] = []
state["IndicesBatch"] = []
state["IndicesSummation"] = []
for i in range(0, state["NumIndicesC"]):
inA = i in state["IndexAssignmentsA"]
inB = i in state["IndexAssignmentsB"]
if inA and inB:
state["IndicesBatch"].append(i)
elif inA or inB:
state["IndicesFree"].append(i)
else:
raise Exception("invalid index %u (inC but not (inA or inB))" % i)
# determine num summation
for i in range(state["NumIndicesC"], state["TotalIndices"]):
inA = i in state["IndexAssignmentsA"]
inB = i in state["IndexAssignmentsB"]
if inA and inB:
state["IndicesSummation"].append(i)
else:
raise Exception("invalid index %u (expected summation but not (inA and inB))" % i)
# print index assignments
if printIndexAssignmentInfo:
print("IndicesFree: %s" % state["IndicesFree"])
print("IndicesBatch: %s" % state["IndicesBatch"])
print("IndicesSum: %s" % state["IndicesSummation"])
print("IndexAssignmentsA: %s" % state["IndexAssignmentsA"])
print("IndexAssignmentsB: %s" % state["IndexAssignmentsB"])
print("NumIndicesC: %s" % state["NumIndicesC"])
for k in ('IndexAssignmentsA','IndexAssignmentsB'):
if len(state[k]) != len(set(state[k])):
raise Exception("duplicate index in %s=%s"% (k,state[k]))
state["NumIndicesFree"] = len(state["IndicesFree"])
state["NumIndicesBatch"] = len(state["IndicesBatch"])
state["NumIndicesSummation"] = len(state["IndicesSummation"])
if not state["AllowNoFreeDims"] and state["NumIndicesFree"] < 2 :
raise Exception("Tensile requires >= 2 free indices or set AllowNoFreeDims; FreeIndices=%s."% state["IndicesFree"])
# by default, unroll index will be the last/inner summation index
state["IndexUnroll"] = state["IndicesSummation"][len(state["IndicesSummation"])-1]
for i in range(0, len(state["IndexAssignmentsA"])):
if state["IndexAssignmentsA"][i] == state["IndexUnroll"]:
state["IndexUnrollA"] = i
break
if state["MXBlockA"]:
for i in range(0, len(state["IndexAssignmentsMXSA"])):
if state["IndexAssignmentsMXSA"][i] == state["IndexUnroll"]:
state["IndexUnrollMXSA"] = i
break
for i in range(0, len(state["IndexAssignmentsB"])):
if state["IndexAssignmentsB"][i] == state["IndexUnroll"]:
state["IndexUnrollB"] = i
break
if state["MXBlockB"]:
for i in range(0, len(state["IndexAssignmentsMXSB"])):
if state["IndexAssignmentsMXSB"][i] == state["IndexUnroll"]:
state["IndexUnrollMXSB"] = i
break
for i in range(0, len(state["IndexAssignmentsMetadata"])):
if state["IndexAssignmentsMetadata"][i] == state["IndexUnroll"]:
state["IndexUnrollM"] = i
break
#print2("IndexUnrollA: %u" % state["IndexUnrollA"])
#print2("IndexUnrollB: %u" % state["IndexUnrollB"])
# assign d0, d1
if state["AllowNoFreeDims"]:
dimList = state["IndicesFree"] + state["IndicesBatch"]
else:
dimList = state["IndicesFree"]
state["Index01A"] = [i for i in state["IndexAssignmentsA"] if i in dimList][0]
if state["MXBlockA"]:
state["Index01MXSA"] = [i for i in state["IndexAssignmentsMXSA"] if i in dimList][0]
state["Index01B"] = [i for i in state["IndexAssignmentsB"] if i in dimList][0]
if state["MXBlockB"]:
state["Index01MXSB"] = [i for i in state["IndexAssignmentsMXSB"] if i in dimList][0]
#print2("Index01A: %u" % state["Index01A"])
#print2("Index01B: %u" % state["Index01B"])
# Store code is optimized for 0 as the fastest-moving in memory
# whichever has lower stride in C (lower value), is 0, other is 1
if state["Index01A"] < state["Index01B"]:
state["Index0"] = state["Index01A"]
state["Index1"] = state["Index01B"]
state["Tensor0"] = 0
state["Tensor1"] = 1
state["TileA"] = 0
state["TileB"] = 1
else:
state["Index0"] = state["Index01B"]
state["Index1"] = state["Index01A"]
state["Tensor0"] = 1
state["Tensor1"] = 0
state["TileA"] = 1
state["TileB"] = 0
# generalize transpose
strideIdxA = state["IndexAssignmentsA"].index(state["Index01A"])
strideIdxB = state["IndexAssignmentsB"].index(state["Index01B"])
unrollIdxA = state["IndexAssignmentsA"].index(state["IndexUnroll"])
unrollIdxB = state["IndexAssignmentsB"].index(state["IndexUnroll"])
state["TLUA"] = strideIdxA < unrollIdxA
if state["MXBlockA"]:
state["TLUMXSA"] = state["TLUA"]
state["TLUB"] = strideIdxB < unrollIdxB
if state["MXBlockB"]:
state["TLUMXSB"] = state["TLUB"]
if printIndexAssignmentInfo:
print("TLUA: %s (stridePosA(%d) <? unrollIdxA(%d)" % \
(state["TLUA"], strideIdxA, unrollIdxA))
print("TLUB: %s (stridePosB(%d) <? unrollIdxB(%d)" % \
(state["TLUB"], strideIdxB, unrollIdxB))
print("Index01A: %s" % state["Index01A"])
print("Index01B: %s" % state["Index01B"])
#unrollDimStrideGreaterThanTileDimStrideA = TLUA = !transA = fast
#!unrollDimStrideLessThanTileDimStrideB = TLUB = transB = fast
state["AssignedDerivedParameters"] = True
if state["Sparse"]:
state["Index01Metadata"] = [i for i in state["IndexAssignmentsMetadata"] if i in dimList][0]
strideIdxM = state["IndexAssignmentsMetadata"].index(state["Index01Metadata"])
unrollIdxM = state["IndexAssignmentsMetadata"].index(state["IndexUnroll"])
state["TLUMetadata"] = strideIdxM < unrollIdxM
if printIndexAssignmentInfo:
print("TLUMetadata: %s (stridePosM(%d) <? unrollIdxM(%d)" % \
(state["TLUMetadata"], strideIdxM, unrollIdxM))
print("Index01Metadata: %s" % state["Index01Metadata"])
########################################
def __str__(self):
indexChars = INDEX_CHARS
# C dimensions
name = ["C" + "".join(indexChars[i].lower() for i in range(0, self["NumIndicesC"]))]
# A dimensions
name.append("A" + "".join(indexChars[i] if i in self["MirrorDimsA"] else indexChars[i].lower() for i in self["IndexAssignmentsA"]))
if self["ComplexConjugateA"]:
name.append("C")
# B dimensions
name.append("B" + "".join(indexChars[i] if i in self["MirrorDimsB"] else indexChars[i].lower() for i in self["IndexAssignmentsB"]))
if self["ComplexConjugateB"]:
name.append("C")
# DataTypes
macTypeStr = self["MacDataTypeA"].toChar()
if self["MacDataTypeA"] != self["MacDataTypeB"]:
macTypeStr = self["MacDataTypeA"].toChar() + self["MacDataTypeB"].toChar()
if self["MacDataTypeA"] != self["DataTypeA"] or self["MacDataTypeB"] != self["DataTypeB"]:
name.append(self["DataTypeA"].toChar() + self["DataTypeB"].toChar())
name.append(macTypeStr) # Type of A/B, split Type of A/B when A!=B
# Special condition for some newly supported kernels:
# HHS, HSS, BSS and I8II kernels, use a clearer naming _TiToTc_
# TODO: Distinguish all kernels by _TiToTc_ to be more consistent with rocblas
gemmType = (self["MacDataTypeA"].toChar(), self["MacDataTypeB"].toChar(),
self["DestDataType"].toChar(), self["ComputeDataType"].toChar())
if gemmType in _HPATypes:
name[-1] += "".join([self["DestDataType"].toChar(), self["ComputeDataType"].toChar()])
if not self["F32XdlMathOp"].isSingle() and self["DataType"].isSingle():
name.append("".join(["M", self["F32XdlMathOp"].toChar()]))
if self["MXBlockA"]:
name.append("MXA" + self["DataTypeMXSA"].toChar() + "B" + str(self["MXBlockA"]))
if self["MXBlockB"]:
name.append("MXB" + self["DataTypeMXSB"].toChar() + "B" + str(self["MXBlockB"]))
if self["SwizzleTensorA"]:
name.append("STA")
if self["SwizzleTensorB"]:
name.append("STB")
# Other
other = ""
if self["UseBeta"]: other += "B"
if self["HighPrecisionAccumulate"] and not self["SilentHighPrecisionAccumulate"]: other += "H"
if self["UseInitialStridesAB"]: other += "I"
if self["UseInitialStridesCD"]: other += "Ic"
if other: name.append(other)
if self["UseBias"]:
name.append("Bias")
if self["BiasDataTypeList"] != getBiasDataTypeListDefault(self):
name.append("".join(i.toChar() for i in self["BiasDataTypeList"]))
if self["BiasSrc"] and self["Gradient"]: # Show bias src if gradient = True
name.append(f"{self['BiasSrc']}")
factorDim = max(self["UseScaleAlphaVec"], self["UseBias"])
if factorDim > 1 :
s = ("N" if factorDim == 2 else "MN")
name.append(f"FD{s}")
if self["UseE"]:
if self["Gradient"]:
name.append(f"Grad{self['DataTypeE'].toChar()}")
else:
name.append(f"Aux{self['DataTypeE'].toChar()}")
if self["OutputAmaxD"]:
name.append("AmaxD")
if self["Sparse"]:
if self["Sparse"] == 2:
name.append("SPBML%d"%(self["MetadataLayout"]))
else:
name.append("SPAML%d"%(self["MetadataLayout"]))
# precision and other
# name += "_SB" if self["StridedBatched"] else "_GB"
if self["GroupedGemm"]:
name.append("GG")
else:
if not self["StridedBatched"]:
name.append("GB") # legacy
# Activation Naming
if self["ActivationType"] != 'none':
if self["ActivationType"] == 'all':
name.append("A")
elif self["ActivationType"] == 'hipblaslt_all':
name.append("HA")
else:
name.append(f"{str(self['ActivationType']).upper()}")
name.append(self["ActivationComputeDataType"].toChar())
if self["ActivationNoGuard"]: name[-1] += "NG"
if self["UseScaleAB"] == "Scalar":
name.append("SAB")
elif self["UseScaleAB"] == "Vector":
name.append("SABV")
if self["UseScaleCD"]: name.append("SCD")
if self["UseScaleAlphaVec"]: name.append("SAV")
if self["UseGateResidual"]:
name.append("GateRes")
if self["GateResidualDataTypeList"] != getGateResidualDataTypeListDefault(self):
name.append("".join(i.toChar() for i in self["GateResidualDataTypeList"]))
if self["SupportUserArgs"]: name.append("UserArgs")
return "_".join(name)
def keys(self):
return list(self.state.keys())
def __len__(self):
return len(self.state)
def __iter__(self):
return iter(self.state)
def __getitem__(self, key):
return self.state[key]
def __setitem__(self, key, value):
self.state[key] = value
def __repr__(self):
return self.__str__()
def getAttributes(self):
return self.state
def __hash__(self):
return hash(str(self))
def __eq__(self, other):
return isinstance(other, ProblemType) and self.getAttributes() == other.getAttributes()
def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result
def get(self, key, default=None):
try:
return self.state[key]
except:
return default
################################################################################
# Bias Type
################################################################################
def getBiasDataTypeListDefault(problem: ProblemType) -> List[DataType]:
bList = []
for d in ["DataType", "ComputeDataType", "DestDataType"]:
dtype = DataType(problem[d])
# filter out sizeof(dtype) <= 1, because it is not supported by bias datatype
if dtype.numBytes() > 1:
bList.append(dtype)
biasDataTypeList = list(set(bList))
biasDataTypeList.sort() # Make name unique
return biasDataTypeList
################################################################################
# Gate Residual Type
################################################################################
def getGateResidualDataTypeListDefault(problem: ProblemType) -> List[DataType]:
gList = []
for d in ["DataType", "ComputeDataType"]:
dtype = DataType(problem[d])
if dtype.numBytes() > 1:
gList.append(dtype)
gateResidualDataTypeList = list(set(gList))
gateResidualDataTypeList.sort() # Make name unique
return gateResidualDataTypeList