diff --git a/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.cc b/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.cc index ba125ad4098..624ee6fbd20 100644 --- a/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.cc +++ b/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.cc @@ -15,6 +15,8 @@ limitations under the License. #include "tensorflow/core/kernels/hexagon/hexagon_control_wrapper.h" +#include + #ifdef USE_HEXAGON_LIBS #include "tensorflow/core/platform/hexagon/soc_interface.h" #include "tensorflow/core/platform/profile_utils/cpu_utils.h" @@ -23,12 +25,19 @@ limitations under the License. namespace tensorflow { +const bool SHOW_DBG_IN_SOC = false; +const bool DBG_DUMP_RESULT = true; +const bool DBG_USE_DUMMY_INPUT = false; + #ifdef USE_HEXAGON_LIBS int HexagonControlWrapper::GetVersion() { return soc_interface_GetSocControllerVersion(); } -bool HexagonControlWrapper::Init() { return soc_interface_Init(); } +bool HexagonControlWrapper::Init() { + soc_interface_SetLogLevel(SHOW_DBG_IN_SOC ? -1 /* debug */ : 0 /* info */); + return soc_interface_Init(); +} bool HexagonControlWrapper::Finalize() { return soc_interface_Finalize(); } bool HexagonControlWrapper::SetupGraph( @@ -169,14 +178,22 @@ bool HexagonControlWrapper::TeardownGraph() { bool HexagonControlWrapper::FillInputNode(const string node_name, const ByteArray bytes) { + uint64 byte_size; // TODO(satok): Use arguments instead of dummy input - const int x = 1; - const int y = 299; - const int z = 299; - const int d = 3; - const int array_length = x * y * z * d; - const int byte_size = array_length * sizeof(float); - dummy_input_float_.resize(array_length); + if (DBG_USE_DUMMY_INPUT) { + const int x = 1; + const int y = 299; + const int z = 299; + const int d = 3; + const int array_length = x * y * z * d; + byte_size = array_length * sizeof(float); + dummy_input_float_.resize(array_length); + } else { + CHECK(std::get<2>(bytes) == DT_FLOAT); + byte_size = std::get<1>(bytes); + dummy_input_float_.resize(byte_size / sizeof(float)); + std::memcpy(dummy_input_float_.data(), std::get<0>(bytes), byte_size); + } return soc_interface_FillInputNodeFloat( 1, 299, 299, 3, reinterpret_cast(dummy_input_float_.data()), byte_size); @@ -188,8 +205,15 @@ bool HexagonControlWrapper::ReadOutputNode( ByteArray output; soc_interface_ReadOutputNodeFloat(node_name.c_str(), &std::get<0>(output), &std::get<1>(output)); + // TODO: Accept all results std::get<2>(output) = DT_FLOAT; outputs->emplace_back(output); + if (DBG_DUMP_RESULT) { + const int byte_size = std::get<1>(output); + const int element_count = byte_size / sizeof(float); + const float* float_array = reinterpret_cast(std::get<0>(output)); + DumpTopNFloatResults(float_array, element_count, 10 /* top_n */); + } return true; } @@ -211,4 +235,19 @@ bool HexagonControlWrapper::ReadOutputNode(const string, } #endif +void HexagonControlWrapper::DumpTopNFloatResults(const float* data, + const float element_count, + const int top_n) { + std::priority_queue> queue; + for (int i = 0; i < element_count; ++i) { + queue.emplace(data[i], i); + } + LOG(INFO) << "=== Dump ranking ==="; + for (int i = 0; i < top_n; ++i) { + const std::tuple& entry = queue.top(); + LOG(INFO) << i << ": " << std::get<1>(entry) << ", " << std::get<0>(entry); + queue.pop(); + } +} + } // namespace tensorflow diff --git a/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.h b/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.h index 0ba0b323cbe..dfae5aa5e25 100644 --- a/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.h +++ b/tensorflow/core/kernels/hexagon/hexagon_control_wrapper.h @@ -46,6 +46,9 @@ class HexagonControlWrapper final : public ISocControlWrapper { // CAVEAT: Need offset as HVX library reserves some ids static constexpr int NODE_ID_OFFSET = 0x10000; + void DumpTopNFloatResults(const float *data, const float element_count, + const int top_n); + // Dummy float array for input node. // TODO(satok): Use actual data passed by FillInputNode and remove std::vector dummy_input_float_; diff --git a/tensorflow/core/kernels/hexagon/hexagon_graph_execution_test.cc b/tensorflow/core/kernels/hexagon/hexagon_graph_execution_test.cc index 51cec9aba27..d06fb5fabc2 100644 --- a/tensorflow/core/kernels/hexagon/hexagon_graph_execution_test.cc +++ b/tensorflow/core/kernels/hexagon/hexagon_graph_execution_test.cc @@ -25,25 +25,36 @@ limitations under the License. #include "tensorflow/core/kernels/hexagon/hexagon_control_wrapper.h" #include "tensorflow/core/kernels/hexagon/hexagon_ops_definitions.h" #include "tensorflow/core/kernels/hexagon/i_graph_transfer_ops_definitions.h" +#include "tensorflow/core/kernels/hexagon/i_soc_control_wrapper.h" +#include "tensorflow/core/lib/core/casts.h" #include "tensorflow/core/lib/core/status.h" #include "tensorflow/core/lib/io/path.h" +#include "tensorflow/core/platform/env.h" #include "tensorflow/core/platform/test.h" namespace tensorflow { +using ByteArray = ISocControlWrapper::ByteArray; + +const bool DBG_DUMP_FLOAT_DATA = false; +const int WIDTH = 299; +const int HEIGHT = 299; +const int DEPTH = 3; + // CAVEAT: This test only runs when you specify hexagon library using // makefile. // TODO(satok): Make this generic so that this can run without any // additionanl steps. #ifdef USE_HEXAGON_LIBS TEST(GraphTransferer, RunInceptionV3OnHexagonExample) { - const string filename = + const string image_filename = "/data/local/tmp/img_299x299.bmp"; + const string model_filename = "/data/local/tmp/tensorflow_inception_v3_stripped_optimized_quantized.pb"; const IGraphTransferOpsDefinitions* ops_definitions = &HexagonOpsDefinitions::getInstance(); std::vector input_node_info_list = { - GraphTransferer::InputNodeInfo{"Mul", - Tensor{DT_FLOAT, {1, 299, 299, 3}}}}; + GraphTransferer::InputNodeInfo{ + "Mul", Tensor{DT_FLOAT, {1, WIDTH, HEIGHT, DEPTH}}}}; std::vector output_node_names = {"softmax"}; const bool is_text_proto = false; @@ -51,7 +62,7 @@ TEST(GraphTransferer, RunInceptionV3OnHexagonExample) { GraphTransferer gt; gt.EnableStrictCheckMode(false); Status status = gt.LoadGraphFromProtoFile( - *ops_definitions, filename, input_node_info_list, output_node_names, + *ops_definitions, model_filename, input_node_info_list, output_node_names, is_text_proto, true /* dry_run_for_unknown_shape */, &output_tensor_info); ASSERT_TRUE(status.ok()) << status; @@ -60,6 +71,54 @@ TEST(GraphTransferer, RunInceptionV3OnHexagonExample) { ASSERT_GE(version, 1); LOG(INFO) << "Hexagon controller version is " << version; + // Read the data from the bitmap file into memory + string bmp; + TF_CHECK_OK(ReadFileToString(Env::Default(), image_filename, &bmp)); + const int fsize = bmp.size(); + LOG(INFO) << "Read " << image_filename << ", size = " << fsize << "bytes"; + const int64 pixel_count = WIDTH * HEIGHT * DEPTH; + uint8* const img_bytes = bit_cast(bmp.data()); + const int header_size = *(reinterpret_cast(img_bytes + 10)); + const int size = *(reinterpret_cast(img_bytes + 14)); + const int width = *(reinterpret_cast(img_bytes + 18)); + const int height = *(reinterpret_cast(img_bytes + 22)); + LOG(INFO) << header_size << ", " << size << ", " << width << ", " << height; + CHECK(fsize >= (WIDTH + 1) * WIDTH * 3 + header_size); + + uint8* const bmp_pixels = &img_bytes[header_size]; + + std::vector img_floats(pixel_count); + int src_pixel_index = 0; + CHECK(pixel_count % 3 == 0); + for (int i = 0; i < pixel_count / 3; ++i) { + const int src_pos = 3 * src_pixel_index; + const int dst_pos = 3 * i; + ++src_pixel_index; + CHECK(src_pos + 2 + header_size < fsize); + CHECK(dst_pos + 2 < pixel_count); + // Convert (B, G, R) in bitmap to (R, G, B) + img_floats[dst_pos] = + (static_cast(bmp_pixels[src_pos + 2]) - 128.0f) / 128.0f; + img_floats[dst_pos + 1] = + (static_cast(bmp_pixels[src_pos + 1]) - 128.0f) / 128.0f; + img_floats[dst_pos + 2] = + (static_cast(bmp_pixels[src_pos]) - 128.0f) / 128.0f; + if (DBG_DUMP_FLOAT_DATA) { + LOG(INFO) << i << " (" << img_floats[dst_pos] << ", " + << img_floats[dst_pos + 1] << ", " << img_floats[dst_pos + 2] + << ") (" << static_cast(bmp_pixels[src_pos + 2]) << ", " + << static_cast(bmp_pixels[src_pos + 1]) << ", " + << static_cast(bmp_pixels[src_pos]) << ")"; + } + if (src_pixel_index % (WIDTH + 1) == (WIDTH - 1)) { + // skip bmp padding + ++src_pixel_index; + } + } + const ByteArray ba = + std::make_tuple(reinterpret_cast(img_floats.data()), + pixel_count * sizeof(float), DT_FLOAT); + // 1. Initialize hexagon hexagon_control_wrapper.Init(); @@ -67,7 +126,7 @@ TEST(GraphTransferer, RunInceptionV3OnHexagonExample) { hexagon_control_wrapper.SetupGraph(gt); // 3. Fill input node's output - hexagon_control_wrapper.FillInputNode("Mul", {}); + hexagon_control_wrapper.FillInputNode("Mul", ba); // 4. Execute graph hexagon_control_wrapper.ExecuteGraph();