feat(v8): ultimate performance + CAM full optimization + visualization/IGA/quality
v8.1 — 极致性能 (SIMD + LockFree + Transaction + NUMA): - simd_vector.h: Vec4d/Vec4f SSE/AVX/NEON auto-detect, batch AABB, SoA transpose - concurrent_data: LockFreeQueue (MPMC CAS), LockFreeStack (Treiber), ConcurrentHashMap (64-segment sharded) - transaction: Command pattern, UndoManager (infinite undo/redo), crash-recovery journal - performance_tuning: NUMA-aware, cache_line aligned, prefetch, hot/cold separation - 20 tests (concurrent + transaction), ~2600 lines v8.2 — CAM 全面优化 + 装配模式: - cam_optimization: chip_thinning, HSM, constant_engagement, trochoidal_turn_milling - tool_life_management, probing_cycle, thread_milling - cam_advanced enhanced: Mazak/Okuma/Haas/DMG post-processors (8 total) - assembly_patterns: Circular/Rectangular/Mirror/PatternDriven/fill arrays - assembly_feature enhanced: assembly-level PMI propagation, batch interference check - 28 tests, compiled 0 errors (~2800 lines) v8.3 — 可视化+压缩+IGA+质量闭环: - visualization_quality: ambient_occlusion, edge_highlighting, wireframe, normals - topology_compression: Brep compression, Edgebreaker, vertex quantization - iga_prep: knot_insertion, degree_elevation, Bezier extraction for IGA analysis - quality_feedback: design_rule_check, manufacturability, cost_estimation, quality_score (0-100) - 28 tests, ~2349 lines 27 files, ~7750 lines, 76 tests
This commit is contained in:
@@ -234,4 +234,137 @@ struct Material {
|
||||
const Toolpath& toolpath,
|
||||
const Material& material);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 新增后处理器
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// Mazak (Mazatrol) 控制器后处理器
|
||||
class MazakPost : public PostProcessor {
|
||||
public:
|
||||
[[nodiscard]] std::string prologue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string epilogue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string format_gcode(const PathSegment& seg) const override;
|
||||
};
|
||||
|
||||
/// Okuma (OSP) 控制器后处理器
|
||||
class OkumaPost : public PostProcessor {
|
||||
public:
|
||||
[[nodiscard]] std::string prologue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string epilogue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string format_gcode(const PathSegment& seg) const override;
|
||||
};
|
||||
|
||||
/// Haas 控制器后处理器
|
||||
class HaasPost : public PostProcessor {
|
||||
public:
|
||||
[[nodiscard]] std::string prologue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string epilogue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string format_gcode(const PathSegment& seg) const override;
|
||||
};
|
||||
|
||||
/// DMG Mori (Siemens 840D + DMG 扩展) 控制器后处理器
|
||||
class DMGPost : public PostProcessor {
|
||||
public:
|
||||
[[nodiscard]] std::string prologue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string epilogue(const Toolpath& tp) const override;
|
||||
[[nodiscard]] std::string format_gcode(const PathSegment& seg) const override;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 探测循环与螺纹铣削
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 探测循环类型
|
||||
enum class ProbingCycleType {
|
||||
SingleSurface, ///< 单面探测
|
||||
Boss, ///< 凸台中心探测
|
||||
Pocket, ///< 槽/孔中心探测
|
||||
Corner, ///< 角落探测
|
||||
Angle ///< 角度探测
|
||||
};
|
||||
|
||||
/// 探测循环参数
|
||||
struct ProbingParams {
|
||||
ProbingCycleType cycle_type = ProbingCycleType::SingleSurface;
|
||||
Point3D approach_position = Point3D::Zero(); ///< 接近位置
|
||||
Point3D target_position = Point3D::Zero(); ///< 目标/期望接触位置
|
||||
Vector3D probe_direction = Vector3D(0, 0, -1); ///< 探测方向
|
||||
double overtravel = 5.0; ///< 超程距离 (mm)
|
||||
double feed_rate = 200.0; ///< 探测进给 (mm/min)
|
||||
double retract_distance = 3.0; ///< 接触后退回距离 (mm)
|
||||
double safe_z = 15.0; ///< 安全高度 (mm)
|
||||
};
|
||||
|
||||
/// 探测结果
|
||||
struct ProbingResult {
|
||||
Point3D contact_point; ///< 实际接触点坐标
|
||||
bool contact_made = false; ///< 是否探测到接触
|
||||
double deviation = 0.0; ///< 与目标位置的偏差 (mm)
|
||||
std::string message; ///< 结果消息
|
||||
};
|
||||
|
||||
/// 螺纹铣削参数
|
||||
struct ThreadMillParams {
|
||||
double thread_diameter = 10.0; ///< 螺纹公称直径 (mm)
|
||||
double thread_pitch = 1.5; ///< 螺距 (mm)
|
||||
double thread_depth = 20.0; ///< 螺纹深度 (mm)
|
||||
double major_diameter = 10.0; ///< 大径 (mm)
|
||||
double minor_diameter = 8.376; ///< 小径 (mm,M10×1.5 标准)
|
||||
int num_passes = 3; ///< 切削刀数(径向分层)
|
||||
double feed_rate = 300.0; ///< 进给速度 (mm/min)
|
||||
double safe_z = 15.0; ///< 安全高度 (mm)
|
||||
bool right_hand = true; ///< 右旋螺纹
|
||||
bool internal_thread = true; ///< 内螺纹 (false = 外螺纹)
|
||||
Point3D hole_center = Point3D::Zero(); ///< 孔中心/螺纹起点
|
||||
};
|
||||
|
||||
/// 探测循环 — 生成探测刀路
|
||||
///
|
||||
/// 使用接触式测头执行工件找正/测量循环。
|
||||
/// 支持单面、凸台、孔/槽、角落、角度等探测类型。
|
||||
///
|
||||
/// @param tool 测头(接触式探头)
|
||||
/// @param params 探测参数
|
||||
/// @return 探测刀路
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] Toolpath probing_cycle(
|
||||
const Tool& tool,
|
||||
const ProbingParams& params);
|
||||
|
||||
/// 探测循环(含结果)— 执行探测并返回结果
|
||||
///
|
||||
/// @param tool 测头
|
||||
/// @param params 探测参数
|
||||
/// @param body 工件模型(模拟碰撞检测)
|
||||
/// @return 探测结果
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] ProbingResult probing_cycle_with_result(
|
||||
const Tool& tool,
|
||||
const ProbingParams& params,
|
||||
const brep::BrepModel& body);
|
||||
|
||||
/// 螺纹铣削 — 生成螺纹铣削刀路
|
||||
///
|
||||
/// 使用螺纹铣刀以螺旋插补方式加工螺纹。
|
||||
/// 支持内/外螺纹、右旋/左旋、多刀切削。
|
||||
///
|
||||
/// 刀路策略:
|
||||
/// 1. 定位到孔中心上方安全高度
|
||||
/// 2. 下刀到螺纹起始深度
|
||||
/// 3. 径向切入到螺纹小径
|
||||
/// 4. 以螺旋方式从底向上(或从顶向下)走 360° × 圈数
|
||||
/// 5. 径向退出
|
||||
/// 6. 如有多次走刀 (num_passes > 1),逐步增加半径
|
||||
///
|
||||
/// @param tool 螺纹铣刀
|
||||
/// @param params 螺纹参数
|
||||
/// @return 螺纹铣削刀路
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] Toolpath thread_milling(
|
||||
const Tool& tool,
|
||||
const ThreadMillParams& params);
|
||||
|
||||
} // namespace vde::core
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file cam_optimization.h
|
||||
* @brief CAM 综合优化 — 切屑减薄、车铣复合摆线、HSM高速加工、恒接触角铣削、刀具寿命管理
|
||||
*
|
||||
* cam_advanced.h 提供高级加工策略,本模块在其之上提供面向效率与刀具保护的优化:
|
||||
* - chip_thinning_optimization — 切屑减薄优化,调整进给以维持恒定切屑厚度
|
||||
* - trochoidal_turn_milling — 车铣复合摆线加工
|
||||
* - high_speed_machining — HSM 高速加工策略(浅层大切宽)
|
||||
* - constant_engagement_milling — 恒接触角铣削,保持刀具啮合角恒定
|
||||
* - tool_life_management — 刀具寿命管理,基于磨损模型预测剩余寿命
|
||||
*
|
||||
* @ingroup core
|
||||
*/
|
||||
#include "vde/core/point.h"
|
||||
#include "vde/core/aabb.h"
|
||||
#include "vde/core/cam_toolpath.h"
|
||||
#include "vde/core/cam_strategies.h"
|
||||
#include "vde/core/cam_advanced.h"
|
||||
#include "vde/mesh/halfedge_mesh.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <chrono>
|
||||
|
||||
namespace vde::brep {
|
||||
class BrepModel;
|
||||
} // namespace vde::brep
|
||||
|
||||
namespace vde::core {
|
||||
|
||||
using core::Point3D;
|
||||
using core::Vector3D;
|
||||
using core::AABB3D;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 优化参数类型
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 高速加工 (HSM) 参数
|
||||
struct HSMParams {
|
||||
double step_down = 0.5; ///< 每层切深 (mm) — HSM 通常浅层
|
||||
double step_over = 4.0; ///< 横向步距 (mm) — HSM 大切宽
|
||||
double feed_rate = 2000.0; ///< 进给速度 (mm/min)
|
||||
double spindle_rpm = 15000.0; ///< 主轴转速 (rpm)
|
||||
double safe_z = 15.0; ///< 安全高度 (mm)
|
||||
double stock_to_leave = 0.3; ///< 留量 (mm)
|
||||
double corner_radius = 2.0; ///< 拐角过渡半径 (mm)
|
||||
bool trochoidal_corners = true; ///< 拐角处是否使用摆线过渡
|
||||
};
|
||||
|
||||
/// 恒接触角铣削参数
|
||||
struct ConstantEngagementParams {
|
||||
double target_engagement = 45.0; ///< 目标啮合角 (度)
|
||||
double step_down = 0.8; ///< 每层切深 (mm)
|
||||
double feed_rate = 600.0; ///< 进给速度 (mm/min)
|
||||
double safe_z = 15.0; ///< 安全高度 (mm)
|
||||
double min_step_over = 0.3; ///< 最小横向步距 (mm)
|
||||
double max_step_over = 4.0; ///< 最大横向步距 (mm)
|
||||
double engagement_tolerance = 5.0; ///< 啮合角容差 (度)
|
||||
};
|
||||
|
||||
/// 刀具寿命管理参数
|
||||
struct ToolLifeParams {
|
||||
double max_cutting_time_min = 60.0; ///< 最大切削时间 (分钟)
|
||||
double max_cutting_length_m = 500.0; ///< 最大切削距离 (米)
|
||||
double wear_coefficient = 1.0e-6; ///< 磨损系数 (mm³/N·m)
|
||||
double critical_flank_wear = 0.3; ///< 临界后刀面磨损 (mm)
|
||||
double spindle_speed_factor = 1.2; ///< 主轴转速修正系数
|
||||
bool enable_adaptive_feed = true; ///< 是否启用自适应进给降级
|
||||
};
|
||||
|
||||
/// 刀具寿命预估结果
|
||||
struct ToolLifeResult {
|
||||
double estimated_life_min = 0.0; ///< 预估剩余寿命 (分钟)
|
||||
double accumulated_wear_mm = 0.0; ///< 累计磨损量 (mm)
|
||||
double material_removed_mm3 = 0.0; ///< 已切除材料体积 (mm³)
|
||||
double current_flank_wear = 0.0; ///< 当前后刀面磨损 (mm)
|
||||
bool needs_replacement = false;///< 是否需要更换刀具
|
||||
double recommended_feed_scale = 1.0; ///< 推荐的进给缩放系数
|
||||
std::string warning_message; ///< 警告消息
|
||||
};
|
||||
|
||||
/// 车铣复合摆线参数
|
||||
struct TurnMillParams {
|
||||
double stock_diameter = 0.0; ///< 毛坯直径 (mm),0 表示从模型推测
|
||||
double step_down = 1.0; ///< 每层切深 (mm)
|
||||
double trochoid_radius = 2.5; ///< 摆线圈半径 (mm)
|
||||
double step_forward = 1.0; ///< 每圈前进量 (mm)
|
||||
double feed_rate = 500.0; ///< 进给速度 (mm/min)
|
||||
double spindle_rpm = 8000.0; ///< 铣主轴转速 (rpm)
|
||||
double turning_rpm = 500.0; ///< 车主轴转速 (rpm)
|
||||
double safe_z = 20.0; ///< 安全高度 (mm)
|
||||
bool sync_axes = true; ///< C/Y 轴同步
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// CAM 优化函数
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 切屑减薄优化 (Chip Thinning Optimization)
|
||||
///
|
||||
/// 当径向切削深度小于刀具半径时,实际切屑厚度小于设定的每齿进给量。
|
||||
/// 此函数调整进给率以补偿切屑减薄效应,维持恒定的实际切屑厚度,
|
||||
/// 避免切削过薄导致刀具摩擦而非切削。
|
||||
///
|
||||
/// 公式: f_adj = f_nominal × (D / (2 × √(ae×(D−ae)))) 当 ae < D/2
|
||||
///
|
||||
/// @param toolpath 原始刀路(进给率为名义值)
|
||||
/// @param tool 使用的刀具
|
||||
/// @return 进给补偿后的刀路
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] Toolpath chip_thinning_optimization(
|
||||
const Toolpath& toolpath,
|
||||
const Tool& tool);
|
||||
|
||||
/// 车铣复合摆线加工 (Trochoidal Turn-Milling)
|
||||
///
|
||||
/// 在车铣复合机床上使用铣刀对旋转工件进行摆线切削。
|
||||
/// 工件旋转(C 轴)提供车削运动,铣刀沿径向和轴向做摆线切入。
|
||||
/// 适用于轴类零件的深槽、偏心特征等。
|
||||
///
|
||||
/// 策略:
|
||||
/// 1. 工件以 turning_rpm 旋转
|
||||
/// 2. 铣刀沿径向做摆线切入(trochoid_radius / step_forward)
|
||||
/// 3. 轴向分层推进(step_down)
|
||||
///
|
||||
/// @param body B-Rep 实体模型
|
||||
/// @param tool 铣刀参数
|
||||
/// @param params 车铣复合参数
|
||||
/// @return 车铣复合刀路
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] Toolpath trochoidal_turn_milling(
|
||||
const brep::BrepModel& body,
|
||||
const Tool& tool,
|
||||
const TurnMillParams& params = TurnMillParams{});
|
||||
|
||||
/// 高速加工 (High-Speed Machining)
|
||||
///
|
||||
/// 采用浅层大切宽策略,配合拐角摆线过渡以维持恒定切削负载。
|
||||
/// 不同于传统粗加工的重切削,HSM 利用高主轴转速和快速进给,
|
||||
/// 以"高速轻切"的方式实现更高的材料去除率。
|
||||
///
|
||||
/// 特点:
|
||||
/// - 浅切深 (step_down ~0.5mm),大切宽 (step_over ~4mm)
|
||||
/// - 拐角自动摆线过渡,避免负载突变
|
||||
/// - 更高进给率 (通常 >2000 mm/min)
|
||||
/// - 平滑的刀路轨迹减少加减速
|
||||
///
|
||||
/// @param toolpath 原始刀路(作基础路径参考)
|
||||
/// @param params HSM 参数
|
||||
/// @return 优化后的高速刀路
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] Toolpath high_speed_machining(
|
||||
const Toolpath& toolpath,
|
||||
const HSMParams& params);
|
||||
|
||||
/// 恒接触角铣削 (Constant Engagement Milling)
|
||||
///
|
||||
/// 在加工过程中动态调整横向步距,使刀具与材料的啮合角始终保持恒定。
|
||||
/// 恒定啮合角 = 恒定切削力 = 刀具负载稳定 → 延长刀具寿命、提高表面质量。
|
||||
///
|
||||
/// 在进入角落或狭窄区域时减小步距,在开放区域时增大步距。
|
||||
///
|
||||
/// @param body B-Rep 实体模型
|
||||
/// @param tool 使用的刀具
|
||||
/// @param params 恒接触角参数
|
||||
/// @return 恒接触角刀路
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] Toolpath constant_engagement_milling(
|
||||
const brep::BrepModel& body,
|
||||
const Tool& tool,
|
||||
const ConstantEngagementParams& params = ConstantEngagementParams{});
|
||||
|
||||
/// 刀具寿命管理 (Tool Life Management)
|
||||
///
|
||||
/// 基于 Taylor 刀具寿命公式和累积磨损模型,预测刀具的剩余寿命,
|
||||
/// 并在需要时建议降低进给率以延长刀具寿命至完成当前任务。
|
||||
///
|
||||
/// Taylor 公式: Vc × T^n = C (Vc = 切削速度,T = 刀具寿命,n, C 为材料常数)
|
||||
///
|
||||
/// @param tool 当前刀具
|
||||
/// @param material 工件材料属性
|
||||
/// @param params 刀具寿命管理参数
|
||||
/// @return 刀具寿命预估结果(包含磨损量和建议进给修正)
|
||||
///
|
||||
/// @ingroup core
|
||||
[[nodiscard]] ToolLifeResult tool_life_management(
|
||||
const Tool& tool,
|
||||
const Material& material,
|
||||
const ToolLifeParams& params = ToolLifeParams{});
|
||||
|
||||
} // namespace vde::core
|
||||
@@ -0,0 +1,585 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file concurrent_data.h
|
||||
* @brief 极致性能 — 无锁并发数据结构
|
||||
*
|
||||
* 提供高性能线程安全容器:
|
||||
* - LockFreeQueue<T> : 无锁队列 (CAS),多生产者多消费者
|
||||
* - LockFreeStack<T> : 无锁栈 (CAS),单生产者单消费者为主
|
||||
* - ConcurrentHashMap<K,V>: 分段锁哈希表,读写并发
|
||||
* - ReadWriteSpinLock : 读写自旋锁(读优先)
|
||||
*
|
||||
* @ingroup core
|
||||
*/
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
namespace vde::core {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 无锁队列 (Lock-Free Queue)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 多生产者-多消费者无锁环形队列
|
||||
///
|
||||
/// 基于原子 CAS 操作实现,无互斥锁,适用于高吞吐场景。
|
||||
/// 内部使用 2 的幂次容量保证索引取模为位运算。
|
||||
///
|
||||
/// @tparam T 元素类型(必须可平凡拷贝)
|
||||
///
|
||||
/// @note 容量必须为 2 的幂,自动向上取整
|
||||
template<typename T>
|
||||
class LockFreeQueue {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"LockFreeQueue requires trivially copyable types");
|
||||
|
||||
public:
|
||||
/// 构造函数
|
||||
/// @param capacity 期望容量(自动向上取整到 2 的幂,最小 8)
|
||||
explicit LockFreeQueue(size_t capacity = 1024)
|
||||
: mask_(next_power_of_two(std::max(capacity, size_t(8))) - 1)
|
||||
{
|
||||
size_t real = mask_ + 1;
|
||||
buffer_ = static_cast<Element*>(
|
||||
::operator new(real * sizeof(Element), std::nothrow));
|
||||
// placement new 初始化
|
||||
for (size_t i = 0; i < real; ++i) {
|
||||
new (&buffer_[i]) Element();
|
||||
}
|
||||
head_.store(0, std::memory_order_relaxed);
|
||||
tail_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
~LockFreeQueue() {
|
||||
if (buffer_) {
|
||||
size_t real = mask_ + 1;
|
||||
for (size_t i = 0; i < real; ++i) {
|
||||
buffer_[i].~Element();
|
||||
}
|
||||
::operator delete(buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
// 禁止拷贝
|
||||
LockFreeQueue(const LockFreeQueue&) = delete;
|
||||
LockFreeQueue& operator=(const LockFreeQueue&) = delete;
|
||||
|
||||
/// 尝试入队(非阻塞)
|
||||
/// @param item 要入队的元素
|
||||
/// @return true 若入队成功
|
||||
bool try_push(const T& item) {
|
||||
size_t tail = tail_.load(std::memory_order_relaxed);
|
||||
for (;;) {
|
||||
size_t head = head_.load(std::memory_order_acquire);
|
||||
// 满判断:tail + 1 == head (mod capacity)
|
||||
if ((tail - head) >= mask_) return false;
|
||||
|
||||
if (tail_.compare_exchange_weak(tail, tail + 1,
|
||||
std::memory_order_release, std::memory_order_relaxed)) {
|
||||
// 成功分配 slot
|
||||
size_t idx = tail & mask_;
|
||||
buffer_[idx].data = item;
|
||||
buffer_[idx].ready.store(true, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
// CAS 失败,tail 已更新,重试
|
||||
}
|
||||
}
|
||||
|
||||
/// 尝试移动入队
|
||||
bool try_push(T&& item) {
|
||||
size_t tail = tail_.load(std::memory_order_relaxed);
|
||||
for (;;) {
|
||||
size_t head = head_.load(std::memory_order_acquire);
|
||||
if ((tail - head) >= mask_) return false;
|
||||
|
||||
if (tail_.compare_exchange_weak(tail, tail + 1,
|
||||
std::memory_order_release, std::memory_order_relaxed)) {
|
||||
size_t idx = tail & mask_;
|
||||
buffer_[idx].data = std::move(item);
|
||||
buffer_[idx].ready.store(true, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 尝试出队(非阻塞)
|
||||
/// @param item 输出参数,接收出队元素
|
||||
/// @return true 若出队成功
|
||||
bool try_pop(T& item) {
|
||||
size_t head = head_.load(std::memory_order_relaxed);
|
||||
for (;;) {
|
||||
size_t tail = tail_.load(std::memory_order_acquire);
|
||||
if (head == tail) return false; // 空
|
||||
|
||||
size_t idx = head & mask_;
|
||||
if (!buffer_[idx].ready.load(std::memory_order_acquire)) {
|
||||
// 生产者尚未写入完成,重试
|
||||
head = head_.load(std::memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (head_.compare_exchange_weak(head, head + 1,
|
||||
std::memory_order_release, std::memory_order_relaxed)) {
|
||||
item = std::move(buffer_[idx].data);
|
||||
buffer_[idx].ready.store(false, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
// CAS 失败,重试
|
||||
}
|
||||
}
|
||||
|
||||
/// 是否为空
|
||||
[[nodiscard]] bool empty() const {
|
||||
return head_.load(std::memory_order_acquire) ==
|
||||
tail_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/// 当前元素数(近似值,并发下非精确)
|
||||
[[nodiscard]] size_t size() const {
|
||||
size_t h = head_.load(std::memory_order_acquire);
|
||||
size_t t = tail_.load(std::memory_order_acquire);
|
||||
return (t >= h) ? (t - h) : 0;
|
||||
}
|
||||
|
||||
/// 容量
|
||||
[[nodiscard]] size_t capacity() const { return mask_ + 1; }
|
||||
|
||||
private:
|
||||
struct Element {
|
||||
T data{};
|
||||
std::atomic<bool> ready{false};
|
||||
};
|
||||
|
||||
Element* buffer_;
|
||||
const size_t mask_; // capacity - 1 (power of 2)
|
||||
alignas(64) std::atomic<size_t> head_{0};
|
||||
alignas(64) std::atomic<size_t> tail_{0};
|
||||
|
||||
static size_t next_power_of_two(size_t v) {
|
||||
v--;
|
||||
v |= v >> 1; v |= v >> 2;
|
||||
v |= v >> 4; v |= v >> 8;
|
||||
v |= v >> 16; v |= v >> 32;
|
||||
return v + 1;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 无锁栈 (Lock-Free Stack)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 无锁栈 — Treiber Stack
|
||||
///
|
||||
/// 基于 CAS 的单链表实现,适用于单生产者单消费者或低竞争多生产者场景。
|
||||
///
|
||||
/// @tparam T 元素类型
|
||||
template<typename T>
|
||||
class LockFreeStack {
|
||||
struct Node {
|
||||
T data;
|
||||
Node* next;
|
||||
Node(const T& d, Node* n = nullptr) : data(d), next(n) {}
|
||||
Node(T&& d, Node* n = nullptr) : data(std::move(d)), next(n) {}
|
||||
};
|
||||
|
||||
public:
|
||||
LockFreeStack() : head_(nullptr) {}
|
||||
|
||||
~LockFreeStack() {
|
||||
Node* node = head_.load(std::memory_order_relaxed);
|
||||
while (node) {
|
||||
Node* next = node->next;
|
||||
delete node;
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
LockFreeStack(const LockFreeStack&) = delete;
|
||||
LockFreeStack& operator=(const LockFreeStack&) = delete;
|
||||
|
||||
/// 压栈
|
||||
void push(const T& item) {
|
||||
Node* node = new Node(item);
|
||||
node->next = head_.load(std::memory_order_relaxed);
|
||||
while (!head_.compare_exchange_weak(node->next, node,
|
||||
std::memory_order_release, std::memory_order_relaxed)) {
|
||||
// 自旋重试
|
||||
}
|
||||
}
|
||||
|
||||
/// 移动压栈
|
||||
void push(T&& item) {
|
||||
Node* node = new Node(std::move(item));
|
||||
node->next = head_.load(std::memory_order_relaxed);
|
||||
while (!head_.compare_exchange_weak(node->next, node,
|
||||
std::memory_order_release, std::memory_order_relaxed)) {
|
||||
// 自旋重试
|
||||
}
|
||||
}
|
||||
|
||||
/// 尝试弹栈
|
||||
/// @return true 若弹栈成功
|
||||
bool try_pop(T& item) {
|
||||
Node* node = head_.load(std::memory_order_acquire);
|
||||
while (node) {
|
||||
if (head_.compare_exchange_weak(node, node->next,
|
||||
std::memory_order_release, std::memory_order_relaxed)) {
|
||||
item = std::move(node->data);
|
||||
delete node;
|
||||
return true;
|
||||
}
|
||||
// CAS 失败,node 已更新为新的 head,继续
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 是否为栈空
|
||||
[[nodiscard]] bool empty() const {
|
||||
return head_.load(std::memory_order_acquire) == nullptr;
|
||||
}
|
||||
|
||||
/// 清空栈
|
||||
void clear() {
|
||||
Node* node = head_.exchange(nullptr, std::memory_order_acq_rel);
|
||||
while (node) {
|
||||
Node* next = node->next;
|
||||
delete node;
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<Node*> head_;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 分段锁哈希表 (Concurrent HashMap)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 分段锁哈希表
|
||||
///
|
||||
/// 将哈希表分成多个段(Segment),每段独立加锁。
|
||||
/// 不同段的操作完全并行,同一段内部串行。
|
||||
///
|
||||
/// @tparam K 键类型
|
||||
/// @tparam V 值类型
|
||||
/// @tparam Hash 哈希函数(默认 std::hash<K>)
|
||||
///
|
||||
/// @note 段数量固定为 64(2 的幂),保证取模为位运算
|
||||
template<typename K, typename V, typename Hash = std::hash<K>>
|
||||
class ConcurrentHashMap {
|
||||
static constexpr size_t NUM_SEGMENTS = 64;
|
||||
static constexpr size_t SEGMENT_MASK = NUM_SEGMENTS - 1;
|
||||
|
||||
struct Entry {
|
||||
K key;
|
||||
V value;
|
||||
Entry* next = nullptr;
|
||||
Entry(const K& k, const V& v) : key(k), value(v) {}
|
||||
Entry(K&& k, V&& v) : key(std::move(k)), value(std::move(v)) {}
|
||||
};
|
||||
|
||||
struct alignas(64) Bucket {
|
||||
Entry* head = nullptr;
|
||||
};
|
||||
|
||||
struct alignas(64) Segment {
|
||||
std::shared_mutex mutex;
|
||||
std::vector<Bucket> buckets;
|
||||
|
||||
Segment() : buckets(16) {} // 初始 16 桶/段
|
||||
};
|
||||
|
||||
public:
|
||||
ConcurrentHashMap() {
|
||||
for (size_t i = 0; i < NUM_SEGMENTS; ++i) {
|
||||
segments_[i].store(new Segment(), std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
~ConcurrentHashMap() {
|
||||
for (size_t i = 0; i < NUM_SEGMENTS; ++i) {
|
||||
Segment* seg = segments_[i].load(std::memory_order_relaxed);
|
||||
if (seg) {
|
||||
for (auto& bucket : seg->buckets) {
|
||||
Entry* e = bucket.head;
|
||||
while (e) {
|
||||
Entry* next = e->next;
|
||||
delete e;
|
||||
e = next;
|
||||
}
|
||||
}
|
||||
delete seg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConcurrentHashMap(const ConcurrentHashMap&) = delete;
|
||||
ConcurrentHashMap& operator=(const ConcurrentHashMap&) = delete;
|
||||
|
||||
/// 插入或更新
|
||||
/// @param key 键
|
||||
/// @param value 值
|
||||
void insert(const K& key, const V& value) {
|
||||
size_t seg_idx = segment_index(key);
|
||||
Segment* seg = segments_[seg_idx].load(std::memory_order_acquire);
|
||||
std::unique_lock lock(seg->mutex);
|
||||
|
||||
size_t bidx = bucket_index(key, seg->buckets.size());
|
||||
Entry* e = seg->buckets[bidx].head;
|
||||
while (e) {
|
||||
if (e->key == key) { e->value = value; return; }
|
||||
e = e->next;
|
||||
}
|
||||
// 插入链表头
|
||||
Entry* ne = new Entry(key, value);
|
||||
ne->next = seg->buckets[bidx].head;
|
||||
seg->buckets[bidx].head = ne;
|
||||
}
|
||||
|
||||
/// 移动插入
|
||||
void insert(K&& key, V&& value) {
|
||||
size_t seg_idx = segment_index(key);
|
||||
Segment* seg = segments_[seg_idx].load(std::memory_order_acquire);
|
||||
std::unique_lock lock(seg->mutex);
|
||||
|
||||
size_t bidx = bucket_index(key, seg->buckets.size());
|
||||
Entry* e = seg->buckets[bidx].head;
|
||||
while (e) {
|
||||
if (e->key == key) { e->value = std::move(value); return; }
|
||||
e = e->next;
|
||||
}
|
||||
Entry* ne = new Entry(std::move(key), std::move(value));
|
||||
ne->next = seg->buckets[bidx].head;
|
||||
seg->buckets[bidx].head = ne;
|
||||
}
|
||||
|
||||
/// 查找
|
||||
/// @param key 键
|
||||
/// @param value 输出参数
|
||||
/// @return true 若找到
|
||||
[[nodiscard]] bool find(const K& key, V& value) const {
|
||||
size_t seg_idx = segment_index(key);
|
||||
Segment* seg = segments_[seg_idx].load(std::memory_order_acquire);
|
||||
std::shared_lock lock(seg->mutex);
|
||||
|
||||
size_t bidx = bucket_index(key, seg->buckets.size());
|
||||
Entry* e = seg->buckets[bidx].head;
|
||||
while (e) {
|
||||
if (e->key == key) { value = e->value; return true; }
|
||||
e = e->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 检查键是否存在
|
||||
[[nodiscard]] bool contains(const K& key) const {
|
||||
V dummy;
|
||||
return find(key, dummy);
|
||||
}
|
||||
|
||||
/// 删除键
|
||||
/// @return true 若删除成功
|
||||
bool erase(const K& key) {
|
||||
size_t seg_idx = segment_index(key);
|
||||
Segment* seg = segments_[seg_idx].load(std::memory_order_acquire);
|
||||
std::unique_lock lock(seg->mutex);
|
||||
|
||||
size_t bidx = bucket_index(key, seg->buckets.size());
|
||||
Entry** prev = &seg->buckets[bidx].head;
|
||||
Entry* e = *prev;
|
||||
while (e) {
|
||||
if (e->key == key) {
|
||||
*prev = e->next;
|
||||
delete e;
|
||||
return true;
|
||||
}
|
||||
prev = &e->next;
|
||||
e = e->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 清空
|
||||
void clear() {
|
||||
for (size_t i = 0; i < NUM_SEGMENTS; ++i) {
|
||||
Segment* seg = segments_[i].load(std::memory_order_acquire);
|
||||
std::unique_lock lock(seg->mutex);
|
||||
for (auto& bucket : seg->buckets) {
|
||||
Entry* e = bucket.head;
|
||||
while (e) {
|
||||
Entry* next = e->next;
|
||||
delete e;
|
||||
e = next;
|
||||
}
|
||||
bucket.head = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 总元素数(近似值,遍历所有段)
|
||||
[[nodiscard]] size_t size() const {
|
||||
size_t total = 0;
|
||||
for (size_t i = 0; i < NUM_SEGMENTS; ++i) {
|
||||
Segment* seg = segments_[i].load(std::memory_order_acquire);
|
||||
std::shared_lock lock(seg->mutex);
|
||||
for (auto& bucket : seg->buckets) {
|
||||
Entry* e = bucket.head;
|
||||
while (e) { ++total; e = e->next; }
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<Segment*> segments_[NUM_SEGMENTS];
|
||||
Hash hasher_;
|
||||
|
||||
size_t segment_index(const K& key) const {
|
||||
return hasher_(key) & SEGMENT_MASK;
|
||||
}
|
||||
|
||||
size_t bucket_index(const K& key, size_t num_buckets) const {
|
||||
return (hasher_(key) >> 6) % num_buckets;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 读写自旋锁
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 读优先读写自旋锁
|
||||
///
|
||||
/// 多个读者可以同时持有锁;写者独占。
|
||||
/// 使用原子自旋等待,适合临界区极短(< 100 ns)的场景。
|
||||
///
|
||||
/// @note 读者数量限制为 2^30 - 1
|
||||
class ReadWriteSpinLock {
|
||||
static constexpr int WRITER_BIT = 0x40000000; // bit 30
|
||||
static constexpr int READER_MASK = 0x3FFFFFFF; // bits 0-29
|
||||
|
||||
public:
|
||||
ReadWriteSpinLock() : state_(0) {}
|
||||
|
||||
// ── 读锁 ──
|
||||
|
||||
/// 获取读锁(自旋等待直到无写者)
|
||||
void lock_read() {
|
||||
for (int spins = 0; ; ++spins) {
|
||||
int s = state_.load(std::memory_order_relaxed);
|
||||
if (!(s & WRITER_BIT)) {
|
||||
if (state_.compare_exchange_weak(s, s + 1,
|
||||
std::memory_order_acquire, std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 自适应退避
|
||||
if (spins > 100) {
|
||||
// PAUSE / YIELD
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
__builtin_ia32_pause();
|
||||
#elif defined(__aarch64__)
|
||||
__asm__ volatile("yield");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 释放读锁
|
||||
void unlock_read() {
|
||||
state_.fetch_sub(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
// ── 写锁 ──
|
||||
|
||||
/// 获取写锁(自旋等待直到无读者且无写者)
|
||||
void lock_write() {
|
||||
for (int spins = 0; ; ++spins) {
|
||||
int expected = 0;
|
||||
if (state_.compare_exchange_weak(expected, WRITER_BIT,
|
||||
std::memory_order_acquire, std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
if (spins > 100) {
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
__builtin_ia32_pause();
|
||||
#elif defined(__aarch64__)
|
||||
__asm__ volatile("yield");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 释放写锁
|
||||
void unlock_write() {
|
||||
state_.store(0, std::memory_order_release);
|
||||
}
|
||||
|
||||
/// 尝试获取读锁(非阻塞)
|
||||
[[nodiscard]] bool try_lock_read() {
|
||||
int s = state_.load(std::memory_order_relaxed);
|
||||
if (s & WRITER_BIT) return false;
|
||||
return state_.compare_exchange_strong(s, s + 1,
|
||||
std::memory_order_acquire, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// 尝试获取写锁(非阻塞)
|
||||
[[nodiscard]] bool try_lock_write() {
|
||||
int expected = 0;
|
||||
return state_.compare_exchange_strong(expected, WRITER_BIT,
|
||||
std::memory_order_acquire, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// 是否有写者
|
||||
[[nodiscard]] bool is_write_locked() const {
|
||||
return state_.load(std::memory_order_acquire) & WRITER_BIT;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<int> state_;
|
||||
};
|
||||
|
||||
/// RAII 读锁守卫
|
||||
class ReadLockGuard {
|
||||
public:
|
||||
explicit ReadLockGuard(ReadWriteSpinLock& lock) : lock_(&lock) { lock_->lock_read(); }
|
||||
~ReadLockGuard() { if (lock_) lock_->unlock_read(); }
|
||||
ReadLockGuard(const ReadLockGuard&) = delete;
|
||||
ReadLockGuard& operator=(const ReadLockGuard&) = delete;
|
||||
ReadLockGuard(ReadLockGuard&& o) noexcept : lock_(o.lock_) { o.lock_ = nullptr; }
|
||||
ReadLockGuard& operator=(ReadLockGuard&& o) noexcept {
|
||||
if (this != &o) { lock_ = o.lock_; o.lock_ = nullptr; }
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
ReadWriteSpinLock* lock_;
|
||||
};
|
||||
|
||||
/// RAII 写锁守卫
|
||||
class WriteLockGuard {
|
||||
public:
|
||||
explicit WriteLockGuard(ReadWriteSpinLock& lock) : lock_(&lock) { lock_->lock_write(); }
|
||||
~WriteLockGuard() { if (lock_) lock_->unlock_write(); }
|
||||
WriteLockGuard(const WriteLockGuard&) = delete;
|
||||
WriteLockGuard& operator=(const WriteLockGuard&) = delete;
|
||||
WriteLockGuard(WriteLockGuard&& o) noexcept : lock_(o.lock_) { o.lock_ = nullptr; }
|
||||
WriteLockGuard& operator=(WriteLockGuard&& o) noexcept {
|
||||
if (this != &o) { lock_ = o.lock_; o.lock_ = nullptr; }
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
ReadWriteSpinLock* lock_;
|
||||
};
|
||||
|
||||
} // namespace vde::core
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <unordered_map>
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace vde::core {
|
||||
|
||||
@@ -391,4 +392,291 @@ struct Point3DSoA {
|
||||
void clear() { x.clear(); y.clear(); z.clear(); }
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// NUMA 感知分配
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// NUMA 节点信息
|
||||
struct NumaInfo {
|
||||
int node_count = 1; ///< NUMA 节点数
|
||||
int current_node = 0; ///< 当前线程所在节点
|
||||
std::vector<int> cpu_count; ///< 每节点 CPU 数
|
||||
std::vector<size_t> memory_mb; ///< 每节点内存 MB
|
||||
|
||||
/// 是否运行在 NUMA 架构上
|
||||
[[nodiscard]] bool is_numa() const { return node_count > 1; }
|
||||
};
|
||||
|
||||
/// 检测 NUMA 拓扑信息
|
||||
[[nodiscard]] NumaInfo detect_numa_topology();
|
||||
|
||||
/// NUMA 感知分配器
|
||||
///
|
||||
/// 在指定 NUMA 节点上分配内存,避免跨节点访问延迟。
|
||||
/// 如果系统不支持 NUMA(单节点),回退到普通 malloc。
|
||||
///
|
||||
/// @tparam T 元素类型
|
||||
template<typename T>
|
||||
struct NumaAllocator {
|
||||
using value_type = T;
|
||||
int node_id; ///< 目标 NUMA 节点 (-1 = 当前节点)
|
||||
|
||||
explicit NumaAllocator(int node = -1) : node_id(node) {}
|
||||
|
||||
template<typename U>
|
||||
NumaAllocator(const NumaAllocator<U>& other) : node_id(other.node_id) {}
|
||||
|
||||
[[nodiscard]] T* allocate(std::size_t n) {
|
||||
size_t bytes = n * sizeof(T);
|
||||
void* ptr = numa_allocate(bytes, node_id);
|
||||
if (!ptr) throw std::bad_alloc();
|
||||
return static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
void deallocate(T* ptr, std::size_t /*n*/) {
|
||||
numa_deallocate(ptr);
|
||||
}
|
||||
|
||||
/// 在指定 NUMA 节点上分配内存
|
||||
static void* numa_allocate(size_t bytes, int node);
|
||||
|
||||
/// 释放 NUMA 内存
|
||||
static void numa_deallocate(void* ptr);
|
||||
|
||||
/// 将已分配内存绑定到指定 NUMA 节点
|
||||
static void bind_to_node(void* ptr, size_t bytes, int node);
|
||||
|
||||
/// 将当前线程绑定到指定 NUMA 节点
|
||||
static bool pin_thread_to_node(int node);
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
bool operator==(const NumaAllocator<T>& a, const NumaAllocator<U>& b) {
|
||||
return a.node_id == b.node_id;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
bool operator!=(const NumaAllocator<T>& a, const NumaAllocator<U>& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// NUMA 感知 STL vector(自动在本地节点分配)
|
||||
template<typename T>
|
||||
using NumaVector = std::vector<T, NumaAllocator<T>>;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 缓存行对齐工具
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 缓存行对齐宏
|
||||
#define VDE_CACHE_ALIGN alignas(64)
|
||||
|
||||
/// 缓存行补齐(确保对象独占缓存行,防止假共享)
|
||||
///
|
||||
/// 使用示例:
|
||||
/// @code
|
||||
/// struct alignas(64) ThreadLocalData {
|
||||
/// int counter;
|
||||
/// };
|
||||
/// // 或
|
||||
/// VDE_CACHE_ALIGN int shared_counter;
|
||||
/// @endcode
|
||||
|
||||
/// 缓存行大小模板常量
|
||||
template<typename T>
|
||||
constexpr size_t cache_line_count(size_t elements_per_line = 1) {
|
||||
return CACHE_LINE_SIZE / sizeof(T) * elements_per_line;
|
||||
}
|
||||
|
||||
/// 数据结构缓存对齐包装
|
||||
///
|
||||
/// 将任意类型 T 对齐到缓存行边界并填充至完整缓存行,彻底消除假共享。
|
||||
template<typename T>
|
||||
struct alignas(CACHE_LINE_SIZE) CacheLineAligned {
|
||||
T data;
|
||||
|
||||
CacheLineAligned() = default;
|
||||
explicit CacheLineAligned(const T& d) : data(d) {}
|
||||
explicit CacheLineAligned(T&& d) : data(std::move(d)) {}
|
||||
|
||||
T* operator->() { return &data; }
|
||||
const T* operator->() const { return &data; }
|
||||
T& operator*() { return data; }
|
||||
const T& operator*() const { return data; }
|
||||
|
||||
// 填充至完整缓存行
|
||||
private:
|
||||
char padding_[CACHE_LINE_SIZE - sizeof(T)] = {};
|
||||
};
|
||||
|
||||
/// 缓存行对齐的可变数组
|
||||
///
|
||||
/// 分配对齐到缓存行的动态数组。使用场景:多线程分别访问不重叠的
|
||||
/// 缓存行(例如每个线程写一个计数器,不产生假共享)。
|
||||
template<typename T>
|
||||
class CacheLineArray {
|
||||
public:
|
||||
explicit CacheLineArray(size_t count) : count_(count) {
|
||||
// posix_memalign 分配
|
||||
void* p = nullptr;
|
||||
if (posix_memalign(&p, CACHE_LINE_SIZE, count * sizeof(T)) != 0) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
data_ = static_cast<T*>(p);
|
||||
// placement new
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
new (&data_[i]) T();
|
||||
}
|
||||
}
|
||||
|
||||
~CacheLineArray() {
|
||||
for (size_t i = 0; i < count_; ++i) {
|
||||
data_[i].~T();
|
||||
}
|
||||
free(data_);
|
||||
}
|
||||
|
||||
CacheLineArray(const CacheLineArray&) = delete;
|
||||
CacheLineArray& operator=(const CacheLineArray&) = delete;
|
||||
|
||||
T& operator[](size_t i) { return data_[i]; }
|
||||
const T& operator[](size_t i) const { return data_[i]; }
|
||||
[[nodiscard]] size_t size() const { return count_; }
|
||||
[[nodiscard]] T* data() { return data_; }
|
||||
[[nodiscard]] const T* data() const { return data_; }
|
||||
|
||||
private:
|
||||
T* data_;
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 预取提示(增强版)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 预取距离辅助
|
||||
enum class PrefetchDistance {
|
||||
NEAR = 1, ///< L1 近距 (1 cache line)
|
||||
MID = 8, ///< L2 中距 (8 cache lines)
|
||||
FAR = 16, ///< L3 远距 (16 cache lines)
|
||||
};
|
||||
|
||||
/// 预取数组元素(带步进)
|
||||
template<size_t Stride = 1, PrefetchDistance Dist = PrefetchDistance::NEAR>
|
||||
inline void prefetch_array(const void* base, size_t index) {
|
||||
const char* ptr = static_cast<const char*>(base)
|
||||
+ index * Stride * CACHE_LINE_SIZE;
|
||||
__builtin_prefetch(ptr, 0, static_cast<int>(Dist) > 1 ? 2 : 3);
|
||||
}
|
||||
|
||||
/// 预取下一个元素(循环优化)
|
||||
/// @code
|
||||
/// for (size_t i = 0; i < n; ++i) {
|
||||
/// prefetch_next(&data[i+8]); // 提前 8 步预取
|
||||
/// process(data[i]);
|
||||
/// }
|
||||
/// @endcode
|
||||
template<size_t Lookahead = 4>
|
||||
inline void prefetch_next(const void* addr) {
|
||||
__builtin_prefetch(addr, 0, 3);
|
||||
}
|
||||
|
||||
/// 预取写入(用于 scatter 操作)
|
||||
template<size_t Lookahead = 4>
|
||||
inline void prefetch_write_next(void* addr) {
|
||||
__builtin_prefetch(addr, 1, 3);
|
||||
}
|
||||
|
||||
/// 顺序预取批量数据
|
||||
///
|
||||
/// @param base 数组基址
|
||||
/// @param count 元素数
|
||||
/// @param stride 元素步长 (bytes)
|
||||
/// @param distance 预取距离(元素数)
|
||||
inline void prefetch_range(const void* base, size_t count,
|
||||
size_t stride, size_t distance = 8) {
|
||||
const char* ptr = static_cast<const char*>(base);
|
||||
size_t limit = std::min(count, count);
|
||||
for (size_t i = 0; i < limit; i += distance) {
|
||||
__builtin_prefetch(ptr + i * stride, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// hot/cold 数据分离
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// hot 数据标记(频繁访问,应保持在一级缓存)
|
||||
#define VDE_HOT_DATA [[gnu::hot]]
|
||||
|
||||
/// cold 数据标记(极少访问,不应污染缓存)
|
||||
#define VDE_COLD_DATA [[gnu::cold]]
|
||||
|
||||
/// hot 函数标记(频繁调用路径)
|
||||
#define VDE_HOT_FUNC __attribute__((hot))
|
||||
|
||||
/// cold 函数标记(错误处理、日志等冷路径)
|
||||
#define VDE_COLD_FUNC __attribute__((cold))
|
||||
|
||||
/// 将热数据和冷数据分离存储的结构体模式
|
||||
///
|
||||
/// 使用示例:将 Point3D 的热字段 (xyz) 和冷字段 (id, metadata) 分离:
|
||||
/// @code
|
||||
/// struct HotPoint {
|
||||
/// VDE_CACHE_ALIGN double x, y, z; // 热:每次遍历都访问
|
||||
/// };
|
||||
/// struct ColdPoint {
|
||||
/// int id;
|
||||
/// std::string metadata; // 冷:偶尔访问
|
||||
/// };
|
||||
/// // SoA: std::vector<HotPoint> hot, std::vector<ColdPoint> cold;
|
||||
/// @endcode
|
||||
|
||||
/// 冷数据引用包装器
|
||||
///
|
||||
/// 将大型/不常访问的数据移出热路径,通过指针间接访问。
|
||||
template<typename T>
|
||||
class ColdStorage {
|
||||
public:
|
||||
ColdStorage() : ptr_(std::make_unique<T>()) {}
|
||||
explicit ColdStorage(T&& val) : ptr_(std::make_unique<T>(std::move(val))) {}
|
||||
explicit ColdStorage(const T& val) : ptr_(std::make_unique<T>(val)) {}
|
||||
|
||||
T& get() { return *ptr_; }
|
||||
const T& get() const { return *ptr_; }
|
||||
T* operator->() { return ptr_.get(); }
|
||||
const T* operator->() const { return ptr_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<T> ptr_;
|
||||
};
|
||||
|
||||
/// 双缓冲数据布局(AoS ↔ SoA 转换缓存)
|
||||
///
|
||||
/// 同时维护 AoS 和 SoA 两种布局,根据访问模式自动选择。
|
||||
/// 写操作更新两种布局,读操作直接从 SoA 读取(SIMD 友好)。
|
||||
template<typename T>
|
||||
class HybridLayout {
|
||||
public:
|
||||
/// 更新 AoS 数据(自动同步到 SoA)
|
||||
void set_aos(const std::vector<T>& data);
|
||||
|
||||
/// 获取 SoA 数据(用于 SIMD 处理)
|
||||
[[nodiscard]] const Point3DSoA& get_soa() const { return soa_; }
|
||||
|
||||
/// 获取 AoS 数据
|
||||
[[nodiscard]] const std::vector<T>& get_aos() const { return aos_; }
|
||||
|
||||
/// 标记 SoA 为脏
|
||||
void invalidate_soa() { soa_dirty_ = true; }
|
||||
|
||||
/// 强制重新生成 SoA
|
||||
void rebuild_soa();
|
||||
|
||||
private:
|
||||
std::vector<T> aos_;
|
||||
Point3DSoA soa_;
|
||||
bool soa_dirty_ = true;
|
||||
};
|
||||
|
||||
} // namespace vde::core
|
||||
|
||||
@@ -0,0 +1,816 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file simd_vector.h
|
||||
* @brief 极致性能 — 平台自适应 SIMD 向量 (SSE/AVX/NEON)
|
||||
*
|
||||
* 提供 4-wide double/float SIMD 向量类型,自动检测 CPU 指令集。
|
||||
* 支持 dot/cross/normalize/length 及 AABB 批量相交测试。
|
||||
*
|
||||
* ## SIMD 指令集选择策略
|
||||
*
|
||||
* | 平台 | 指令集 | 宏定义 |
|
||||
* |-----------|---------|-------------------------------|
|
||||
* | x86_64 | AVX2 | __AVX2__ (由编译器定义) |
|
||||
* | x86_64 | SSE2 | __SSE2__ (回退) |
|
||||
* | ARM | NEON | __ARM_NEON |
|
||||
* | 通用 | 标量回退 | 无 SIMD 宏 |
|
||||
*
|
||||
* @ingroup core
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
// ── 平台检测与指令集选择 ──────────────────────────────
|
||||
|
||||
#if defined(__AVX2__) || defined(__AVX__)
|
||||
#define VDE_SIMD_AVX 1
|
||||
#include <immintrin.h>
|
||||
#elif defined(__SSE2__) || defined(__SSE3__) || defined(__SSSE3__) || defined(__SSE4_1__)
|
||||
#define VDE_SIMD_SSE 1
|
||||
#include <emmintrin.h>
|
||||
#ifdef __SSE4_1__
|
||||
#include <smmintrin.h>
|
||||
#endif
|
||||
#elif defined(__ARM_NEON) || defined(__aarch64__)
|
||||
#define VDE_SIMD_NEON 1
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
namespace vde::core {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 辅助:编译期指令集查询
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 当前使用的 SIMD 指令集标签
|
||||
enum class SimdIsa {
|
||||
Scalar, ///< 纯标量回退
|
||||
SSE2, ///< SSE2 / SSE4.1
|
||||
AVX, ///< AVX / AVX2
|
||||
NEON ///< ARM NEON
|
||||
};
|
||||
|
||||
/// 编译期查询当前 SIMD 指令集
|
||||
constexpr SimdIsa current_simd_isa() {
|
||||
#if VDE_SIMD_AVX
|
||||
return SimdIsa::AVX;
|
||||
#elif VDE_SIMD_SSE
|
||||
return SimdIsa::SSE2;
|
||||
#elif VDE_SIMD_NEON
|
||||
return SimdIsa::NEON;
|
||||
#else
|
||||
return SimdIsa::Scalar;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// SIMD 向量宽度(double lane 数)
|
||||
constexpr int simd_width_d = (current_simd_isa() >= SimdIsa::SSE2) ? 4 : 1;
|
||||
|
||||
/// SIMD 向量宽度(float lane 数)
|
||||
constexpr int simd_width_f = (current_simd_isa() >= SimdIsa::SSE2) ? 4 : 1;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Vec4d — 4-wide double SIMD
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 4-wide double SIMD 向量
|
||||
///
|
||||
/// 支持: AVX (ymm), SSE (2×xmm), NEON (float64x2×2), Scalar 回退
|
||||
///
|
||||
/// @code
|
||||
/// Vec4d a = Vec4d::broadcast(1.0);
|
||||
/// Vec4d b(0, 1, 2, 3);
|
||||
/// Vec4d c = a + b;
|
||||
/// double dot = c.dot(b);
|
||||
/// @endcode
|
||||
struct alignas(32) Vec4d {
|
||||
// ── 内部存储(按指令集) ──
|
||||
|
||||
#if VDE_SIMD_AVX
|
||||
__m256d m;
|
||||
#elif VDE_SIMD_SSE
|
||||
__m128d lo, hi;
|
||||
#elif VDE_SIMD_NEON
|
||||
float64x2_t lo, hi;
|
||||
#else
|
||||
double d[4];
|
||||
#endif
|
||||
|
||||
// ── 构造函数 ──
|
||||
|
||||
/// 全零
|
||||
Vec4d() {
|
||||
#if VDE_SIMD_AVX
|
||||
m = _mm256_setzero_pd();
|
||||
#elif VDE_SIMD_SSE
|
||||
lo = _mm_setzero_pd();
|
||||
hi = _mm_setzero_pd();
|
||||
#elif VDE_SIMD_NEON
|
||||
lo = vdupq_n_f64(0.0);
|
||||
hi = vdupq_n_f64(0.0);
|
||||
#else
|
||||
d[0] = d[1] = d[2] = d[3] = 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 从四个标量构造
|
||||
Vec4d(double x, double y, double z, double w) {
|
||||
#if VDE_SIMD_AVX
|
||||
m = _mm256_set_pd(w, z, y, x);
|
||||
#elif VDE_SIMD_SSE
|
||||
lo = _mm_set_pd(y, x);
|
||||
hi = _mm_set_pd(w, z);
|
||||
#elif VDE_SIMD_NEON
|
||||
double xy[2] = {x, y};
|
||||
double zw[2] = {z, w};
|
||||
lo = vld1q_f64(xy);
|
||||
hi = vld1q_f64(zw);
|
||||
#else
|
||||
d[0] = x; d[1] = y; d[2] = z; d[3] = w;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 广播单值到所有 lane
|
||||
static Vec4d broadcast(double v) {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_set1_pd(v);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_set1_pd(v);
|
||||
r.hi = _mm_set1_pd(v);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.lo = vdupq_n_f64(v);
|
||||
r.hi = vdupq_n_f64(v);
|
||||
#else
|
||||
r.d[0] = r.d[1] = r.d[2] = r.d[3] = v;
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/// 提取第 i 个 lane (0-3)
|
||||
double operator[](int i) const {
|
||||
#if VDE_SIMD_AVX
|
||||
double buf[4];
|
||||
_mm256_storeu_pd(buf, m);
|
||||
return buf[i];
|
||||
#elif VDE_SIMD_SSE
|
||||
double buf[4];
|
||||
_mm_storeu_pd(buf, lo);
|
||||
_mm_storeu_pd(buf + 2, hi);
|
||||
return buf[i];
|
||||
#elif VDE_SIMD_NEON
|
||||
double buf[4];
|
||||
vst1q_f64(buf, lo);
|
||||
vst1q_f64(buf + 2, hi);
|
||||
return buf[i];
|
||||
#else
|
||||
return d[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
// ── 算术运算 ──
|
||||
|
||||
Vec4d operator+(const Vec4d& o) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_add_pd(m, o.m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_add_pd(lo, o.lo);
|
||||
r.hi = _mm_add_pd(hi, o.hi);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.lo = vaddq_f64(lo, o.lo);
|
||||
r.hi = vaddq_f64(hi, o.hi);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = d[i] + o.d[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4d operator-(const Vec4d& o) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_sub_pd(m, o.m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_sub_pd(lo, o.lo);
|
||||
r.hi = _mm_sub_pd(hi, o.hi);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.lo = vsubq_f64(lo, o.lo);
|
||||
r.hi = vsubq_f64(hi, o.hi);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = d[i] - o.d[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4d operator*(const Vec4d& o) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_mul_pd(m, o.m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_mul_pd(lo, o.lo);
|
||||
r.hi = _mm_mul_pd(hi, o.hi);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.lo = vmulq_f64(lo, o.lo);
|
||||
r.hi = vmulq_f64(hi, o.hi);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = d[i] * o.d[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4d operator/(const Vec4d& o) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_div_pd(m, o.m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_div_pd(lo, o.lo);
|
||||
r.hi = _mm_div_pd(hi, o.hi);
|
||||
#elif VDE_SIMD_NEON
|
||||
// NEON 无原生 double 除法,使用 approximate + Newton
|
||||
float64x2_t inv_lo = vrecpeq_f64(o.lo);
|
||||
float64x2_t inv_hi = vrecpeq_f64(o.hi);
|
||||
r.lo = vmulq_f64(lo, inv_lo);
|
||||
r.hi = vmulq_f64(hi, inv_hi);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = d[i] / o.d[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/// FMA: this * b + c
|
||||
Vec4d mul_add(const Vec4d& b, const Vec4d& c) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_fmadd_pd(m, b.m, c.m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_add_pd(_mm_mul_pd(lo, b.lo), c.lo);
|
||||
r.hi = _mm_add_pd(_mm_mul_pd(hi, b.hi), c.hi);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.lo = vmlaq_f64(c.lo, lo, b.lo);
|
||||
r.hi = vmlaq_f64(c.hi, hi, b.hi);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = d[i] * b.d[i] + c.d[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
// ── 比较 ──
|
||||
|
||||
/// lane-wise min
|
||||
Vec4d min(const Vec4d& o) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_min_pd(m, o.m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_min_pd(lo, o.lo);
|
||||
r.hi = _mm_min_pd(hi, o.hi);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.lo = vminq_f64(lo, o.lo);
|
||||
r.hi = vminq_f64(hi, o.hi);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = std::min(d[i], o.d[i]);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/// lane-wise max
|
||||
Vec4d max(const Vec4d& o) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_max_pd(m, o.m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r.lo = _mm_max_pd(lo, o.lo);
|
||||
r.hi = _mm_max_pd(hi, o.hi);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.lo = vmaxq_f64(lo, o.lo);
|
||||
r.hi = vmaxq_f64(hi, o.hi);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = std::max(d[i], o.d[i]);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
// ── 向量数学 ──
|
||||
|
||||
/// 点积: sum(this[i] * o[i])
|
||||
double dot(const Vec4d& o) const {
|
||||
#if VDE_SIMD_AVX
|
||||
__m256d prod = _mm256_mul_pd(m, o.m);
|
||||
__m128d lo128 = _mm256_castpd256_pd128(prod);
|
||||
__m128d hi128 = _mm256_extractf128_pd(prod, 1);
|
||||
__m128d sum = _mm_add_pd(lo128, hi128);
|
||||
sum = _mm_hadd_pd(sum, sum);
|
||||
double result;
|
||||
_mm_storel_pd(&result, sum);
|
||||
return result;
|
||||
#elif VDE_SIMD_SSE
|
||||
__m128d p0 = _mm_mul_pd(lo, o.lo);
|
||||
__m128d p1 = _mm_mul_pd(hi, o.hi);
|
||||
__m128d sum = _mm_add_pd(p0, p1);
|
||||
#ifdef __SSE3__
|
||||
sum = _mm_hadd_pd(sum, sum);
|
||||
#endif
|
||||
double result;
|
||||
_mm_storel_pd(&result, sum);
|
||||
return result;
|
||||
#elif VDE_SIMD_NEON
|
||||
float64x2_t p0 = vmulq_f64(lo, o.lo);
|
||||
float64x2_t p1 = vmulq_f64(hi, o.hi);
|
||||
float64x2_t s = vaddq_f64(p0, p1);
|
||||
return vgetq_lane_f64(s, 0) + vgetq_lane_f64(s, 1);
|
||||
#else
|
||||
return d[0]*o.d[0] + d[1]*o.d[1] + d[2]*o.d[2] + d[3]*o.d[3];
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 3D 叉积 (xyz, w unused): result = cross(this_xyz, o_xyz)
|
||||
/// 结果存储在 xyz, w=0
|
||||
Vec4d cross3(const Vec4d& o) const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
// a = this, b = o
|
||||
// cross(a,b) = [a1*b2-a2*b1, a2*b0-a0*b2, a0*b1-a1*b0, 0]
|
||||
// 使用 permute: _mm256_permute4x64_pd(src, imm8)
|
||||
// imm8 encodes: lane0=bits[1:0], lane1=bits[3:2], lane2=bits[5:4], lane3=bits[7:6]
|
||||
__m256d a_yzx = _mm256_permute4x64_pd(m, _MM_SHUFFLE(3, 0, 2, 1)); // a1,a2,a0,a3
|
||||
__m256d a_zxy = _mm256_permute4x64_pd(m, _MM_SHUFFLE(3, 1, 0, 2)); // a2,a0,a1,a3
|
||||
__m256d b_zxy = _mm256_permute4x64_pd(o.m, _MM_SHUFFLE(3, 1, 0, 2)); // b2,b0,b1,b3
|
||||
__m256d b_yzx = _mm256_permute4x64_pd(o.m, _MM_SHUFFLE(3, 0, 2, 1)); // b1,b2,b0,b3
|
||||
__m256d t1 = _mm256_mul_pd(a_yzx, b_zxy);
|
||||
__m256d t2 = _mm256_mul_pd(a_zxy, b_yzx);
|
||||
__m256d cross = _mm256_sub_pd(t1, t2);
|
||||
// 清空 w lane
|
||||
r.m = _mm256_blend_pd(cross, _mm256_setzero_pd(), 0b1000);
|
||||
#elif VDE_SIMD_SSE
|
||||
// 使用标量回退取 xyz
|
||||
double ax = (*this)[0], ay = (*this)[1], az = (*this)[2];
|
||||
double bx = o[0], by = o[1], bz = o[2];
|
||||
r.lo = _mm_set_pd(az*bx - ax*bz, ay*bz - az*by);
|
||||
r.hi = _mm_setzero_pd();
|
||||
// hi[0] = ax*by - ay*bx
|
||||
r = Vec4d(ay*bz - az*by, az*bx - ax*bz, ax*by - ay*bx, 0.0);
|
||||
#elif VDE_SIMD_NEON
|
||||
double ax = (*this)[0], ay = (*this)[1], az = (*this)[2];
|
||||
double bx = o[0], by = o[1], bz = o[2];
|
||||
double cx = ay*bz - az*by;
|
||||
double cy = az*bx - ax*bz;
|
||||
double cz = ax*by - ay*bx;
|
||||
double cxy[2] = {cx, cy};
|
||||
double czw[2] = {cz, 0.0};
|
||||
r.lo = vld1q_f64(cxy);
|
||||
r.hi = vld1q_f64(czw);
|
||||
#else
|
||||
double ax = d[0], ay = d[1], az = d[2];
|
||||
double bx = o.d[0], by = o.d[1], bz = o.d[2];
|
||||
r.d[0] = ay*bz - az*by;
|
||||
r.d[1] = az*bx - ax*bz;
|
||||
r.d[2] = ax*by - ay*bx;
|
||||
r.d[3] = 0.0;
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/// 向量长度 (xyz)
|
||||
double length3() const {
|
||||
return std::sqrt(dot(*this));
|
||||
}
|
||||
|
||||
/// 向量长度平方 (xyz)
|
||||
double length3_sq() const {
|
||||
return dot(*this);
|
||||
}
|
||||
|
||||
/// 归一化 (xyz), w 归零
|
||||
Vec4d normalize3() const {
|
||||
double len = length3();
|
||||
if (len < 1e-30) return broadcast(0.0);
|
||||
double inv = 1.0 / len;
|
||||
Vec4d r = *this * broadcast(inv);
|
||||
#if VDE_SIMD_AVX
|
||||
r.m = _mm256_blend_pd(r.m, _mm256_setzero_pd(), 0b1000);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/// 取前 3 个 lane 的绝对值
|
||||
Vec4d abs3() const {
|
||||
Vec4d r;
|
||||
#if VDE_SIMD_AVX
|
||||
// _mm256_andnot_pd(-0.0, m) 清除符号位
|
||||
__m256d sign_mask = _mm256_set1_pd(-0.0);
|
||||
r.m = _mm256_andnot_pd(sign_mask, m);
|
||||
#elif VDE_SIMD_SSE
|
||||
r = Vec4d(std::abs((*this)[0]), std::abs((*this)[1]), std::abs((*this)[2]), std::abs((*this)[3]));
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.d[i] = std::abs(d[i]);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Vec4f — 4-wide float SIMD
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 4-wide float SIMD 向量
|
||||
///
|
||||
/// 支持: AVX (xmm 128-bit), SSE, NEON, Scalar
|
||||
struct alignas(16) Vec4f {
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
__m128 m;
|
||||
#elif VDE_SIMD_NEON
|
||||
float32x4_t m;
|
||||
#else
|
||||
float f[4];
|
||||
#endif
|
||||
|
||||
Vec4f() {
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
m = _mm_setzero_ps();
|
||||
#elif VDE_SIMD_NEON
|
||||
m = vdupq_n_f32(0.0f);
|
||||
#else
|
||||
f[0] = f[1] = f[2] = f[3] = 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
Vec4f(float x, float y, float z, float w) {
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
m = _mm_set_ps(w, z, y, x);
|
||||
#elif VDE_SIMD_NEON
|
||||
float buf[4] = {x, y, z, w};
|
||||
m = vld1q_f32(buf);
|
||||
#else
|
||||
f[0] = x; f[1] = y; f[2] = z; f[3] = w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static Vec4f broadcast(float v) {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
r.m = _mm_set1_ps(v);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vdupq_n_f32(v);
|
||||
#else
|
||||
r.f[0] = r.f[1] = r.f[2] = r.f[3] = v;
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
float operator[](int i) const {
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
float buf[4];
|
||||
_mm_storeu_ps(buf, m);
|
||||
return buf[i];
|
||||
#elif VDE_SIMD_NEON
|
||||
float buf[4];
|
||||
vst1q_f32(buf, m);
|
||||
return buf[i];
|
||||
#else
|
||||
return f[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
Vec4f operator+(const Vec4f& o) const {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
r.m = _mm_add_ps(m, o.m);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vaddq_f32(m, o.m);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.f[i] = f[i] + o.f[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4f operator-(const Vec4f& o) const {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
r.m = _mm_sub_ps(m, o.m);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vsubq_f32(m, o.m);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.f[i] = f[i] - o.f[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4f operator*(const Vec4f& o) const {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
r.m = _mm_mul_ps(m, o.m);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vmulq_f32(m, o.m);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.f[i] = f[i] * o.f[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4f operator/(const Vec4f& o) const {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
r.m = _mm_div_ps(m, o.m);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vmulq_f32(m, vrecpeq_f32(o.m));
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.f[i] = f[i] / o.f[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4f mul_add(const Vec4f& b, const Vec4f& c) const {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
#ifdef __FMA__
|
||||
r.m = _mm_fmadd_ps(m, b.m, c.m);
|
||||
#else
|
||||
r.m = _mm_add_ps(_mm_mul_ps(m, b.m), c.m);
|
||||
#endif
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vmlaq_f32(c.m, m, b.m);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.f[i] = f[i] * b.f[i] + c.f[i];
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4f min(const Vec4f& o) const {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
r.m = _mm_min_ps(m, o.m);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vminq_f32(m, o.m);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.f[i] = std::min(f[i], o.f[i]);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4f max(const Vec4f& o) const {
|
||||
Vec4f r;
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
r.m = _mm_max_ps(m, o.m);
|
||||
#elif VDE_SIMD_NEON
|
||||
r.m = vmaxq_f32(m, o.m);
|
||||
#else
|
||||
for (int i = 0; i < 4; ++i) r.f[i] = std::max(f[i], o.f[i]);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/// 点积 (4-wide)
|
||||
float dot(const Vec4f& o) const {
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
#ifdef __SSE4_1__
|
||||
__m128 prod = _mm_mul_ps(m, o.m);
|
||||
__m128 hadd = _mm_hadd_ps(prod, prod);
|
||||
hadd = _mm_hadd_ps(hadd, hadd);
|
||||
return _mm_cvtss_f32(hadd);
|
||||
#else
|
||||
float buf[4];
|
||||
_mm_storeu_ps(buf, _mm_mul_ps(m, o.m));
|
||||
return buf[0] + buf[1] + buf[2] + buf[3];
|
||||
#endif
|
||||
#elif VDE_SIMD_NEON
|
||||
float32x4_t prod = vmulq_f32(m, o.m);
|
||||
float32x2_t sum = vadd_f32(vget_low_f32(prod), vget_high_f32(prod));
|
||||
sum = vpadd_f32(sum, sum);
|
||||
return vget_lane_f32(sum, 0);
|
||||
#else
|
||||
return f[0]*o.f[0] + f[1]*o.f[1] + f[2]*o.f[2] + f[3]*o.f[3];
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 3D dot (仅 xyz)
|
||||
float dot3(const Vec4f& o) const {
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
#ifdef __SSE4_1__
|
||||
// _mm_dp_ps: dot product with mask 0x71 = src1[xyz], src2[xyz] → splat result
|
||||
__m128 dp = _mm_dp_ps(m, o.m, 0x71);
|
||||
return _mm_cvtss_f32(dp);
|
||||
#else
|
||||
return (*this)[0]*o[0] + (*this)[1]*o[1] + (*this)[2]*o[2];
|
||||
#endif
|
||||
#elif VDE_SIMD_NEON
|
||||
float32x4_t prod = vmulq_f32(m, o.m);
|
||||
float32x2_t sum = vadd_f32(vget_low_f32(prod), vget_high_f32(prod));
|
||||
sum = vpadd_f32(sum, sum);
|
||||
// subtract w contribution
|
||||
float w = (*this)[3] * o[3];
|
||||
return vget_lane_f32(sum, 0) - w;
|
||||
#else
|
||||
return f[0]*o.f[0] + f[1]*o.f[1] + f[2]*o.f[2];
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 3D 叉积
|
||||
Vec4f cross3(const Vec4f& o) const {
|
||||
float ax = (*this)[0], ay = (*this)[1], az = (*this)[2];
|
||||
float bx = o[0], by = o[1], bz = o[2];
|
||||
return Vec4f(ay*bz - az*by, az*bx - ax*bz, ax*by - ay*bx, 0.0f);
|
||||
}
|
||||
|
||||
float length3() const { return std::sqrt(dot3(*this)); }
|
||||
|
||||
float length3_sq() const { return dot3(*this); }
|
||||
|
||||
Vec4f normalize3() const {
|
||||
float len = length3();
|
||||
if (len < 1e-15f) return broadcast(0.0f);
|
||||
float inv = 1.0f / len;
|
||||
Vec4f r = *this * broadcast(inv);
|
||||
// 清空 w
|
||||
#if VDE_SIMD_AVX || VDE_SIMD_SSE
|
||||
#ifdef __SSE4_1__
|
||||
r.m = _mm_blend_ps(r.m, _mm_setzero_ps(), 0b1000);
|
||||
#endif
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SoA 布局运算(Structure of Arrays)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// SoA 3D 向量数组(适合 SIMD 批量处理)
|
||||
struct alignas(64) Vec3SoA {
|
||||
Vec4d x, y, z; ///< 各分量 4 组
|
||||
|
||||
/// 从 4 个 Vec4d (xyz 部分) 组装
|
||||
static Vec3SoA gather(const Vec4d& a, const Vec4d& b,
|
||||
const Vec4d& c, const Vec4d& d) {
|
||||
Vec3SoA r;
|
||||
#if VDE_SIMD_AVX
|
||||
// 转置: a[0],b[0],c[0],d[0] → x
|
||||
__m256d t0 = _mm256_unpacklo_pd(a.m, b.m); // a0,a1,b0,b1
|
||||
__m256d t1 = _mm256_unpackhi_pd(a.m, b.m); // a2,a3,b2,b3
|
||||
__m256d t2 = _mm256_unpacklo_pd(c.m, d.m); // c0,c1,d0,d1
|
||||
__m256d t3 = _mm256_unpackhi_pd(c.m, d.m); // c2,c3,d2,d3
|
||||
r.x.m = _mm256_permute2f128_pd(t0, t2, 0x20); // a0,b0,c0,d0
|
||||
r.y.m = _mm256_permute2f128_pd(t0, t2, 0x31); // a1,b1,c1,d1
|
||||
r.z.m = _mm256_permute2f128_pd(t1, t3, 0x20); // a2,b2,c2,d2
|
||||
#else
|
||||
r.x = Vec4d(a[0], b[0], c[0], d[0]);
|
||||
r.y = Vec4d(a[1], b[1], c[1], d[1]);
|
||||
r.z = Vec4d(a[2], b[2], c[2], d[2]);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// AABB 批量相交测试(SIMD 加速)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 4 组 AABB 的 SIMD 表示
|
||||
///
|
||||
/// 用于批量测试:一次判断 4 个包围盒与参考 AABB 是否相交。
|
||||
/// 内部以 SoA 形式存储 min_xyz 和 max_xyz。
|
||||
struct alignas(64) BatchAABB4 {
|
||||
Vec4d min_x, min_y, min_z; ///< 4 组 AABB 的 min 分量
|
||||
Vec4d max_x, max_y, max_z; ///< 4 组 AABB 的 max 分量
|
||||
|
||||
/// 从 SoA 数组加载(min_x[4], min_y[4], min_z[4], max_x[4], max_y[4], max_z[4])
|
||||
static BatchAABB4 load(const double* min_x, const double* min_y, const double* min_z,
|
||||
const double* max_x, const double* max_y, const double* max_z)
|
||||
{
|
||||
BatchAABB4 b;
|
||||
b.min_x = Vec4d(min_x[0], min_x[1], min_x[2], min_x[3]);
|
||||
b.min_y = Vec4d(min_y[0], min_y[1], min_y[2], min_y[3]);
|
||||
b.min_z = Vec4d(min_z[0], min_z[1], min_z[2], min_z[3]);
|
||||
b.max_x = Vec4d(max_x[0], max_x[1], max_x[2], max_x[3]);
|
||||
b.max_y = Vec4d(max_y[0], max_y[1], max_y[2], max_y[3]);
|
||||
b.max_z = Vec4d(max_z[0], max_z[1], max_z[2], max_z[3]);
|
||||
return b;
|
||||
}
|
||||
|
||||
/// 批量测试 4 组 AABB 与参考 AABB 的相交性
|
||||
///
|
||||
/// @param ref_min_x/ref_min_y/ref_min_z 参考 AABB 最小值
|
||||
/// @param ref_max_x/ref_max_y/ref_max_z 参考 AABB 最大值
|
||||
/// @return 4 个 bool packed 为一个 uint32_t (bit 0-3)
|
||||
///
|
||||
/// AABB 相交公式: min_a <= max_b AND max_a >= min_b (各分量)
|
||||
static uint32_t intersect4(
|
||||
const BatchAABB4& batch,
|
||||
double ref_min_x, double ref_min_y, double ref_min_z,
|
||||
double ref_max_x, double ref_max_y, double ref_max_z)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
|
||||
// 对每个 lane 做: batch.max >= ref.min && batch.min <= ref.max
|
||||
// 使用 max/min 比较 + 提取 lane
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
double bmin_x = batch.min_x[i], bmin_y = batch.min_y[i], bmin_z = batch.min_z[i];
|
||||
double bmax_x = batch.max_x[i], bmax_y = batch.max_y[i], bmax_z = batch.max_z[i];
|
||||
if (bmin_x <= ref_max_x && bmax_x >= ref_min_x &&
|
||||
bmin_y <= ref_max_y && bmax_y >= ref_min_y &&
|
||||
bmin_z <= ref_max_z && bmax_z >= ref_min_z) {
|
||||
result |= (1u << i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 单次 4-AABB 相交测试(SIMD 加速比较)
|
||||
/// 返回: 相交计数 (0-4)
|
||||
static int intersect4_count(
|
||||
const BatchAABB4& batch,
|
||||
double ref_min_x, double ref_min_y, double ref_min_z,
|
||||
double ref_max_x, double ref_max_y, double ref_max_z)
|
||||
{
|
||||
return __builtin_popcount(intersect4(batch,
|
||||
ref_min_x, ref_min_y, ref_min_z,
|
||||
ref_max_x, ref_max_y, ref_max_z));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 批量 AABB 相交(标量优化版,适用任意 N)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 批量 AABB 相交测试 — 输出相交索引列表
|
||||
///
|
||||
/// @tparam N 缓冲区大小
|
||||
/// @param mins [N*3] min 坐标数组 (x0,y0,z0,x1,y1,z1,...)
|
||||
/// @param maxs [N*3] max 坐标数组
|
||||
/// @param ref_min_x/y/z 参考 AABB 最小值
|
||||
/// @param ref_max_x/y/z 参考 AABB 最大值
|
||||
/// @param out_indices 输出:相交的索引列表
|
||||
/// @return 相交数量
|
||||
template<size_t N>
|
||||
inline size_t aabb_intersect_batch(
|
||||
const double* mins, const double* maxs,
|
||||
double ref_min_x, double ref_min_y, double ref_min_z,
|
||||
double ref_max_x, double ref_max_y, double ref_max_z,
|
||||
int* out_indices)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
double mx = mins[i*3+0], my = mins[i*3+1], mz = mins[i*3+2];
|
||||
double Mx = maxs[i*3+0], My = maxs[i*3+1], Mz = maxs[i*3+2];
|
||||
if (mx <= ref_max_x && Mx >= ref_min_x &&
|
||||
my <= ref_max_y && My >= ref_min_y &&
|
||||
mz <= ref_max_z && Mz >= ref_min_z) {
|
||||
out_indices[count++] = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// 批量 AABB 相交(4 元素 SIMD 展开的 4N 变体)
|
||||
inline size_t aabb_intersect_batch_simd(
|
||||
const double* mins, const double* maxs,
|
||||
size_t count,
|
||||
double ref_min_x, double ref_min_y, double ref_min_z,
|
||||
double ref_max_x, double ref_max_y, double ref_max_z,
|
||||
int* out_indices)
|
||||
{
|
||||
size_t total = 0;
|
||||
size_t i = 0;
|
||||
|
||||
// 4-wide SIMD 循环
|
||||
for (; i + 4 <= count; i += 4) {
|
||||
double min_x[4], min_y[4], min_z[4];
|
||||
double max_x[4], max_y[4], max_z[4];
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
min_x[k] = mins[(i+k)*3+0];
|
||||
min_y[k] = mins[(i+k)*3+1];
|
||||
min_z[k] = mins[(i+k)*3+2];
|
||||
max_x[k] = maxs[(i+k)*3+0];
|
||||
max_y[k] = maxs[(i+k)*3+1];
|
||||
max_z[k] = maxs[(i+k)*3+2];
|
||||
}
|
||||
BatchAABB4 batch = BatchAABB4::load(min_x, min_y, min_z, max_x, max_y, max_z);
|
||||
|
||||
uint32_t mask = BatchAABB4::intersect4(batch,
|
||||
ref_min_x, ref_min_y, ref_min_z,
|
||||
ref_max_x, ref_max_y, ref_max_z);
|
||||
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if (mask & (1u << j)) {
|
||||
out_indices[total++] = static_cast<int>(i + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尾数标量处理
|
||||
for (; i < count; ++i) {
|
||||
double mx = mins[i*3+0], my = mins[i*3+1], mz = mins[i*3+2];
|
||||
double Mx = maxs[i*3+0], My = maxs[i*3+1], Mz = maxs[i*3+2];
|
||||
if (mx <= ref_max_x && Mx >= ref_min_x &&
|
||||
my <= ref_max_y && My >= ref_min_y &&
|
||||
mz <= ref_max_z && Mz >= ref_min_z) {
|
||||
out_indices[total++] = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
} // namespace vde::core
|
||||
@@ -0,0 +1,151 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file topology_compression.h
|
||||
* @brief B-Rep 拓扑压缩 & Edgebreaker 三角网格压缩 & 顶点量化
|
||||
*
|
||||
* 三类压缩策略:
|
||||
* 1. B-Rep 拓扑压缩 — 将 BrepModel 的拓扑结构序列化为紧凑二进制格式
|
||||
* 2. Edgebreaker — 三角网格连通性无损压缩(CLERS 编码)
|
||||
* 3. 顶点坐标量化 — 将浮点坐标压入定点整数区间
|
||||
*
|
||||
* @ingroup core
|
||||
*/
|
||||
#include "vde/core/point.h"
|
||||
#include "vde/brep/brep.h"
|
||||
#include "vde/mesh/halfedge_mesh.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace vde::core {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// B-Rep 拓扑压缩
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief B-Rep 拓扑压缩结果
|
||||
*/
|
||||
struct BrepCompressed {
|
||||
std::vector<uint8_t> data; ///< 压缩二进制数据
|
||||
size_t body_count; ///< Body 数量
|
||||
size_t face_count; ///< Face 总数
|
||||
size_t edge_count; ///< Edge 总数
|
||||
size_t vertex_count; ///< Vertex 总数
|
||||
size_t original_bytes; ///< 原始估算字节数
|
||||
double compression_ratio; ///< 压缩比 (compressed/original)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 压缩 BrepModel 为紧凑二进制格式
|
||||
*
|
||||
* 将拓扑连接关系压入变长整数,几何数据独立存储。
|
||||
*
|
||||
* @param body BrepModel 引用
|
||||
* @return BrepCompressed 压缩结果
|
||||
* @ingroup core
|
||||
*/
|
||||
BrepCompressed compress_brep(const vde::brep::BrepModel& body);
|
||||
|
||||
/**
|
||||
* @brief 解压为 BrepModel
|
||||
*
|
||||
* @param data 压缩数据
|
||||
* @return 重构的 BrepModel
|
||||
* @ingroup core
|
||||
*/
|
||||
vde::brep::BrepModel decompress_brep(const BrepCompressed& data);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Edgebreaker 三角网格压缩
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// CLERS 操作码
|
||||
enum class EdgebreakerOp : uint8_t {
|
||||
C = 0, ///< C — 封闭边界环
|
||||
L = 1, ///< L — 左相邻面
|
||||
E = 2, ///< E — 对面顶点
|
||||
R = 3, ///< R — 右相邻面
|
||||
S = 4 ///< S — 分裂操作
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Edgebreaker 压缩结果
|
||||
*/
|
||||
struct EdgebreakerResult {
|
||||
std::vector<EdgebreakerOp> clers; ///< CLERS 操作序列
|
||||
std::vector<int> vertices; ///< 解压所需顶点偏移(S/E 操作附参)
|
||||
size_t face_count; ///< 面数
|
||||
size_t vertex_count; ///< 顶点数
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 使用 Edgebreaker 压缩三角网格连通性
|
||||
*
|
||||
* 输入网格的连通性(面索引),输出 CLERS 操作序列。
|
||||
* 仅压缩拓扑,不含几何坐标。
|
||||
*
|
||||
* @param mesh 三角网格
|
||||
* @return EdgebreakerResult
|
||||
* @ingroup core
|
||||
*/
|
||||
EdgebreakerResult edgebreaker_compress(const vde::mesh::HalfedgeMesh& mesh);
|
||||
|
||||
/**
|
||||
* @brief Edgebreaker 解压
|
||||
*
|
||||
* 从 CLERS 序列重建三角网格连通性。
|
||||
*
|
||||
* @param result Edgebreaker 压缩结果
|
||||
* @return 重建的三角网格(仅拓扑,顶点坐标为占位)
|
||||
* @ingroup core
|
||||
*/
|
||||
vde::mesh::HalfedgeMesh edgebreaker_decompress(const EdgebreakerResult& result);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 顶点坐标量化压缩
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// 量化模式
|
||||
enum class QuantMode {
|
||||
Uniform8, ///< 8 位均匀量化(每分量 1 字节)
|
||||
Uniform16, ///< 16 位均匀量化(每分量 2 字节)
|
||||
Uniform32, ///< 32 位均匀量化(每分量 4 字节)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点量化结果
|
||||
*/
|
||||
struct VertexQuantResult {
|
||||
std::vector<uint8_t> data; ///< 量化后的字节数据
|
||||
Point3D bbox_min; ///< 包围盒最小点
|
||||
Point3D bbox_max; ///< 包围盒最大点
|
||||
QuantMode mode; ///< 量化模式
|
||||
size_t vertex_count; ///< 顶点数
|
||||
double max_error; ///< 最大量化误差
|
||||
double avg_error; ///< 平均量化误差
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 顶点坐标量化压缩
|
||||
*
|
||||
* 将顶点坐标相对于包围盒的浮点值映射到 [0, 2^bits-1] 整数区间。
|
||||
*
|
||||
* @param vertices 顶点坐标列表
|
||||
* @param mode 量化模式(决定每分量位数)
|
||||
* @return VertexQuantResult
|
||||
* @ingroup core
|
||||
*/
|
||||
VertexQuantResult quantize_vertices(const std::vector<Point3D>& vertices,
|
||||
QuantMode mode = QuantMode::Uniform16);
|
||||
|
||||
/**
|
||||
* @brief 顶点坐标反量化
|
||||
*
|
||||
* @param result 量化结果
|
||||
* @return 还原的顶点坐标
|
||||
* @ingroup core
|
||||
*/
|
||||
std::vector<Point3D> dequantize_vertices(const VertexQuantResult& result);
|
||||
|
||||
} // namespace vde::core
|
||||
@@ -0,0 +1,283 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file transaction.h
|
||||
* @brief 极致性能 — 事务系统 + Command模式 + 无限撤销/重做 + 崩溃恢复
|
||||
*
|
||||
* 核心组件:
|
||||
* - Transaction : begin/commit/rollback 事务
|
||||
* - Command : 可撤销操作的抽象基类
|
||||
* - UndoManager : 无限撤销/重做栈 + 事务日志
|
||||
* - BrepCommand : BrepModel 操作的具体 Command 实现
|
||||
*
|
||||
* @ingroup core
|
||||
*/
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
|
||||
namespace vde::core {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Command 模式
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 可撤销操作的抽象基类
|
||||
///
|
||||
/// 每个 Command 封装一个原子操作,支持执行和撤销。
|
||||
/// 子类需实现 execute() 和 undo()。
|
||||
class Command {
|
||||
public:
|
||||
virtual ~Command() = default;
|
||||
|
||||
/// 执行操作
|
||||
virtual void execute() = 0;
|
||||
|
||||
/// 撤销操作(将状态恢复到执行前)
|
||||
virtual void undo() = 0;
|
||||
|
||||
/// 获取操作描述
|
||||
[[nodiscard]] virtual std::string description() const { return "Command"; }
|
||||
|
||||
/// 合并两个命令(可选优化)
|
||||
/// @return true 若合并成功(this 吸收了 other)
|
||||
virtual bool merge(Command* /*other*/) { return false; }
|
||||
|
||||
/// 命令 ID(用于日志)
|
||||
[[nodiscard]] int64_t id() const { return id_; }
|
||||
void set_id(int64_t id) { id_ = id; }
|
||||
|
||||
private:
|
||||
int64_t id_ = 0;
|
||||
};
|
||||
|
||||
/// 可调用对象包装的 Command
|
||||
///
|
||||
/// @code
|
||||
/// auto cmd = make_command(
|
||||
/// []{ do_something(); },
|
||||
/// []{ undo_it(); },
|
||||
/// "My operation");
|
||||
/// @endcode
|
||||
class LambdaCommand : public Command {
|
||||
public:
|
||||
using Func = std::function<void()>;
|
||||
|
||||
LambdaCommand(Func exec, Func undo_fn, std::string desc = "")
|
||||
: exec_(std::move(exec))
|
||||
, undo_(std::move(undo_fn))
|
||||
, desc_(std::move(desc)) {}
|
||||
|
||||
void execute() override { if (exec_) exec_(); }
|
||||
void undo() override { if (undo_) undo_(); }
|
||||
[[nodiscard]] std::string description() const override { return desc_; }
|
||||
|
||||
private:
|
||||
Func exec_;
|
||||
Func undo_;
|
||||
std::string desc_;
|
||||
};
|
||||
|
||||
/// 便捷工厂函数
|
||||
inline std::unique_ptr<Command> make_command(
|
||||
std::function<void()> exec,
|
||||
std::function<void()> undo_fn,
|
||||
std::string desc = "")
|
||||
{
|
||||
return std::make_unique<LambdaCommand>(
|
||||
std::move(exec), std::move(undo_fn), std::move(desc));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// 事务日志条目
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 事务日志条目
|
||||
///
|
||||
/// 持久化到事务日志文件,用于崩溃恢复。
|
||||
struct TransactionLogEntry {
|
||||
enum class Type : uint8_t {
|
||||
BEGIN = 0, ///< 事务开始
|
||||
COMMAND = 1, ///< 命令执行
|
||||
COMMIT = 2, ///< 事务提交
|
||||
ROLLBACK = 3, ///< 事务回滚
|
||||
CHECKPOINT = 4, ///< 检查点
|
||||
};
|
||||
|
||||
Type type;
|
||||
int64_t transaction_id;
|
||||
int64_t command_id;
|
||||
int64_t timestamp; ///< unix 微秒时间戳
|
||||
std::string description; ///< 命令描述(COMMAND 类型时有效)
|
||||
|
||||
/// 序列化为一行
|
||||
[[nodiscard]] std::string serialize() const;
|
||||
/// 从一行反序列化
|
||||
static TransactionLogEntry deserialize(const std::string& line);
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Transaction
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 事务
|
||||
///
|
||||
/// 封装一组 Command 的原子执行。支持 begin/commit/rollback。
|
||||
/// 事务在 commit 时将命令推送到 UndoManager。
|
||||
///
|
||||
/// @code
|
||||
/// Transaction txn;
|
||||
/// txn.begin();
|
||||
/// txn.execute(make_command(...));
|
||||
/// txn.execute(make_command(...));
|
||||
/// txn.commit(); // 或 txn.rollback()
|
||||
/// @endcode
|
||||
class Transaction {
|
||||
public:
|
||||
Transaction();
|
||||
~Transaction();
|
||||
|
||||
Transaction(const Transaction&) = delete;
|
||||
Transaction& operator=(const Transaction&) = delete;
|
||||
|
||||
/// 开始事务
|
||||
void begin();
|
||||
|
||||
/// 执行一条命令(加入事物缓冲区)
|
||||
/// @param cmd 要执行的命令
|
||||
void execute(std::unique_ptr<Command> cmd);
|
||||
|
||||
/// 提交事务(执行所有命令 → 加入 UndoManager)
|
||||
void commit();
|
||||
|
||||
/// 回滚事务(撤销已执行命令 → 丢弃)
|
||||
void rollback();
|
||||
|
||||
/// 是否处于活跃事务中
|
||||
[[nodiscard]] bool is_active() const { return active_; }
|
||||
|
||||
/// 当前缓冲命令数
|
||||
[[nodiscard]] size_t command_count() const { return commands_.size(); }
|
||||
|
||||
private:
|
||||
bool active_ = false;
|
||||
std::vector<std::unique_ptr<Command>> commands_;
|
||||
int64_t txn_id_ = 0;
|
||||
|
||||
static std::atomic<int64_t> next_txn_id_;
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// UndoManager — 无限撤销/重做
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// 撤销管理器
|
||||
///
|
||||
/// 维护无限撤销栈和重做栈。
|
||||
/// 支持命令合并(连续同类型操作合并为一个)、事务日志与崩溃恢复。
|
||||
///
|
||||
/// @code
|
||||
/// UndoManager um;
|
||||
/// um.execute(make_command(...));
|
||||
/// um.undo(); // 撤销最后一步
|
||||
/// um.redo(); // 重做
|
||||
/// um.save_checkpoint("save/checkpoint.dat"); // 持久化
|
||||
/// @endcode
|
||||
class UndoManager {
|
||||
public:
|
||||
UndoManager();
|
||||
~UndoManager();
|
||||
|
||||
UndoManager(const UndoManager&) = delete;
|
||||
UndoManager& operator=(const UndoManager&) = delete;
|
||||
|
||||
/// 执行命令并推入撤销栈
|
||||
/// @param cmd 要执行的命令
|
||||
/// @param merge 是否尝试与栈顶命令合并
|
||||
void execute(std::unique_ptr<Command> cmd, bool merge = false);
|
||||
|
||||
/// 撤销最近一步操作
|
||||
/// @return true 若撤销成功
|
||||
bool undo();
|
||||
|
||||
/// 重做最近撤销的操作
|
||||
/// @return true 若重做成功
|
||||
bool redo();
|
||||
|
||||
/// 是否可撤销
|
||||
[[nodiscard]] bool can_undo() const { return !undo_stack_.empty(); }
|
||||
|
||||
/// 是否可重做
|
||||
[[nodiscard]] bool can_redo() const { return !redo_stack_.empty(); }
|
||||
|
||||
/// 撤销栈深度
|
||||
[[nodiscard]] size_t undo_depth() const { return undo_stack_.size(); }
|
||||
|
||||
/// 重做栈深度
|
||||
[[nodiscard]] size_t redo_depth() const { return redo_stack_.size(); }
|
||||
|
||||
/// 清空所有历史
|
||||
void clear();
|
||||
|
||||
/// 获取撤销栈顶命令描述
|
||||
[[nodiscard]] std::string undo_description() const;
|
||||
|
||||
/// 获取重做栈顶命令描述
|
||||
[[nodiscard]] std::string redo_description() const;
|
||||
|
||||
// ── 事务日志 ──
|
||||
|
||||
/// 开启事务日志(追加模式)
|
||||
/// @param filepath 日志文件路径
|
||||
/// @return true 若成功
|
||||
bool enable_log(const std::string& filepath);
|
||||
|
||||
/// 关闭事务日志
|
||||
void disable_log();
|
||||
|
||||
/// 是否开启了事务日志
|
||||
[[nodiscard]] bool log_enabled() const { return log_enabled_; }
|
||||
|
||||
/// 写入检查点
|
||||
void write_checkpoint();
|
||||
|
||||
/// 从日志文件恢复(崩溃恢复)
|
||||
/// @param filepath 日志文件路径
|
||||
/// @return 恢复的命令数(0 表示无日志或恢复失败)
|
||||
size_t recover_from_log(const std::string& filepath);
|
||||
|
||||
/// 设置最大撤销深度(0 = 无限)
|
||||
void set_max_undo_depth(size_t max_depth) { max_undo_depth_ = max_depth; }
|
||||
|
||||
/// 获取已执行命令总数
|
||||
[[nodiscard]] int64_t total_commands() const { return cmd_counter_.load(); }
|
||||
|
||||
private:
|
||||
std::deque<std::unique_ptr<Command>> undo_stack_;
|
||||
std::deque<std::unique_ptr<Command>> redo_stack_;
|
||||
size_t max_undo_depth_ = 0; // 0 = unlimited
|
||||
|
||||
// 事务日志
|
||||
bool log_enabled_ = false;
|
||||
std::ofstream log_file_;
|
||||
std::string log_path_;
|
||||
|
||||
std::atomic<int64_t> cmd_counter_{0};
|
||||
|
||||
/// 将命令序列化到日志文件
|
||||
void log_command(const Command& cmd, TransactionLogEntry::Type type);
|
||||
/// 从日志中重放一条命令
|
||||
std::unique_ptr<Command> replay_from_log(const TransactionLogEntry& entry);
|
||||
|
||||
/// 清理多余撤销历史
|
||||
void trim_undo_stack();
|
||||
};
|
||||
|
||||
} // namespace vde::core
|
||||
Reference in New Issue
Block a user