1.浮点数计算结果与计算顺序相关
例如矩阵乘法
设mc = ma * mb,都是方阵,维数为8。
以c00的计算作为例子,以下这种是最常见的计算顺序
c00 = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30
+ a04 * b40 + a05 * b50 + a06 * b60 + a07 * b70
计算顺序大致是
tc = 0
tc = tc + a00 * b00
tc = tc + a01 * b10
tc = tc + a02 * b20
tc = tc + a03 * b30
tc = tc + a04 * b40
tc = tc + a05 * b50
tc = tc + a06 * b60
tc = tc + a07 * b70
就是不断的大鱼吃小鱼,大鱼变得越来越大的过程
但是,这种计算顺序耦合性非常高,每一次tc = tc + axx * bxx依赖于上一次的计算结果,我测试过,gcc在O3的情况也并没智能到能优化计算顺序。所以,如果我们要优化并行的话,一般需要手动优化成以下的计算顺序。
tc =0, tc0 = 1, tc1 = 0, tc2 = 0, tc3 = 0
tc0 = tc0 + a00 * b00
tc0 = tc0 + a01 * b10
tc1 = tc1 + a02 * b20
tc1 = tc1 + a03 * b30
tc2 = tc2 + a04 * b40
tc2 = tc2 + a05 * b50
tc3 = tc3 + a06 * b60
tc3 = tc3 + a07 * b70
tc = tc + tc0
tc = tc + tc1
tc = tc + tc2
tc = tc + tc3
就是有4条大鱼分别在吃小鱼,最后4条大鱼间进行大鱼吃小鱼
第一种模式,有7次数据依赖,第二种模式,只有5次数据依赖,要知道现在只是8维的情况,维度大的时候,依赖数是大大减少的。
然后呢?问题就出现!由于浮点数的特性,是需要对进过四舍五入的,所以前后两种计算顺序,最终结果是有一点不同的。真的坑的不行,然后还有一种提高优化方法是采用strassen方法,在这种计算方式下,结果差异更恐怖。
2.浮点数矩阵乘法的正确性测试方法
- 先写一个基准程序,计算顺序要用最传统的大鱼吃小鱼;
- 确定一个可接受误差范围;
- 将优化了计算顺序的程序结果,与传统计算顺序结果这个进行减法操作,
判断它们的差是否在可接受误差范围内;