Merge pull request #7519 from microsoft/user/dwayner/ort1.8dml1.5.1

Update DML EP changes related to DML 1.5.1 for ORT 1.8
This commit is contained in:
Dwayne Robinson 2021-04-30 19:25:16 -07:00 committed by GitHub
commit 93e93d0851
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 258 additions and 55 deletions

View file

@ -477,9 +477,9 @@ namespace Dml
std::optional<uint32_t> requiredInputCount = internalRegInfo->graphNodeFactoryRegistration->requiredInputCount;
if (requiredCpuInputsConstant &&
TryGetStaticInputShapes( node, graphNodeProperty.first->second.inputShapes) &&
!ContainsEmptyDimensions(graphNodeProperty.first->second.inputShapes) &&
!ContainsEmptyDimensions(graphNodeProperty.first->second.inputShapes, internalRegInfo->requiredConstantCpuInputs) &&
TryGetStaticOutputShapes(node, graphNodeProperty.first->second.outputShapes) &&
!ContainsEmptyDimensions(graphNodeProperty.first->second.outputShapes) &&
!ContainsEmptyDimensions(graphNodeProperty.first->second.outputShapes, internalRegInfo->requiredConstantCpuInputs) &&
(requiredInputCount == std::nullopt || *requiredInputCount == node.InputDefs().size()))
{
*isDmlGraphNode = true;

View file

@ -27,7 +27,8 @@ namespace Dml
onnxruntime::common::Status GraphTransformer::ApplyImpl(
onnxruntime::Graph& graph,
bool& modified,
int graph_level, const onnxruntime::logging::Logger&) const {
int graph_level, const onnxruntime::logging::Logger&) const
{
modified = false;
// Perform fusion
@ -36,8 +37,13 @@ namespace Dml
PerformOperatorFusion(&graph, &transformModifiedGraph);
modified |= transformModifiedGraph;
if (modified) {
ORT_RETURN_IF_ERROR(graph.Resolve());
transformModifiedGraph = false;
PerformQuantizedOperatorDecomposition(&graph, &transformModifiedGraph);
modified |= transformModifiedGraph;
if (modified)
{
ORT_RETURN_IF_ERROR(graph.Resolve());
}
}
@ -110,9 +116,10 @@ namespace Dml
// We need to predict whether the nodes will be assigned to the DML transformer by Lotus,
// which occurs in IExecutionProvider::GetCapability.
if (!onnxruntime::KernelRegistry::HasImplementationOf(*registry, outputNode, onnxruntime::kDmlExecutionProvider)) {
// Can't fuse nodes that don't belong to this execution provider
continue;
if (!onnxruntime::KernelRegistry::HasImplementationOf(*registry, outputNode, onnxruntime::kDmlExecutionProvider))
{
// Can't fuse nodes that don't belong to this execution provider
continue;
}
if (outputNode.InputDefs().size() != 1)
@ -160,12 +167,14 @@ namespace Dml
fusedNode.activationAttributes = activationNode.GetAttributes();
// Inputs to the fused node are the inputs to the fuseable node
for (const auto *input : fuseableNode.InputDefs()) {
for (const auto *input : fuseableNode.InputDefs())
{
fusedNode.inputs.push_back(graph->GetNodeArg(input->Name()));
}
// Outputs from the fused node are the outputs to the activation node
for (const auto *output : activationNode.OutputDefs()){
for (const auto *output : activationNode.OutputDefs())
{
fusedNode.outputs.push_back(graph->GetNodeArg(output->Name()));
}
@ -210,4 +219,103 @@ namespace Dml
}
}
// Converts certain QLinear operations unsupported by the DML API into a sequence of DeQuantizeLinear, 32-bit operator, QuantizeLinear
void GraphTransformer::PerformQuantizedOperatorDecomposition(onnxruntime::Graph* graph, bool* modified) const
{
struct NodeToAdd
{
std::string name;
std::string description;
std::string opType;
std::string domain;
onnxruntime::NodeAttributes attributes;
std::vector<onnxruntime::NodeArg*> inputs;
std::vector<onnxruntime::NodeArg*> outputs;
};
// Defer adding and removing nodes in the graph until after we're done iterating over it, because we can't mutate the
// graph while iterating over it
std::vector<NodeToAdd> nodesToAdd;
std::vector<onnxruntime::NodeIndex> nodesToRemove;
for (auto& node : graph->Nodes())
{
// For now, only QLinearSigmoid is handled
if (node.Domain() == onnxruntime::kMSDomain &&
node.OpType() == "QLinearSigmoid")
{
// Intermediate node arg type proto with floating point format
onnx::TypeProto floatTensorProto;
floatTensorProto.mutable_tensor_type()->set_elem_type(onnx::TensorProto_DataType_FLOAT);
// Add intermediate graph edges for the input and output of the FP32 sigmoid operator
auto* sigmoidInputArg = &graph->GetOrCreateNodeArg("decomposed_QLinearSigmoid_input_" + GetUniqueNodeName(&node), &floatTensorProto);
auto* sigmoidOutputArg = &graph->GetOrCreateNodeArg("decomposed_QLinearSigmoid_output_" + GetUniqueNodeName(&node), &floatTensorProto);
{
NodeToAdd dequantizeNode;
dequantizeNode.name = "decomposed_QLinearSigmoid_DequantizeLinear_" + GetUniqueNodeName(&node);
dequantizeNode.description = "";
dequantizeNode.opType = "DequantizeLinear";
dequantizeNode.domain = "";
dequantizeNode.inputs.push_back(graph->GetNodeArg(node.InputDefs()[0]->Name()));
dequantizeNode.inputs.push_back(graph->GetNodeArg(node.InputDefs()[1]->Name()));
dequantizeNode.inputs.push_back(graph->GetNodeArg(node.InputDefs()[2]->Name()));
dequantizeNode.outputs.push_back(sigmoidInputArg);
nodesToAdd.push_back(std::move(dequantizeNode));
}
{
NodeToAdd sigmoidNode;
sigmoidNode.name = "decomposed_QLinearSigmoid_Sigmoid_" + GetUniqueNodeName(&node);
sigmoidNode.description = "";
sigmoidNode.opType = "Sigmoid";
sigmoidNode.domain = "";
sigmoidNode.inputs.push_back(sigmoidInputArg);
sigmoidNode.outputs.push_back(sigmoidOutputArg);
nodesToAdd.push_back(std::move(sigmoidNode));
}
{
NodeToAdd quantizeNode;
quantizeNode.name = "decomposed_QLinearSigmoid_QuantizeLinear_" + GetUniqueNodeName(&node);
quantizeNode.description = "";
quantizeNode.opType = "QuantizeLinear";
quantizeNode.domain = "";
quantizeNode.inputs.push_back(sigmoidOutputArg);
quantizeNode.inputs.push_back(graph->GetNodeArg(node.InputDefs()[3]->Name()));
quantizeNode.inputs.push_back(graph->GetNodeArg(node.InputDefs()[4]->Name()));
quantizeNode.outputs.push_back(graph->GetNodeArg(node.OutputDefs()[0]->Name()));
nodesToAdd.push_back(std::move(quantizeNode));
}
nodesToRemove.push_back(node.Index());
*modified = true;
}
}
for (auto& nodeToAdd : nodesToAdd)
{
auto& node = graph->AddNode(
nodeToAdd.name,
nodeToAdd.opType,
nodeToAdd.description,
nodeToAdd.inputs,
nodeToAdd.outputs,
&nodeToAdd.attributes,
nodeToAdd.domain);
}
for (const auto& nodeIndex : nodesToRemove)
{
onnxruntime::Node* node = graph->GetNode(nodeIndex);
onnxruntime::graph_utils::RemoveNodeOutputEdges(*graph, *node);
graph->RemoveNode(node->Index());
}
}
} // namespace Dml

View file

@ -28,6 +28,8 @@ namespace Dml
private:
void PerformOperatorFusion(onnxruntime::Graph* graph, bool* modified) const;
void PerformQuantizedOperatorDecomposition(onnxruntime::Graph* graph, bool* modified) const;
std::shared_ptr<onnxruntime::KernelRegistry> m_registry;
uint32_t m_supportedDataTypeMask = 0;
const ExecutionProviderImpl* m_providerImpl = nullptr;

View file

@ -1876,12 +1876,13 @@ bool TryGetStaticOutputShapes(const onnxruntime::Node& node, EdgeShapes& outputS
return true;
}
bool ContainsEmptyDimensions(const EdgeShapes& shapes) {
bool ContainsEmptyDimensions(const EdgeShapes& shapes, gsl::span<const uint32_t> ignoredShapeIndices) {
for (size_t i = 0; i < shapes.EdgeCount(); i++) {
const std::vector<uint32_t>& shape = shapes.GetShape(i);
if (std::find(shape.begin(), shape.end(), 0) != shape.end()) {
return true;
if (std::find(shape.begin(), shape.end(), 0) != shape.end() &&
std::find(ignoredShapeIndices.begin(), ignoredShapeIndices.end(), i) == ignoredShapeIndices.end()) {
return true;
}
}

View file

@ -637,7 +637,7 @@ onnx::AttributeProto_AttributeType ToProto(MLOperatorAttributeType type);
bool TryGetStaticInputShapes(const onnxruntime::Node& node, EdgeShapes& inputShapes);
bool TryGetStaticOutputShapes(const onnxruntime::Node& node, EdgeShapes& outputShapes);
bool ContainsEmptyDimensions(const EdgeShapes& shapes);
bool ContainsEmptyDimensions(const EdgeShapes& shapes, gsl::span<const uint32_t> ignoredShapeIndices = gsl::span<const uint32_t>());
std::tuple<std::unique_ptr<std::byte[]>, size_t> UnpackTensor(const onnx::TensorProto& initializer);
} // namespace Windows::AI::MachineLearning::Adapter

View file

@ -13,17 +13,10 @@ public:
DmlOperatorCast(
const MLOperatorKernelCreationContext& kernelInfo
) : DmlOperator(kernelInfo),
m_toDataType(static_cast<MLOperatorTensorDataType>(kernelInfo.GetAttribute<int64_t>(AttrName::To)))
) : DmlOperator(kernelInfo)
{
Initialize(kernelInfo);
// Zero the output tensor's memory for 64-bit integer emulation with strides.
if (m_toDataType == MLOperatorTensorDataType::UInt64 || m_toDataType == MLOperatorTensorDataType::Int64)
{
m_zeroOperator = InitializeZeroInt64Tensor(m_outputTensorDescs[0].GetBufferSizeInBytes());
}
std::vector<DML_TENSOR_DESC> inputDescs = GetDmlInputDescs();
std::vector<DML_TENSOR_DESC> outputDescs = GetDmlOutputDescs();
@ -41,23 +34,12 @@ public:
std::vector<IMLOperatorTensor*> inputTensors = GetInputTensorsForExecute(kernelContext);
std::vector<IMLOperatorTensor*> outputTensors = GetOutputTensorsForExecute(kernelContext);
// Zero the output tensor's memory for 64-bit integer emulation with strides.
if (m_zeroOperator)
{
assert(m_toDataType == MLOperatorTensorDataType::UInt64 || m_toDataType == MLOperatorTensorDataType::Int64);
ExecuteZeroInt64Tensor(m_zeroOperator.Get(), outputTensors[0]);
}
THROW_IF_FAILED(m_executionProvider->ExecuteOperator(
m_compiledOperator.Get(),
m_persistentResourceBinding ? &*m_persistentResourceBinding : nullptr,
gsl::make_span(inputTensors),
gsl::make_span(outputTensors)));
}
private:
MLOperatorTensorDataType m_toDataType;
ComPtr<IDMLCompiledOperator> m_zeroOperator;
};
DML_OP_DEFINE_CREATION_FUNCTION(Cast, DmlOperatorCast);

View file

@ -6,6 +6,24 @@
namespace Dml
{
constexpr NameAndIndex coordinateTransformationModes[] =
{
{"half_pixel", 0},
{"pytorch_half_pixel", 1},
{"align_corners", 2},
{"asymmetric", 3},
{"tf_half_pixel_for_nn", 4},
{"tf_crop_and_resize", 5},
};
constexpr NameAndIndex nearestNeighborRoundingModes[] =
{
{"", 0},
{"round_prefer_floor", 0},
{"round_prefer_ceil", 1},
{"floor", 2},
};
void ComputePixelOffsetsAndScales(
const MLOperatorKernelCreationContext& kernelCreationContext,
gsl::span<const float> regionOfInterest, // May be empty depending on mode.
@ -23,17 +41,12 @@ void ComputePixelOffsetsAndScales(
assert(regionOfInterest.empty() || regionOfInterest.size() == inputDimensions.size() * 2);
std::string coordinateTransformationMode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::CoordinateTransformationMode, "half_pixel");
uint32_t coordinateTransformationModeValue = UINT32_MAX;
const char* modes[] = { "half_pixel", "pytorch_half_pixel", "align_corners", "asymmetric", "tf_half_pixel_for_nn", "tf_crop_and_resize" };
for (uint32_t i = 0; i < std::size(modes); ++i)
auto optionalCoordinateTransformationModeValue = TryMapStringToIndex(coordinateTransformationMode, coordinateTransformationModes);
if (!optionalCoordinateTransformationModeValue)
{
if (strcmp(modes[i], coordinateTransformationMode.c_str()) == 0)
{
coordinateTransformationModeValue = i;
break;
}
ML_INVALID_ARGUMENT("Unsupported 'coordinate_transformation_mode'");
}
uint32_t coordinateTransformationModeValue = *optionalCoordinateTransformationModeValue;
ML_CHECK_VALID_ARGUMENT(
!regionOfInterest.empty() || coordinateTransformationModeValue != 5 /*tf_crop_and_resize*/,
@ -150,7 +163,7 @@ void ComputePixelOffsetsAndScales(
break;
default:
ML_INVALID_ARGUMENT("Unknown 'coordinate_transformation_mode'");
assert(false); // TryMapStringToIndex would have already bailed above.
}
inputPixelOffsets[i] = inputPixelOffset;
@ -233,6 +246,34 @@ public:
std::string mode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::Mode, "NEAREST");
DML_INTERPOLATION_MODE interpolationMode = Dml::MapStringToInteropolationMode(mode);
// DML's nearest neighbor mode uses round-halves-up (or round_prefer_ceil) via floor(input.x + 0.5).
// So to support floor, adjust the input by half a pixel.
// round_prefer_floor is not supported without an API extension,
// but existing code already default to treating it as round_prefer_ceil.
// So continue that.
if (interpolationMode == DML_INTERPOLATION_MODE_NEAREST_NEIGHBOR)
{
std::string nearestMode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::NearestMode, "round_prefer_floor");
auto optionalNearestModeValue = TryMapStringToIndex(nearestMode, nearestNeighborRoundingModes);
if (optionalNearestModeValue)
{
switch (*optionalNearestModeValue)
{
case 0: // round_prefer_floor
case 1: // round_prefer_ceil
break;
case 2: // floor
for (auto& offset : inputPixelOffsets)
{
offset += 0.5;
}
break;
default:
assert(false);
}
}
}
// Create the operator description.
std::vector<DML_TENSOR_DESC> inputDescs = GetDmlInputDescs();
std::vector<DML_TENSOR_DESC> outputDescs = GetDmlOutputDescs();
@ -282,7 +323,8 @@ void CALLBACK QueryResize(IMLOperatorSupportQueryContextPrivate* context, bool*
// DML's nearest neighbor mode uses half pixels rounded down.
std::string nearestMode = attributes.GetOptionalAttribute<std::string>(AttrName::NearestMode, "round_prefer_floor");
if (nearestMode != "round_prefer_floor")
auto optionalNearestModeValue = TryMapStringToIndex(nearestMode, nearestNeighborRoundingModes);
if (!optionalNearestModeValue)
{
return;
}

View file

@ -31,10 +31,11 @@ public:
{"avg", DML_REDUCE_FUNCTION_AVERAGE},
};
const std::string mode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::Mode, "avg");
const auto reductionFunction = MapStringToIndex<DML_REDUCE_FUNCTION>(mode, mapping);
const auto optionalReductionFunction = TryMapStringToIndex<DML_REDUCE_FUNCTION>(mode, mapping);
const float spatialScale = kernelCreationContext.GetOptionalAttribute<float>(AttrName::SpatialScale, 1.0f);
const int32_t samplesPerOutput = kernelCreationContext.GetOptionalAttribute<int32_t>(AttrName::SamplingRatio, 0u);
ML_CHECK_VALID_ARGUMENT(samplesPerOutput >= 0, "sampling_ratio must be 0 or positive.");
ML_CHECK_VALID_ARGUMENT(!!optionalReductionFunction, "Unsupported RoiAlign mode.");
DML_ROI_ALIGN_OPERATOR_DESC operatorDesc = {};
operatorDesc.InputTensor = &inputDescs[0];
@ -46,7 +47,7 @@ public:
operatorDesc.OutOfBoundsInputValue = 0.0f; // ONNX does not specify a value for input elements outside bounds.
operatorDesc.MinimumSamplesPerOutput = (samplesPerOutput == 0) ? 1 : samplesPerOutput;
operatorDesc.MaximumSamplesPerOutput = (samplesPerOutput == 0) ? UINT32_MAX : samplesPerOutput;
operatorDesc.ReductionFunction = reductionFunction;
operatorDesc.ReductionFunction = *optionalReductionFunction;
operatorDesc.InterpolationMode = DML_INTERPOLATION_MODE_LINEAR;
DML_OPERATOR_DESC opDesc = { DML_OPERATOR_ROI_ALIGN, &operatorDesc };

View file

@ -544,8 +544,8 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
// Uncategorized
{REG_INFO( 7, MatMul, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO( 9, MatMul, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO( 7, Cast, typeNameListTwo, supportedTypeListCast, DmlGraphSupport::Supported|DmlGraphSupport::Prefer64BitTensorsDirectly|DmlGraphSupport::SupportedWith64BitTensorsVia32BitStrides)},
{REG_INFO( 9, Cast, typeNameListTwo, supportedTypeListCast, DmlGraphSupport::Supported|DmlGraphSupport::Prefer64BitTensorsDirectly|DmlGraphSupport::SupportedWith64BitTensorsVia32BitStrides)},
{REG_INFO( 7, Cast, typeNameListTwo, supportedTypeListCast, DmlGraphSupport::Supported)},
{REG_INFO( 9, Cast, typeNameListTwo, supportedTypeListCast, DmlGraphSupport::Supported)},
{REG_INFO( 7, MemcpyFromHost, typeNameListDefault, supportedTypeListAll)},
{REG_INFO( 7, MemcpyToHost, typeNameListDefault, supportedTypeListAll)},
{REG_INFO_VER( 7, TopK, typeNameListTopK, supportedTypeListTopK, DmlGraphSupport::Supported|DmlGraphSupport::SupportedWith64BitTensorsVia32BitStrides)},
@ -620,6 +620,12 @@ void RegisterDmlOperators(IMLOperatorRegistry* registry)
desc.typeConstraints = typeConstraints.data();
desc.typeConstraintCount = static_cast<uint32_t>(typeConstraints.size());
#if _DEBUG
// If some version of the operator is supported for fusion, check that each registered version is also supported.
// This ensures that table of operators and versions supporting fusion does not become stale as operator sets are added.
FusionHelpers::AssertFusableOperatorSupportsVersionIfExists(desc.name, desc.domain, desc.minimumOperatorSetVersion);
#endif
// edgeDescs will accumulate the edge descriptions across all type constraints.
// The values of allowedTypeCount will indicate how many elements of edgeDescs
// belong to each type constraint.

View file

@ -143,12 +143,19 @@ namespace Dml
static const OperatorInfo c_fusableOps[] =
{
OperatorInfo{ "Conv", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_Conv },
OperatorInfo{ "Conv", onnxruntime::kOnnxDomain, OnnxOperatorSet11::sc_sinceVer_Conv },
OperatorInfo{ "ConvTranspose", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_ConvTranspose },
OperatorInfo{ "ConvTranspose", onnxruntime::kOnnxDomain, OnnxOperatorSet11::sc_sinceVer_ConvTranspose },
OperatorInfo{ "BatchNormalization", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_BatchNormalization },
OperatorInfo{ "BatchNormalization", onnxruntime::kOnnxDomain, OnnxOperatorSet9::sc_sinceVer_BatchNormalization },
OperatorInfo{ "InstanceNormalization", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_InstanceNormalization },
OperatorInfo{ "MeanVarianceNormalization", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_MeanVarianceNormalization },
OperatorInfo{ "MeanVarianceNormalization", onnxruntime::kOnnxDomain, OnnxOperatorSet9::sc_sinceVer_MeanVarianceNormalization },
OperatorInfo{ "Gemm", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_Gemm },
OperatorInfo{ "Gemm", onnxruntime::kOnnxDomain, OnnxOperatorSet9::sc_sinceVer_Gemm },
OperatorInfo{ "Gemm", onnxruntime::kOnnxDomain, OnnxOperatorSet11::sc_sinceVer_Gemm },
OperatorInfo{ "MatMul", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_MatMul },
OperatorInfo{ "MatMul", onnxruntime::kOnnxDomain, OnnxOperatorSet9::sc_sinceVer_MatMul },
// The filter for activation functions maps to what DML's fused op internally fuses at the shader level.
OperatorInfo{ "Add", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_Add, {"Relu", "LeakyRelu"} },
@ -166,7 +173,9 @@ namespace Dml
OperatorInfo{ "Relu", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_Relu },
OperatorInfo{ "LeakyRelu", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_LeakyRelu },
OperatorInfo{ "PRelu", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_PRelu },
OperatorInfo{ "PRelu", onnxruntime::kOnnxDomain, OnnxOperatorSet9::sc_sinceVer_PRelu },
OperatorInfo{ "ThresholdedRelu", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_ThresholdedRelu },
OperatorInfo{ "ThresholdedRelu", onnxruntime::kOnnxDomain, OnnxOperatorSet10::sc_sinceVer_ThresholdedRelu },
OperatorInfo{ "Elu", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_Elu },
OperatorInfo{ "Selu", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_Selu },
OperatorInfo{ "Softsign", onnxruntime::kOnnxDomain, OnnxOperatorSet7::sc_sinceVer_Softsign },
@ -330,6 +339,41 @@ namespace Dml
return std::string("fused_").append(name);
}
#if _DEBUG
// This asserts that an exact match for the operator exists in the tables of fusable ops, if a prior version exists
void AssertFusableOperatorSupportsVersionIfExists(
std::string_view type,
std::string_view domain,
int sinceVersion)
{
for (const OperatorInfo& operatorInfo : c_fusableOps)
{
if (operatorInfo.type == type && operatorInfo.domain == domain && operatorInfo.sinceVersion < sinceVersion)
{
assert(std::end(c_fusableOps) != std::find(
std::begin(c_fusableOps),
std::end(c_fusableOps),
OperatorInfo{ type, domain, sinceVersion }));
break;
}
}
for (const OperatorInfo& operatorInfo : c_activationOps)
{
if (operatorInfo.type == type && operatorInfo.domain == domain && operatorInfo.sinceVersion < sinceVersion)
{
assert(std::end(c_activationOps) != std::find(
std::begin(c_activationOps),
std::end(c_activationOps),
OperatorInfo{ type, domain, sinceVersion }));
break;
}
}
}
#endif
} // namespace FusionHelpers
uint32_t GetDmlAdjustedAxis(int32_t onnxAxis, const MLOperatorKernelCreationContext& kernelCreationContext, uint32_t dmlDimCount)
@ -359,7 +403,7 @@ namespace Dml
}
}
uint32_t MapStringToIndex(std::string_view mode, gsl::span<const NameAndIndex> nameAndIndexList)
std::optional<uint32_t> TryMapStringToIndex(std::string_view mode, gsl::span<const NameAndIndex> nameAndIndexList)
{
for (auto& nameAndIndex : nameAndIndexList)
{
@ -369,7 +413,7 @@ namespace Dml
}
}
ML_INVALID_ARGUMENT("Unknown mode value.");
return {};
}
DML_INTERPOLATION_MODE MapStringToInteropolationMode(std::string_view mode)
@ -387,7 +431,11 @@ namespace Dml
{"BILINEAR", DML_INTERPOLATION_MODE_LINEAR},
{"bilinear", DML_INTERPOLATION_MODE_LINEAR},
};
return MapStringToIndex<DML_INTERPOLATION_MODE>(mode, mapping);
if (auto index = TryMapStringToIndex<DML_INTERPOLATION_MODE>(mode, mapping))
{
return *index;
}
ML_INVALID_ARGUMENT("Unknown interpolation mode");
}
DML_DEPTH_SPACE_ORDER MapStringToDepthSpaceMode(std::string_view mode)
@ -397,7 +445,11 @@ namespace Dml
{"DCR", DML_DEPTH_SPACE_ORDER_DEPTH_COLUMN_ROW},
{"CRD", DML_DEPTH_SPACE_ORDER_COLUMN_ROW_DEPTH},
};
return MapStringToIndex<DML_DEPTH_SPACE_ORDER>(mode, mapping);
if (auto index = TryMapStringToIndex<DML_DEPTH_SPACE_ORDER>(mode, mapping))
{
return *index;
}
ML_INVALID_ARGUMENT("Unknown depth/space order");
}
} // namespace Dml

View file

@ -47,6 +47,13 @@ namespace Dml
// produce the attribute for Activation in a fused Conv+Activation kernel.
std::string GetFusedAttributeName(std::string_view name);
#if _DEBUG
void AssertFusableOperatorSupportsVersionIfExists(
std::string_view type,
std::string_view domain,
int sinceVersion);
#endif
} // namespace FusionHelpers
// Given an axis in ONNX axis numbering, return the axis adjusted for DML based on how the sizes have been coerced.
@ -64,12 +71,14 @@ namespace Dml
};
template<typename T>
T MapStringToIndex(std::string_view mode, gsl::span<const NameAndIndex> nameAndIndexList)
std::optional<T> TryMapStringToIndex(std::string_view mode, gsl::span<const NameAndIndex> nameAndIndexList)
{
return static_cast<T>(MapStringToIndex(mode, nameAndIndexList));
static_assert(sizeof(T) == sizeof(uint32_t));
auto result = TryMapStringToIndex(mode, nameAndIndexList);
return *reinterpret_cast<std::optional<T>*>(std::addressof(result));
}
uint32_t MapStringToIndex(std::string_view mode, gsl::span<const NameAndIndex> nameAndIndexList);
std::optional<uint32_t> TryMapStringToIndex(std::string_view mode, gsl::span<const NameAndIndex> nameAndIndexList);
DML_INTERPOLATION_MODE MapStringToInteropolationMode(std::string_view mode);