PR #3279 - use unique_ptr instead of shared_ptr in the timestep tree

This commit is contained in:
godeffroy 2020-09-09 11:04:37 +02:00
parent 3a49344ccb
commit f07c10452b
5 changed files with 41 additions and 33 deletions

View File

@ -37,8 +37,6 @@ DecoderState::init(const Alphabet& alphabet,
prefix_root_.reset(root); prefix_root_.reset(root);
prefixes_.push_back(root); prefixes_.push_back(root);
timestep_tree_root_=std::make_shared<TimestepTreeNode>(nullptr, 0);
if (ext_scorer && (bool)(ext_scorer_->dictionary)) { if (ext_scorer && (bool)(ext_scorer_->dictionary)) {
// no need for std::make_shared<>() since Copy() does 'new' behind the doors // no need for std::make_shared<>() since Copy() does 'new' behind the doors
auto dict_ptr = std::shared_ptr<PathTrie::FstType>(ext_scorer->dictionary->Copy(true)); auto dict_ptr = std::shared_ptr<PathTrie::FstType>(ext_scorer->dictionary->Copy(true));
@ -183,7 +181,7 @@ DecoderState::next(const double *probs,
prefix_root_->iterate_to_vec(prefixes_); prefix_root_->iterate_to_vec(prefixes_);
if (abs_time_step_ == 0) { if (abs_time_step_ == 0) {
for (PathTrie* prefix:prefixes_) { for (PathTrie* prefix:prefixes_) {
prefix->timesteps = timestep_tree_root_; prefix->timesteps = &timestep_tree_root_;
} }
} }

View File

@ -21,7 +21,7 @@ class DecoderState {
std::shared_ptr<Scorer> ext_scorer_; std::shared_ptr<Scorer> ext_scorer_;
std::vector<PathTrie*> prefixes_; std::vector<PathTrie*> prefixes_;
std::unique_ptr<PathTrie> prefix_root_; std::unique_ptr<PathTrie> prefix_root_;
std::shared_ptr<TimestepTreeNode> timestep_tree_root_; TimestepTreeNode timestep_tree_root_{nullptr, 0};
public: public:
DecoderState() = default; DecoderState() = default;

View File

@ -175,7 +175,7 @@ void PathTrie::iterate_to_vec(std::vector<PathTrie*>& output) {
timesteps = nullptr; timesteps = nullptr;
for (auto const& child : previous_timesteps->children) { for (auto const& child : previous_timesteps->children) {
if (child->data == new_timestep) { if (child->data == new_timestep) {
timesteps=child; timesteps = child.get();
break; break;
} }
} }

View File

@ -16,16 +16,16 @@
*/ */
template<class DataT> template<class DataT>
struct TreeNode{ struct TreeNode{
std::shared_ptr<TreeNode<DataT>> parent; TreeNode<DataT>* parent;
std::vector<std::shared_ptr<TreeNode<DataT>>> children; std::vector<std::unique_ptr< TreeNode<DataT>, godefv::memory::object_pool_deleter_t<TreeNode<DataT>> >> children;
DataT data; DataT data;
TreeNode(std::shared_ptr<TreeNode<DataT>> const& parent_, DataT const& data_): parent{parent_}, data{data_} {} TreeNode(TreeNode<DataT>* parent_, DataT const& data_): parent{parent_}, data{data_} {}
}; };
template<class NodeDataT, class ChildDataT> template<class NodeDataT, class ChildDataT>
std::shared_ptr<TreeNode<NodeDataT>> add_child(std::shared_ptr<TreeNode<NodeDataT>> const& node, ChildDataT&& data_); TreeNode<NodeDataT>* add_child(TreeNode<NodeDataT>* node, ChildDataT&& data_);
template<class DataT> template<class DataT>
std::vector<DataT> get_history(TreeNode<DataT>*); std::vector<DataT> get_history(TreeNode<DataT>*);
@ -85,10 +85,10 @@ public:
float score; float score;
float approx_ctc; float approx_ctc;
unsigned int character; unsigned int character;
std::shared_ptr<TimestepTreeNode> timesteps; TimestepTreeNode* timesteps=nullptr;
// timestep temporary storage for each decoding step. // timestep temporary storage for each decoding step.
std::shared_ptr<TimestepTreeNode> previous_timesteps=nullptr; TimestepTreeNode* previous_timesteps=nullptr;
unsigned int new_timestep; unsigned int new_timestep;
PathTrie* parent; PathTrie* parent;
@ -108,21 +108,21 @@ private:
// TreeNode implementation // TreeNode implementation
template<class NodeDataT, class ChildDataT> template<class NodeDataT, class ChildDataT>
std::shared_ptr<TreeNode<NodeDataT>> add_child(std::shared_ptr<TreeNode<NodeDataT>> const& node, ChildDataT&& data_){ TreeNode<NodeDataT>* add_child(TreeNode<NodeDataT>* node, ChildDataT&& data_){
static godefv::memory::object_pool_t<TreeNode<NodeDataT>> tree_node_pool; static godefv::memory::object_pool_t<TreeNode<NodeDataT>> tree_node_pool;
node->children.emplace_back(tree_node_pool.make_unique(node, std::forward<ChildDataT>(data_))); node->children.push_back(tree_node_pool.make_unique(node, std::forward<ChildDataT>(data_)));
return node->children.back(); return node->children.back().get();
} }
template<class DataT> template<class DataT>
void get_history_helper(std::shared_ptr<TreeNode<DataT>> const& tree_node, std::vector<DataT>* output){ void get_history_helper(TreeNode<DataT>* tree_node, std::vector<DataT>* output){
if(tree_node==nullptr) return; if(tree_node==nullptr) return;
assert(tree_node->parent != tree_node); assert(tree_node->parent != tree_node);
get_history_helper(tree_node->parent, output); get_history_helper(tree_node->parent, output);
output->push_back(tree_node->data); output->push_back(tree_node->data);
} }
template<class DataT> template<class DataT>
std::vector<DataT> get_history(std::shared_ptr<TreeNode<DataT>> const& tree_node){ std::vector<DataT> get_history(TreeNode<DataT>* tree_node){
std::vector<DataT> output; std::vector<DataT> output;
get_history_helper(tree_node, &output); get_history_helper(tree_node, &output);
return output; return output;

View File

@ -8,9 +8,29 @@
namespace godefv{ namespace memory{ namespace godefv{ namespace memory{
template<class Object, template<class T> class Allocator = std::allocator, std::size_t ChunkSize = 1024>
class object_pool_t;
//! Custom deleter to recycle the deleted pointers.
template<class Object, template<class T> class Allocator = std::allocator, std::size_t ChunkSize = 1024>
struct object_pool_deleter_t{
private:
object_pool_t<Object, Allocator, ChunkSize>* object_pool_ptr;
public:
explicit object_pool_deleter_t(decltype(object_pool_ptr) input_object_pool_ptr) :
object_pool_ptr(input_object_pool_ptr)
{}
//! When a pointer provided by the ObjectPool is deleted, its memory is converted to an object slot to be recycled.
void operator()(Object* object_ptr)
{
object_pool_ptr->delete_object(object_ptr);
}
};
//! Allocates instances of Object efficiently (constant time and log((maximum number of Objects used at the same time)/ChunkSize) calls to malloc in the whole lifetime of the object pool). //! Allocates instances of Object efficiently (constant time and log((maximum number of Objects used at the same time)/ChunkSize) calls to malloc in the whole lifetime of the object pool).
//! When an instance returned by the object pool is destroyed, its allocated memory is recycled by the object pool. Defragmenting the object pool to free memory is not possible. //! When an instance returned by the object pool is destroyed, its allocated memory is recycled by the object pool. Defragmenting the object pool to free memory is not possible.
template<class Object, template<class T> class Allocator = std::allocator, std::size_t ChunkSize = 1024> template<class Object, template<class T> class Allocator, std::size_t ChunkSize>
class object_pool_t{ class object_pool_t{
//! An object slot is an uninitialized memory space of the same size as Object. //! An object slot is an uninitialized memory space of the same size as Object.
//! It is initially "free". It can then be "used" to construct an Object in place and the pointer to it is returned by the object pool. When the pointer is destroyed, the object slot is "recycled" and can be used again but it is not "free" anymore because "free" object slots are contiguous in memory. //! It is initially "free". It can then be "used" to construct an Object in place and the pointer to it is returned by the object pool. When the pointer is destroyed, the object slot is "recycled" and can be used again but it is not "free" anymore because "free" object slots are contiguous in memory.
@ -28,25 +48,15 @@ class object_pool_t{
object_slot_t* free_object_slots_begin; object_slot_t* free_object_slots_begin;
object_slot_t* free_object_slots_end; object_slot_t* free_object_slots_end;
//! Custom deleter to recycle the deleted pointers. void delete_object(Object* object_ptr){
struct deleter_t{ object_ptr->~Object();
private: recycled_object_slots.push_back(reinterpret_cast<object_slot_t*>(object_ptr));
object_pool_t<Object, Allocator, ChunkSize>* object_pool_ptr; }
public: friend object_pool_deleter_t<Object, Allocator, ChunkSize>;
explicit deleter_t(decltype(object_pool_ptr) input_object_pool_ptr) :
object_pool_ptr(input_object_pool_ptr)
{}
//! When a pointer provided by the ObjectPool is deleted, its memory is converted to an object slot to be recycled.
void operator()(Object* object_ptr)
{
object_ptr->~Object();
object_pool_ptr->recycled_object_slots.push_back(reinterpret_cast<object_slot_t*>(object_ptr));
}
};
public: public:
using object_t = Object; using object_t = Object;
using deleter_t = object_pool_deleter_t<Object, Allocator, ChunkSize>;
using object_unique_ptr_t = std::unique_ptr<object_t, deleter_t>; //!< The type returned by the object pool. using object_unique_ptr_t = std::unique_ptr<object_t, deleter_t>; //!< The type returned by the object pool.
object_pool_t(Allocator<chunk_t> const& allocator = Allocator<chunk_t>{}) : object_pool_t(Allocator<chunk_t> const& allocator = Allocator<chunk_t>{}) :