我正在讨论有关fork()Vs thread()的任务并行化的相对成本。
我们理解Vs Thread之间的基本区别
线:
stream程:
但是我们不同意进程与线程的启动成本。
所以要testing理论我写了下面的代码。 我的问题:这是一个衡量启动成本的有效testing,或者我错过了一些东西。 另外我会对每个testing在不同平台上的performance感兴趣。
#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在进程创build方面做得更差。
但是我期望像系统这样的现代Unix具有相当低的分支成本,并且至less可以与线程相媲美。 在旧的Unix风格的系统上(在fork()被实现为在写页面上使用copy之前),情况会更糟。
无论如何我的计时结果是:
> 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
做一千个孩子会导致fork版本失败。
所以我减less了孩子的数量。 但做一个单一的testing也似乎不公平,所以这里是一个价值的范围。
咕… …我不喜欢你的解决方案,原因很多:
您没有考虑子进程/线程的执行时间。
您应该比较cpu使用情况,而不是裸时间。 这样,你的统计数据将不会依赖于磁盘访问拥塞。
让你的孩子进行一些事情。 请记住,“现代”分支使用写入时复制机制,以避免分配内存到子进程,直到需要。 立即退出太容易了。 这样你可以避免fork的所有缺点。
CPU时间不是唯一的成本你必须考虑。 内存消耗和IPC的慢度都是叉解决方案的缺点。
您可以使用“rusage”而不是“clock”来衡量实际的资源使用情况。
PS我不认为你真的可以测量过程/线程开销写一个简单的测试程序。 有太多的因素,通常,线程和进程之间的选择是由其他原因驱动,而不仅仅是CPU使用。
在Linux下, fork
是对sys_clone
的特殊调用,可以在库中或内核中调用。 克隆有很多开关可以打开和关闭,而且每个开关都会影响启动的开销。
实际的库函数clone
可能比fork
更昂贵,因为它的功能更多,尽管大部分是在子端(堆栈交换和通过指针调用函数)。
微基准测试显示的是线程创建和加入(写这个时没有分支结果)需要几十或几百微秒(假设你的系统有CLOCKS_PER_SEC = 1000000,这可能是因为它是XSI要求)。
既然你说过fork()花费了3倍的线程成本,我们仍然在说十分之一毫秒。 如果在应用程序中这是显而易见的,你可以使用进程/线程池,比如Apache 1.3。 无论如何,我想说启动时间是一个有争议的问题。
线程与进程(在Linux和大多数类Unix上)的重要区别是在进程中你明确地选择要共享的东西,使用IPC,共享内存(SYSV或mmap风格),管道,套接字(你可以发送文件描述符AF_UNIX套接字,这意味着你可以选择分享哪个fd),…在线程中几乎所有东西都默认共享,无论是否需要共享。 事实上,这就是Plan 9的原因之一,而Linux有clone()(和最近的unshare()),所以你可以选择共享。