Files
ViewDesignEngine/include/vde/core/performance_tuning.h
T
茂之钳 2ecad1543f
CI / Build & Test (push) Failing after 1m31s
CI / Release Build (push) Failing after 31s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
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
2026-07-26 23:13:22 +08:00

683 lines
24 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 performance_tuning.h
* @brief 性能调优 — 任务图并行调度、工作窃取、内存池、缓存优化、数据布局
*
* 提供计算密集型任务的系统级性能优化工具:
* - parallel_task_graph — 基于任务依赖图的并行调度
* - work_stealing_scheduler — 工作窃取线程池
* - memory_pool_integration — 全局内存池(减少 malloc 开销)
* - cache_optimization_hints — 缓存对齐/预取提示
* - profile_guided_layout — 基于性能分析的数据布局优化
*
* @ingroup core
*/
#include "vde/core/point.h"
#include "vde/core/aabb.h"
#include <vector>
#include <string>
#include <memory>
#include <functional>
#include <future>
#include <atomic>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <type_traits>
#include <cstddef>
#include <cstdlib>
namespace vde::core {
// ═══════════════════════════════════════════════════════════════════════════
// 任务图并行调度
// ═══════════════════════════════════════════════════════════════════════════
/// 单个任务节点
struct TaskNode {
int id = 0; ///< 任务 ID
std::string name; ///< 任务名称
std::function<void()> func; ///< 任务执行函数
std::vector<int> dependencies; ///< 依赖任务 ID 列表
int priority = 0; ///< 优先级(越大越优先)
};
/// 并行任务图 — 基于拓扑排序 + 线程池的 DAG 调度器
///
/// 使用示例:
/// @code
/// ParallelTaskGraph graph(4); // 4 线程
/// graph.add_task({0, "read", []{ read_data(); }, {}});
/// graph.add_task({1, "proc1", []{ proc1(); }, {0}}); // 依赖任务 0
/// graph.add_task({2, "proc2", []{ proc2(); }, {0}}); // 依赖任务 0
/// graph.add_task({3, "merge", []{ merge(); }, {1,2}}); // 依赖 1,2
/// graph.execute();
/// @endcode
class ParallelTaskGraph {
public:
/// 构造函数
/// @param num_threads 线程数(0 = 硬件并发数)
explicit ParallelTaskGraph(int num_threads = 0);
~ParallelTaskGraph();
// 禁止拷贝
ParallelTaskGraph(const ParallelTaskGraph&) = delete;
ParallelTaskGraph& operator=(const ParallelTaskGraph&) = delete;
/// 添加任务节点
void add_task(const TaskNode& task);
/// 批量添加任务
void add_tasks(const std::vector<TaskNode>& tasks);
/// 执行所有任务(拓扑排序 + 并行调度)
/// 如果存在循环依赖,抛出 std::runtime_error
void execute();
/// 重置所有任务(可复用)
void reset();
/// 获取任务数
[[nodiscard]] size_t task_count() const { return tasks_.size(); }
/// 获取线程数
[[nodiscard]] int thread_count() const { return num_threads_; }
private:
int num_threads_;
std::vector<TaskNode> tasks_;
struct Impl;
std::unique_ptr<Impl> impl_;
};
/// 便捷函数:直接对任务列表执行并行图调度
///
/// @param tasks 任务图节点列表
/// @param num_threads 线程数(0 = 硬件并发数)
///
/// @ingroup core
void parallel_task_graph(
const std::vector<TaskNode>& tasks,
int num_threads = 0);
// ═══════════════════════════════════════════════════════════════════════════
// 工作窃取调度器
// ═══════════════════════════════════════════════════════════════════════════
/// 工作窃取调度器 — 线程池从任务队列中窃取工作
///
/// 每个线程维护自己的任务队列,空闲线程从其他线程队列窃取任务。
/// 适用于负载不均的批处理场景(如面片处理、CAD 算法随机化)。
class WorkStealingScheduler {
public:
/// 构造函数
/// @param num_threads 线程数(0 = 硬件并发数)
explicit WorkStealingScheduler(int num_threads = 0);
~WorkStealingScheduler();
// 禁止拷贝
WorkStealingScheduler(const WorkStealingScheduler&) = delete;
WorkStealingScheduler& operator=(const WorkStealingScheduler&) = delete;
/// 提交带优先级的任务
/// @param func 任务函数
/// @param priority 优先级(越大越优先,默认 0)
void submit(std::function<void()> func, int priority = 0);
/// 提交任务并返回 future
/// @tparam F 可调用对象类型
/// @tparam Args 参数类型
/// @param f 可调用对象
/// @param args 参数
/// @return 任务的 std::future
template<typename F, typename... Args>
auto submit_with_result(F&& f, Args&&... args)
-> std::future<decltype(f(args...))>;
/// 等待所有任务完成
void wait_all();
/// 获取活跃线程数
[[nodiscard]] int active_threads() const;
/// 获取队列中待处理任务数
[[nodiscard]] size_t pending_tasks() const;
/// 停止调度器
void shutdown();
private:
int num_threads_;
struct Impl;
std::unique_ptr<Impl> impl_;
};
/// 全局工作窃取调度器
///
/// @return 全局共享的 WorkStealingScheduler 实例
///
/// @ingroup core
[[nodiscard]] WorkStealingScheduler& work_stealing_scheduler();
// ═══════════════════════════════════════════════════════════════════════════
// 全局内存池集成
// ═══════════════════════════════════════════════════════════════════════════
/// 内存池统计数据
struct MemoryPoolStats {
size_t total_allocations = 0; ///< 总分配次数
size_t total_deallocations = 0; ///< 总释放次数
size_t current_bytes = 0; ///< 当前占用字节
size_t peak_bytes = 0; ///< 峰值占用字节
size_t cache_hits = 0; ///< 缓存命中(池中直接分配)
size_t cache_misses = 0; ///< 缓存未命中(需 malloc
};
/// 全局内存池集成
///
/// 单例内存池,为常见大小的 Point3D、Vector3D、AABB 等对象
/// 提供预分配池,减少频繁 malloc/free 的开销。
///
/// 使用方式:
/// @code
/// auto& pool = memory_pool_integration();
/// auto* pt = pool.allocate_point3d();
/// // ... 使用 pt ...
/// pool.deallocate_point3d(pt);
/// @endcode
class MemoryPoolIntegration {
public:
/// 获取单例
[[nodiscard]] static MemoryPoolIntegration& instance();
/// 分配一个 Point3D
[[nodiscard]] Point3D* allocate_point3d();
/// 释放一个 Point3D
void deallocate_point3d(Point3D* p);
/// 分配一个 Vector3D
[[nodiscard]] Vector3D* allocate_vector3d();
/// 释放一个 Vector3D
void deallocate_vector3d(Vector3D* v);
/// 分配指定大小的内存块
[[nodiscard]] void* allocate(size_t bytes);
/// 释放内存块
void deallocate(void* ptr, size_t bytes);
/// 获取统计信息
[[nodiscard]] MemoryPoolStats stats() const;
/// 重置池(释放所有缓存)
void reset();
/// 设置池大小
/// @param pool_size 每种大小的缓存数量
void set_pool_size(size_t pool_size);
/// 预热池(预分配指定数量的对象)
void warm_up(size_t count);
private:
MemoryPoolIntegration();
~MemoryPoolIntegration();
MemoryPoolIntegration(const MemoryPoolIntegration&) = delete;
MemoryPoolIntegration& operator=(const MemoryPoolIntegration&) = delete;
struct Impl;
std::unique_ptr<Impl> impl_;
};
/// 便捷函数:获取全局内存池
///
/// @return 全局 MemoryPoolIntegration 实例
///
/// @ingroup core
[[nodiscard]] inline MemoryPoolIntegration& memory_pool_integration() {
return MemoryPoolIntegration::instance();
}
// ═══════════════════════════════════════════════════════════════════════════
// 缓存优化提示
// ═══════════════════════════════════════════════════════════════════════════
/// 缓存行大小(典型值为 64 字节)
constexpr size_t CACHE_LINE_SIZE = 64;
/// 将值对齐到缓存行
template<typename T>
constexpr size_t cache_aligned_size() {
constexpr size_t s = sizeof(T);
return ((s + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE) * CACHE_LINE_SIZE;
}
/// 缓存对齐分配器
template<typename T>
struct CacheAlignedAllocator {
using value_type = T;
CacheAlignedAllocator() = default;
template<typename U>
CacheAlignedAllocator(const CacheAlignedAllocator<U>&) {}
[[nodiscard]] T* allocate(std::size_t n) {
void* ptr = nullptr;
if (posix_memalign(&ptr, CACHE_LINE_SIZE, n * sizeof(T)) != 0) {
throw std::bad_alloc();
}
return static_cast<T*>(ptr);
}
void deallocate(T* ptr, std::size_t) {
free(ptr);
}
};
/// 缓存优化提示
///
/// 返回当前硬件平台的优化建议:
/// - 缓存行大小
/// - 预取距离(以缓存行为单位的步进距离)
/// - NUMA 节点信息(如果可用)
///
struct CacheOptimizationHints {
size_t l1_cache_size = 32 * 1024; ///< L1 数据缓存大小 (bytes)
size_t l2_cache_size = 256 * 1024; ///< L2 缓存大小 (bytes)
size_t l3_cache_size = 8 * 1024 * 1024; ///< L3 缓存大小 (bytes)
size_t cache_line_size = 64; ///< 缓存行大小 (bytes)
int numa_node_count = 1; ///< NUMA 节点数
bool hyperthreading = true; ///< 是否超线程
};
/// 获取缓存优化提示
///
/// @return 当前硬件平台的缓存优化提示
///
/// @ingroup core
[[nodiscard]] CacheOptimizationHints cache_optimization_hints();
/// 预取内存地址到缓存(编译器提示)
///
/// @param addr 要预取的内存地址
///
/// @ingroup core
inline void prefetch(const void* addr) {
__builtin_prefetch(addr, 0, 3);
}
/// 预取写入(使缓存行进入修改状态)
///
/// @param addr 要预取的内存地址
///
/// @ingroup core
inline void prefetch_write(const void* addr) {
__builtin_prefetch(addr, 1, 3);
}
/// 防止假共享的填充字段
///
/// 用法:将共享原子变量放在填充结构体中
/// @code
/// struct alignas(64) PaddedCounter {
/// std::atomic<int> value{0};
/// };
/// @endcode
template<size_t Alignment = CACHE_LINE_SIZE>
struct PaddedAtomic {
std::atomic<int> value{0};
char padding[Alignment - sizeof(std::atomic<int>)]{};
};
static_assert(sizeof(PaddedAtomic<CACHE_LINE_SIZE>) == CACHE_LINE_SIZE,
"PaddedAtomic must be exactly one cache line");
// ═══════════════════════════════════════════════════════════════════════════
// 数据布局优化 (Profile-Guided Layout)
// ═══════════════════════════════════════════════════════════════════════════
/// 访问频率记录
struct AccessRecord {
std::string field_name; ///< 字段名
size_t access_count = 0; ///< 访问次数
size_t cache_misses = 0; ///< 缓存未命中次数
double hotness = 0.0; ///< 热度(访问/总访问)
};
/// SoA (Structure of Arrays) 布局变换方案
///
/// 将 AoS 布局(结构体数组)转换为 SoA 布局(数组结构体)以改善缓存利用率。
/// 适用场景:遍历大量 Point3D/Vector3D 进行坐标变换、碰撞检测等。
struct SoALayoutPlan {
std::vector<std::string> hot_fields; ///< 热字段列表(应放在前面)
std::vector<std::string> cold_fields; ///< 冷字段列表(可放在后面)
size_t stride_bytes = 0; ///< 行跨距 (bytes)
double estimated_improvement = 0.0; ///< 预估性能提升比例
};
/// 基于性能分析的数据布局优化
///
/// 分析给定类型的访问模式,生成 SoA 布局变换方案。
/// 热字段(频繁访问)放在一起以提高缓存命中率,
/// 冷字段(偶尔访问)分离以减少缓存污染。
///
/// @param access_records 各字段的访问记录
/// @param type_name 类型名称
/// @return SoA 布局变换方案
///
/// @ingroup core
[[nodiscard]] SoALayoutPlan profile_guided_layout(
const std::vector<AccessRecord>& access_records,
const std::string& type_name = "");
/// Point3D 的 SoA 布局
struct Point3DSoA {
std::vector<double> x; ///< X 坐标数组
std::vector<double> y; ///< Y 坐标数组
std::vector<double> z; ///< Z 坐标数组
/// 从 AoS 转换为 SoA
static Point3DSoA from_aos(const std::vector<Point3D>& points);
/// 从 SoA 转换为 AoS
std::vector<Point3D> to_aos() const;
/// 点数
[[nodiscard]] size_t size() const { return x.size(); }
/// 清空
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