Eigen Vs CV
矩阵基本运算
差异
OpenCV在x86 & arm上Mat矩阵的用float进行存储,但是计算时高于float,计算,再截断 VS Eigen使用Matrix定义是指定的类型进行存储和计算
- Eigen MatrixXf随机矩阵乘法与c++实现float矩阵乘法京都一直
- OpenCV 随机矩阵乘法与C++ & Eigen 不一致
- OpenCV与C++ float 强转成double 相乘再强转成foat 京都一直
- OpenCV与Eigen MatrixXf cast到double进行乘法计算后强转成float精度移植
策略
-
Eigen使用Float进行计算(长期推荐),预计计算效率会比OpenCV计算高(当前评测中性能提升的来源之一,同时能够比较充分的利用Neon向量处理加速)
-
将所有Eigen Matrix基本运算,使用Eigen MatrixXf cast到double进行乘法计算后强转成float精度策略,模拟OpenCV的计算过程。最大程度减少基本运算带来的数值差异,但是会影响计算速度。Neon向量运算可能会慢1倍(TBD)
-
将涉及的Eigen Matrix基本运算,转换回OpenCV进行计算,使用OpenCV的算子计算后转回Eigen数据结构。最大程度消除数值差异(CR中使用本策略)
-
Eigen使用Double计算,输出与Baseline也会存在较大差别,会导致,预计计算与存储消耗均会一定程度提升。UKF及其派生类内存占用翻倍(不推荐)实验发现,如果Eigen FixValue UKF使用Double计算,也会导致结果产生一定差异(相对明显)。
Cholesky Decomposition数值
差异
Baseline中使用OpenCV提供的cv::hal::Cholesky32f(L, lstep, lsize, NULL, 0, 0)做Cholesky分解
Eigen中使用也提供了Cholesky LLT实现,但是两者实现数值对比有差异(单元素矩阵就会有差异)。
策略
-
使用OpenCV中提供的Cholesky Decomposition算法,会存在Eigen-CV矩阵转换。可以消除Eigen-CV数值差异,会引入不必要转换开销,且CV LLT计算较Eigen慢(CR中使用本策略)
-
使用Eigen提供的LLT算法。在FixValue UKF场景比较,Eigen LLT分解数值精度在一些场景比OpenCV准确。(比较方法:将LL*相乘,与原阵作差,长期推荐)
矩阵逆运算数值
差异
OpenCV当前使用SVD(奇异值分解算法)做矩阵逆运算,支持对非方阵进行广义逆运算,对矩阵形状没有要求。 Eigen没有基于奇异值分解矩阵逆运算实现。仅提供默认inverse进行逆矩阵运算。(UKF开源实现直接使用了inverse)
策略
-
直接使用Eigen提供的inverse函数进行矩阵逆运算。预计性能较好,且稳定性及可靠性优秀。(长期推荐)
-
基于Eigen SVD实现奇异值分解逆运算。预计会最大程度消除逆运算数值差异;会有额外性能开销,有可靠性&数值验证成本。(CR中使用本策略, 用SVD)
矩阵Gemm运算数值
差异
可以被写成这种类型的乘法
OpenCV在X86 & Arm上Mat矩阵是使用float进行存储的,但是计算时会使用高于float的计算单元进行计算,然后截断成float。Eigen则会实际使用Matrix定义时指定的类型进行存储与计算。
当算式中包含可以被化简成上述形式的表达式时,OpenCV在处理matExpr的时候会使用gemm运算,而不是进行单步运算。调用opencv gemm与opencv单步运算结果存在数值差异。
根据Eigen官网描述https://eigen.tuxfamily.org/dox/TopicWritingEfficientProductExpression.html,
Eigen也有gemm实现。实验发现,Eigen gemm实现与单步计算数值结果一致。 OpenCV单步计算数值与Eigen数值一致。
策略
-
Eigen直接使用进行计算Gemm计算(长期推荐),预计计算效率会比OpenCV计算高,但是由于开发过程中需要进行一致性比较,故没有使用此方案。(相关代码以注释形式提交,方便后续开发评测使用)
-
将包含乘加运算的Eigen Matrix表达式,转换成Opencv进行计算,计算后结果再转回Eigen。从而消除这种基本运算带来的数值差异,从而继续推进一致性比较。(本CR采用此策略) 由于Opencv和本身单步计算存在数值差异,故没有使用Eigen模拟OpenCV的计算过程。
矩阵标量计算
差异
使用cv::sum对矩阵中元素进行求和运算(对应eigen matrix.sum()),以及矩阵对标量的除法计算(matrix/scalar),cv与Eigen存在数值差异
策略
使用Eigen矩阵表达式直接进行标量计算,性能较优(长期推荐)
将Eigen MatrixXf转会OpenCV进行计算,转换会有不必要内存拷贝带来性能损失,OpenCV计算数值差异较double计算的差别比Eigen计算差别大,计算完成再转成Eigen MatrixXf。(一致性版本CR中使用本策略)
cv::scaleAdd与Eigen矩阵加法表达式
cv::scaleAdd底层调用DAXPY算法,与Eigen矩阵加法表达式存在数值差异。
使用Eigen矩阵表达式直接进行标量加法计算,性能较优(长期推荐)
将Eigen MatrixXf转会OpenCV进行计算,转换会有不必要内存拷贝带来性能损失,,计算完成再转成Eigen MatrixXf。(一致性版本CR中使用本策略)
Eigen 性能为什么好
并行计算
多核并行
OpenMp 线性加速
SIMD
使用向量化,在单个周期内对多组数据进行向量级并行
Cache
最大化Cache命中率,对于单个计算获得5-10倍访存性能提升
列优先存储访问【OpenCV行优先】
模板表达式分解+延迟计算
对表达式进行分解,能置标记位设置标记位,最小化实际计算
Matrix m1, m2, m3;
m3 = m1 + m2 + m3;
m2 = m3.transponse()
m3.transponse()
m3 = m3.transponseInPla()
// 传统C++矩阵预算
tmp1 = m1 + m2;
tmp2 = tmp1 + m3;
m3 = tmp2;
// 3个循环
// 2个临时变量
// 8*m*n次内存计算
// c++运算符重载,定义加法类
Sum operator+(const Matrix& A, const Matrix& B) { return Sum(A, B); }
template class Sum {
const type_of_A& A;
const type_of_B& B;
};
充分利用编译时信息
TODO