首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何估算线程上下文切换开销?

如何估算线程上下文切换开销?
EN

Stack Overflow用户
提问于 2008-11-20 09:21:23
回答 9查看 45.7K关注 0票数 61

我正在尝试用实时截止期来提高线程应用程序的性能。它运行在Windows Mobile上,用C/ C++编写。我怀疑频繁的线程切换可能会导致明显的开销,但既不能证明这一点,也不能证明这一点。众所周知,缺乏证据并不是相反的证明:)。

因此,我的问题是双重的:

  • 如果真的存在,我在哪里可以找到切换线程context?
  • Without花费时间编写测试应用程序的成本的任何实际测量方法,在现有application?
  • Does中估计线程切换开销的方法有哪些有人知道如何找出给定线程的上下文切换(开/关)的数量?
EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2008-11-20 09:37:23

虽然您说您不想编写测试应用程序,但我在ARM9 Linux平台上的前一个测试中这样做是为了找出开销是什么。这只是两个线程,它将boost::thread::yield() (或者,你知道的)和一些变量递增,大约一分钟后(没有其他正在运行的进程,至少没有进程做一些事情),应用程序打印出它每秒可以执行多少次上下文切换。当然,这并不是很准确,但重点是两个线程都将CPU分配给对方,而且速度如此之快,以至于考虑开销已经没有任何意义了。所以,简单地去写一个简单的测试,而不是过多地考虑一个可能不存在的问题。

除此之外,您可以尝试使用性能计数器建议的1800。

哦,我还记得在Windows CE 4.X上运行的一个应用程序,在那里我们也有四个线程,有时会有密集的切换,并且从来没有遇到过性能问题。我们还尝试在完全不使用线程的情况下实现核心线程,但没有看到任何性能改进( GUI只是响应速度慢了很多,但其他一切都是一样的)。也许您可以尝试相同的方法,通过减少上下文切换的数量或完全删除线程(仅用于测试)。

票数 12
EN

Stack Overflow用户

发布于 2008-11-20 10:46:15

我怀疑你在任何现有的平台上都能在网络上找到这样的开销。存在太多不同的平台。开销取决于两个因素:

  • The CPU,因为在不同的CPU类型
  • 系统内核上,必要的操作可能更容易或更难,因为不同的内核必须在每个交换机

上执行不同的操作。

其他因素包括如何进行切换。在以下情况下,可以发生切换

  1. 线程已用完其所有时间段。当一个线程启动时,它可能会运行一段给定的时间,然后它必须将控制权交还给内核,内核将决定谁是下一个。
  2. 线程被抢占。当另一个线程需要CPU时间并且具有更高的优先级时,就会发生这种情况。例如,处理鼠标/键盘输入的线程可以是这样的线程。无论哪个线程现在拥有CPU,当用户输入或单击某些内容时,他不想等到当前线程的时间段完全用完,他希望看到系统立即做出反应。因此,一些系统会使当前线程立即停止,并将控制权返回给其他具有更高休眠时间的线程。

:线程不再需要CPU时间,因为它正在阻塞某些操作或只是调用priority.

  • the ()(或类似)来停止运行。

这3个场景在理论上可能有不同的线程切换时间。例如,我认为最后一个是最慢的,因为调用sleep()意味着CPU被返回给内核,内核需要设置一个唤醒调用,以确保线程在请求休眠的时间后被唤醒,然后它必须将线程从调度进程中删除,一旦线程被唤醒,它必须将线程再次添加到调度进程中。所有这些浸泡都需要一些时间。因此,实际的休眠调用可能比切换到另一个线程所需的时间更长。

我认为如果你想确定,你必须进行基准测试。问题在于,您通常必须将线程置于睡眠状态,或者必须使用互斥来同步它们。休眠或锁定/解锁互斥锁本身就有开销。这意味着您的基准测试也将包括这些开销。如果没有一个强大的分析器,以后就很难说出实际的切换使用了多少CPU时间,以及有多少CPU时间用于睡眠/互斥调用。另一方面,在现实生活场景中,您的线程也将休眠或通过锁进行同步。纯粹测量上下文切换时间的基准是一个综合基准,因为它不会对任何现实生活场景进行建模。如果基准基于现实生活中的场景,那么它们就更加“现实”。如果我的GPU在理论上每秒可以处理20亿个多边形,如果这个结果永远不能在现实生活中的3D应用程序中实现,那么GPU基准测试有什么用呢?知道现实生活中的3D应用程序可以让GPU处理多少个多边形,这不是更有趣吗?

不幸的是,我对Windows编程一无所知。我可以用Java或者C#为Windows写一个应用程序,但是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

超过100,000并不是太糟糕,即使我们有锁定和条件等待。我猜想,如果没有这些东西,一秒钟内至少可能有两倍的线程切换。

票数 29
EN

Stack Overflow用户

发布于 2008-11-20 14:07:08

你不能估计它。你需要测量它。它将根据设备中的处理器而变化。

有两种相当简单的方法来测量上下文切换。一个涉及代码,另一个则不涉及。

首先,代码方式(伪代码):

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的调用,并且不能保证调度程序会立即切换到您的另一个线程(尽管优先级10应该有助于增加切换到其他线程的可能性)。

您可以通过连接到调度器事件来使用CeLog获得更准确的度量,但这远不是一件简单的事情,而且文档也不是很好。如果你真的想走这条路,Sue Loh在上面有几个博客,搜索引擎可以找到。

非代码路线是使用远程内核跟踪器。安装eVC 4.0或平台构建器的评估版本以获取它。它将以图形方式显示内核正在执行的所有操作,您可以使用提供的光标功能直接测量线程上下文切换。同样,我相信Sue也有一篇关于使用Kernel Tracker的博客文章。

综上所述,您会发现CE进程内线程上下文切换非常非常快。进程切换是代价高昂的,因为它需要交换RAM中的活动进程,然后进行迁移。

票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/304752

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档