【发布时间】:2011-04-25 12:01:07
【问题描述】:
我正在讨论 fork() 与 thread() 用于并行化任务的相对成本。
我们了解进程与线程之间的基本区别
线程:
- 线程间易于通信
- 快速上下文切换。
进程:
- 容错。
- 与父母沟通不是真正的问题(打开管道)
- 难以与其他子进程通信
但我们在进程与线程的启动成本上存在分歧。
因此,为了测试这些理论,我编写了以下代码。我的问题:这是衡量启动成本的有效测试还是我遗漏了什么。此外,我会对每个测试在不同平台上的执行情况感兴趣。
fork.cpp
#include <boost/lexical_cast.hpp>
#include <vector>
#include <unistd.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
extern "C" int threadStart(void* threadData)
{
return 0;
}
int main(int argc,char* argv[])
{
int threadCount = boost::lexical_cast<int>(argv[1]);
std::vector<pid_t> data(threadCount);
clock_t start = clock();
for(int loop=0;loop < threadCount;++loop)
{
data[loop] = fork();
if (data[looo] == -1)
{
std::cout << "Abort\n";
exit(1);
}
if (data[loop] == 0)
{
exit(threadStart(NULL));
}
}
clock_t middle = clock();
for(int loop=0;loop < threadCount;++loop)
{
int result;
waitpid(data[loop], &result, 0);
}
clock_t end = clock();
std::cout << threadCount << "\t" << middle - start << "\t" << end - middle << "\t"<< end - start << "\n";
}
线程.cpp
#include <boost/lexical_cast.hpp>
#include <vector>
#include <iostream>
#include <pthread.h>
#include <time.h>
extern "C" void* threadStart(void* threadData)
{
return NULL;
}
int main(int argc,char* argv[])
{
int threadCount = boost::lexical_cast<int>(argv[1]);
std::vector<pthread_t> data(threadCount);
clock_t start = clock();
for(int loop=0;loop < threadCount;++loop)
{
if (pthread_create(&data[loop], NULL, threadStart, NULL) != 0)
{
std::cout << "Abort\n";
exit(1);
}
}
clock_t middle = clock();
for(int loop=0;loop < threadCount;++loop)
{
void* result;
pthread_join(data[loop], &result);
}
clock_t end = clock();
std::cout << threadCount << "\t" << middle - start << "\t" << end - middle << "\t"<< end - start << "\n";
}
我希望 Windows 在进程创建方面做得更差。
但我希望现代类 Unix 系统的分叉成本相当低,并且至少可以与线程相媲美。在较旧的 Unix 风格系统上(在 fork() 被实现为在写入页面上使用复制之前),情况会更糟。
反正我的计时结果是:
> uname -a
Darwin Alpha.local 10.4.0 Darwin Kernel Version 10.4.0: Fri Apr 23 18:28:53 PDT 2010; root:xnu-1504.7.4~1/RELEASE_I386 i386
> gcc --version | grep GCC
i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5659)
> g++ thread.cpp -o thread -I~/include
> g++ fork.cpp -o fork -I~/include
> foreach a ( 1 2 3 4 5 6 7 8 9 10 12 15 20 30 40 50 60 70 80 90 100 )
foreach? ./thread ${a} >> A
foreach? end
> foreach a ( 1 2 3 4 5 6 7 8 9 10 12 15 20 30 40 50 60 70 80 90 100 )
foreach? ./fork ${a} >> A
foreach? end
vi A
Thread: Fork:
C Start Wait Total C Start Wait Total
==============================================================
1 26 145 171 1 160 37 197
2 44 198 242 2 290 37 327
3 62 234 296 3 413 41 454
4 77 275 352 4 499 59 558
5 91 107 10808 5 599 57 656
6 99 332 431 6 665 52 717
7 130 388 518 7 741 69 810
8 204 468 672 8 833 56 889
9 164 469 633 9 1067 76 1143
10 165 450 615 10 1147 64 1211
12 343 585 928 12 1213 71 1284
15 232 647 879 15 1360 203 1563
20 319 921 1240 20 2161 96 2257
30 461 1243 1704 30 3005 129 3134
40 559 1487 2046 40 4466 166 4632
50 686 1912 2598 50 4591 292 4883
60 827 2208 3035 60 5234 317 5551
70 973 2885 3858 70 7003 416 7419
80 3545 2738 6283 80 7735 293 8028
90 1392 3497 4889 90 7869 463 8332
100 3917 4180 8097 100 8974 436 9410
编辑:
做 1000 个孩子导致 fork 版本失败。
所以我减少了孩子的数量。但是做一个单一的测试也似乎不公平,所以这里有一个值范围。
【问题讨论】:
-
看看这个关于线程和进程在性能方面有何不同的答案:stackoverflow.com/questions/3609469/…
-
很难看出这将是一个有意义的测试。你还没有做任何事情来衡量设置 fork 所需的通信的成本。
-
fork 和 thread 之间的选择(几乎?)从不由性能驱动。功能完全不同;司机是“你想做什么”
-
@Hans Passant:公平点。不幸的是,这变得非常特定于任务。有什么建议。我只是想表明启动成本不是选择是否使用线程/进程的因素。
-
@Matt Joiner:这些都是在选择天气来使用线程/进程时要考虑的充分理由。我正在尝试(尚未成功)启动成本不是影响您决定的因素(差异很小,其他因素更重要)。
标签: c++ c multithreading unix fork