并行化LibTomMath的Karatsuba impl。与OpenMP失败

时间:2016-01-06 01:21:18

标签: c multithreading recursion parallel-processing openmp

我认为在我自己滚动之前我会试一试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还是有人发现严重错误?

0 个答案:

没有答案