Create StackPoolMemoryStrategy
This commit is contained in:
		
							parent
							
								
									7a69265700
								
							
						
					
					
						commit
						9265a198f6
					
				
					 3 changed files with 234 additions and 42 deletions
				
			
		| 
						 | 
				
			
			@ -15,14 +15,16 @@
 | 
			
		|||
#ifndef RCLCPP_RCLCPP_MEMORY_STRATEGIES_HPP_
 | 
			
		||||
#define RCLCPP_RCLCPP_MEMORY_STRATEGIES_HPP_
 | 
			
		||||
 | 
			
		||||
#include <rclcpp/strategies/static_memory_strategy.hpp>
 | 
			
		||||
#include <rclcpp/strategies/heap_pool_memory_strategy.hpp>
 | 
			
		||||
#include <rclcpp/strategies/stack_pool_memory_strategy.hpp>
 | 
			
		||||
 | 
			
		||||
namespace rclcpp
 | 
			
		||||
{
 | 
			
		||||
namespace memory_strategies
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
using rclcpp::memory_strategies::static_memory_strategy::StaticMemoryStrategy;
 | 
			
		||||
using rclcpp::memory_strategies::heap_pool_memory_strategy::HeapPoolMemoryStrategy;
 | 
			
		||||
using rclcpp::memory_strategies::stack_pool_memory_strategy::StackPoolMemoryStrategy;
 | 
			
		||||
 | 
			
		||||
}  /* memory_strategies */
 | 
			
		||||
}  /* rclcpp */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,8 @@
 | 
			
		|||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
#ifndef RCLCPP_RCLCPP_STATIC_MEMORY_STRATEGY_HPP_
 | 
			
		||||
#define RCLCPP_RCLCPP_STATIC_MEMORY_STRATEGY_HPP_
 | 
			
		||||
#ifndef RCLCPP_RCLCPP_HEAP_POOL_MEMORY_STRATEGY_HPP_
 | 
			
		||||
#define RCLCPP_RCLCPP_HEAP_POOL_MEMORY_STRATEGY_HPP_
 | 
			
		||||
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ namespace rclcpp
 | 
			
		|||
namespace memory_strategies
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace static_memory_strategy
 | 
			
		||||
namespace heap_pool_memory_strategy
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/// Representation of the upper bounds on the memory pools managed by StaticMemoryStrategy.
 | 
			
		||||
| 
						 | 
				
			
			@ -115,21 +115,18 @@ public:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Static memory allocation alternative to the default memory strategy.
 | 
			
		||||
/// Heap-based memory pool allocation strategy, an alternative to the default memory strategy.
 | 
			
		||||
/**
 | 
			
		||||
 * The name is a bit of a misnomer. The memory managed by this class is actually allocated
 | 
			
		||||
 * dynamically in the constructor, but no subsequent accesses to the class (besides the destructor)
 | 
			
		||||
 * allocate or free memory.
 | 
			
		||||
 * StaticMemoryStrategy puts a hard limit on the number of subscriptions, etc. that can be executed
 | 
			
		||||
 * The memory managed by this class is allocated dynamically in the constructor, but no subsequent
 | 
			
		||||
 * accesses to the class (besides the destructor) allocate or free memory.
 | 
			
		||||
 * HeapPoolMemoryStrategy puts a hard limit on the number of subscriptions, etc. that can be executed
 | 
			
		||||
 * in one iteration of `Executor::spin`. Thus it allows for memory allocation optimization for
 | 
			
		||||
 * situations where a limit on the number of such entities is known.
 | 
			
		||||
 */
 | 
			
		||||
class StaticMemoryStrategy : public memory_strategy::MemoryStrategy
 | 
			
		||||
class HeapPoolMemoryStrategy : public memory_strategy::MemoryStrategy
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  /// Default constructor.
 | 
			
		||||
  // \param[in] bounds Representation of the limits on memory managed by this class.
 | 
			
		||||
  StaticMemoryStrategy(ObjectPoolBounds bounds = ObjectPoolBounds())
 | 
			
		||||
  HeapPoolMemoryStrategy(ObjectPoolBounds bounds = ObjectPoolBounds())
 | 
			
		||||
  : bounds_(bounds), memory_pool_(nullptr), subscription_pool_(nullptr),
 | 
			
		||||
    service_pool_(nullptr), guard_condition_pool_(nullptr), executable_pool_(nullptr)
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +174,7 @@ public:
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  /// Default destructor. Free all allocated memory.
 | 
			
		||||
  ~StaticMemoryStrategy()
 | 
			
		||||
  ~HeapPoolMemoryStrategy()
 | 
			
		||||
  {
 | 
			
		||||
    if (bounds_.pool_size) {
 | 
			
		||||
      delete[] memory_pool_;
 | 
			
		||||
| 
						 | 
				
			
			@ -196,12 +193,6 @@ public:
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Borrow handles by returning a pointer to the preallocated object pool for the specified type.
 | 
			
		||||
  /**
 | 
			
		||||
   * \param[in] The type of entity that this function is requesting for.
 | 
			
		||||
   * \param[in] The number of handles to borrow.
 | 
			
		||||
   * \return Pointer to the allocated handles.
 | 
			
		||||
   */
 | 
			
		||||
  void ** borrow_handles(HandleType type, size_t number_of_handles)
 | 
			
		||||
  {
 | 
			
		||||
    switch (type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -235,11 +226,6 @@ public:
 | 
			
		|||
    throw std::runtime_error("Unrecognized enum, could not borrow handle memory.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Return the borrowed handles by clearing the object pool for the correspondign type.
 | 
			
		||||
  /**
 | 
			
		||||
   * \param[in] The type of entity that this function is returning.
 | 
			
		||||
   * \param[in] Pointer to the handles returned.
 | 
			
		||||
   */
 | 
			
		||||
  void return_handles(HandleType type, void ** handles)
 | 
			
		||||
  {
 | 
			
		||||
    (void)handles;
 | 
			
		||||
| 
						 | 
				
			
			@ -269,12 +255,10 @@ public:
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Instantiate the next executable by borrowing space from the preallocated executables pool.
 | 
			
		||||
  // \return Shared pointer to the executable.
 | 
			
		||||
  executor::AnyExecutable::SharedPtr instantiate_next_executable()
 | 
			
		||||
  {
 | 
			
		||||
    if (exec_seq_ >= bounds_.max_executables) {
 | 
			
		||||
      // wrap around (ring buffer logic)
 | 
			
		||||
      // wrap around
 | 
			
		||||
      exec_seq_ = 0;
 | 
			
		||||
    }
 | 
			
		||||
    size_t prev_exec_seq_ = exec_seq_;
 | 
			
		||||
| 
						 | 
				
			
			@ -284,7 +268,6 @@ public:
 | 
			
		|||
      throw std::runtime_error("Executable pool member was NULL");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Make sure to clear the executable fields.
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->subscription.reset();
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->timer.reset();
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->service.reset();
 | 
			
		||||
| 
						 | 
				
			
			@ -295,11 +278,6 @@ public:
 | 
			
		|||
    return executable_pool_[prev_exec_seq_];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// General allocate: reserve space in the memory pool reserved by this function.
 | 
			
		||||
  /**
 | 
			
		||||
   * \param[in] size Number of bytes to allocate.
 | 
			
		||||
   * \return Pointer to the allocated chunk of memory.
 | 
			
		||||
   */
 | 
			
		||||
  void * alloc(size_t size)
 | 
			
		||||
  {
 | 
			
		||||
    // Extremely naive static allocation strategy
 | 
			
		||||
| 
						 | 
				
			
			@ -311,7 +289,7 @@ public:
 | 
			
		|||
    void * ptr = memory_pool_[pool_seq_];
 | 
			
		||||
    if (memory_map_.count(ptr) == 0) {
 | 
			
		||||
      // We expect to have the state for all blocks pre-mapped into memory_map_
 | 
			
		||||
      throw std::runtime_error("Unexpected pointer in StaticMemoryStrategy::alloc.");
 | 
			
		||||
      throw std::runtime_error("Unexpected pointer in HeapPoolMemoryStrategy::alloc.");
 | 
			
		||||
    }
 | 
			
		||||
    memory_map_[ptr] = size;
 | 
			
		||||
    size_t prev_pool_seq = pool_seq_;
 | 
			
		||||
| 
						 | 
				
			
			@ -319,15 +297,11 @@ public:
 | 
			
		|||
    return memory_pool_[prev_pool_seq];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Release the allocated memory in the memory pool.
 | 
			
		||||
  /**
 | 
			
		||||
   * \param[in] Pointer to deallocate.
 | 
			
		||||
   */
 | 
			
		||||
  void free(void * ptr)
 | 
			
		||||
  {
 | 
			
		||||
    if (memory_map_.count(ptr) == 0) {
 | 
			
		||||
      // We expect to have the state for all blocks pre-mapped into memory_map_
 | 
			
		||||
      throw std::runtime_error("Unexpected pointer in StaticMemoryStrategy::free.");
 | 
			
		||||
      throw std::runtime_error("Unexpected pointer in HeapPoolMemoryStrategy::free.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(ptr, 0, memory_map_[ptr]);
 | 
			
		||||
| 
						 | 
				
			
			@ -349,7 +323,7 @@ private:
 | 
			
		|||
  std::unordered_map<void *, size_t> memory_map_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  /* static_memory_strategy */
 | 
			
		||||
}  /* heap_pool_memory_strategy */
 | 
			
		||||
 | 
			
		||||
}  /* memory_strategies */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										216
									
								
								rclcpp/include/rclcpp/strategies/stack_pool_memory_strategy.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								rclcpp/include/rclcpp/strategies/stack_pool_memory_strategy.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,216 @@
 | 
			
		|||
// Copyright 2015 Open Source Robotics Foundation, Inc.
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
 | 
			
		||||
#ifndef RCLCPP_RCLCPP_STACK_POOL_MEMORY_STRATEGY_HPP_
 | 
			
		||||
#define RCLCPP_RCLCPP_STACK_POOL_MEMORY_STRATEGY_HPP_
 | 
			
		||||
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include <rclcpp/memory_strategy.hpp>
 | 
			
		||||
 | 
			
		||||
namespace rclcpp
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace memory_strategies
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace stack_pool_memory_strategy
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/// Stack-based memory pool allocation strategy, an alternative to the default memory strategy.
 | 
			
		||||
/**
 | 
			
		||||
 * The memory managed by this class is allocated statically (on the heap) in the constructor.
 | 
			
		||||
 * StackPoolMemoryStrategy puts a hard limit on the number of subscriptions, etc. that can be
 | 
			
		||||
 * executed in one iteration of `Executor::spin`. Thus it allows for memory allocation optimization
 | 
			
		||||
 * for situations where a limit on the number of such entities is known.
 | 
			
		||||
 * Because this class is templated on the sizes of the memory pools for each entity, the amount of
 | 
			
		||||
 * memory required by this class is known at compile time.
 | 
			
		||||
 */
 | 
			
		||||
template<size_t MaxSubscriptions = 10, size_t MaxServices = 10, size_t MaxClients = 10,
 | 
			
		||||
size_t MaxExecutables = 1, size_t MaxGuardConditions = 2, size_t PoolSize = 0>
 | 
			
		||||
class StackPoolMemoryStrategy : public memory_strategy::MemoryStrategy
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  StackPoolMemoryStrategy()
 | 
			
		||||
  {
 | 
			
		||||
    if (PoolSize) {
 | 
			
		||||
      memory_pool_.fill(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (MaxSubscriptions) {
 | 
			
		||||
      subscription_pool_.fill(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (MaxServices) {
 | 
			
		||||
      service_pool_.fill(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (MaxClients) {
 | 
			
		||||
      client_pool_.fill(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (MaxGuardConditions) {
 | 
			
		||||
      guard_condition_pool_.fill(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < MaxExecutables; ++i) {
 | 
			
		||||
      executable_pool_[i] = std::make_shared<executor::AnyExecutable>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pool_seq_ = 0;
 | 
			
		||||
    exec_seq_ = 0;
 | 
			
		||||
 | 
			
		||||
    // Reserve pool_size_ buckets in the memory map.
 | 
			
		||||
    memory_map_.reserve(PoolSize);
 | 
			
		||||
    for (size_t i = 0; i < PoolSize; ++i) {
 | 
			
		||||
      memory_map_[memory_pool_[i]] = 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void ** borrow_handles(HandleType type, size_t number_of_handles)
 | 
			
		||||
  {
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case HandleType::subscription_handle:
 | 
			
		||||
        if (number_of_handles > MaxSubscriptions) {
 | 
			
		||||
          throw std::runtime_error("Requested size exceeded maximum subscriptions.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return subscription_pool_.data();
 | 
			
		||||
      case HandleType::service_handle:
 | 
			
		||||
        if (number_of_handles > MaxServices) {
 | 
			
		||||
          throw std::runtime_error("Requested size exceeded maximum services.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return service_pool_.data();
 | 
			
		||||
      case HandleType::client_handle:
 | 
			
		||||
        if (number_of_handles > MaxClients) {
 | 
			
		||||
          throw std::runtime_error("Requested size exceeded maximum clients.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return client_pool_.data();
 | 
			
		||||
      case HandleType::guard_condition_handle:
 | 
			
		||||
        if (number_of_handles > MaxGuardConditions) {
 | 
			
		||||
          throw std::runtime_error("Requested size exceeded maximum guard_conditions.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return guard_condition_pool_.data();
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    throw std::runtime_error("Unrecognized enum, could not borrow handle memory.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void return_handles(HandleType type, void ** handles)
 | 
			
		||||
  {
 | 
			
		||||
    (void)handles;
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case HandleType::subscription_handle:
 | 
			
		||||
        if (MaxSubscriptions) {
 | 
			
		||||
          subscription_pool_.fill(0);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case HandleType::service_handle:
 | 
			
		||||
        if (MaxServices) {
 | 
			
		||||
          service_pool_.fill(0);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case HandleType::client_handle:
 | 
			
		||||
        if (MaxClients) {
 | 
			
		||||
          client_pool_.fill(0);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case HandleType::guard_condition_handle:
 | 
			
		||||
        if (MaxGuardConditions) {
 | 
			
		||||
          guard_condition_pool_.fill(0);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        throw std::runtime_error("Unrecognized enum, could not return handle memory.");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  executor::AnyExecutable::SharedPtr instantiate_next_executable()
 | 
			
		||||
  {
 | 
			
		||||
    if (exec_seq_ >= MaxExecutables) {
 | 
			
		||||
      // wrap around
 | 
			
		||||
      exec_seq_ = 0;
 | 
			
		||||
    }
 | 
			
		||||
    size_t prev_exec_seq_ = exec_seq_;
 | 
			
		||||
    ++exec_seq_;
 | 
			
		||||
 | 
			
		||||
    if (!executable_pool_[prev_exec_seq_]) {
 | 
			
		||||
      throw std::runtime_error("Executable pool member was NULL");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Make sure to clear the executable fields.
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->subscription.reset();
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->timer.reset();
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->service.reset();
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->client.reset();
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->callback_group.reset();
 | 
			
		||||
    executable_pool_[prev_exec_seq_]->node.reset();
 | 
			
		||||
 | 
			
		||||
    return executable_pool_[prev_exec_seq_];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void * alloc(size_t size)
 | 
			
		||||
  {
 | 
			
		||||
    // Extremely naive static allocation strategy
 | 
			
		||||
    // Keep track of block size at a given pointer
 | 
			
		||||
    if (pool_seq_ + size > PoolSize) {
 | 
			
		||||
      // Start at 0
 | 
			
		||||
      pool_seq_ = 0;
 | 
			
		||||
    }
 | 
			
		||||
    void * ptr = memory_pool_[pool_seq_];
 | 
			
		||||
    if (memory_map_.count(ptr) == 0) {
 | 
			
		||||
      // We expect to have the state for all blocks pre-mapped into memory_map_
 | 
			
		||||
      throw std::runtime_error("Unexpected pointer in StackPoolMemoryStrategy::alloc.");
 | 
			
		||||
    }
 | 
			
		||||
    memory_map_[ptr] = size;
 | 
			
		||||
    size_t prev_pool_seq = pool_seq_;
 | 
			
		||||
    pool_seq_ += size;
 | 
			
		||||
    return memory_pool_[prev_pool_seq];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void free(void * ptr)
 | 
			
		||||
  {
 | 
			
		||||
    if (memory_map_.count(ptr) == 0) {
 | 
			
		||||
      // We expect to have the state for all blocks pre-mapped into memory_map_
 | 
			
		||||
      throw std::runtime_error("Unexpected pointer in StackPoolMemoryStrategy::free.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(ptr, 0, memory_map_[ptr]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  std::array<void *, PoolSize> memory_pool_;
 | 
			
		||||
  std::array<void *, MaxSubscriptions> subscription_pool_;
 | 
			
		||||
  std::array<void *, MaxServices> service_pool_;
 | 
			
		||||
  std::array<void *, MaxClients> client_pool_;
 | 
			
		||||
  std::array<void *, MaxGuardConditions> guard_condition_pool_;
 | 
			
		||||
  std::array<executor::AnyExecutable::SharedPtr, MaxExecutables> executable_pool_;
 | 
			
		||||
 | 
			
		||||
  size_t pool_seq_;
 | 
			
		||||
  size_t exec_seq_;
 | 
			
		||||
 | 
			
		||||
  std::unordered_map<void *, size_t> memory_map_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  /* stack_pool_memory_strategy */
 | 
			
		||||
 | 
			
		||||
}  /* memory_strategies */
 | 
			
		||||
 | 
			
		||||
}  /* rclcpp */
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue