278 lines
7.9 KiB
C++
278 lines
7.9 KiB
C++
|
|
#pragma once
|
|||
|
|
/**
|
|||
|
|
* @file object_pool.h
|
|||
|
|
* @brief 高性能对象池 + 写时复制
|
|||
|
|
*
|
|||
|
|
* 对象池:预分配 chunk,free-list 回收,O(1) acquire/release。
|
|||
|
|
* 写时复制:引用计数共享,修改时深拷贝,避免不必要的大对象拷贝。
|
|||
|
|
*
|
|||
|
|
* @ingroup foundation
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include <vector>
|
|||
|
|
#include <memory>
|
|||
|
|
#include <atomic>
|
|||
|
|
#include <mutex>
|
|||
|
|
#include <cstddef>
|
|||
|
|
#include <type_traits>
|
|||
|
|
#include <functional>
|
|||
|
|
|
|||
|
|
namespace vde::core {
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// ObjectPool — 单线程对象池
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 高性能对象池(单线程)
|
|||
|
|
*
|
|||
|
|
* 预分配 chunk 减少 malloc 调用,free-list 实现 O(1) 回收。
|
|||
|
|
*
|
|||
|
|
* @tparam T 池化对象类型(需有默认构造函数)
|
|||
|
|
* @tparam ChunkSize 每次扩容时分配的 chunk 大小
|
|||
|
|
*
|
|||
|
|
* @ingroup foundation
|
|||
|
|
*/
|
|||
|
|
template <typename T, size_t ChunkSize = 256>
|
|||
|
|
class ObjectPool {
|
|||
|
|
static_assert(std::is_default_constructible_v<T>,
|
|||
|
|
"T must be default constructible");
|
|||
|
|
|
|||
|
|
public:
|
|||
|
|
ObjectPool() { expand(); }
|
|||
|
|
~ObjectPool() {
|
|||
|
|
for (auto* chunk : chunks_) {
|
|||
|
|
::operator delete(chunk);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 禁止拷贝
|
|||
|
|
ObjectPool(const ObjectPool&) = delete;
|
|||
|
|
ObjectPool& operator=(const ObjectPool&) = delete;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取一个对象(复用或新建)
|
|||
|
|
* @return 对象指针
|
|||
|
|
*/
|
|||
|
|
[[nodiscard]] T* acquire();
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 归还对象到池中
|
|||
|
|
* @param ptr 之前由 acquire() 返回的指针
|
|||
|
|
*/
|
|||
|
|
void release(T* ptr);
|
|||
|
|
|
|||
|
|
/// 统计
|
|||
|
|
[[nodiscard]] size_t total_allocated() const { return total_allocated_; }
|
|||
|
|
[[nodiscard]] size_t total_reused() const { return total_reused_; }
|
|||
|
|
[[nodiscard]] size_t pool_size() const { return chunks_.size() * ChunkSize; }
|
|||
|
|
[[nodiscard]] size_t free_count() const { return free_count_; }
|
|||
|
|
[[nodiscard]] double reuse_rate() const;
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
struct Node {
|
|||
|
|
Node* next = nullptr;
|
|||
|
|
T data;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
std::vector<Node*> chunks_; ///< 分配的 chunk
|
|||
|
|
Node* free_list_ = nullptr; ///< 空闲列表头
|
|||
|
|
size_t free_count_ = 0;
|
|||
|
|
size_t total_allocated_ = 0;
|
|||
|
|
size_t total_reused_ = 0;
|
|||
|
|
|
|||
|
|
void expand();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
template <typename T, size_t ChunkSize>
|
|||
|
|
T* ObjectPool<T, ChunkSize>::acquire() {
|
|||
|
|
if (free_list_ == nullptr) {
|
|||
|
|
expand();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
total_allocated_++;
|
|||
|
|
if (free_list_ != chunks_.back()) {
|
|||
|
|
total_reused_++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Node* node = free_list_;
|
|||
|
|
free_list_ = node->next;
|
|||
|
|
free_count_--;
|
|||
|
|
|
|||
|
|
// Reset the object to default state
|
|||
|
|
node->data = T{};
|
|||
|
|
return &node->data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template <typename T, size_t ChunkSize>
|
|||
|
|
void ObjectPool<T, ChunkSize>::release(T* ptr) {
|
|||
|
|
if (!ptr) return;
|
|||
|
|
|
|||
|
|
// Recover the Node pointer from the data pointer
|
|||
|
|
Node* node = reinterpret_cast<Node*>(
|
|||
|
|
reinterpret_cast<char*>(ptr) - offsetof(Node, data));
|
|||
|
|
|
|||
|
|
node->next = free_list_;
|
|||
|
|
free_list_ = node;
|
|||
|
|
free_count_++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template <typename T, size_t ChunkSize>
|
|||
|
|
void ObjectPool<T, ChunkSize>::expand() {
|
|||
|
|
auto* chunk = static_cast<Node*>(::operator new(ChunkSize * sizeof(Node)));
|
|||
|
|
chunks_.push_back(chunk);
|
|||
|
|
|
|||
|
|
// Build free list from the new chunk
|
|||
|
|
for (size_t i = 0; i < ChunkSize; ++i) {
|
|||
|
|
chunk[i].next = free_list_;
|
|||
|
|
free_list_ = &chunk[i];
|
|||
|
|
}
|
|||
|
|
free_count_ += ChunkSize;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template <typename T, size_t ChunkSize>
|
|||
|
|
double ObjectPool<T, ChunkSize>::reuse_rate() const {
|
|||
|
|
if (total_allocated_ == 0) return 0.0;
|
|||
|
|
return static_cast<double>(total_reused_) / total_allocated_;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// ThreadSafeObjectPool — 线程安全对象池
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 线程安全对象池
|
|||
|
|
*
|
|||
|
|
* 每线程独立 pool + global fallback,无锁竞争。
|
|||
|
|
*
|
|||
|
|
* @tparam T 池化对象类型
|
|||
|
|
* @tparam ChunkSize 每个线程 local pool 的 chunk 大小
|
|||
|
|
*/
|
|||
|
|
template <typename T, size_t ChunkSize = 256>
|
|||
|
|
class ThreadSafeObjectPool {
|
|||
|
|
public:
|
|||
|
|
[[nodiscard]] T* acquire();
|
|||
|
|
void release(T* ptr);
|
|||
|
|
|
|||
|
|
[[nodiscard]] size_t total_allocated() const { return global_allocated_.load(); }
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
// Per-thread pool (thread_local)
|
|||
|
|
static thread_local ObjectPool<T, ChunkSize> local_pool_;
|
|||
|
|
|
|||
|
|
// Fallback when local pool is empty
|
|||
|
|
ObjectPool<T, ChunkSize> global_pool_;
|
|||
|
|
std::mutex global_mutex_;
|
|||
|
|
std::atomic<size_t> global_allocated_{0};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
template <typename T, size_t ChunkSize>
|
|||
|
|
thread_local ObjectPool<T, ChunkSize> ThreadSafeObjectPool<T, ChunkSize>::local_pool_{};
|
|||
|
|
|
|||
|
|
template <typename T, size_t ChunkSize>
|
|||
|
|
T* ThreadSafeObjectPool<T, ChunkSize>::acquire() {
|
|||
|
|
// Try local pool first (no lock)
|
|||
|
|
if (local_pool_.free_count() > 0) {
|
|||
|
|
return local_pool_.acquire();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Fall back to global pool
|
|||
|
|
std::lock_guard<std::mutex> lock(global_mutex_);
|
|||
|
|
if (global_pool_.free_count() > 0) {
|
|||
|
|
global_allocated_++;
|
|||
|
|
return global_pool_.acquire();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Both empty — expand global
|
|||
|
|
global_allocated_++;
|
|||
|
|
return global_pool_.acquire();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template <typename T, size_t ChunkSize>
|
|||
|
|
void ThreadSafeObjectPool<T, ChunkSize>::release(T* ptr) {
|
|||
|
|
// Return to local pool
|
|||
|
|
local_pool_.release(ptr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// CowPtr — Copy-on-Write 智能指针
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 写时复制 (Copy-on-Write) 智能指针
|
|||
|
|
*
|
|||
|
|
* 类似 shared_ptr,但修改时自动深拷贝(detach)。
|
|||
|
|
* 用于大对象如 NurbsSurface、HalfedgeMesh 的共享。
|
|||
|
|
*
|
|||
|
|
* @tparam T 被包装类型
|
|||
|
|
*/
|
|||
|
|
template <typename T>
|
|||
|
|
class CowPtr {
|
|||
|
|
public:
|
|||
|
|
CowPtr() = default;
|
|||
|
|
|
|||
|
|
explicit CowPtr(T value)
|
|||
|
|
: data_(std::make_shared<T>(std::move(value))) {}
|
|||
|
|
|
|||
|
|
CowPtr(const CowPtr& other) = default;
|
|||
|
|
CowPtr& operator=(const CowPtr& other) = default;
|
|||
|
|
CowPtr(CowPtr&&) = default;
|
|||
|
|
CowPtr& operator=(CowPtr&&) = default;
|
|||
|
|
|
|||
|
|
/// 只读访问
|
|||
|
|
[[nodiscard]] const T& read() const {
|
|||
|
|
ensure_valid();
|
|||
|
|
return *data_;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 可写访问(如有共享则深拷贝)
|
|||
|
|
[[nodiscard]] T& write() {
|
|||
|
|
ensure_valid();
|
|||
|
|
detach();
|
|||
|
|
return *data_;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 只读解引用
|
|||
|
|
[[nodiscard]] const T& operator*() const { return read(); }
|
|||
|
|
[[nodiscard]] const T* operator->() const { return &read(); }
|
|||
|
|
|
|||
|
|
/// 引用计数
|
|||
|
|
[[nodiscard]] long use_count() const {
|
|||
|
|
return data_ ? data_.use_count() : 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 是否唯一持有
|
|||
|
|
[[nodiscard]] bool unique() const {
|
|||
|
|
return data_ && data_.use_count() == 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 浅拷贝(共享数据)
|
|||
|
|
[[nodiscard]] CowPtr shallow_copy() const {
|
|||
|
|
return CowPtr(*this);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 深拷贝(显式复制数据)
|
|||
|
|
[[nodiscard]] CowPtr deep_copy() const {
|
|||
|
|
if (!data_) return CowPtr();
|
|||
|
|
return CowPtr(T(*data_));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
std::shared_ptr<T> data_;
|
|||
|
|
|
|||
|
|
void ensure_valid() const {
|
|||
|
|
if (!data_) {
|
|||
|
|
const_cast<CowPtr*>(this)->data_ = std::make_shared<T>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 如果数据被共享,则深拷贝
|
|||
|
|
void detach() {
|
|||
|
|
if (data_ && data_.use_count() > 1) {
|
|||
|
|
data_ = std::make_shared<T>(*data_);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} // namespace vde::core
|