一位教授曾经在课堂上告诉我们,Windows,Linux,OS X和UNIX在线程上而不是在进程上扩展,因此,即使在单个处理器上,线程也可能会使应用程序受益,因为应用程序将在CPU上获得更多时间。
我试着用我的机器上的以下代码(它只有一个CPU)。
#include #include #include pthread_t xs[10]; void *nop(void *ptr) { unsigned long n = 1UL << 30UL; while(n--); return NULL; } void test_one() { size_t len = (sizeof xs) / (sizeof *xs); while(len--) if(pthread_create(xs+len, NULL, nop, NULL)) exit(EXIT_FAILURE); len = (sizeof xs) / (sizeof *xs); while(len--) if(pthread_join(xs[len], NULL)) exit(EXIT_FAILURE); } void test_two() { size_t len = (sizeof xs) / (sizeof *xs); while(len--) nop(NULL); } int main(int argc, char *argv[]) { test_one(); // test_two(); printf("done\n"); return 0; }
两个testing在速度方面是相同的。
real 0m49.783s user 0m48.023s sys 0m0.224s
real 0m49.792s user 0m49.275s sys 0m0.192s
这个我想,“哇,线程吮吸”。 但是,在四台处理器的大学服务器上重复testing接近四倍的速度。real 0m7.800s user 0m30.170s sys 0m0.006s
real 0m30.190s user 0m30.165s sys 0m0.004s
当我解释我的家用机器的结果时,我可以忽略一些东西吗?
为了理解内部任务/线程…让我们看看这个玩具内核代码…
结构regs {
int eax,ebx,ecx,edx,es,ds,gs,fs,cs,ip,flags;
struct tss * task_sel;
}
结构线程{
结构regs * regs;
int parent_id;
struct thread * next;
}
结构任务{
结构regs * regs;
int * phys_mem_begin;
int * phys_mem_end;
int *文件句柄;
优先
int * num_threads;
诠释量子
整数持续时间
int start_time,end_time;
int parent_id;
结构线程* task_thread;
/ * ... * /
struct task * next;
}
想象一下,内核为结构task (这是一个链表)分配内存,仔细观察quantum领域,即基于priority领域的处理器时间的时间片。 总是会有一个ID 0的任务,它不会睡觉,只是空闲,也许发出nops(没有OPerationS)…调度器围绕广告nauseum旋转,直到无限(即电源被拔下),如果quantum场决定任务运行20ms,设置start_time和end_time + 20ms,当end_time启动时,内核将cpu寄存器的状态保存到regs指针中。 进入链中的下一个任务,将cpu寄存器从指针加载到regs并跳转到指令中,设置量程和持续时间,当持续时间达到零时,继续下一个有效的上下文切换。这是什么使它在一个CPU上同时运行的错觉。
现在看看thread结构,它是task结构中的线程链接列表。 内核为该任务分配线程,为该线程设置CPU状态,并跳转到线程中…现在内核必须管理线程以及任务本身…再次在任务和线程之间的上下文切换…
移动到一个多CPU,内核将被设置为可扩展的,调度程序会做什么,将一个task加载到一个CPU,另一个加载到另一个CPU(双核),并跳转到指令的地方指针指向…现在内核真的在两个CPU上同时运行这两个任务。 扩展到4路,同样的东西,加载到每个CPU上的附加任务,再次扩展,以n路…你得到的漂移。
正如你所看到的那样,线程不会被视为可扩展的概念,因为内核在跟踪cpu运行什么方面非常坦率地工作,而且最重要的是运行哪些线程,从根本上来说解释了为什么我认为线程不完全可扩展…线程消耗了大量的资源…
如果您真的想看看发生了什么,请查看Linux的源代码,特别是在调度程序中。 没有挂,忘记了2.6.x内核版本,看史前版本0.99,调度程序会更容易理解,更容易阅读,当然,它有点旧,但值得一看,这将有助于你理解为什么,也希望我的答案,为什么线程不可扩展……并显示玩具操作系统如何使用基于进程的时间划分。 我努力不去涉及现代CPU的技术方面,可以做更多的事情,正如我所描述的…
希望这可以帮助。
一位教授曾经在课堂上告诉我们,Windows,Linux,OS X和UNIX在线程上而不是在进程上扩展,所以即使是在单个处理器上,线程也可能会对你的应用程序有利,因为你的应用程序在CPU上获得更多的时间。
不必要。 如果你的应用程序是唯一的CPU密集型的东西运行,更多的线程不会神奇地使更多的CPU时间可用 – 所有这些将导致上下文切换浪费更多的CPU时间。
这个我想,“哇,线程吮吸”。 但是,在四台处理器的大学服务器上重复测试接近四倍的速度。
这是因为有四个线程,它可以使用全部四个处理器。
我不确定你在问什么,但这里有一个答案,可能有帮助。
在Linux下,进程和线程本质上是完全一样的。 调度器理解所谓的“任务”,它并不关心它们是否共享地址空间。 共享或不共享的东西真的取决于他们是如何创建的。
是否使用线程或进程是一个关键的设计决策,不应掉以轻心,但调度程序的性能可能不是一个因素(当然,像IPC的要求会大大改变设计)