[英]Multithreaded algorithms work much slower
我曾尝试使用 OpenMP 和 Cilk Plus。 结果是一样的,多线程工作得更慢。 我不知道我做错了什么。 我做了这个人在本教程中所做的
他的代码并行运行效果更好,而我的情况是这样的:
平行:斐波那契数 #42 是 267914296
使用 8 个工人在33.026 秒内计算
序列号:斐波那契数 #42 是 267914296
使用 8 个工人在2.110 秒内计算
我完全复制了教程的源代码。
我也用 OpenMP 尝试过,同样的事情也在那里发生。 我在执行过程中检查 CPU 内核的使用情况。 他们都工作,这很好。
我试图用这个命令改变工人的数量:
export CILK_NWORKERS=4
看起来随着worker数量的增加,算法运行得更慢。 但有时不会。 我在 C 和 C++ 上实现了 Cilk 代码。 没有不同。
这是顺序斐波那契函数:
int fib_s(int n)
{
if (n < 2)
return n;
int x = fib_s(n-1);
int y = fib_s(n-2);
return x + y;
}
这是并行斐波那契函数:
int fib(int n)
{
if (n < 2)
return n;
int x = cilk_spawn fib(n-1);
int y = fib(n-2);
cilk_sync;
return x + y;
}
我在main()
函数中像这样计算运行时间:
clock_t start = clock();
int result = fib(n);
clock_t end = clock();
double duration = (double)(end - start) / CLOCKS_PER_SEC;
谁能帮我?
问:任何人都可以帮助我吗?
是的。 你会看到, fib( 42 )
在单线程解释 (!) 代码中可能需要不到25 [us]
鉴于上面的并行代码已经报道花~33 [s]
上处理,编译代码可以计算一个fib( ~ 1,700,000 )
在同一~33 [s]
,如果右设计。
任何递归公式化的问题描述都是老数学家的罪过:
虽然在纸上看起来很酷,
它在堆栈上缩放丑陋,并为任何更深层次的递归阻塞了大量资源......
使所有“先前”级别的大部分时间都在等待,
直到return 2
和return 1
在它们的所有后代路径中都发生了
并且递归公式化算法的累积阶段开始增长,从深递归潜水的所有深度返回顶部。
这个依赖树相当于一个纯[SERIAL]
(一个接一个)的计算进程,以及任何注入{ [CONCURENT] | [PARALLEL] }
尝试{ [CONCURENT] | [PARALLEL] }
{ [CONCURENT] | [PARALLEL] }
处理编排只会增加处理成本(添加所有附加开销),而不会对结果的依赖驱动累积的纯[SERIAL]
序列进行任何改进。
cilk_spawn fib( N )
是多么糟糕:f(42)
|
x=--> --> --> --> --> --> --> --> --> --> --> -- --> --> --> --> --> --> --> --> --> --> --> --> --> -->f(41)
| |
y=f(40) x=--> --> --> --> --> --> --> --> --> --> f(40)
~ | | |
~ x=--> --> --> --> --> --> --> --> --> f(39) y=f(39) x=--> --> --> --> --> --> --> --> --> f(39)
~ | | ~ | | |
~ y=f(38) x=--> --> --> --> --> --> f(38) ~ x=--> --> --> --> f(38) y=f(38) x=--> --> --> --> --> --> f(38)
~ ~ | | | ~ | | ~ | | |
~ ~ x=--> --> f(37) y=f(37) x=--> --> f(37) ~ y=f(37) x=--> --> --> f(37) ~ x=--> --> f(37) y=f(37) x=--> --> f(37)
~ ~ | | ~ | | | ~ ~ | | | ~ | | ~ | | |
~ ~ y=f(36) x=--> --> f(36) ~ x=--> --> f(36) y=f(36) x=-->f(36) ~ ~ x=--> --> f(36) y=f(36) x= ~ y=f(36) x=--> --> f(36) ~ x=--> --> f(36) y=f(36) x=--> --> f(36)
~ ~ ~ | | | ~ | | ~ | | | ~ ~ | | ~ | | ~ ~ | | | ~ | | ~ | | |
~ ~ ~ x=-->f y=f(35) x=-->f ~ y=f(35) x=-->f(35) ~ x=-->f y=f(35) x=-->f ~ ~ y=f(35) x= ~ x=-->f(35) y= ~ ~ x=-->f y=f(35) x=-->f(35) ~ y=f(35) x=-->f(35) ~ x=--> y=f(35) x=-->f(35)
~ ~ ~ | ~ | | ~ ~ | | | ~ | ~ | | ~ ~ ~ | | ~ | | ~ ~ ~ | ~ | | | ~ ~ | | | ~ | ~ | | |
~ ~ ~ y=f(34) ~ x=-->f y=f(34) ~ ~ x=-->f y=f(34) x= ~ y=f(34) ~ x= y=f(34) ~ ~ ~ x=-->f y= ~ y=f(34) x= ~ ~ ~ y=f(34) ~ x=-->f y=f(34) x= ~ ~ x=-->f y=f(34) x= ~ y=f(34) ~ x=-->f y=f(34) x=-->f
~ ~ ~ ~ | ~ | ~ | ~ ~ | ~ | | ~ ~ | ~ | ~ | ~ ~ ~ | ~ ~ ~ | | ~ ~ ~ ~ | ~ | ~ | | ~ ~ | ~ | | ~ ~ | ~ | ~ |
~ ~ ~ ~ x= ~ y=f(33) ~ x= ~ ~ y=f(33) ~ x= y= ~ ~ x= ~ y= ~ x= ~ ~ ~ y=f(33) ~ ~ ~ x= y= ~ ~ ~ ~ x= ~ y=f(33) ~ x= y= ~ ~ y=f(33) ~ x= y= ~ ~ x= ~ y=f(33) ~ y=f(33)
~ ~ ~ ~ | ~ ~ | ~ | ~ ~ ~ | ~ | ~ ~ ~ | ~ ~ ~ | ~ ~ ~ ~ | ~ ~ ~ | ~ ~ ~ ~ ~ | ~ ~ | ~ | ~ ~ ~ ~ | ~ | ~ ~ ~ | ~ ~ | ~ ~ |
~ ~ ~ ~ y= ~ ~ x= ~ y= ~ ~ ~ x= ~ y= ~ ~ ~ y= ~ ~ ~ y= ~ ~ ~ ~ x= ~ ~ ~ y= ~ ~ ~ ~ ~ y= ~ ~ x= ~ y= ~ ~ ~ ~ x= ~ y= ~ ~ ~ y= ~ ~ x= ~ ~ x=-->f
~ ~ ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ~ | ~ ~ |
: : : : :
: : : :
: : :
~ ~ --SYNC-----------f(36)+f(37)
~ ~ <--RET x+y // <-- f(38)
~ --SYNC----------------f(38)+f(39)
~ <--RET x+y // <-- f(40)
--SYNC---------------------f(40)+f(41)
<--RET x+y // <-- f(42)
只需计算一下, Fib( N )
的自上而下运行的递归方法已经为N
每个值重新计算了多少次 - 是的,您一次又一次地多次计算相同的事情,只是由于递归方法的“数学” -懒惰:
fib( N == 42 ) was during recursion calculated .........1x times...
fib( N == 41 ) was during recursion calculated .........1x times...
fib( N == 40 ) was during recursion calculated .........2x times...
fib( N == 39 ) was during recursion calculated .........3x times...
fib( N == 38 ) was during recursion calculated .........5x times...
fib( N == 37 ) was during recursion calculated .........8x times...
fib( N == 36 ) was during recursion calculated ........13x times...
fib( N == 35 ) was during recursion calculated ........21x times...
fib( N == 34 ) was during recursion calculated ........34x times...
fib( N == 33 ) was during recursion calculated ........55x times...
fib( N == 32 ) was during recursion calculated ........89x times...
fib( N == 31 ) was during recursion calculated .......144x times...
fib( N == 30 ) was during recursion calculated .......233x times...
fib( N == 29 ) was during recursion calculated .......377x times...
fib( N == 28 ) was during recursion calculated .......610x times...
fib( N == 27 ) was during recursion calculated .......987x times...
fib( N == 26 ) was during recursion calculated ......1597x times...
fib( N == 25 ) was during recursion calculated ......2584x times...
fib( N == 24 ) was during recursion calculated ......4181x times...
fib( N == 23 ) was during recursion calculated ......6765x times...
fib( N == 22 ) was during recursion calculated .....10946x times...
fib( N == 21 ) was during recursion calculated .....17711x times...
fib( N == 20 ) was during recursion calculated .....28657x times...
fib( N == 19 ) was during recursion calculated .....46368x times...
fib( N == 18 ) was during recursion calculated .....75025x times...
fib( N == 17 ) was during recursion calculated ....121393x times...
fib( N == 16 ) was during recursion calculated ....196418x times...
fib( N == 15 ) was during recursion calculated ....317811x times...
fib( N == 14 ) was during recursion calculated ....514229x times...
fib( N == 13 ) was during recursion calculated ....832040x times...
fib( N == 12 ) was during recursion calculated ...1346269x times...
fib( N == 11 ) was during recursion calculated ...2178309x times...
fib( N == 10 ) was during recursion calculated ...3524578x times...
fib( N == 9 ) was during recursion calculated ...5702887x times...
fib( N == 8 ) was during recursion calculated ...9227465x times...
fib( N == 7 ) was during recursion calculated ..14930352x times...
fib( N == 6 ) was during recursion calculated ..24157817x times...
fib( N == 5 ) was during recursion calculated ..39088169x times...
fib( N == 4 ) was during recursion calculated ..63245986x times...
fib( N == 3 ) was during recursion calculated .102334155x times...
fib( N == 2 ) was during recursion calculated .165580141x times...
fib( N == 1 ) was during recursion calculated .102334155x times...
虽然原始的递归计算调用了535,828,591
次 (!!!) 相同的琐碎fib()
(通常是一个,已经在其他地方计算过)
----有的甚至数亿多次已经〜 102,334,155x
倍......作为fib( 3 )
产卵多达267,914,295
只是- [CONCURRENT]
代码执行块,排队等待,但8工人,所有的等待大多数情况下,要不是为了让他们产生的孩子深入到return 1
和return 2
之后什么都不做,只是添加一对然后返回的数字并从昂贵的产生自己的过程中返回,一种“直接”方法处理是不可能的方式更聪明,方式更快:
int fib_direct( int n ) // PSEUDO-CODE
{ assert( n > 0 && "EXCEPTION: fib_direct() was called with a wrong parameter value" );
if ( n == 1
|| n == 2
) return n;
// ---------------------------- .ALLOC + .SET
int fib_[ max(4,n) ];
fib_[3] = 3;
fib_[4] = 5;
// ---------------------------- .LOOP LESS THAN N-TIMES
for( int i = 5; i <= n; i++ )
{ fib_[i] = fib_[i-2]
+ fib_[i-1];
}
// ---------------------------- .RET
return fib_[n];
}
更有效的实现(仍然只是一个线程并且仍然只是解释)设法在不到2.1 [s]
时间内轻松计算fib_direct( 230000 )
,这是您编译的代码运行时仅fib( 42 )
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.