Files
ViewDesignEngine/include/vde/core/object_pool.h
T
茂之钳 cf38d76fd0
CI / Build & Test (push) Failing after 16m56s
CI / Release Build (push) Failing after 24s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v4.6): memory pool + parallel boolean/MC/intersection + GMP exact predicates
M1 — Memory Pool (内存池):
- ObjectPool<T>: O(1) acquire/release with free-list, chunk-based expansion
- ThreadSafeObjectPool<T>: per-thread local pools with global fallback
- CowPtr<T>: copy-on-write smart pointer with shallow/deep copy
- 10 tests: single/multi-thread, CoW detach, reuse rate

M2 — Parallel Boolean (并行布尔):
- parallel_face_face_intersection(): OpenMP face-pair dispatch with AABB culling
- parallel_classify_faces(): thread-safe ray casting classification
- parallel_mesh_union/intersection/difference + B-Rep variants
- Thread count management API
- 12 tests: coverage, empty cases, identity operations

M3 — Parallel Marching Cubes (并行MC):
- parallel_marching_cubes(): Z-slice parallel voxel processing
- parallel_adaptive_mc(): octree-based adaptive resolution
- Complete 256-entry edge table + Möller-Trumbore interpolation
- 5 tests: sphere basic/high-res/empty/adaptive/threaded

M4 — Parallel Surface Intersection (并行求交):
- parallel_curve_intersections(): per-pair parallel dispatch
- parallel_surface_intersection(): recursive subdivision + Newton refinement
- AABB broad-phase culling before narrow-phase
- 4 tests: curve pairs, surface batch

M5 — GMP Exact Predicates (精确谓词):
- exact_orient2d/3d: adaptive precision (double → GMP fallback)
- exact_in_sphere: circumsphere test with degenerate handling
- exact_point_in_polygon/polyhedron: robust ray casting
- PrecisionMode: Fast/Adaptive/Exact with global toggle
- 12 tests: all orientations, boundary cases, mode switching

21 files, +2263 lines
2026-07-26 17:28:15 +08:00

278 lines
7.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
/**
* @file object_pool.h
* @brief 高性能对象池 + 写时复制
*
* 对象池:预分配 chunkfree-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