任何想法为什么它适用于0,1,2,3,4等值,并且对于像> 15这样的值的seg错误? #包括 #包括 #include
void *fib(void *fibToFind);
main(){
pthread_t mainthread;
long fibToFind = 15;
long finalFib;
pthread_create(&mainthread,NULL,fib,(void*) fibToFind);
pthread_join(mainthread,(void*)&finalFib);
printf("The number is: %d\n",finalFib);
}
void *fib(void *fibToFind){
long retval;
long newFibToFind = ((long)fibToFind);
long returnMinusOne;
long returnMinustwo;
pthread_t minusone;
pthread_t minustwo;
if(newFibToFind == 0 || newFibToFind == 1)
return newFibToFind;
else{
long newFibToFind1 = ((long)fibToFind) - 1;
long newFibToFind2 = ((long)fibToFind) - 2;
pthread_create(&minusone,NULL,fib,(void*) newFibToFind1);
pthread_create(&minustwo,NULL,fib,(void*) newFibToFind2);
pthread_join(minusone,(void*)&returnMinusOne);
pthread_join(minustwo,(void*)&returnMinustwo);
return returnMinusOne + returnMinustwo;
}
}
答案 0 :(得分:3)
运行内存(堆栈空间不足)或有效的线程句柄?
你需要大量的线程,这需要大量的堆栈/上下文。 Windows(和Linux)有一个愚蠢的“大[连续]堆栈”的想法。
从pthreads_create上的文档: “在Linux / x86-32上,新线程的默认堆栈大小为2兆字节。” 如果您生产10,000个线程,则需要20 Gb的RAM。 我构建了一个版本的OP程序,并用3500(p)线程进行了轰炸 在Windows XP64上。
有关为什么大堆栈是一个非常糟糕的主意的更多详细信息,请参阅此SO线程: Why are stack overflows still a problem?
如果放弃大堆栈,并使用堆分配实现并行语言 用于激活记录 (我们的PARLANSE是 其中一个)问题消失了。
这是我们在PARLANSE中编写的第一个(顺序)程序:
(define fibonacci_argument 45)
(define fibonacci
(lambda(function natural natural )function
`Given n, computes nth fibonacci number'
(ifthenelse (<= ? 1)
?
(+ (fibonacci (-- ?))
(fibonacci (- ? 2))
)+
)ifthenelse
)lambda
)define
这是i7上的执行:
C:\DMS\Domains\PARLANSE\Tools\PerformanceTest>run fibonaccisequential
Starting Sequential Fibonacci(45)...Runtime: 33.752067 seconds
Result: 1134903170
这是第二个,它是平行的:
(define coarse_grain_threshold 30) ; technology constant: tune to amortize fork overhead across lots of work
(define parallel_fibonacci
(lambda (function natural natural )function
`Given n, computes nth fibonacci number'
(ifthenelse (<= ? coarse_grain_threshold)
(fibonacci ?)
(let (;; [n natural ] [m natural ] )
(value (|| (= m (parallel_fibonacci (-- ?)) )=
(= n (parallel_fibonacci (- ? 2)) )=
)||
(+ m n)
)value
)let
)ifthenelse
)lambda
)define
使并行性显式化使得程序也更容易编写。
我们通过调用(parallel_fibonacci 45)测试的并行版本。这里 是在同一个i7上运行(可以说有8个处理器, 但它实际上是4个处理器超线程,所以它真的不是8 等效的CPU):
C:\DMS\Domains\PARLANSE\Tools\PerformanceTest>run fibonacciparallelcoarse
Parallel Coarse-grain Fibonacci(45) with cutoff 30...Runtime: 5.511126 seconds
Result: 1134903170
6+附近的加速,对于不太8的处理器来说也不错。另一个 这个问题的答案运行了pthreads版本;花了几秒钟 (炸毁)计算Fib(18),这对于Fib(45)来说是5.5秒。 这告诉你pthreads 是一个从根本上说是很糟糕的方法来做很多细粒度并行,因为 它有非常非常高的分叉开销。 (PARLANSE旨在 最小化分叉费用。)
如果您将技术常量设置为零(在每个调用上分叉),会发生以下情况 到了):
C:\DMS\Domains\PARLANSE\Tools\PerformanceTest>run fibonacciparallel
Starting Parallel Fibonacci(45)...Runtime: 15.578779 seconds
Result: 1134903170
你可以看到,即使你有快速分叉,摊销也是一个好主意。
Fib(45)产生批次的谷物。堆分配 激活记录解决了OP的一阶问题(每个数千个pthread) 使用1Mb堆栈烧掉千兆字节的RAM。
但是有一个二阶问题:2 ^ 45 PARLANSE“grain”也会烧掉你所有的记忆 即使您的谷物控制块很小,也只是跟踪谷物。 因此,有一个调度程序有助于一旦你“很多”限制分叉 (对于一些定义“很多”明显少于2 ^ 45)谷物来防止 用“谷物”跟踪数据结构淹没机器的并行性爆炸。 当谷物数量低于阈值时,它必须不用油门 同样,要确保物理上始终存在大量逻辑,并行的工作 要做的CPU。
答案 1 :(得分:0)
您没有检查错误 - 特别是来自pthread_create()
。 pthread_create()
失败时,pthread_t
变量未定义,后续pthread_join()
可能会崩溃。
如果您确实检查了错误,您会发现pthread_create()
失败。这是因为您尝试生成近2000个线程 - 使用默认设置,这将需要单独分配16GB的线程堆栈。
您应该修改算法,使其不会生成这么多线程。
答案 2 :(得分:0)
我试图运行你的代码,并遇到了几个惊喜:
printf("The number is: %d\n", finalFib);
此行有一个小错误:%d
表示printf
需要int
,但会传递long int
。在大多数平台上,这是相同的,或者会有相同的行为,但是迂腐地说(或者如果你只是想阻止警告出现,这也是一个非常崇高的理想),你应该使用%ld
相反,它会期望long int
。
另一方面,您的fib
功能似乎无法正常运行。在我的机器上测试它,它不会崩溃,但它会产生1047
,这不是斐波纳契数。仔细观察,似乎你的程序在几个方面是不正确的:
void *fib(void *fibToFind)
{
long retval; // retval is never used
long newFibToFind = ((long)fibToFind);
long returnMinusOne; // variable is read but never initialized
long returnMinustwo; // variable is read but never initialized
pthread_t minusone; // variable is never used (?)
pthread_t minustwo; // variable is never used
if(newFibToFind == 0 || newFibToFind == 1)
// you miss a cast here (but you really shouldn't do it this way)
return newFibToFind;
else{
long newFibToFind1 = ((long)fibToFind) - 1; // variable is never used
long newFibToFind2 = ((long)fibToFind) - 2; // variable is never used
// reading undefined variables (and missing a cast)
return returnMinusOne + returnMinustwo;
}
}
始终注意编译器警告:当你得到一个警告时,通常,真的正在做一些可疑的事情。
也许你应该稍微修改一下算法:现在,你所有的函数都会返回两个未定义值的总和,因此我得到的是1047。
使用递归算法实现Fibonacci套件意味着您需要再次调用该函数。正如其他人所指出的那样,这是一种效率很低的方式,但这很简单,所以我想所有的计算机科学教师都会以此为例。
常规递归算法如下所示:
int fibonacci(int iteration)
{
if (iteration == 0 || iteration == 1)
return 1;
return fibonacci(iteration - 1) + fibonacci(iteration - 2);
}
我不知道你应该在多大程度上使用线程 - 只需在辅助线程上运行算法,或者为每次调用创建新线程?让我们假设现在是第一个,因为它更直接。
将整数转换为指针反之亦然是一种不好的做法,因为如果你试图在更高层次上看待事物,它们应该是截然不同的。整数执行数学运算,指针解析内存地址。它恰好起作用,因为它们以相同的方式表示,但实际上,你不应该这样做。相反,您可能会注意到,运行新线程的函数接受void*
参数:我们可以使用它来传达输入所在的 和其中输出将是。
因此,基于我之前的fibonacci
函数,您可以将此代码用作线程主程序:
void* fibonacci_offshored(void* pointer)
{
int* pointer_to_number = pointer;
int input = *pointer_to_number;
*pointer_to_number = fibonacci(input);
return NULL;
}
它需要一个指向整数的指针,并从中获取它的输入,然后将输出写入那里。 1 然后你会创建这样的线程:
int main()
{
int value = 15;
pthread_t thread;
// on input, value should contain the number of iterations;
// after the end of the function, it will contain the result of
// the fibonacci function
int result = pthread_create(&thread, NULL, fibonacci_offshored, &value);
// error checking is important! try to crash gracefully at the very least
if (result != 0)
{
perror("pthread_create");
return 1;
}
if (pthread_join(thread, NULL)
{
perror("pthread_join");
return 1;
}
// now, value contains the output of the fibonacci function
// (note that value is an int, so just %d is fine)
printf("The value is %d\n", value);
return 0;
}
如果你需要从新的不同线程中调用Fibonacci函数(请注意:这不是我建议的,其他人似乎同意我的观点;它只会爆发足够大量的迭代),你首先需要将fibonacci
函数与fibonacci_offshored
函数合并。它会大大增加它,因为处理线程比处理常规函数更重。
void* threaded_fibonacci(void* pointer)
{
int* pointer_to_number = pointer;
int input = *pointer_to_number;
if (input == 0 || input == 1)
{
*pointer_to_number = 1;
return NULL;
}
// we need one argument per thread
int minus_one_number = input - 1;
int minus_two_number = input - 2;
pthread_t minus_one;
pthread_t minus_two;
// don't forget to check! especially that in a recursive function where the
// recursion set actually grows instead of shrinking, you're bound to fail
// at some point
if (pthread_create(&minus_one, NULL, threaded_fibonacci, &minus_one_number) != 0)
{
perror("pthread_create");
*pointer_to_number = 0;
return NULL;
}
if (pthread_create(&minus_two, NULL, threaded_fibonacci, &minus_two_number) != 0)
{
perror("pthread_create");
*pointer_to_number = 0;
return NULL;
}
if (pthread_join(minus_one, NULL) != 0)
{
perror("pthread_join");
*pointer_to_number = 0;
return NULL;
}
if (pthread_join(minus_two, NULL) != 0)
{
perror("pthread_join");
*pointer_to_number = 0;
return NULL;
}
*pointer_to_number = minus_one_number + minus_two_number;
return NULL;
}
现在你有这个庞大的功能,调整你的main
功能会非常简单:只需将fibonacci_offshored
的引用更改为threaded_fibonacci
。
int main()
{
int value = 15;
pthread_t thread;
int result = pthread_create(&thread, NULL, threaded_fibonacci, &value);
if (result != 0)
{
perror("pthread_create");
return 1;
}
pthread_join(thread, NULL);
printf("The value is %d\n", value);
return 0;
}
你可能已经被告知线程可以加速并行进程,但是在设置线程比运行其内容更昂贵的地方存在限制。 这是这种情况的一个非常好的例子:程序的线程版本运行得比非线程版本慢得多。
出于教育目的,当所需迭代次数为18时,此程序在我的机器上运行线程,并且需要几秒钟才能运行。相比之下,使用迭代实现,我们永远不会用完线程,我们的答案只需几毫秒。它也相当简单。这将是一个很好的例子,说明如何使用更好的算法修复许多问题。
另外,出于好奇,看看它是否会在您的机器上崩溃以及在何处/如何崩溃会很有趣。
<子> 1。通常,在函数返回后,应尽量避免在输入值和值之间更改变量的含义。例如,这里,在输入时,变量是我们想要的迭代次数;在输出上,它是函数的结果。这是两个非常不同的含义,这不是一个好的做法。我不想使用动态分配通过void*
返回值返回值。