我正在讨论fork()Vs thread()与任务并行化的相对成本。
我们理解进程与线程之间的基本区别
主题:
过程:
但是我们不同意流程Vs线程的启动成本 因此,为了测试理论,我编写了以下代码。我的问题:这是衡量启动成本的有效测试,还是我遗漏了一些东西。此外,我对每个测试在不同平台上的表现感兴趣。
#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";
}
#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版本失败 所以我减少了孩子的数量。但做一次测试似乎也不公平,所以这里有一系列的价值观。
答案 0 :(得分:10)
您没有考虑子进程/线程的执行时间。
您应该比较cpu-usage而不是经过的时间。这样,您的统计信息将不会依赖于磁盘访问拥塞等。
让您的孩子进程做点什么。请记住,“modern”fork使用copy-on-write机制来避免在需要之前为子进程分配内存。立即退出太容易了。这样就可以避免fork的所有缺点。
CPU时间不是您需要考虑的唯一成本。内存消耗和IPC的缓慢都是fork解决方案的缺点。
您可以使用“rusage”代替“clock”来衡量实际资源使用情况。
P.S。我认为你不能真正衡量编写简单测试程序的进程/线程开销。有太多因素,通常,线程和进程之间的选择是由除了cpu使用之外的其他原因驱动的。
答案 1 :(得分:0)
在Linux下fork
是对sys_clone
的特殊调用,无论是在库内还是在内核中。克隆有许多开关可以打开和关闭,每个开关都会影响启动的成本。
实际的库函数clone
可能比fork
更昂贵,因为它做得更多,尽管大多数是在子端(堆栈交换和通过指针调用函数)。
答案 2 :(得分:0)
微基准显示的是线程创建和连接(当我写这个时没有fork结果)需要几十或几百微秒(假设你的系统有CLOCKS_PER_SEC = 1000000,它可能有,因为它是XSI要求)。
既然你说fork()花费了线程成本的3倍,那么我们仍然在谈论最差的十分之一毫秒。如果在应用程序中这是显而易见的,您可以使用进程/线程池,如Apache 1.3。无论如何,我认为启动时间是一个没有实际意义的点。
线程与进程(在Linux和大多数类Unix上)之间的重要区别在于您使用IPC,共享内存(SYSV或mmap样式),管道,套接字(您可以发送)明确选择要共享的进程AF_UNIX套接字上的文件描述符,意味着您可以选择要共享的fd),...在线程上,默认情况下几乎所有内容都是共享的,无论是否需要共享它。事实上,这就是Plan 9有rfork()和Linux有clone()(最近是unshare())的原因,所以你可以选择分享内容。