Layer dev paulm (#2492)

* commetns for dml graph transformer
fixed ort value passing using the allocatir info

* fixed and coded maps and sequences across the abi
This commit is contained in:
Paul McDaniel 2019-11-26 18:53:08 -08:00 committed by GitHub
parent 8346ddf222
commit c27f56c38e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 124 additions and 104 deletions

View file

@ -18,7 +18,7 @@ set(WINML_TEST_INC_DIR
function(set_winml_target_properties target)
set_target_properties(${target} PROPERTIES
FOLDER "WinMLTest"
FOLDER "ONNXRuntimeTest/winml"
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO

View file

@ -273,6 +273,7 @@ struct Value : Base<OrtValue> {
size_t GetStringTensorDataLength() const;
void GetStringTensorContent(void* buffer, size_t buffer_length, size_t* offsets, size_t offsets_count) const;
std::vector<std::string> GetStrings();
template <typename T>
T* GetTensorMutableData();

View file

@ -444,6 +444,33 @@ inline void Value::GetStringTensorContent(void* buffer, size_t buffer_length, si
ThrowOnError(Global<void>::api_.GetStringTensorContent(p_, buffer, buffer_length, offsets, offsets_count));
}
inline std::vector<std::string> Value::GetStrings() {
std::vector<std::string> out;
// make sure this is an array of strings
auto shape = this->GetTensorTypeAndShapeInfo().GetShape();
// there needs to be only one dimension
if (shape.size() != 1) throw Ort::Exception("shape.size() != 1", ORT_INVALID_ARGUMENT);
// make a big buffer to hold all the string data
size_t buflen = this->GetStringTensorDataLength();
std::vector<uint8_t> buf(buflen);
std::vector<size_t> offsets(shape[0]);
this->GetStringTensorContent(buf.data(), buf.size(), offsets.data(), offsets.size());
// now go build all the strings
for (auto i = 0; i < shape[0]; ++i) {
std::string str;
size_t strlen = 0;
// are we on the last one?
if (i == (shape[0] - 1ll)) {
strlen = buflen - offsets[i];
} else {
strlen = offsets[i + 1ll] - offsets[i];
}
str.append(reinterpret_cast<const char *>(buf.data() + offsets[i]), strlen);
out.push_back(str);
}
return out;
}
template <typename T>
T* Value::GetTensorMutableData() {
T* out;

View file

@ -25,48 +25,17 @@
namespace GraphTransformerHelpers
{
void RegisterGraphTransformers(onnxruntime::InferenceSession* lotusSession, bool registerLotusTransforms)
void RegisterGraphTransformers(onnxruntime::InferenceSession* lotusSession)
{
// Register Lotus graph transformers
// we were able to combine all of the winml/dml/ort work except for 2 transformers.
// these 2 are tracked by :
// Bug 22973884 : Fix issues with BatchNorm + Add and BatchNorm + Mul handling implicit inputs, and move from Winml to ORT
//
// TODO: Work out issues controlling graph optimization passes through ORT's optimization level
// and rule list. In the meantime (and before new transformers are tested in Winml), passes
// are registered explicitly, and the optimization level is set to default above (no optimization).
//
// Issues:
// Why is UnsqueezeElimination not registered by name in ORT?
// Why are level 2 (default) transformers not run before partitioning, which the DML XP requires?
// Why are level2 transformers only enabled on the CPU provider in GenerateTransformers?
// Why does name filtering only apply to rule based graph transformers?
// Why is Matmul+Add not used when contrib ops are disabled?
if (registerLotusTransforms)
{
lotusSession->RegisterGraphTransformer(std::move(std::make_unique<onnxruntime::ConstantFolding>()), onnxruntime::TransformerLevel::Level1);
}
std::unique_ptr<onnxruntime::RuleBasedGraphTransformer> rule_transformer =
std::make_unique<onnxruntime::RuleBasedGraphTransformer>("WinmlRuleTransformer");
if (registerLotusTransforms)
{
rule_transformer->Register(std::make_unique<onnxruntime::EliminateIdentity>());
rule_transformer->Register(std::make_unique<onnxruntime::UnsqueezeElimination>());
rule_transformer->Register(std::make_unique<onnxruntime::EliminateDropout>());
rule_transformer->Register(std::make_unique<onnxruntime::EliminateSlice>());
rule_transformer->Register(std::make_unique<onnxruntime::ConvBNFusion>());
rule_transformer->Register(std::make_unique<onnxruntime::ConvMulFusion>());
rule_transformer->Register(std::make_unique<onnxruntime::ConvAddFusion>());
}
rule_transformer->Register(std::make_unique<onnxruntime::BatchNormalizationMulFusion>());
rule_transformer->Register(std::make_unique<onnxruntime::BatchNormalizationAddFusion>());
lotusSession->RegisterGraphTransformer(std::move(rule_transformer), onnxruntime::TransformerLevel::Level1);
if (registerLotusTransforms)
{
lotusSession->RegisterGraphTransformer(std::move(std::make_unique<onnxruntime::MatMulAddFusion>()), onnxruntime::TransformerLevel::Level1);
}
}
}

View file

@ -5,5 +5,5 @@
namespace GraphTransformerHelpers
{
void RegisterGraphTransformers(onnxruntime::InferenceSession* lotusSession, bool registerLotusTransforms);
void RegisterGraphTransformers(onnxruntime::InferenceSession* lotusSession);
}

View file

@ -574,12 +574,11 @@ HRESULT STDMETHODCALLTYPE GetOperatorRegistry(ILearningModelOperatorProviderNati
return static_cast<AllocatorWrapper*>(this_)->impl_->Free(p);
}
static const struct OrtMemoryInfo* ORT_API_CALL InfoImpl(const struct OrtAllocator* this_) {
return &(static_cast<const AllocatorWrapper*>(this_)->info_);
return &(static_cast<const AllocatorWrapper*>(this_)->impl_->Info());
}
private:
onnxruntime::AllocatorPtr impl_;
OrtMemoryInfo info_;
};
HRESULT STDMETHODCALLTYPE GetProviderAllocator(
@ -654,9 +653,10 @@ class IOBinding : public Microsoft::WRL::RuntimeClass<
InferenceSession::InferenceSession(onnxruntime::InferenceSession* session) : session_(session) {
}
void STDMETHODCALLTYPE InferenceSession::RegisterGraphTransformers(bool registerLotusTransforms) {
void STDMETHODCALLTYPE InferenceSession::RegisterGraphTransformers() {
#ifdef USE_DML
GraphTransformerHelpers::RegisterGraphTransformers(session_.get(), registerLotusTransforms);
// Bug 22973884 : Fix issues with BatchNorm + Add and BatchNorm + Mul handling implicit inputs, and move from Winml to ORT
GraphTransformerHelpers::RegisterGraphTransformers(session_.get());
#endif USE_DML
}

View file

@ -39,7 +39,7 @@ MIDL_INTERFACE("a848faf6-5a2e-4a7f-b622-cc036f71e28a") IModelProto : IUnknown{
MIDL_INTERFACE("6ec766ef-6365-42bf-b64f-ae85c015adb8") IInferenceSession : IUnknown {
virtual onnxruntime::InferenceSession* STDMETHODCALLTYPE get() = 0;
virtual void STDMETHODCALLTYPE RegisterGraphTransformers(bool registerLotusTransforms) = 0;
virtual void STDMETHODCALLTYPE RegisterGraphTransformers() = 0;
virtual HRESULT STDMETHODCALLTYPE RegisterCustomRegistry(IMLOperatorRegistry * registry) = 0;
virtual HRESULT STDMETHODCALLTYPE LoadModel(IModelProto* model_proto) = 0;
virtual HRESULT STDMETHODCALLTYPE NewIOBinding(IIOBinding** io_binding) = 0;
@ -131,7 +131,7 @@ public:
InferenceSession(onnxruntime::InferenceSession * session);
onnxruntime::InferenceSession* STDMETHODCALLTYPE get() override { return session_.get(); }
void STDMETHODCALLTYPE RegisterGraphTransformers(bool registerLotusTransforms) override;
void STDMETHODCALLTYPE RegisterGraphTransformers() override;
HRESULT STDMETHODCALLTYPE RegisterCustomRegistry(IMLOperatorRegistry* registry) override;
HRESULT STDMETHODCALLTYPE LoadModel(IModelProto* model_proto) override;
HRESULT STDMETHODCALLTYPE NewIOBinding(IIOBinding** io_binding) override;

View file

@ -130,8 +130,7 @@ void LearningModelSession::Initialize() {
WINML_THROW_IF_FAILED(session->RegisterCustomRegistry(model->GetOperatorRegistry()));
// Register only the transformers not already in ORT
const bool registerLotusTransformers = false;
session->RegisterGraphTransformers(registerLotusTransformers);
session->RegisterGraphTransformers();
// Load the model into the session
WINML_THROW_IF_FAILED(session->LoadModel(model_proto.get()));

View file

@ -63,14 +63,29 @@ struct MapBase : winrt::implements<
}
template <typename TRawType>
static TRawType ConvertToABIType(const typename ValidLotusType<TRawType>::Type& lotusValue) {
TRawType copy = lotusValue;
return copy;
static std::vector<TRawType> ConvertToABIType(Ort::Value& ort_value) {
// make sure this is an array of these types
auto shape = ort_value.GetTensorTypeAndShapeInfo().GetShape();
// there needs to be only one dimension
THROW_HR_IF(E_INVALIDARG, shape.size() != 1);
auto lotus_value = ort_value.GetTensorMutableData<typename ValidLotusType<TRawType>::Type>();
// now go through all the entries
std::vector<TRawType> out;
for (auto i = 0; i < shape[0]; i++) {
out.push_back(lotus_value[i]);
}
// retun the vector
return out;
}
template <>
static typename winrt::hstring ConvertToABIType(const typename ValidLotusType<winrt::hstring>::Type& lotusValue) {
return WinML::Strings::HStringFromUTF8(lotusValue);
static std::vector<winrt::hstring> ConvertToABIType<winrt::hstring>(Ort::Value& ort_value) {
auto strings = ort_value.GetStrings();
std::vector<winrt::hstring> out;
for (auto i = 0; i < strings.size(); ++i) {
out.push_back(WinML::Strings::HStringFromUTF8(strings[i].c_str()));
}
return out;
}
MapBase(ABIMap const& data) : data_(data) {}
@ -133,7 +148,7 @@ struct MapBase : winrt::implements<
}
template <typename TLotusKey, typename TLotusValue>
static Ort::Value CreateOrtMap(TLotusKey * keys, TLotusValue * values, size_t len) {
static Ort::Value CreateOrtMap(TLotusKey* keys, TLotusValue* values, size_t len) {
// now create OrtValue wrappers over the buffers
auto cpu_memory = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeDefault);
std::vector<int64_t> shape = {static_cast<int64_t>(len)};
@ -149,6 +164,7 @@ struct MapBase : winrt::implements<
// TODO : we need to handle inputs. for now only handle outputs and don't pre allocate anything
if (context.type == WinML::BindingType::kOutput) {
*ort_value = nullptr;
return S_OK;
}
@ -156,7 +172,7 @@ struct MapBase : winrt::implements<
ConvertToLotusMap(data_);
// and make the map
*ort_value = CreateOrtMap(lotus_data_->first.data(), lotus_data_->second.data(), lotus_data_->first.size());
*ort_value = CreateOrtMap(lotus_data_->first.data(), lotus_data_->second.data(), lotus_data_->first.size()).release();
return S_OK;
}
@ -171,8 +187,25 @@ struct MapBase : winrt::implements<
(BindingContext& context, OrtValue* ort_value) {
data_.Clear();
void* pResource = nullptr;
Ort::ThrowOnError(Ort::GetApi().GetTensorMutableData(ort_value, &pResource));
Ort::AllocatorWithDefaultOptions allocator;
// get the keys
OrtValue* ptr = nullptr;
Ort::ThrowOnError(Ort::GetApi().GetValue(ort_value, 0, allocator, &ptr));
Ort::Value keys{ptr};
// get the values
ptr = nullptr;
Ort::ThrowOnError(Ort::GetApi().GetValue(ort_value, 1, allocator, &ptr));
Ort::Value values{ptr};
auto keys_vector = ConvertToABIType<TKey>(keys);
auto values_vector = ConvertToABIType<TValue>(values);
auto len = keys.GetCount();
for (auto i = 0; i < len; ++i) {
data_.Insert(keys_vector[i], values_vector[i]);
}
return S_OK;
// TODO: code this
//const LotusMap& map = *static_cast<LotusMap*>(pResource);

View file

@ -34,6 +34,8 @@ struct SequenceBase : public winrt::implements<
using TKey = std::string;
using TValue = float;
using Type = std::pair<std::vector<TKey>, std::vector<TValue>>;
using ABIKey = winrt::hstring;
using ABIValue = TValue;
};
template <>
struct ValidLotusType<AbiMapInt64BitToFloat> {
@ -41,6 +43,8 @@ struct SequenceBase : public winrt::implements<
using TKey = int64_t;
using TValue = float;
using Type = std::pair<std::vector<TKey>, std::vector<TValue>>;
using ABIKey = TKey;
using ABIValue = TValue;
};
template <typename TElement>
@ -189,8 +193,7 @@ struct SequenceBase : public winrt::implements<
return Ort::Value::CreateMap(keys_ort_value, values_ort_value);
}
STDMETHOD(GetOrtValue)
(
STDMETHOD(GetOrtValue)(
WinML::BindingContext& context,
OrtValue** ort_value) {
// TODO: Tensorized data should be cached so multiple bindings work more efficiently
@ -235,71 +238,59 @@ struct SequenceBase : public winrt::implements<
}
template <typename TRawType>
static TRawType
ConvertToABIType(
const typename ValidLotusType<TRawType>::Type& lotus_value) {
// make a copy
TRawType copy = lotus_value;
return copy;
static std::vector<TRawType> ConvertToABIType(Ort::Value& ort_value) {
// make sure this is an array of these types
auto shape = ort_value.GetTensorTypeAndShapeInfo().GetShape();
// there needs to be only one dimension
THROW_HR_IF(E_INVALIDARG, shape.size() != 1);
auto lotus_value = ort_value.GetTensorMutableData<typename ValidLotusType<TRawType>::Type>();
// now go through all the entries
std::vector<TRawType> out;
for (auto i = 0; i < shape[0]; i++) {
out.push_back(lotus_value[i]);
}
// return the vector
return out;
}
template <>
static winrt::hstring
ConvertToABIType(
const typename ValidLotusType<winrt::hstring>::Type& lotus_value) {
return WinML::Strings::HStringFromUTF8(lotus_value);
}
static AbiMapStringToFloat
ConvertToABIType(
typename ValidLotusType<AbiMapStringToFloat>::TKey* keys,
typename ValidLotusType<AbiMapStringToFloat>::TValue* values,
size_t len) {
// need to make a copy to convert std::string to hstring
std::map<winrt::hstring, float> copy;
for (auto i = 0; i < len; ++i) {
auto key = WinML::Strings::HStringFromUTF8(keys[i]);
copy[key] = values[i];
static std::vector<winrt::hstring> ConvertToABIType<winrt::hstring>(Ort::Value& ort_value) {
auto strings = ort_value.GetStrings();
std::vector<winrt::hstring> out;
for (auto i = 0; i < strings.size(); ++i) {
out.push_back(WinML::Strings::HStringFromUTF8(strings[i].c_str()));
}
return winrt::single_threaded_map<winrt::hstring, float>(
std::move(copy));
return out;
}
static AbiMapInt64BitToFloat
ConvertToABIType(
typename ValidLotusType<AbiMapInt64BitToFloat>::TKey* keys,
typename ValidLotusType<AbiMapInt64BitToFloat>::TValue* values,
size_t len) {
// need to make a copy since stl objects are not ABI safe.
std::map<int64_t, float> copy;
for (auto i = 0; i < len; ++i) {
copy[keys[i]] = values[i];
}
return winrt::single_threaded_map<int64_t, float>(
std::move(copy));
}
STDMETHOD(UpdateSourceResourceData)
(
STDMETHOD(UpdateSourceResourceData)(
BindingContext& context,
OrtValue* ort_value) {
auto writable_vector = data_.as<wfc::IVector<T>>();
writable_vector.Clear();
Ort::AllocatorWithDefaultOptions allocator;
Ort::Value ort_value_in(ort_value);
auto len = ort_value_in.GetCount();
size_t len;
Ort::ThrowOnError(Ort::GetApi().GetValueCount(ort_value, &len));
for (auto i = 0; i < len; ++i) {
auto map = ort_value_in.GetValue(i, allocator);
OrtValue* out = nullptr;
Ort::ThrowOnError(Ort::GetApi().GetValue(ort_value, i, allocator, &out));
Ort::Value map{out};
auto keys = map.GetValue(0, allocator);
auto values = map.GetValue(1, allocator);
auto keys_data = keys.GetTensorMutableData<ValidLotusType<T>::TKey>();
auto values_data = keys.GetTensorMutableData<ValidLotusType<T>::TValue>();
writable_vector.Append(ConvertToABIType(keys_data, values_data, keys.GetTensorTypeAndShapeInfo().GetElementCount()));
}
auto keys_vector = ConvertToABIType<typename ValidLotusType<T>::ABIKey>(keys);
auto values_vector = ConvertToABIType<typename ValidLotusType<T>::ABIValue>(values);
std::map<typename ValidLotusType<T>::ABIKey, typename ValidLotusType<T>::ABIValue> std_map;
for (auto j = 0; j < keys_vector.size(); ++j) {
std_map[keys_vector[j]] = values_vector[j];
}
auto abi_map = winrt::single_threaded_map<typename ValidLotusType<T>::ABIKey, typename ValidLotusType<T>::ABIValue>(
std::move(std_map));
writable_vector.Append(abi_map);
}
return S_OK;
}