--- /dev/null
+#ifndef __NNFW_UTIL_FEATURE_TEXT_FORMATTER_H__
+#define __NNFW_UTIL_FEATURE_TEXT_FORMATTER_H__
+
+#include "util/feature/Shape.h"
+#include "util/feature/Reader.h"
+
+#include <ostream>
+#include <iomanip>
+#include <limits>
+
+namespace nnfw
+{
+namespace util
+{
+namespace feature
+{
+
+template <typename T> class TextFormatter
+{
+public:
+ TextFormatter(const Shape &shape, const Reader<T> &data)
+ : _shape(shape), _data(data)
+ {
+ // DO NOTHING
+ }
+
+public:
+ const Shape &shape(void) const { return _shape; }
+ const Reader<T> &data(void) const { return _data; }
+
+private:
+ const Shape &_shape;
+ const Reader<T> &_data;
+};
+
+template <typename T>
+std::ostream &operator<<(std::ostream &os, const TextFormatter<T> &fmt)
+{
+ const auto &shape = fmt.shape();
+
+ for (uint32_t ch = 0; ch < shape.C; ++ch)
+ {
+ os << " Channel " << ch << ":" << std::endl;
+ for (uint32_t row = 0; row < shape.H; ++row)
+ {
+ os << " ";
+ for (uint32_t col = 0; col < shape.W; ++col)
+ {
+ const auto value = fmt.data().at(ch, row, col);
+ os << std::right;
+ os << std::fixed;
+ os << std::setw(std::numeric_limits<T>::digits10 + 2);
+ os << std::setprecision(5);
+ os << value;
+ os << " ";
+ }
+ os << std::endl;
+ }
+ }
+
+ return os;
+}
+
+} // namespace feature
+} // namespace util
+} // namespace nnfw
+
+#endif // __NNFW_UTIL_FEATURE_TEXT_FORMATTER_H__
#include "IO_accessor.h"
+#include <cassert>
+
namespace nnfw {
namespace kernel {
namespace acl {
return offset;
}
+static uint32_t getElementOffset(const android::nn::Shape& shape,
+ uint32_t ch, uint32_t row, uint32_t col)
+{
+ assert(getSizeOfDimension(shape, 0) == 1);
+ assert(shape.dimensions.size() == 4);
+
+ // TODO Optimize this!
+ const uint32_t W = getSizeOfDimension(shape, 2);
+ const uint32_t C = getSizeOfDimension(shape, 3);
+
+ int offset = 0;
+
+ // NNAPI uses NHWC ordering
+ offset += row * W * C;
+ offset += col * C;
+ offset += ch;
+
+ return offset;
+}
+
+static uint32_t getElementOffset(const android::nn::Shape& shape,
+ uint32_t nth, uint32_t ch, uint32_t row, uint32_t col)
+{
+ assert(shape.dimensions.size() == 4);
+
+ // TODO Optimize this!
+ const uint32_t H = getSizeOfDimension(shape, 1);
+ const uint32_t W = getSizeOfDimension(shape, 2);
+ const uint32_t C = getSizeOfDimension(shape, 3);
+
+ int offset = 0;
+
+ // NNAPI uses NHWC ordering
+ offset += nth * H * W * C;
+ offset += row * W * C;
+ offset += col * C;
+ offset += ch;
+
+ return offset;
+}
+
bool InputAccessor::access_tensor(arm_compute::ITensor &tensor)
{
arm_compute::Window window;
execute_window_loop(window, [&](const arm_compute::Coordinates& id)
{
- uint32_t offset = getOffsetNCHW(_inputShape, id);
+ const uint32_t ch = id[2];
+ const uint32_t row = id[1];
+ const uint32_t col = id[0];
+
+ uint32_t offset = getElementOffset(_inputShape, ch, row, col);
+
*reinterpret_cast<float *>(tensor.ptr_to_element(id)) =
*(_inputData + offset);
});
execute_window_loop(window, [&](const arm_compute::Coordinates& id)
{
- uint32_t offset = getOffsetNCHW(_filterShape, id);
+ const uint32_t nth = id[3];
+ const uint32_t ch = id[2];
+ const uint32_t row = id[1];
+ const uint32_t col = id[0];
+
+ uint32_t offset = getElementOffset(_filterShape, nth, ch, row, col);
+
*reinterpret_cast<float *>(tensor.ptr_to_element(id)) =
*(_filterData + offset);
});
execute_window_loop(window, [&](const arm_compute::Coordinates& id)
{
- uint32_t offset = getOffsetNCHW(_outputShape, id);
+ const uint32_t ch = id[2];
+ const uint32_t row = id[1];
+ const uint32_t col = id[0];
+
+ uint32_t offset = getElementOffset(_outputShape, ch, row, col);
+
*(_outputData + offset) =
*reinterpret_cast<float *>(tensor.ptr_to_element(id));
});
#include <cassert>
+#include "util/feature/Shape.h"
+#include "util/feature/Reader.h"
+#include "util/feature/TextFormatter.h"
+
+namespace nnfw
+{
+namespace support
+{
+namespace nnapi
+{
+namespace feature
+{
+
+template<typename T> class Reader;
+
+template<> class Reader<float> : public nnfw::util::feature::Reader<float>
+{
+public:
+ Reader(const nnfw::util::feature::Shape &shape, const float *base)
+ : _shape{shape}, _base{base}
+ {
+ // DO NOTHING
+ }
+
+public:
+ float at(uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ return *(_base + getElementOffset(ch, row, col));
+ }
+
+private:
+ uint32_t getElementOffset(uint32_t ch, uint32_t row, uint32_t col) const
+ {
+ uint32_t res = 0;
+
+ // NNAPI assumes that NHWC ordering for feature map
+ res += row * _shape.W * _shape.C;
+ res += col * _shape.C;
+ res += ch;
+
+ return res;
+ }
+
+private:
+ nnfw::util::feature::Shape _shape;
+ const float *_base;
+};
+
+nnfw::util::feature::Shape asFeatureShape(const android::nn::Shape& shape)
+{
+ // NNAPI assumes the following ordering:
+ //
+ // dim(0) -> N
+ // dim(1) -> H
+ // dim(2) -> W
+ // dim(3) -> C
+ //
+ int32_t c = android::nn::getSizeOfDimension(shape, 3);
+ int32_t h = android::nn::getSizeOfDimension(shape, 1);
+ int32_t w = android::nn::getSizeOfDimension(shape, 2);
+
+ assert(android::nn::getSizeOfDimension(shape, 0) == 1);
+
+ return nnfw::util::feature::Shape{c, h, w};
+}
+
+} // namespace feature
+} // namespace nnapi
+} // namespace support
+} // namespace nnfw
+
+namespace nnfw
+{
+namespace support
+{
+namespace acl
+{
+namespace feature
+{
+
+template<typename T> class Reader;
+
+template<> class Reader<float> final : public nnfw::util::feature::Reader<float>
+{
+public:
+ Reader(arm_compute::ITensor *tensor) : _tensor{tensor}
+ {
+ assert(tensor->info()->data_type() == arm_compute::DataType::F32);
+ }
+
+public:
+ float at(uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ return *ptr_to_element(ch, row, col);
+ }
+
+private:
+ float *ptr_to_element(uint32_t ch, uint32_t row, uint32_t col) const
+ {
+ // ARM Compute uses CHW ordering
+ return reinterpret_cast<float *>(_tensor->ptr_to_element(arm_compute::Coordinates{col, row, ch}));
+ }
+
+private:
+ arm_compute::ITensor *_tensor;
+};
+
+} // namespace feature
+} // namespace acl
+} // namespace support
+} // namespace nnfw
+
namespace nnfw {
namespace kernel {
namespace acl {
}
}
+class Flag
+{
+public:
+ Flag(const std::string &k) : _flag{false}
+ {
+ auto env = std::getenv(k.c_str());
+
+ if (env)
+ {
+ _flag = (std::atoi(env) > 0);
+ }
+ }
+
+public:
+ bool value(void) const { return _flag; }
+
+private:
+ bool _flag;
+};
+
+static Flag verbose{"CONV2D_VERBOSE"};
+
bool convFloat32(const float* inputData, const android::nn::Shape& inputShape,
const float* filterData, const android::nn::Shape& filterShape,
const float* biasData, const android::nn::Shape& biasShape,
TensorAccess<BiasAccessor>(bias.ref(), biasData, biasShape);
TensorAccess<WeightAccessor>(filter.ref(), filterData, filterShape);
+ if (verbose.value())
+ {
+ input.ref().map();
+ auto ifm_shape = nnfw::support::nnapi::feature::asFeatureShape(inputShape);
+ nnfw::support::nnapi::feature::Reader<float> nnapi_ifm_reader{ifm_shape, inputData};
+ nnfw::support::acl::feature::Reader<float> acl_ifm_reader{input.ptr()};
+
+ std::cout << "NNAPI IFM:" << std::endl;
+ std::cout << nnfw::util::feature::TextFormatter<float>{ifm_shape, nnapi_ifm_reader} << std::endl;
+
+ std::cout << "ARM Compute IFM:" << std::endl;
+ std::cout << nnfw::util::feature::TextFormatter<float>{ifm_shape, acl_ifm_reader} << std::endl;
+ input.ref().unmap();
+ }
+
for (const auto &fn : fns)
{
fn->run();
arm_compute::CLScheduler::get().sync();
- // TODO put conversion inside OutputAccessor
- uint32_t numItems = getNumItems(outputShape);
- float* outputDataT = new float[numItems];
- TensorAccess<OutputAccessor>(output.ref(), outputDataT, outputShape);
- NCHW2NHWC(outputDataT, outputData, outputShape);
- delete [] outputDataT;
+ TensorAccess<OutputAccessor>(output.ref(), outputData, outputShape);
return true;
}