如何估计线程上下文切换开销?
我正试图通过实时期限来提高线程应用程序的性能。 它运行在Windows Mobile上,用C / C ++编写。 我怀疑线程切换的高频率可能会导致有形的开销,但是既不能certificate也不能反证。 大家都知道,缺乏证据不是相反的certificate:)。
因此我的问题是双重的:
-
如果存在,我可以在哪里find切换线程上下文成本的实际测量值?
-
不花时间编写testing应用程序,估计现有应用程序中线程切换开销的方法是什么?
-
有没有人知道一个方法来找出一个给定的线程上下文切换的数量(开/关)?
虽然你说你不想写一个testing应用程序,但是我在一个ARM9 Linux平台上进行了以前的testing,以了解这个开销是多less。 这只是两个线程,将boost :: thread :: yield()(或者,你知道)和增加一些variables,一分钟左右(没有其他正在运行的进程,至less没有做什么),应用程序打印每秒可以执行多less个上下文切换。 当然这并不完全正确,但问题是两个线程都让CPU相互影响,而且速度如此之快以至于再没有任何意义了。 所以,简单地说,只要写一个简单的testing,而不是过多考虑一个可能不存在的问题。
除此之外,你可能会尝试像性能指标1800build议。
哦,我还记得在Windows CE 4.X上运行的一个应用程序,在那里我们也有四个线程密集切换的时间,从来没有遇到性能问题。 我们也试图在没有线程的情况下实现核心线程,并没有看到性能的改进(GUI只是响应速度慢得多,但其他的都是一样的)。 也许你可以尝试一下,通过减less上下文切换的数量或完全删除线程(仅用于testing)。
我怀疑你可以在networking上的任何现有平台上find这个开销。 存在太多不同的平台。 开销取决于两个因素:
- 对于不同的CPUtypes,CPU作为必要的操作可能更容易或更难
- 系统内核,因为不同的内核将不得不在每个交换机上执行不同的操作
其他因素包括开关如何发生。 交换机可以在什么时候发生
-
线程已经使用了它的所有时间量。 当一个线程启动时,它可能会运行一定的时间,然后才能将控制权交还给内核来决定下一个谁。
-
线程被抢占。 当另一个线程需要CPU时间并具有更高优先级时,会发生这种情况 例如,处理鼠标/键盘input的线程可能是这样一个线程。 不pipe现在哪个线程拥有 CPU,当用户input某个东西或点击某个东西时,他都不想等到当前线程时间段完全用完,他想立即看到系统反应。 因此,一些系统会使当前线程立即停止,并将控制权返回给其他具有更高优先级的线程。
-
线程不再需要CPU时间,因为它阻塞了一些操作,或者只是叫做sleep()(或类似的)来停止运行。
理论上这三种情况可能有不同的线程切换时间。 例如,我期望最后一个是最慢的,因为调用sleep()意味着CPU返回给内核,内核需要设置一个唤醒调用,以确保线程在唤醒之后被唤醒它要求hibernate的时间量,然后必须将线程从调度过程中移出,一旦线程被唤醒,它就必须再次将线程添加到调度过程中。 所有这些陡峭将需要一些时间。 所以实际的睡眠呼叫可能比切换到另一个线程所用的时间更长。
我想如果你想知道肯定,你必须基准。 问题是,你通常不得不把线程睡觉,或者你必须使用互斥锁来同步它们。 睡眠或locking/解锁互斥锁本身具有开销。 这意味着您的基准testing也将包含这些开销。 没有一个强大的分析器,以后很难说实际交换机使用了多lessCPU时间,以及睡眠/互斥量多less。 另一方面,在现实生活场景中,线程将通过锁来睡眠或同步。 纯粹测量上下文切换时间的基准是一个综合基准,因为它不模拟任何真实生活场景。 基准如果基于现实生活情景,则更为“现实”。 GPU的基准是什么,告诉我我的GPU在理论上每秒可以处理20亿个多边形,如果这个结果在现实生活中的3D应用中是不可能达到的。 知道一个真实生活的3D应用程序可以让GPU处理多less个多边形,是不是会更有趣?
不幸的是我对Windows编程一无所知。 我可以用Java编写Windows应用程序,也可以用C#编写应用程序,但Windows上的C / C ++让我哭了。 我只能为您提供一些POSIX的源代码。
#include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <pthread.h> #include <sys/time.h> #include <unistd.h> uint32_t COUNTER; pthread_mutex_t LOCK; pthread_mutex_t START; pthread_cond_t CONDITION; void * threads ( void * unused ) { // Wait till we may fire away pthread_mutex_lock(&START); pthread_mutex_unlock(&START); pthread_mutex_lock(&LOCK); // If I'm not the first thread, the other thread is already waiting on // the condition, thus Ihave to wake it up first, otherwise we'll deadlock if (COUNTER > 0) { pthread_cond_signal(&CONDITION); } for (;;) { COUNTER++; pthread_cond_wait(&CONDITION, &LOCK); // Always wake up the other thread before processing. The other // thread will not be able to do anything as long as I don't go // back to sleep first. pthread_cond_signal(&CONDITION); } pthread_mutex_unlock(&LOCK); //To unlock } int64_t timeInMS () { struct timeval t; gettimeofday(&t, NULL); return ( (int64_t)t.tv_sec * 1000 + (int64_t)t.tv_usec / 1000 ); } int main ( int argc, char ** argv ) { int64_t start; pthread_t t1; pthread_t t2; int64_t myTime; pthread_mutex_init(&LOCK, NULL); pthread_mutex_init(&START, NULL); pthread_cond_init(&CONDITION, NULL); pthread_mutex_lock(&START); COUNTER = 0; pthread_create(&t1, NULL, threads, NULL); pthread_create(&t2, NULL, threads, NULL); pthread_detach(t1); pthread_detach(t2); // Get start time and fire away myTime = timeInMS(); pthread_mutex_unlock(&START); // Wait for about a second sleep(1); // Stop both threads pthread_mutex_lock(&LOCK); // Find out how much time has really passed. sleep won't guarantee me that // I sleep exactly one second, I might sleep longer since even after being // woken up, it can take some time before I gain back CPU time. Further // some more time might have passed before I obtained the lock! myTime = timeInMS() - myTime; // Correct the number of thread switches accordingly COUNTER = (uint32_t)(((uint64_t)COUNTER * 1000) / myTime); printf("Number of thread switches in about one second was %u\n", COUNTER); return 0; }
产量
Number of thread switches in about one second was 108406
超过10万也不算太坏,即使我们有locking和有条件的等待。 我估计没有这些东西,至less有两倍的线程切换是可能的。
你不能估计它。 你需要测量它。 它会根据设备中的处理器而变化。
有两个相当简单的方法来测量上下文切换。 一个涉及代码,另一个不涉及。
一,代码方式(伪代码):
DWORD tick; main() { HANDLE hThread = CreateThread(..., ThreadProc, CREATE_SUSPENDED, ...); tick = QueryPerformanceCounter(); CeSetThreadPriority(hThread, 10); // real high ResumeThread(hThread); Sleep(10); } ThreadProc() { tick = QueryPerformanceCounter() - tick; RETAILMSG(TRUE, (_T("ET: %i\r\n"), tick)); }
显然,循环和平均做起来会更好。 请记住,这不仅仅是测量上下文切换。 您也正在测量对ResumeThread的调用,并且不保证调度程序将立即切换到您的其他线程(尽pipe10的优先级应该有助于增加它的可能性)。
您可以通过挂钩到日程安排事件中来获得更准确的CeLog测量,但这远非易事,而且logging不完善。 如果你真的想走这条路,苏蕙有几个博客可以findsearch引擎。
非代码路由将使用远程内核跟踪器。 安装eVC 4.0或平台生成器的eval版本来获取它。 它将以graphics方式显示内核正在执行的所有内容,您可以使用提供的游标function直接测量线程上下文切换。 同样,我确定Sue也有使用Kernel Tracker的博客。
所有这一切,你会发现CE内部进程线程上下文切换真的非常快。 这是过程开关昂贵,因为它需要交换RAM中的活动进程,然后进行迁移。
我的50行C ++展示了Linux(QuadCore Q6600)上下文切换时间〜0.9us(2线程0.75us,50线程0.95)。 在这个基准线程中,当线程调用一个时间量时立即调用yield。
我只曾试图估计这一次,那是在486! 结果是,处理器上下文切换需要大约70条指令来完成(注意这是发生在许多OS API调用以及线程切换)。 我们计算出DX3上的每个线程切换(包括OS开销)大约需要30us。 我们每秒钟处理的数千个上下文开关吸收了5-10%的处理器时间。
这怎么会转化为一个多核心,多ghz的现代处理器,我不知道,但我会猜测,除非你完全用线程切换顶部,它可以忽略不计的开销。
请注意,线程创build/删除是比激活/取消激活线程更为昂贵的CPU / OS hogger。 针对线程严重的应用程序的一个很好的策略是使用线程池并根据需要激活/停用。
上下文切换是昂贵的,作为一个经验法则,需要30μs的CPU开销http://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html
上下文切换的问题是他们有一个固定的时间。 GPU在线程之间实现了1个周期的上下文切换。 下面的例子不能在CPU上进行线程化:
double * a; ... for (i = 0; i < 1000; i ++) { a[i] = a[i] + a[i] }
因为它的执行时间远远less于上下文切换成本。 在酷睿i7这个代码需要大约1微秒(取决于编译器)。 所以上下文切换时间很重要,因为它定义了小型作业可以被multithreading化。 我想这也提供了一种有效测量上下文切换的方法。 检查数组(在上面的例子中)要多久才能使线程池中的两个线程开始显示出与单线程相比的真正优势。 这可能很容易成为10万个元素,因此有效的上下文切换时间将在同一个应用程序的20us范围内。
线程池使用的所有封装都必须计入线程切换时间,因为这就是最终的结果。
Atmapuri
我不知道,但你有在Windows Mobile平常的性能计数器? 你可以看看上下文切换/秒。 我不知道是否有专门测量上下文切换时间的东西。