You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Eigen不同尺寸数组逐元素布尔运算及性能优化问询

Eigen数组操作效率与高频计算优化问题

一、数组逐对比较的实现与效率疑问

我定义了两个Eigen数组:

Array<float, 10, 1> a;
Array<float, 4, 1> b;

希望得到一个Array<bool, 10, 4> result;,其中每个位置result(i,j)为true当且仅当a(i)大于b(j)。目前实现方式如下:

Array<float, 10, 1> a; 
Array<float, 4, 1> b;
// ... 填充数组
Array<bool, 10, 4> result = a.replicate(1 , 4) > (b.transpose()).replicate(10, 1);

实际场景中数组尺寸远大于示例,现有实现可行,但有两个疑问:

  1. 若将Array<bool, 10, 4> result = ...替换为auto result = ...,后续使用result时速度明显变慢;
  2. 不确定replicate是否会产生拷贝,考虑用循环替代:
Array<float, 10, 1> a; 
Array<float, 4, 1> b;
// ... 填充数组
Array<bool, 10, 4> result;
for(int i = 0; i < 4; i++){
    result.col(i) = a > b(i, 0);
}

但循环会引入迭代开销,想了解哪种方案效率更高。

解答

  1. auto导致变慢的原因:Eigen采用表达式模板机制,auto会直接保存未求值的表达式对象,而非计算后的布尔数组。每次使用result时都会重新执行整个比较逻辑,因此速度骤降。解决方法是显式指定结果类型,或调用.eval()强制求值后再赋值:

    auto result = (a.replicate(1,4) > b.transpose().replicate(10,1)).eval();
    
  2. replicate与循环的效率对比:

    • replicate本身是表达式模板,不会立即创建拷贝数组,只有赋值时才会一次性完成向量化计算,能充分利用CPU的SIMD指令集批量处理数据。
    • 手动循环逐列赋值无法充分利用向量化优化,且循环迭代本身会带来额外开销。对于大尺寸数组,replicate的实现效率更高。
    • 更简洁高效的写法是利用Eigen的广播特性,无需显式replicate:
      Array<bool,10,4> result = a.colwise() > b.transpose();
      
      该写法与replicate逻辑一致,代码更简洁且享受相同的向量化优化。

二、高频线段相交检测的性能优化

以下高频调用的线段相交检测代码占用了90%的执行时间,其中85%耗时在inlined'Eigen::Array::Array',推测临时数组构造开销过大,但这些临时变量会被多次使用,寻求优化方法:

Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x1 = rays.col(0).replicate(1, 4*obs.size());
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x2 = (rays.col(2) + rays.col(0)).replicate(1, 4*obs.size());
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y1 = rays.col(1).replicate(1, 4*obs.size());
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y2 = (rays.col(3) + rays.col(1)).replicate(1, 4*obs.size());

    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x3 = obstacles.col(0).transpose().replicate(num_rays*num_ships, 1);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x4 = obstacles.col(2).transpose().replicate(num_rays*num_ships, 1);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y3 = obstacles.col(1).transpose().replicate(num_rays*num_ships, 1);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y4 = obstacles.col(3).transpose().replicate(num_rays*num_ships, 1);

    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> t_den = (x1-x2)*(y3-y4) -(y1-y2)*(x3-x4);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> t = (x1-x3)*(y3-y4) -(y1-y3)*(x3-x4)/t_den;
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> u = ((x1-x3)*(y1-y2) - (y1-y3)*(x1-x2));
    Eigen::Array<bool, Eigen::Dynamic, Eigen::Dynamic> col_r = 0 <= t && 0 <= u && u <= t_den;
    t_rays = col_r.select(t, 1000).rowwise().minCoeff();


    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x1_ = ship_b_boxs.col(0).replicate(1, 4*obs.size());
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x2_ = (ship_b_boxs.col(2)).replicate(1, 4*obs.size());
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y1_ = ship_b_boxs.col(1).replicate(1, 4*obs.size());
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y2_ = (ship_b_boxs.col(3)).replicate(1, 4*obs.size());

    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x3_ = x3(Eigen::seq(0, 4*num_ships-1), Eigen::all);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> x4_ = x4(Eigen::seq(0, 4*num_ships-1), Eigen::all);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y3_ = y3(Eigen::seq(0, 4*num_ships-1), Eigen::all);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> y4_ = y4(Eigen::seq(0, 4*num_ships-1), Eigen::all);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> t_den_ = (x1_-x2_)*(y3_-y4_) -(y1_-y2_)*(x3_-x4_);
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> t_ = (x1_-x3_)*(y3_-y4_) -(y1_-y3_)*(x3_-x4_)/t_den_;
    Eigen::Array<float, Eigen::Dynamic, Eigen::Dynamic> u_ = ((x1_-x3_)*(y1_-y2_) - (y1_-y3_)*(x1_-x2_));
    Eigen::Array<bool, Eigen::Dynamic, Eigen::Dynamic> col_s = (0 <= t_ && t_ <= 1 && 0 <= u_ &&  u_ <= t_den_).rowwise().maxCoeff();

优化方案

1. 避免显式临时数组,用表达式模板延迟计算

将所有临时数组替换为表达式引用,避免内存分配与拷贝。Eigen的表达式模板会在最终计算时直接展开为高效的向量化代码,无需中间数组存储:

// 用const引用保存表达式,避免拷贝
    const auto& x1_expr = rays.col(0).replicate(1, 4*obs.size());
    const auto& x2_expr = (rays.col(2) + rays.col(0)).replicate(1, 4*obs.size());
    const auto& y1_expr = rays.col(1).replicate(1, 4*obs.size());
    const auto& y2_expr = (rays.col(3) + rays.col(1)).replicate(1, 4*obs.size());

    const auto& x3_expr = obstacles.col(0).transpose().replicate(num_rays*num_ships, 1);
    const auto& x4_expr = obstacles.col(2).transpose().replicate(num_rays*num_ships, 1);
    const auto& y3_expr = obstacles.col(1).transpose().replicate(num_rays*num_ships, 1);
    const auto& y4_expr = obstacles.col(3).transpose().replicate(num_rays*num_ships, 1);

    // 直接基于表达式计算,无需临时数组
    const auto t_den = (x1_expr - x2_expr) * (y3_expr - y4_expr) - (y1_expr - y2_expr) * (x3_expr - x4_expr);
    const auto t = (x1_expr - x3_expr) * (y3_expr - y4_expr) - (y1_expr - y3_expr) * (x3_expr - x4_expr) / t_den;
    const auto u = ((x1_expr - x3_expr) * (y1_expr - y2_expr) - (y1_expr - y3_expr) * (x1_expr - x2_expr));
    const auto col_r = 0 <= t && 0 <= u && u <= t_den;
    t_rays = col_r.select(t, 1000).rowwise().minCoeff();

    // 同理处理第二部分
    const auto& x1__expr = ship_b_boxs.col(0).replicate(1, 4*obs.size());
    const auto& x2__expr = ship_b_boxs.col(2).replicate(1, 4*obs.size());
    const auto& y1__expr = ship_b_boxs.col(1).replicate(1, 4*obs.size());
    const auto& y2__expr = ship_b_boxs.col(3).replicate(1, 4*obs.size());

    // 直接切片访问,无需创建新数组
    const auto x3__expr = x3_expr(Eigen::seq(0, 4*num_ships-1), Eigen::all);
    const auto x4__expr = x4_expr(Eigen::seq(0, 4*num_ships-1), Eigen::all);
    const auto y3__expr = y3_expr(Eigen::seq(0, 4*num_ships-1), Eigen::all);
    const auto y4__expr = y4_expr(Eigen::seq(0, 4*num_ships-1), Eigen::all);

    const auto t_den_ = (x1__expr - x2__expr) * (y3__expr - y4__expr) - (y1__expr - y2__expr) * (x3__expr - x4__expr);
    const auto t_ = (x1__expr - x3__expr) * (y3__expr - y4__expr) - (y1__expr - y3__expr) * (x3__expr - x4__expr) / t_den_;
    const auto u_ = ((x1__expr - x3__expr) * (y1__expr - y2__expr) - (y1__expr - y3__expr) * (x1__expr - x2__expr));
    const auto col_s = (0 <= t_ && t_ <= 1 && 0 <= u_ &&  u_ <= t_den_).rowwise().maxCoeff();

2. 预分配内存与固定尺寸优化

如果数组尺寸在运行前可确定,优先使用固定大小的Array<float, N, M>而非动态尺寸的Array<float, Dynamic, Dynamic>,避免动态内存分配开销。若尺寸必须动态,可提前调用.resize()分配空间,避免每次调用时重复分配。

3. 使用NoAlias()避免不必要拷贝

当赋值目标与源表达式无内存重叠时,使用.noalias()直接赋值,避免Eigen内部的拷贝检查与临时内存分配:

t_rays.noalias() = col_r.select(t, 1000).rowwise().minCoeff();

4. 合并计算步骤

将多个小计算合并为单个表达式,减少中间临时对象的生成。例如,将(rays.col(2) + rays.col(0)).replicate(...)直接写入后续计算,无需单独保存。


内容的提问来源于stack exchange,提问作者Finn Busch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 12:15:38