return (_tensors.find(index) != _tensors.end());
}
+ /**
+ * @brief Allocate tensor using operand info
+ * @param[in] index Tensor index
+ * @param[in] info Operand info
+ * @note If already allocated, just return
+ * @TODO More smart allocation policy
+ */
+ void allocateIfNeeded(const model::OperandIndex index, const model::OperandInfo &info)
+ {
+ // already allocated, or constant
+ if (contains(index))
+ {
+ return;
+ }
+
+ auto tensor = std::make_shared<Tensor>(info);
+ tensor->setBuffer(new uint8_t[tensor->total_size()]);
+ assignTensor(index, tensor);
+ _buffers.insert(index);
+ }
+
+ /**
+ * @brief Free buffer if allocated by allocateIfNeed
+ * @param[in] index Tensor index
+ * @note If allocated by outside, just return
+ */
+ void freeIfAllocated(const model::OperandIndex index)
+ {
+ if (_buffers.find(index) != _buffers.end())
+ {
+ delete[] _tensors.at(index)->buffer();
+ }
+ }
+
private:
std::shared_ptr<const model::Model> _model;
// Tensor map to use in interpreter
// It should map tensors that have allocated or assigned buffer pointer
std::unordered_map<model::OperandIndex, std::shared_ptr<ITensor>> _tensors;
+ // Tensors allocated by allocateIfNeed (buffer)
+ std::unordered_set<model::OperandIndex> _buffers;
};
} // namespace interp
// Execution
std::unordered_set<model::OperandIndex> ready_check;
+ std::unordered_set<model::OperationIndex> executed;
OperationExecutor executor{_env.get()};
while (!operand_stack.empty())
{
// 1. Prepare output tensor
// 2. Call operation kernel
executor.execute(current_operation_index);
+ executed.insert(current_operation_index);
// 3. Push each output into operand stack
const auto def_operands = _env->model().operations.at(current_operation_index).getOutputs();
<< std::endl;
operand_stack.push(def_operand);
}
+
+ // 4. Free if lifetime of buffer operands used by input is finished
+ for (auto input_index : _env->model().operations.at(current_operation_index).getInputs())
+ {
+ const auto use_operators = _env->model().operands.at(input_index).getUses();
+ bool dead_buffer = true;
+ for (auto use_operator : use_operators.list())
+ {
+ if (executed.find(use_operator) == executed.end())
+ {
+ dead_buffer = false;
+ break;
+ }
+ }
+
+ if (dead_buffer)
+ {
+ _env->freeIfAllocated(input_index);
+ }
+ }
}
}
}
// Output's shape and type should be same with input (don't consider broadcast)
auto output_info = lhs_tensor->tensorInfo();
// We can handle already allocated (ex. model output)
- if (!env->contains(out_index))
- {
- throw std::runtime_error("NYI: buffer allocator");
- }
+ env->allocateIfNeeded(out_index, output_info);
auto out_tensor = env->tensorAt(out_index);
EXPECT_NO_THROW(_executor->setInput(input1, reinterpret_cast<const void *>(input1_buffer), 16));
EXPECT_NO_THROW(_executor->setInput(input2, reinterpret_cast<const void *>(input2_buffer), 16));
EXPECT_NO_THROW(_executor->setOutput(output, reinterpret_cast<void *>(output_buffer), 16));
- EXPECT_THROW(_executor->execute(), std::runtime_error);
- // EXPECT_EQ(output_buffer[0], 5);
- // EXPECT_EQ(output_buffer[1], -2);
- // EXPECT_EQ(output_buffer[2], 0);
- // EXPECT_EQ(output_buffer[3], -1);
+ EXPECT_NO_THROW(_executor->execute());
+ EXPECT_EQ(output_buffer[0], 5);
+ EXPECT_EQ(output_buffer[1], -2);
+ EXPECT_EQ(output_buffer[2], 0);
+ EXPECT_EQ(output_buffer[3], -1);
}
} // namespace