c++ - 如何估计线程上下文切换开销?

标签 c++ c multithreading windows-mobile

我正在尝试通过实时截止日期来提高线程应用程序的性能。它在 Windows Mobile 上运行并用 C/C++ 编写。我怀疑高频率的线程切换可能会导致有形的开销,但无法证明或反驳它。众所周知,缺乏证据并不是相反的证据:)。

因此我的问题是双重的:

  • 如果存在,我在哪里可以找到切换线程上下文成本的任何实际测量值?

  • 在不花时间编写测试应用程序的情况下,有哪些方法可以估算现有应用程序中的线程切换开销?

  • 有谁知道找出给定线程的上下文切换次数(开/关)的方法吗?

最佳答案

我怀疑您是否可以在任何现有平台的网络上找到此开销。存在太多不同的平台。开销取决于两个因素:

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

其他因素包括转换的发生方式。当

  1. 线程已经使用了它所有的时间片。当一个线程启动时,它可能会运行一段给定的时间,然后才必须将控制权交还给决定下一个是谁的内核。

  2. 线程被抢占。当另一个线程需要 CPU 时间并具有更高的优先级时,就会发生这种情况。例如。处理鼠标/键盘输入的线程可能就是这样的线程。不管现在哪个线程拥有 CPU,当用户输入或点击某物时,他不想等到当前线程的时间量完全用完,他想看到系统立即使用react。因此,一些系统会立即停止当前线程并将控制权返回给其他具有更高优先级的线程。

  3. 线程不再需要 CPU 时间,因为它会阻塞某些操作,或者只是调用 sleep()(或类似方法)来停止运行。

这3种场景理论上可能有不同的线程切换时间。例如。我希望最后一个最慢,因为对 sleep() 的调用意味着 CPU 被归还给内核,内核需要设置一个唤醒调用,以确保线程在大约它请求 sleep 的时间,然后必须将线程从调度进程中取出,一旦线程被唤醒,它必须再次将线程添加到调度进程中。所有这些陡峭的斜坡都需要一些时间。所以实际的 sleep-call 可能比切换到另一个线程所需的时间更长。

我认为,如果您想确定,您必须进行基准测试。问题是您通常必须让线程进入休眠状态,或者您必须使用互斥锁同步它们。休眠或锁定/解锁互斥体本身就有开销。这意味着您的基准测试也将包括这些开销。如果没有强大的分析器,以后很难说实际切换使用了多少 CPU 时间以及 sleep /互斥调用使用了多少。另一方面,在现实生活中,你的线程要么休眠,要么通过锁同步。纯粹测量上下文切换时间的基准是综合基准,因为它不模拟任何现实生活场景。如果基准基于现实生活场景,它们会更加“现实”。如果这个结果在现实生活中的 3D 应用程序中永远无法实现,那么 GPU 基准测试告诉我理论上我的 GPU 每秒可以处理 20 亿个多边形有什么用?知道一个现实生活中的 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 并不算太糟糕,即使我们有锁定和条件等待。我猜如果没有这些东西,每秒线程切换的次数至少会增加一倍。

关于c++ - 如何估计线程上下文切换开销?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/304752/

相关文章:

c++ - 提示在 boost 中的行为与在标准库中的行为不同?

c - 为格式说明符向 printf 传递的参数太少——这是未定义的行为吗?

c - 使用未初始化变量的问题

c - 同步线程 - C

java - 我的搜索过程通过多线程变得更慢

java - 在 C++ 中是否有 Java 'volatile' 的等价物?

c++ - 如何将 RAM 数据视为真实文件?

c++ - 尝试理解 C++14 中的 §7.2/6

c++ - MPI 接收和发送 Eigen 压缩稀疏矩阵

C、K&R练习1-6,卡住,困惑