我认为在我自己滚动之前我会试一试OpenMP但是我(它?)失败了很多。
Karatsuba乘法和平方需要一个小的变化来递归到自身而不是bn_mp_mul.c
中的公共乘法分支以及OpenMP pragma的添加。
Karatsuba乘法的变化为差异(平方以相同的方式完成)到LibTomMath master:
diff -rtbBw libtommath-master-original/bn_mp_mul.c libtommath-master/bn_mp_mul.c
32a34,37
> #ifdef USE_OPEN_MP
> #include <omp.h>
> #pragma omp parallel
> #pragma omp master
33a39,41
> #else
> res = mp_karatsuba_mul(a, b, c);
> #endif
diff -rtbBw libtommath-master-original/bn_mp_karatsuba_mul.c libtommath-master/bn_mp_karatsuba_mul.c 51a52,54
> #ifdef USE_OPEN_MP
> int e1,e2;
> #endif
57c60,62
<
---
> if (B < KARATSUBA_MUL_CUTOFF) {
> return mp_mul(a,b,c);
> }
120c125,135
< if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY)
---
> #ifdef USE_OPEN_MP
> #include <omp.h>
> #pragma omp task shared(x0y0)
> e1 = mp_karatsuba_mul(&x0, &y0, &x0y0);
> #pragma omp task shared(x1y1)
> e2 = mp_karatsuba_mul(&x1, &y1, &x1y1);
> #pragma omp taskwait
> if ((e1 != MP_OKAY) || (e2 != MP_OKAY))
> goto X1Y1;
> #else
> if (mp_karatsuba_mul(&x0, &y0, &x0y0) != MP_OKAY)
122c137
< if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY)
---
> if (mp_karatsuba_mul(&x1, &y1, &x1y1) != MP_OKAY)
123a139
> #endif
130,131d145
< if (mp_mul(&t1, &x0, &t1) != MP_OKAY)
< goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */
132a147,157
> #ifdef USE_OPEN_MP
> #include <omp.h>
> #pragma omp task shared(t1)
> e1 = mp_karatsuba_mul(&t1, &x0, &t1);
> #pragma omp taskwait
> if (e1 != MP_OKAY)
> goto X1Y1;
> #else
> if (mp_karatsuba_mul(&t1, &x0, &t1) != MP_OKAY)
> goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */
> #endif
diff -rtbBw libtommath-master-original/makefile libtommath-master/makefile
17c17,19
< CFLAGS += -O3 -funroll-loops
---
> #CFLAGS += -O3 -funroll-loops
> # the oficial macro is _OPENMP
> CFLAGS += -g3 -O3 -funroll-loops -fopenmp -DUSE_OPEN_MP
使用许多人推荐的OpenMP task
这样的递归函数。对于一个线程(通过在num_threads(1)
之后添加parallel
)计算log(2)到50,000位(使用LibTomFloat)的测试在AMD A8-6600K(使用gcc-4.8)上给出了预期结果。 real(Ubuntu 4.8.4-2ubuntu1~14.04)4.8.4和Kernel Linux debian2 3.16.0-38-generic,全部为64位)。
real 0m7.748s
user 0m7.728s
sys 0m0.013s
但是有更多线程?嗯......有两个线程
real 0m8.025s
user 0m14.936s
sys 0m0.060s
并有四个帖子
real 0m14.198s
user 0m48.224s
sys 0m0.109s
您可以在图形系统监视器中看到CPU出汗但在此无效。
我使用atanh()方法计算Libtomfloat中的常量log(2),并使用二进制拆分算法实现了acoth()的计算。 递归有效(上面的测试已关闭),并且也以与Karatsuba递归相同的方式使用OpenMP的task
实现。它对于log(2)(约5%)没有太大的好处,但它至少获得某些,尽管5%非常接近统计误差。
那么,我在这里做错了什么?
修改
我按照RichardBruce的建议运行了一些测试来尝试开销。随着Karatsube设置为250(120不是48,对不起RichardBruce)我得到一个最佳的Karatsuba 1.1分钟。一个和四个线程的实时时间(200k位)和1.5分钟。四个线程的用户时间:没有收益。截止设置为500我得1.5分钟。一个线程的实时时间和1.3个四线程的实时时间和2.3分钟。用户时间有四个线程:相对增益但绝对损失。
我真的没想到OpenMP会产生这么多开销。那么它是手动pthreads还是有人发现严重错误?