diff options
Diffstat (limited to 'compiler/souschef')
-rw-r--r-- | compiler/souschef/CMakeLists.txt | 9 | ||||
-rw-r--r-- | compiler/souschef/include/souschef/Data/Explicit.h | 56 | ||||
-rw-r--r-- | compiler/souschef/include/souschef/Data/Gaussian.h | 63 | ||||
-rw-r--r-- | compiler/souschef/include/souschef/DataChef.def | 19 | ||||
-rw-r--r-- | compiler/souschef/include/souschef/Dims.h | 1 | ||||
-rw-r--r-- | compiler/souschef/src/Explicit.cpp | 98 | ||||
-rw-r--r-- | compiler/souschef/src/Gaussian.cpp | 121 | ||||
-rw-r--r-- | compiler/souschef/src/LexicalCast.cpp | 27 |
8 files changed, 342 insertions, 52 deletions
diff --git a/compiler/souschef/CMakeLists.txt b/compiler/souschef/CMakeLists.txt index ca7eddc6f..8dcf4c2b8 100644 --- a/compiler/souschef/CMakeLists.txt +++ b/compiler/souschef/CMakeLists.txt @@ -1,13 +1,20 @@ nnas_find_package(Protobuf QUIET) +nnas_find_package(Fp16Source QUIET) if(NOT Protobuf_FOUND) - message(STATUS "Build souschef: FAILED (missing Protobuf") + message(STATUS "Build souschef: FAILED (missing Protobuf)") return() endif(NOT Protobuf_FOUND) +if(NOT Fp16Source_FOUND) + message(STATUS "Build souschef: FAILED (missing Fp16Source)") + return() +endif(NOT Fp16Source_FOUND) + file(GLOB_RECURSE SOURCES "src/*.cpp") add_library(souschef STATIC ${SOURCES}) set_target_properties(souschef PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(souschef PRIVATE ${Fp16Source_DIR}/include) target_include_directories(souschef PUBLIC include) target_link_libraries(souschef PUBLIC libprotobuf) diff --git a/compiler/souschef/include/souschef/Data/Explicit.h b/compiler/souschef/include/souschef/Data/Explicit.h index 6e5ee819e..434d0ec2c 100644 --- a/compiler/souschef/include/souschef/Data/Explicit.h +++ b/compiler/souschef/include/souschef/Data/Explicit.h @@ -59,6 +59,27 @@ private: std::vector<T> _values; }; +template <> class ExplicitDataChef<std::string> final : public DataChef +{ +public: + ExplicitDataChef() + { + // DO NOTHING + } + +public: + std::vector<uint8_t> generate(int32_t count) const override; + +public: + void insert(const std::string &value) { _values.emplace_back(value); } + +private: + void write_value(std::vector<uint8_t> &res, int32_t value) const; + +private: + std::vector<std::string> _values; +}; + template <typename T> struct ExplicitDataChefFactory : public DataChefFactory { std::unique_ptr<DataChef> create(const Arguments &args) const @@ -75,6 +96,41 @@ template <typename T> struct ExplicitDataChefFactory : public DataChefFactory } }; +class ExplicitFloat16DataChef final : public DataChef +{ +public: + ExplicitFloat16DataChef() + { + // DO NOTHING + } + +public: + std::vector<uint8_t> generate(int32_t count) const override; + +public: + void insert(const float &value) { _values.emplace_back(value); } + +private: + // NOTE store values in float but will convert to uint16_t in generate() + std::vector<float> _values; +}; + +struct ExplicitFloat16DataChefFactory : public DataChefFactory +{ + std::unique_ptr<DataChef> create(const Arguments &args) const + { + std::unique_ptr<ExplicitFloat16DataChef> res{new ExplicitFloat16DataChef}; + + for (uint32_t n = 0; n < args.count(); ++n) + { + auto const value = to_number<float>(args.value(n)); + res->insert(value); + } + + return std::move(res); + } +}; + } // namespace souschef #endif // __SOUSCHEF_DATA_EXPLICIT_H__ diff --git a/compiler/souschef/include/souschef/Data/Gaussian.h b/compiler/souschef/include/souschef/Data/Gaussian.h index 75570e0b8..801bff8e9 100644 --- a/compiler/souschef/include/souschef/Data/Gaussian.h +++ b/compiler/souschef/include/souschef/Data/Gaussian.h @@ -41,6 +41,22 @@ private: float _stddev; }; +class GaussianFloat16DataChef final : public DataChef +{ +public: + GaussianFloat16DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev} + { + // DO NOTHING + } + +public: + std::vector<uint8_t> generate(int32_t count) const override; + +private: + float _mean; + float _stddev; +}; + class GaussianInt32DataChef final : public DataChef { public: @@ -57,6 +73,22 @@ private: float _stddev; }; +class GaussianInt16DataChef final : public DataChef +{ +public: + GaussianInt16DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev} + { + // DO NOTHING + } + +public: + std::vector<uint8_t> generate(int32_t count) const override; + +private: + float _mean; + float _stddev; +}; + class GaussianUint8DataChef final : public DataChef { public: @@ -73,6 +105,22 @@ private: float _stddev; }; +class GaussianInt8DataChef final : public DataChef +{ +public: + GaussianInt8DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev} + { + // DO NOTHING + } + +public: + std::vector<uint8_t> generate(int32_t count) const override; + +private: + float _mean; + float _stddev; +}; + struct GaussianFloat32DataChefFactory : public DataChefFactory { std::unique_ptr<DataChef> create(const Arguments &args) const; @@ -83,11 +131,26 @@ struct GaussianInt32DataChefFactory : public DataChefFactory std::unique_ptr<DataChef> create(const Arguments &args) const; }; +struct GaussianInt16DataChefFactory : public DataChefFactory +{ + std::unique_ptr<DataChef> create(const Arguments &args) const; +}; + struct GaussianUint8DataChefFactory : public DataChefFactory { std::unique_ptr<DataChef> create(const Arguments &args) const; }; +struct GaussianFloat16DataChefFactory : public DataChefFactory +{ + std::unique_ptr<DataChef> create(const Arguments &args) const; +}; + +struct GaussianInt8DataChefFactory : public DataChefFactory +{ + std::unique_ptr<DataChef> create(const Arguments &args) const; +}; + } // namespace souschef #endif // __SOUSCHEF_DATA_GAUSSIAN_H__ diff --git a/compiler/souschef/include/souschef/DataChef.def b/compiler/souschef/include/souschef/DataChef.def deleted file mode 100644 index 28901db18..000000000 --- a/compiler/souschef/include/souschef/DataChef.def +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef DATA_CHEF -#error "Define DATA_CHEF first" -#endif // DATA_CHEF - -// DATA_CHEF(TYPE, NAME, FACTORY_CLASS) -// "TYPE" SHOULD BE an enum tag of tflchef::TensorType -DATA_CHEF(FLOAT32, constant, ConstantDataChefFactory<float>) -DATA_CHEF(BOOL, constant, ConstantDataChefFactory<bool>) -DATA_CHEF(UINT8, constant, ConstantDataChefFactory<uint8_t>) -DATA_CHEF(INT32, constant, ConstantDataChefFactory<int32_t>) -DATA_CHEF(INT64, constant, ConstantDataChefFactory<int64_t>) -DATA_CHEF(INT64, explicit, ExplicitDataChefFactory<int64_t>) -DATA_CHEF(INT32, explicit, ExplicitDataChefFactory<int32_t>) -DATA_CHEF(UINT8, explicit, ExplicitDataChefFactory<uint8_t>) -DATA_CHEF(BOOL, explicit, ExplicitDataChefFactory<bool>) -DATA_CHEF(FLOAT32, explicit, ExplicitDataChefFactory<float>) -DATA_CHEF(FLOAT32, gaussian, GaussianFloat32DataChefFactory) -DATA_CHEF(INT32, gaussian, GaussianInt32DataChefFactory) -DATA_CHEF(UINT8, gaussian, GaussianUint8DataChefFactory) diff --git a/compiler/souschef/include/souschef/Dims.h b/compiler/souschef/include/souschef/Dims.h index 52c64dd47..fabbf3f95 100644 --- a/compiler/souschef/include/souschef/Dims.h +++ b/compiler/souschef/include/souschef/Dims.h @@ -17,6 +17,7 @@ #ifndef __SOUSCHEF_DIMS_H__ #define __SOUSCHEF_DIMS_H__ +#include <cstdint> #include <functional> #include <numeric> #include <vector> diff --git a/compiler/souschef/src/Explicit.cpp b/compiler/souschef/src/Explicit.cpp new file mode 100644 index 000000000..3278ae3c3 --- /dev/null +++ b/compiler/souschef/src/Explicit.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "souschef/Data/Explicit.h" + +#include <string> +#include <vector> + +#include <fp16.h> + +namespace souschef +{ + +/** + * @note This emulates TensorFlow int DynamicBuffer::WriteToBuffer(char** buffer) method + * Memory structure: + * int32_t count + * int32_t offsets[count + 1] + * string values[count] + * where string is like std::string without ending null byte + */ +std::vector<uint8_t> ExplicitDataChef<std::string>::generate(int32_t count) const +{ + std::vector<uint8_t> res; + + // write count + write_value(res, count); + + // write first item offset + int32_t start = sizeof(int32_t) * (count + 2); + write_value(res, start); + + // write succeeding items offset (or the end) + int32_t offset = start; + for (uint32_t n = 0; n < count; ++n) + { + std::string const value = (n < _values.size()) ? _values.at(n) : std::string{}; + offset += value.length(); + write_value(res, offset); + } + + for (uint32_t n = 0; n < count; ++n) + { + std::string const value = (n < _values.size()) ? _values.at(n) : std::string{}; + const uint8_t *arr = reinterpret_cast<const uint8_t *>(value.c_str()); + + for (uint32_t b = 0; b < value.length(); ++b) + { + res.emplace_back(arr[b]); + } + } + + return res; +} + +void ExplicitDataChef<std::string>::write_value(std::vector<uint8_t> &res, int32_t value) const +{ + const uint8_t *arr = reinterpret_cast<const uint8_t *>(&value); + + for (uint32_t b = 0; b < sizeof(int32_t); ++b) + { + res.emplace_back(arr[b]); + } +} + +std::vector<uint8_t> ExplicitFloat16DataChef::generate(int32_t count) const +{ + std::vector<uint8_t> res; + + for (uint32_t n = 0; n < count; ++n) + { + float const fvalue = (n < _values.size()) ? _values.at(n) : 0.0; + uint16_t const value = fp16_ieee_from_fp32_value(fvalue); + auto const arr = reinterpret_cast<const uint8_t *>(&value); + + for (uint32_t b = 0; b < sizeof(uint16_t); ++b) + { + res.emplace_back(arr[b]); + } + } + + return res; +} + +} // namespace souschef diff --git a/compiler/souschef/src/Gaussian.cpp b/compiler/souschef/src/Gaussian.cpp index 4a5083d8e..71ab56792 100644 --- a/compiler/souschef/src/Gaussian.cpp +++ b/compiler/souschef/src/Gaussian.cpp @@ -22,26 +22,32 @@ #include <cassert> #include <stdexcept> +#include <limits> // std::numeric_limits + +#include <fp16.h> namespace souschef { -std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const +template <typename T> +static std::vector<uint8_t> generate_gaussian(int32_t count, float mean, float stddev, + std::minstd_rand::result_type seed) { - // TODO Support seed value override - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); - std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)}; - std::normal_distribution<float> dist{_mean, _stddev}; + std::normal_distribution<float> dist{mean, stddev}; std::vector<uint8_t> res; + constexpr float max_cap = std::numeric_limits<T>::max(); + constexpr float min_cap = std::numeric_limits<T>::lowest(); for (uint32_t n = 0; n < count; ++n) { - auto const value = dist(rand); + float raw_value = dist(rand); + const float capped_value = std::max(min_cap, std::min(max_cap, raw_value)); + auto const value = static_cast<T>(capped_value); auto const arr = reinterpret_cast<const uint8_t *>(&value); - for (uint32_t b = 0; b < sizeof(float); ++b) + for (uint32_t b = 0; b < sizeof(T); ++b) { res.emplace_back(arr[b]); } @@ -50,22 +56,42 @@ std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const return res; } -std::vector<uint8_t> GaussianInt32DataChef::generate(int32_t count) const +template <typename T> +static std::vector<uint8_t> generate_gaussian(int32_t count, float mean, float stddev) { - // TODO Support seed value override - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + auto time_stamp = std::chrono::system_clock::now().time_since_epoch().count(); + + // Note this is implementation defined, change if needed. + auto seed = static_cast<std::minstd_rand::result_type>(time_stamp); + + return generate_gaussian<T>(count, mean, stddev, seed); +} + +std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const +{ + return generate_gaussian<float>(count, _mean, _stddev); +} + +std::vector<uint8_t> GaussianFloat16DataChef::generate(int32_t count) const +{ + auto time_stamp = std::chrono::system_clock::now().time_since_epoch().count(); + auto seed = static_cast<std::minstd_rand::result_type>(time_stamp); std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)}; std::normal_distribution<float> dist{_mean, _stddev}; std::vector<uint8_t> res; + constexpr float max_cap = 1e9; + constexpr float min_cap = -1e9; for (uint32_t n = 0; n < count; ++n) { - auto const value = static_cast<int32_t>(dist(rand)); + float raw_value = dist(rand); + const float capped_value = std::max(min_cap, std::min(max_cap, raw_value)); + const uint16_t value = fp16_ieee_from_fp32_value(capped_value); auto const arr = reinterpret_cast<const uint8_t *>(&value); - for (uint32_t b = 0; b < sizeof(int32_t); ++b) + for (uint32_t b = 0; b < sizeof(uint16_t); ++b) { res.emplace_back(arr[b]); } @@ -74,28 +100,24 @@ std::vector<uint8_t> GaussianInt32DataChef::generate(int32_t count) const return res; } -std::vector<uint8_t> GaussianUint8DataChef::generate(int32_t count) const +std::vector<uint8_t> GaussianInt32DataChef::generate(int32_t count) const { - // TODO Support seed value override - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); - - std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)}; - std::normal_distribution<float> dist{_mean, _stddev}; - - std::vector<uint8_t> res; + return generate_gaussian<int32_t>(count, _mean, _stddev); +} - for (uint32_t n = 0; n < count; ++n) - { - auto const value = static_cast<uint8_t>(dist(rand)); // uint8_t for data type - auto const arr = reinterpret_cast<const uint8_t *>(&value); // uint8_t for byte streaming +std::vector<uint8_t> GaussianInt16DataChef::generate(int32_t count) const +{ + return generate_gaussian<int16_t>(count, _mean, _stddev); +} - for (uint32_t b = 0; b < sizeof(uint8_t); ++b) - { - res.emplace_back(arr[b]); - } - } +std::vector<uint8_t> GaussianUint8DataChef::generate(int32_t count) const +{ + return generate_gaussian<uint8_t>(count, _mean, _stddev); +} - return res; +std::vector<uint8_t> GaussianInt8DataChef::generate(int32_t count) const +{ + return generate_gaussian<int8_t>(count, _mean, _stddev); } std::unique_ptr<DataChef> GaussianFloat32DataChefFactory::create(const Arguments &args) const @@ -124,6 +146,19 @@ std::unique_ptr<DataChef> GaussianInt32DataChefFactory::create(const Arguments & return std::unique_ptr<DataChef>{new GaussianInt32DataChef{mean, stddev}}; } +std::unique_ptr<DataChef> GaussianInt16DataChefFactory::create(const Arguments &args) const +{ + if (args.count() != 2) + { + throw std::runtime_error{"invalid argument count: two arguments (mean/stddev) are expected"}; + } + + auto const mean = to_number<float>(args.value(0)); + auto const stddev = to_number<float>(args.value(1)); + + return std::unique_ptr<DataChef>{new GaussianInt16DataChef{mean, stddev}}; +} + std::unique_ptr<DataChef> GaussianUint8DataChefFactory::create(const Arguments &args) const { if (args.count() != 2) @@ -137,4 +172,30 @@ std::unique_ptr<DataChef> GaussianUint8DataChefFactory::create(const Arguments & return std::unique_ptr<DataChef>{new GaussianUint8DataChef{mean, stddev}}; } +std::unique_ptr<DataChef> GaussianInt8DataChefFactory::create(const Arguments &args) const +{ + if (args.count() != 2) + { + throw std::runtime_error{"invalid argument count: two arguments (mean/stddev) are expected"}; + } + + auto const mean = to_number<float>(args.value(0)); + auto const stddev = to_number<float>(args.value(1)); + + return std::unique_ptr<DataChef>{new GaussianInt8DataChef{mean, stddev}}; +} + +std::unique_ptr<DataChef> GaussianFloat16DataChefFactory::create(const Arguments &args) const +{ + if (args.count() != 2) + { + throw std::runtime_error{"invalid argument count: two arguments (mean/stddev) are expected"}; + } + + auto const mean = to_number<float>(args.value(0)); + auto const stddev = to_number<float>(args.value(1)); + + return std::unique_ptr<DataChef>{new GaussianFloat16DataChef{mean, stddev}}; +} + } // namespace souschef diff --git a/compiler/souschef/src/LexicalCast.cpp b/compiler/souschef/src/LexicalCast.cpp index 8e3d4cbbb..4468f1ec1 100644 --- a/compiler/souschef/src/LexicalCast.cpp +++ b/compiler/souschef/src/LexicalCast.cpp @@ -18,12 +18,25 @@ #include <cassert> #include <limits> +#include <stdexcept> namespace souschef { template <> float to_number(const std::string &s) { return std::stof(s); } template <> int to_number(const std::string &s) { return std::stoi(s); } +template <> int16_t to_number(const std::string &s) +{ + // There are no standard function to parse int16_t or short int + // This function simulates behavior similar stoi, stol and stoll + int res = std::stol(s); + // standard does not specify string in error message, this is arbitrary + if (res < std::numeric_limits<int16_t>::min() || res > std::numeric_limits<int16_t>::max()) + { + throw std::out_of_range("to_number<int16_t>"); + } + return res; +} template <> int64_t to_number(const std::string &s) { return std::stoll(s); } template <> uint8_t to_number(const std::string &s) { @@ -32,11 +45,21 @@ template <> uint8_t to_number(const std::string &s) assert(temp <= std::numeric_limits<uint8_t>::max()); return static_cast<uint8_t>(temp); } +template <> int8_t to_number(const std::string &s) +{ + int temp = std::stoi(s); + assert(temp >= std::numeric_limits<int8_t>::min()); + assert(temp <= std::numeric_limits<int8_t>::max()); + return static_cast<int8_t>(temp); +} template <> bool to_number(const std::string &s) { - if (std::stoi(s) || s == "T" || s == "t" || s == "TRUE" || s == "true") + if (s == "T" || s == "t" || s == "TRUE" || s == "true" || s == "1") return true; - return false; + if (s == "F" || s == "f" || s == "FALSE" || s == "false" || s == "0") + return false; + throw std::invalid_argument("Unsupported boolean argument"); } +template <> std::string to_number(const std::string &s) { return s; } } // namespace souschef |